@onekeyfe/hd-core 1.2.0-alpha.96 → 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 (38) hide show
  1. package/__tests__/firmware-update/device-update-bootloader.test.ts +8 -1
  2. package/__tests__/firmware-update/firmware-prepared-host-digest.test.ts +106 -2
  3. package/__tests__/firmware-update/firmware-prepared-resource-archive.test.ts +150 -0
  4. package/__tests__/firmware-update/firmware-update-bootloader-poll.test.ts +112 -0
  5. package/__tests__/firmware-update/firmware-update-plan.test.ts +101 -0
  6. package/__tests__/firmware-update/firmware-update-prepared-resource-executors.test.ts +326 -0
  7. package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +2 -2
  8. package/__tests__/protocol-v2.test.ts +131 -0
  9. package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
  10. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  11. package/dist/api/FirmwareUpdateV4.d.ts +2 -0
  12. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  13. package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
  14. package/dist/api/firmware/FirmwarePreparedResourceArchive.d.ts +11 -0
  15. package/dist/api/firmware/FirmwarePreparedResourceArchive.d.ts.map +1 -0
  16. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  17. package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -1
  18. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +6 -1
  19. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -1
  20. package/dist/index.d.ts +5 -3
  21. package/dist/index.js +359 -157
  22. package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
  23. package/dist/types/api/firmwareUpdate.d.ts +1 -1
  24. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  25. package/dist/types/api/firmwareUpdatePlan.d.ts +2 -2
  26. package/dist/types/api/firmwareUpdatePlan.d.ts.map +1 -1
  27. package/package.json +4 -4
  28. package/src/api/FirmwareUpdateV2.ts +95 -72
  29. package/src/api/FirmwareUpdateV3.ts +74 -51
  30. package/src/api/FirmwareUpdateV4.ts +72 -7
  31. package/src/api/device/DeviceUpdateBootloader.ts +17 -12
  32. package/src/api/firmware/FirmwarePreparedResourceArchive.ts +207 -0
  33. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +2 -0
  34. package/src/api/firmware/FirmwareUpdatePlan.ts +70 -48
  35. package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +17 -0
  36. package/src/types/api/deviceUpdateBootloader.ts +1 -0
  37. package/src/types/api/firmwareUpdate.ts +2 -1
  38. package/src/types/api/firmwareUpdatePlan.ts +2 -2
package/dist/index.js CHANGED
@@ -595,6 +595,16 @@ const planError = (message) => {
595
595
  firmwareUpdateCode: 'FirmwarePlanInvalid',
596
596
  });
597
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
+ };
598
608
  const FIRMWARE_UPDATE_PLAN_FORCE_TARGETS = new Set([
599
609
  'firmware',
600
610
  'ble',
@@ -705,7 +715,7 @@ const assertFirmwareUpdatePlan = (value) => {
705
715
  if (!artifact) {
706
716
  return planError('Firmware update plan artifact is invalid');
707
717
  }
708
- assertExactKeys(artifact, ['artifactId', 'role', 'target', 'url', 'container'], ['logicalName', 'expectedSize', 'expectedSha256', 'targetVersion']);
718
+ assertExactKeys(artifact, ['artifactId', 'role', 'target', 'url', 'container', 'expectedSize', 'expectedSha256'], ['logicalName', 'targetVersion']);
709
719
  const artifactId = assertBoundedString(artifact.artifactId, 'artifact id', 160);
710
720
  if (artifactIds.has(artifactId)) {
711
721
  return planError('Firmware update plan contains duplicate artifact ids');
@@ -721,13 +731,11 @@ const assertFirmwareUpdatePlan = (value) => {
721
731
  if (artifact.logicalName !== undefined) {
722
732
  assertBoundedString(artifact.logicalName, 'logical name', 256);
723
733
  }
724
- if (artifact.expectedSize !== undefined &&
725
- (!Number.isSafeInteger(artifact.expectedSize) || artifact.expectedSize <= 0)) {
734
+ if (!Number.isSafeInteger(artifact.expectedSize) || artifact.expectedSize <= 0) {
726
735
  return planError('Firmware update plan artifact size is invalid');
727
736
  }
728
- if (artifact.expectedSha256 !== undefined &&
729
- (typeof artifact.expectedSha256 !== 'string' ||
730
- !/^[a-f0-9]{64}$/u.test(artifact.expectedSha256))) {
737
+ if (typeof artifact.expectedSha256 !== 'string' ||
738
+ !/^[a-f0-9]{64}$/u.test(artifact.expectedSha256)) {
731
739
  return planError('Firmware update plan artifact digest is invalid');
732
740
  }
733
741
  if (artifact.targetVersion !== undefined) {
@@ -836,13 +844,7 @@ const buildProtocolV2Artifacts = (release, { includeComponents = true, component
836
844
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 component ${key} duplicates target ${target}`, { firmwareUpdateCode: 'FirmwarePlanInvalid' });
837
845
  }
838
846
  componentTargetSet.add(target);
839
- const integrity = asIntegrity({
840
- size: component.expectedSize,
841
- sha256: component.fingerprint,
842
- });
843
- if (integrity.expectedSize === undefined || integrity.expectedSha256 === undefined) {
844
- planError(`Protocol V2 component ${key} integrity metadata is invalid`);
845
- }
847
+ const integrity = requireArtifactIntegrity({ size: component.expectedSize, sha256: component.fingerprint }, `Protocol V2 component ${key}`);
846
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) } : {})));
847
849
  targets.push(target);
848
850
  }
@@ -934,13 +936,7 @@ const buildProtocolV2FirmwareUpdatePlan = ({ features, firmwareType, platform, r
934
936
  if (!archive) {
935
937
  return planError('Protocol V2 resource target has no resource archive');
936
938
  }
937
- const integrity = asIntegrity({
938
- size: archive.archiveSize,
939
- sha256: archive.archiveSha256,
940
- });
941
- if (integrity.expectedSize === undefined || integrity.expectedSha256 === undefined) {
942
- planError('Protocol V2 resource archive integrity metadata is invalid');
943
- }
939
+ const integrity = requireArtifactIntegrity({ size: archive.archiveSize, sha256: archive.archiveSha256 }, 'Protocol V2 resource archive');
944
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));
945
941
  protocolV2.targets.push('resource');
946
942
  }
@@ -964,7 +960,7 @@ const buildProtocolV2FirmwareUpdatePlan = ({ features, firmwareType, platform, r
964
960
  });
965
961
  };
966
962
  const buildFirmwareUpdatePlan = ({ features, firmwareType, platform, firmware, ble, bootloader, forceUpdateTargets, }) => {
967
- var _a, _b, _c, _d, _e, _f, _g;
963
+ var _a, _b;
968
964
  const executor = getExecutor(features);
969
965
  let artifacts = [];
970
966
  let targetsToUpdate = [];
@@ -976,7 +972,8 @@ const buildFirmwareUpdatePlan = ({ features, firmwareType, platform, firmware, b
976
972
  ((release === null || release === void 0 ? void 0 : release.status) === 'none' || (release === null || release === void 0 ? void 0 : release.status) === 'unknown') &&
977
973
  !!asRelease(release);
978
974
  const shouldUpdateFirmware = isUpgrade(firmware) || isRecoveryInstall(firmware) || forcedTargets.has('firmware');
979
- const shouldUpdateResource = isUpgrade(firmware) || forcedTargets.has('resource');
975
+ const shouldUpdateResource = !(executor === 'v2' && bootloaderMode) &&
976
+ (isUpgrade(firmware) || forcedTargets.has('resource'));
980
977
  if (executor === 'v4' &&
981
978
  validatedForceTargets.some(target => target === 'ble' || target === 'bootloader')) {
982
979
  planError('Protocol V2 does not support forced BLE or legacy bootloader targets');
@@ -990,12 +987,14 @@ const buildFirmwareUpdatePlan = ({ features, firmwareType, platform, firmware, b
990
987
  }
991
988
  else {
992
989
  if (isUpgrade(bootloader) || forcedTargets.has('bootloader')) {
993
- 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({
994
- size: (_b = asRelease(bootloader)) === null || _b === void 0 ? void 0 : _b.bootloaderExpectedSize,
995
- sha256: (_c = asRelease(bootloader)) === null || _c === void 0 ? void 0 : _c.bootloaderFingerprint,
996
- })), (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)
997
996
  ? {
998
- 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),
999
998
  }
1000
999
  : {})));
1001
1000
  }
@@ -1006,10 +1005,8 @@ const buildFirmwareUpdatePlan = ({ features, firmwareType, platform, firmware, b
1006
1005
  });
1007
1006
  }
1008
1007
  if (shouldUpdateFirmware) {
1009
- artifacts.push(Object.assign(Object.assign({ artifactId: 'firmware', role: 'firmware', target: 'firmware', url: assertArtifactUrl(firmwareRelease.url, 'Firmware release'), container: 'raw' }, asIntegrity({
1010
- size: firmwareRelease.expectedSize,
1011
- sha256: firmwareRelease.fingerprint,
1012
- })), (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)
1013
1010
  ? { targetVersion: asVersion(firmwareRelease.version) }
1014
1011
  : {})));
1015
1012
  }
@@ -1020,23 +1017,25 @@ const buildFirmwareUpdatePlan = ({ features, firmwareType, platform, firmware, b
1020
1017
  platform,
1021
1018
  });
1022
1019
  if (resourceUrl) {
1023
- artifacts.push(Object.assign({ artifactId: 'resource', role: 'resource', target: 'resource', url: assertArtifactUrl(resourceUrl, 'Firmware resource release'), container: 'zip' }, asIntegrity({
1020
+ const integrity = requireArtifactIntegrity({
1024
1021
  size: resourceUrl === asString(firmwareRelease.fullResource)
1025
1022
  ? firmwareRelease.fullResourceExpectedSize
1026
1023
  : firmwareRelease.resourceExpectedSize,
1027
1024
  sha256: resourceUrl === asString(firmwareRelease.fullResource)
1028
1025
  ? firmwareRelease.fullResourceFingerprint
1029
1026
  : firmwareRelease.resourceFingerprint,
1030
- })));
1027
+ }, 'Legacy resource archive');
1028
+ artifacts.push(Object.assign({ artifactId: 'resource', role: 'resource', target: 'resource', url: assertArtifactUrl(resourceUrl, 'Firmware resource release'), container: 'zip' }, integrity));
1031
1029
  }
1032
1030
  }
1033
1031
  }
1034
1032
  if (isUpgrade(ble) || isRecoveryInstall(ble) || forcedTargets.has('ble')) {
1035
1033
  const bleRelease = asRelease(ble);
1036
- 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({
1037
1035
  size: bleRelease === null || bleRelease === void 0 ? void 0 : bleRelease.expectedSize,
1038
- 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,
1039
- })), (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)
1040
1039
  ? { targetVersion: asVersion(bleRelease === null || bleRelease === void 0 ? void 0 : bleRelease.version) }
1041
1040
  : {})));
1042
1041
  }
@@ -1275,6 +1274,14 @@ const validateFirmwareUpdatePreparedPlan = (value) => {
1275
1274
  }
1276
1275
  return preparedPlan;
1277
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
+ };
1278
1285
  const degradedPlanIdentityPins = new Map();
1279
1286
  const DEGRADED_PLAN_IDENTITY_PIN_LIMIT = 32;
1280
1287
  const pinDegradedPlanIdentity = (leaseRef, deviceIdentity) => {
@@ -49404,6 +49411,8 @@ class FirmwareUpdateBaseMethod extends BaseMethod {
49404
49411
  !this.payload.skipWebDevicePrompt &&
49405
49412
  !hasPromptedWebDevice &&
49406
49413
  !isPromptingWebDevice) {
49414
+ clearInterval(intervalTimer);
49415
+ clearTimeout(timeoutTimer);
49407
49416
  isPromptingWebDevice = true;
49408
49417
  try {
49409
49418
  this.postTipMessage(exports.FirmwareUpdateTipMessage.SelectDeviceInBootloaderForWebDevice);
@@ -49783,21 +49792,27 @@ class DeviceUpdateBootloader extends FirmwareUpdateBaseMethod {
49783
49792
  const { features } = device;
49784
49793
  const payload = this.payload;
49785
49794
  const hasPreparedPlan = payload.preparedPlan !== undefined;
49786
- const hasPreparedArtifact = payload.artifact !== undefined;
49787
- if (hasPreparedPlan !== hasPreparedArtifact) {
49788
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallMethodInvalidParameter, 'Prepared bootloader plans require exactly one prepared artifact');
49795
+ if (hasPreparedPlan && payload.binary !== undefined) {
49796
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallMethodInvalidParameter, 'Prepared bootloader plans cannot be combined with a legacy binary');
49789
49797
  }
49790
- if (payload.binary !== undefined && hasPreparedArtifact) {
49791
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallMethodInvalidParameter, 'Bootloader binary and prepared artifact are mutually exclusive');
49798
+ if (!hasPreparedPlan && payload.artifact !== undefined) {
49799
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallMethodInvalidParameter, 'Bootloader artifacts require a prepared plan');
49792
49800
  }
49793
- const preparedPlan = hasPreparedArtifact
49801
+ const preparedPlan = hasPreparedPlan
49794
49802
  ? validateFirmwareUpdatePreparedPlan(payload.preparedPlan)
49795
49803
  : undefined;
49804
+ const plannedArtifact = preparedPlan
49805
+ ? getFirmwareUpdatePreparedRawArtifact({
49806
+ preparedPlan,
49807
+ target: 'bootloader',
49808
+ role: 'bootloader',
49809
+ }).artifact
49810
+ : undefined;
49796
49811
  const artifactReader = preparedPlan
49797
49812
  ? resolveFirmwareUpdateHostBinding(payload.hostBindingGeneration, preparedPlan.preparedPlanDigest).artifactReader
49798
49813
  : payload.artifactReader;
49799
49814
  const executionParams = Object.assign(Object.assign({}, payload), { artifactReader });
49800
- if (payload.artifact) {
49815
+ if (preparedPlan && plannedArtifact) {
49801
49816
  assertFirmwareUpdatePreparedPlanDeviceIdentity({
49802
49817
  preparedPlan,
49803
49818
  deviceIdentity: getDeviceUUID(features) || undefined,
@@ -49811,12 +49826,12 @@ class DeviceUpdateBootloader extends FirmwareUpdateBaseMethod {
49811
49826
  bindings: [
49812
49827
  {
49813
49828
  target: 'bootloader',
49814
- artifact: payload.artifact,
49829
+ artifact: (_a = payload.artifact) !== null && _a !== void 0 ? _a : plannedArtifact,
49815
49830
  },
49816
49831
  ],
49817
49832
  });
49818
49833
  const source = yield openFirmwareByteSource({
49819
- artifact: payload.artifact,
49834
+ artifact: plannedArtifact,
49820
49835
  reader: executionParams.artifactReader,
49821
49836
  });
49822
49837
  if (!source) {
@@ -49824,13 +49839,11 @@ class DeviceUpdateBootloader extends FirmwareUpdateBaseMethod {
49824
49839
  }
49825
49840
  try {
49826
49841
  const deviceType = device.getCurrentDeviceType();
49827
- const deviceFirmwareType = device.getCurrentFirmwareType();
49828
- const firmwareType = (_a = payload.firmwareType) !== null && _a !== void 0 ? _a : deviceFirmwareType;
49829
49842
  if (DeviceModelToTypes.model_touch.includes(deviceType)) {
49830
49843
  return yield this.updateTouchBootloader({
49831
49844
  device,
49832
49845
  features,
49833
- firmwareType,
49846
+ firmwareType: preparedPlan.firmwareType,
49834
49847
  source,
49835
49848
  });
49836
49849
  }
@@ -50104,6 +50117,119 @@ const normalizeFirmwarePreparationError = (error) => {
50104
50117
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareUpdateDownloadFailed, error instanceof Error ? error.message : String(error));
50105
50118
  };
50106
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
+
50107
50233
  const Log$8 = getLogger(exports.LoggerNames.Method);
50108
50234
  const FIRMWARE_DOWNLOAD_REQUEST_OPTIONS = {
50109
50235
  connectTimeoutMs: 60000,
@@ -50178,7 +50304,7 @@ class FirmwareUpdateV2 extends BaseMethod {
50178
50304
  };
50179
50305
  }
50180
50306
  init() {
50181
- var _a;
50307
+ var _a, _b;
50182
50308
  this.allowDeviceMode = [UI_REQUEST.BOOTLOADER, UI_REQUEST.NOT_INITIALIZE];
50183
50309
  this.requireDeviceMode = [];
50184
50310
  this.useDevicePassphraseState = false;
@@ -50191,14 +50317,12 @@ class FirmwareUpdateV2 extends BaseMethod {
50191
50317
  { name: 'platform', type: 'string', required: true },
50192
50318
  { name: 'firmwareType', type: 'string' },
50193
50319
  ]);
50194
- if ('binary' in payload &&
50195
- payload.binary !== undefined &&
50196
- 'artifact' in payload &&
50197
- payload.artifact !== undefined) {
50198
- 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');
50199
50323
  }
50200
- if (payload.preparedPlan !== undefined && payload.artifact === undefined) {
50201
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallMethodInvalidParameter, 'Prepared firmware plans require a prepared artifact');
50324
+ if (!hasPreparedPlan && payload.artifact !== undefined) {
50325
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallMethodInvalidParameter, 'Firmware artifacts require a prepared plan');
50202
50326
  }
50203
50327
  if (!payload.updateType) {
50204
50328
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallMethodInvalidParameter, 'updateType is required');
@@ -50208,16 +50332,21 @@ class FirmwareUpdateV2 extends BaseMethod {
50208
50332
  forcedUpdateRes: payload.forcedUpdateRes,
50209
50333
  isUpdateBootloader: payload.isUpdateBootloader,
50210
50334
  };
50211
- if ('version' in payload) {
50335
+ if (!hasPreparedPlan && 'version' in payload) {
50212
50336
  this.params = Object.assign(Object.assign({}, this.params), { version: payload.version, firmwareType: payload.firmwareType });
50213
50337
  }
50214
- if ('binary' in payload) {
50338
+ if (!hasPreparedPlan && 'binary' in payload) {
50215
50339
  this.params = Object.assign(Object.assign({}, this.params), { binary: payload.binary });
50216
50340
  }
50217
- if ('artifact' in payload) {
50341
+ if (hasPreparedPlan) {
50218
50342
  const preparedPlan = validateFirmwareUpdatePreparedPlan(payload.preparedPlan);
50219
50343
  const hostBinding = resolveFirmwareUpdateHostBinding(payload.hostBindingGeneration, preparedPlan.preparedPlanDigest);
50220
50344
  const target = payload.isUpdateBootloader ? 'bootloader' : payload.updateType;
50345
+ const plannedArtifact = getFirmwareUpdatePreparedRawArtifact({
50346
+ preparedPlan,
50347
+ target,
50348
+ role: target,
50349
+ }).artifact;
50221
50350
  const resourceBindings = ((_a = payload.resourceEntries) !== null && _a !== void 0 ? _a : []).map((entry) => ({
50222
50351
  target: 'resource',
50223
50352
  entryName: entry.entryName,
@@ -50227,10 +50356,13 @@ class FirmwareUpdateV2 extends BaseMethod {
50227
50356
  preparedPlan,
50228
50357
  executor: 'v2',
50229
50358
  platform: payload.platform,
50230
- scopeTargets: [target, ...(target === 'firmware' ? ['resource'] : [])],
50231
- 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],
50232
50364
  });
50233
- this.params = Object.assign(Object.assign({}, this.params), { 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 });
50234
50366
  }
50235
50367
  }
50236
50368
  _promptDeviceInBootloaderForWebDevice() {
@@ -50380,7 +50512,7 @@ class FirmwareUpdateV2 extends BaseMethod {
50380
50512
  }
50381
50513
  }
50382
50514
  run() {
50383
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
50515
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
50384
50516
  return __awaiter(this, void 0, void 0, function* () {
50385
50517
  const { device, params } = this;
50386
50518
  const { features, commands } = device;
@@ -50468,13 +50600,48 @@ class FirmwareUpdateV2 extends BaseMethod {
50468
50600
  throw normalizeFirmwarePreparationError(err);
50469
50601
  }
50470
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
+ }
50471
50609
  if (!device.isBootloader() && features) {
50472
50610
  const serialNo = device.getCurrentSerialNo();
50473
50611
  if (this.isEnteredManuallyBoot()) {
50474
50612
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareUpdateManuallyEnterBoot);
50475
50613
  }
50476
50614
  yield acquireFirmwareSource();
50477
- 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)) {
50478
50645
  this.postTipMessage('CheckLatestUiResource');
50479
50646
  const resourceUrl = DataManager.getSysResourcesLatestRelease({
50480
50647
  features,
@@ -50483,46 +50650,20 @@ class FirmwareUpdateV2 extends BaseMethod {
50483
50650
  });
50484
50651
  if (resourceUrl) {
50485
50652
  this.postTipMessage('DownloadLatestUiResource');
50486
- if ((_b = params.resourceEntries) === null || _b === void 0 ? void 0 : _b.length) {
50487
- const sources = [];
50488
- try {
50489
- for (const entry of params.resourceEntries) {
50490
- const resourceName = getFirmwareUpdateResourceName(entry.entryName);
50491
- const source = yield openFirmwareByteSource({
50492
- artifact: entry.artifact,
50493
- reader: params.artifactReader,
50494
- });
50495
- if (!source) {
50496
- throw new Error('Firmware resource entry is not prepared');
50497
- }
50498
- sources.push({ entryName: resourceName, source });
50499
- }
50500
- yield updateResourcesFromSources(this.device.getCommands().typedCall.bind(this.device.getCommands()), this.postMessage, device, sources.map(entry => ({
50501
- entryName: entry.entryName,
50502
- source: entry.source,
50503
- })));
50504
- }
50505
- finally {
50506
- yield Promise.all(sources.map(entry => entry.source.close().catch(() => undefined)));
50507
- }
50508
- this.postTipMessage('DownloadLatestUiResourceSuccess');
50509
- }
50510
- else {
50511
- if (params.artifactReader ||
50512
- DataManager.getSettings('firmwareManifestMode') === 'external-only') {
50513
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Firmware resource must be prepared by the external firmware host', {
50514
- firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
50515
- artifactName: 'resource',
50516
- });
50517
- }
50518
- const resourceBinary = (yield getSysResourceBinary(resourceUrl))
50519
- .binary;
50520
- this.postTipMessage('DownloadLatestUiResourceSuccess');
50521
- 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
+ });
50522
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);
50523
50664
  }
50524
50665
  }
50525
- (_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();
50526
50667
  try {
50527
50668
  this.postTipMessage('AutoRebootToBootloader');
50528
50669
  const bootRes = yield commands.typedCall('DeviceBackToBoot', 'Success');
@@ -50535,9 +50676,9 @@ class FirmwareUpdateV2 extends BaseMethod {
50535
50676
  DevicePool.clearDeviceCache(serialNo);
50536
50677
  }
50537
50678
  delete DevicePool.devicesCache[''];
50538
- 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);
50539
50680
  this.checkPromise = null;
50540
- (_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();
50541
50682
  const isTouch = DeviceModelToTypes.model_touch.includes(deviceType);
50542
50683
  yield wait(isTouch ? 3000 : 1500);
50543
50684
  }
@@ -50550,7 +50691,7 @@ class FirmwareUpdateV2 extends BaseMethod {
50550
50691
  }
50551
50692
  }
50552
50693
  const source = yield acquireFirmwareSource();
50553
- (_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();
50554
50695
  yield this.device.acquire();
50555
50696
  const response = yield uploadFirmwareFromSource(params.updateType, this.device.getCommands().typedCall.bind(this.device.getCommands()), this.postMessage, device, source, true, params.isUpdateBootloader);
50556
50697
  if (this.connectId) {
@@ -50575,7 +50716,7 @@ class FirmwareUpdateV3 extends FirmwareUpdateBaseMethod {
50575
50716
  this.artifactSources = [];
50576
50717
  }
50577
50718
  init() {
50578
- var _a, _b, _c, _d, _e, _f;
50719
+ var _a, _b, _c;
50579
50720
  this.allowDeviceMode = [UI_REQUEST.BOOTLOADER, UI_REQUEST.NOT_INITIALIZE];
50580
50721
  this.requireDeviceMode = [];
50581
50722
  this.useDevicePassphraseState = false;
@@ -50593,7 +50734,11 @@ class FirmwareUpdateV3 extends FirmwareUpdateBaseMethod {
50593
50734
  { name: 'firmwareType', type: 'string' },
50594
50735
  { name: 'platform', type: 'string' },
50595
50736
  ]);
50596
- const preparedPlan = payload.artifacts || payload.preparedPlan
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
50597
50742
  ? validateFirmwareUpdatePreparedPlan(payload.preparedPlan)
50598
50743
  : undefined;
50599
50744
  const hasLegacyInputs = [
@@ -50611,42 +50756,53 @@ class FirmwareUpdateV3 extends FirmwareUpdateBaseMethod {
50611
50756
  const artifactReader = preparedPlan
50612
50757
  ? resolveFirmwareUpdateHostBinding(payload.hostBindingGeneration, preparedPlan.preparedPlanDigest).artifactReader
50613
50758
  : payload.artifactReader;
50759
+ const preparedArtifacts = {};
50614
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
+ }));
50615
50781
  assertFirmwareUpdatePreparedPlanBinding({
50616
50782
  preparedPlan,
50617
50783
  executor: 'v3',
50618
50784
  platform: payload.platform,
50619
- scopeTargets: ['bootloader', 'firmware', 'resource', 'ble'],
50785
+ scopeTargets: [
50786
+ 'bootloader',
50787
+ 'firmware',
50788
+ 'ble',
50789
+ ...(resourceBindings.length ? ['resource'] : []),
50790
+ ],
50620
50791
  bindings: [
50621
- ...(((_a = payload.artifacts) === null || _a === void 0 ? void 0 : _a.bootloader)
50622
- ? [
50623
- {
50624
- target: 'bootloader',
50625
- artifact: payload.artifacts.bootloader,
50626
- },
50627
- ]
50628
- : []),
50629
- ...(((_b = payload.artifacts) === null || _b === void 0 ? void 0 : _b.firmware)
50630
- ? [
50631
- {
50632
- target: 'firmware',
50633
- artifact: payload.artifacts.firmware,
50634
- },
50635
- ]
50636
- : []),
50637
- ...(((_c = payload.artifacts) === null || _c === void 0 ? void 0 : _c.ble)
50638
- ? [
50639
- {
50640
- target: 'ble',
50641
- artifact: payload.artifacts.ble,
50642
- },
50643
- ]
50644
- : []),
50645
- ...((_e = (_d = payload.artifacts) === null || _d === void 0 ? void 0 : _d.resourceEntries) !== null && _e !== void 0 ? _e : []).map((entry) => ({
50646
- target: 'resource',
50647
- entryName: entry.entryName,
50648
- artifact: entry.artifact,
50649
- })),
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,
50650
50806
  ],
50651
50807
  });
50652
50808
  }
@@ -50660,10 +50816,10 @@ class FirmwareUpdateV3 extends FirmwareUpdateBaseMethod {
50660
50816
  bootloaderBinary: payload.bootloaderBinary,
50661
50817
  firmwareVersion: payload.firmwareVersion,
50662
50818
  resourceBinary: payload.resourceBinary,
50663
- firmwareType: (_f = preparedPlan === null || preparedPlan === void 0 ? void 0 : preparedPlan.firmwareType) !== null && _f !== void 0 ? _f : payload.firmwareType,
50819
+ firmwareType: (_c = preparedPlan === null || preparedPlan === void 0 ? void 0 : preparedPlan.firmwareType) !== null && _c !== void 0 ? _c : payload.firmwareType,
50664
50820
  platform: payload.platform,
50665
50821
  artifactReader,
50666
- artifacts: payload.artifacts,
50822
+ artifacts: preparedPlan ? preparedArtifacts : undefined,
50667
50823
  };
50668
50824
  }
50669
50825
  run() {
@@ -50769,19 +50925,21 @@ class FirmwareUpdateV3 extends FirmwareUpdateBaseMethod {
50769
50925
  prepareResourceInput(firmwareType) {
50770
50926
  var _a;
50771
50927
  return __awaiter(this, void 0, void 0, function* () {
50772
- const preparedEntries = (_a = this.params.artifacts) === null || _a === void 0 ? void 0 : _a.resourceEntries;
50773
- if ((preparedEntries === null || preparedEntries === void 0 ? void 0 : preparedEntries.length) && this.params.resourceBinary) {
50774
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Firmware resource input must not contain both archive binary and prepared entries');
50775
- }
50776
- 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
+ });
50777
50933
  const resourceEntries = [];
50778
- for (const entry of preparedEntries) {
50779
- const resourceName = getFirmwareUpdateResourceName(entry.entryName);
50780
- const source = yield this.openArtifactSource(undefined, entry.artifact);
50934
+ for (const entry of verifiedEntries) {
50935
+ const source = yield this.openArtifactSource(entry.binary, undefined);
50781
50936
  if (!source) {
50782
- 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
+ });
50783
50941
  }
50784
- resourceEntries.push({ entryName: resourceName, source });
50942
+ resourceEntries.push({ entryName: entry.entryName, source });
50785
50943
  }
50786
50944
  return {
50787
50945
  resourceBinary: null,
@@ -51476,12 +51634,12 @@ const PROTOCOL_V2_REMOTE_COMPONENT_TARGETS = {
51476
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}`));
51477
51635
  const PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH = 'vol0:/loaders/bootloader/boot_resource.okpkg';
51478
51636
  const PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_STAGING_PATH = `${PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH}.staging`;
51479
- const resolveProtocolV2ResourceWritePath = (devicePath) => {
51480
- const normalizedPath = devicePath.replace(/^vol0:(?!\/)/i, 'vol0:/').toLowerCase();
51481
- return normalizedPath === PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH
51482
- ? PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_STAGING_PATH
51483
- : devicePath;
51484
- };
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;
51485
51643
  const PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID = new Map([
51486
51644
  [ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_BOOTLOADER, 'boot'],
51487
51645
  [ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P1, 'app_v1'],
@@ -51653,6 +51811,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51653
51811
  super(...arguments);
51654
51812
  this.protocolV2PreparedSources = [];
51655
51813
  this.protocolV2ExecutionInLoader = false;
51814
+ this.protocolV2BootResourceStagingSafe = false;
51656
51815
  this.protocolV2CompletedTargetVersions = new Map();
51657
51816
  this.protocolV2FinalStatusVerified = false;
51658
51817
  }
@@ -52808,6 +52967,15 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52808
52967
  }
52809
52968
  buildProtocolV2ExecutionPhases({ installSources, resourceSources, }) {
52810
52969
  const phases = [];
52970
+ const bootResourceSources = resourceSources.filter(source => isProtocolV2BootResourcePackagePath(source.devicePath));
52971
+ const remainingResourceSources = resourceSources.filter(source => !isProtocolV2BootResourcePackagePath(source.devicePath));
52972
+ if (bootResourceSources.length > 0) {
52973
+ phases.push({
52974
+ kind: 'resource-sync',
52975
+ installSources: [],
52976
+ resourceSources: bootResourceSources,
52977
+ });
52978
+ }
52811
52979
  const bootloaderSources = installSources.filter(source => source.kind === 'bootloader');
52812
52980
  if (bootloaderSources.length > 0) {
52813
52981
  phases.push({
@@ -52820,11 +52988,11 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52820
52988
  resourceSources: [],
52821
52989
  });
52822
52990
  }
52823
- if (resourceSources.length > 0) {
52991
+ if (remainingResourceSources.length > 0) {
52824
52992
  phases.push({
52825
52993
  kind: 'resource-sync',
52826
52994
  installSources: [],
52827
- resourceSources,
52995
+ resourceSources: remainingResourceSources,
52828
52996
  });
52829
52997
  }
52830
52998
  const componentSources = installSources.filter(source => source.kind !== 'bootloader');
@@ -52844,6 +53012,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52844
53012
  }
52845
53013
  executeProtocolV2Phases({ installSources, resourceSources, }) {
52846
53014
  return __awaiter(this, void 0, void 0, function* () {
53015
+ this.protocolV2BootResourceStagingSafe = false;
52847
53016
  const phases = this.buildProtocolV2ExecutionPhases({
52848
53017
  installSources,
52849
53018
  resourceSources,
@@ -52854,10 +53023,14 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52854
53023
  }
52855
53024
  if (phase.kind === 'resource-sync') {
52856
53025
  yield this.enterProtocolV2BootloaderMode();
53026
+ if (!phase.resourceSources.some(source => isProtocolV2BootResourcePackagePath(source.devicePath))) {
53027
+ yield this.ensureProtocolV2BootResourceStagingIsEmpty();
53028
+ }
52857
53029
  yield this.executeProtocolV2TransferPhase(phase);
52858
53030
  }
52859
53031
  else if (phase.kind === 'bootloader-install' || phase.kind === 'component-install') {
52860
53032
  yield this.enterProtocolV2BootloaderMode();
53033
+ yield this.ensureProtocolV2BootResourceStagingIsEmpty();
52861
53034
  yield this.executeProtocolV2TransferPhase(phase);
52862
53035
  yield this.exitProtocolV2BootloaderToNormal();
52863
53036
  }
@@ -52869,6 +53042,31 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52869
53042
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 execution has no final verification phase');
52870
53043
  });
52871
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
+ }
52872
53070
  executeProtocolV2SourceUpdate({ installSources, resourceSources, }) {
52873
53071
  return __awaiter(this, void 0, void 0, function* () {
52874
53072
  return this.executeProtocolV2Phases({
@@ -52907,7 +53105,8 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52907
53105
  let totalSize = installSources.reduce((total, item) => total + item.source.size, 0);
52908
53106
  const resourcesToSync = [];
52909
53107
  for (const resource of resourceSources) {
52910
- if (yield this.isProtocolV2ResourceBundleUpToDate(resource)) {
53108
+ const requiresFreshStaging = isProtocolV2BootResourcePackagePath(resource.devicePath);
53109
+ if (!requiresFreshStaging && (yield this.isProtocolV2ResourceBundleUpToDate(resource))) {
52911
53110
  Log$6.log(`[FirmwareUpdateV4] skip RESC bundle ${resource.name}; already up to date`);
52912
53111
  }
52913
53112
  else {
@@ -52926,6 +53125,9 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52926
53125
  totalSize,
52927
53126
  });
52928
53127
  yield this.verifyProtocolV2StagedFile(writePath, resource.source.size);
53128
+ if (isProtocolV2BootResourcePackagePath(resource.devicePath)) {
53129
+ this.protocolV2BootResourceStagingSafe = true;
53130
+ }
52929
53131
  }
52930
53132
  const stagedInstallTargets = [];
52931
53133
  for (const item of installSources) {