@onekeyfe/hd-core 1.2.0-alpha.112 → 1.2.0-alpha.113

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 (35) hide show
  1. package/__tests__/DeviceCommands.test.ts +2 -2
  2. package/__tests__/device-lifecycle-events.test.ts +1 -1
  3. package/__tests__/device-state-mapper.test.ts +4 -4
  4. package/__tests__/device-utils.test.ts +1 -1
  5. package/__tests__/firmware-update/firmware-update-v3-reconnect.test.ts +116 -0
  6. package/__tests__/firmware-update/firmware-update-v4-install-poll.test.ts +89 -36
  7. package/__tests__/get-device-state.test.ts +8 -8
  8. package/__tests__/protocol-v2-resources.test.ts +0 -20
  9. package/__tests__/protocol-v2.test.ts +240 -219
  10. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  11. package/dist/api/FirmwareUpdateV4.d.ts +3 -0
  12. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  13. package/dist/api/protocol-v2/DeviceInfoGet.d.ts +1 -1
  14. package/dist/api/protocol-v2/DeviceInfoGet.d.ts.map +1 -1
  15. package/dist/index.d.ts +20 -5
  16. package/dist/index.js +230 -140
  17. package/dist/protocols/protocol-v2/features.d.ts +4 -4
  18. package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
  19. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +1 -1
  20. package/dist/types/settings.d.ts +19 -4
  21. package/dist/types/settings.d.ts.map +1 -1
  22. package/dist/utils/patch.d.ts +1 -1
  23. package/dist/utils/patch.d.ts.map +1 -1
  24. package/package.json +4 -4
  25. package/src/api/FirmwareUpdateV3.ts +29 -19
  26. package/src/api/FirmwareUpdateV4.ts +178 -85
  27. package/src/api/protocol-v2/DeviceFactoryInfoSet.ts +1 -1
  28. package/src/api/protocol-v2/DeviceInfoGet.ts +3 -3
  29. package/src/data/messages/messages-protocol-v2.json +14 -31
  30. package/src/device/DeviceStateMapper.ts +15 -15
  31. package/src/deviceProfile/buildDeviceFeatures.ts +3 -3
  32. package/src/protocols/protocol-v2/features.ts +6 -6
  33. package/src/protocols/protocol-v2/resources.ts +49 -10
  34. package/src/protocols/protocol-v2/unlockPolicyRunner.ts +0 -1
  35. package/src/types/settings.ts +19 -4
@@ -193,7 +193,7 @@ describe('DeviceCommands failure mapping', () => {
193
193
  Device_type: 7,
194
194
  hardware_version: '1.0.0',
195
195
  },
196
- main_mcu: {
196
+ fw: {
197
197
  application: {
198
198
  version: '5.0.0',
199
199
  build_id: '20260811',
@@ -218,7 +218,7 @@ describe('DeviceCommands failure mapping', () => {
218
218
  Device_type: 7,
219
219
  hardware_version: '1.0.0',
220
220
  },
221
- main_mcu: {
221
+ fw: {
222
222
  application: {
223
223
  version: '5.0.0',
224
224
  build_id: '20260811',
@@ -353,7 +353,7 @@ describe('public device lifecycle events', () => {
353
353
  message: {
354
354
  protocol_version: 2,
355
355
  hw: { Device_type: protocolV2DeviceType, serial_no: 'SERIAL-001' },
356
- main_mcu: { application: { version: '5.0.0' } },
356
+ fw: { application: { version: '5.0.0' } },
357
357
  },
358
358
  };
359
359
  }
@@ -115,7 +115,7 @@ describe('DeviceStateMapper', () => {
115
115
  {
116
116
  protocol_version: 2,
117
117
  hw: { Device_type: DeviceType.PRO2, serial_no: 'SERIAL-1' },
118
- main_mcu: {
118
+ fw: {
119
119
  application: { version: '5.0.0', build_id: 'p1-build', hash: [0x01, 0x02] },
120
120
  application_data: { version: '5.0.1', build_id: 'p2-build', hash: [0x03, 0x04] },
121
121
  bootloader: { version: '2.0.0' },
@@ -188,7 +188,7 @@ describe('DeviceStateMapper', () => {
188
188
  {
189
189
  protocol_version: 2,
190
190
  hw: { serial_no: 'SERIAL-NORMAL' },
191
- main_mcu: {
191
+ fw: {
192
192
  application: { version: '5.0.0' },
193
193
  bootloader: { version: '2.0.0' },
194
194
  },
@@ -214,7 +214,7 @@ describe('DeviceStateMapper', () => {
214
214
  'bootloader',
215
215
  {
216
216
  hw: { serial_no: 'SERIAL-BOOT' },
217
- main_mcu: { bootloader: { version: '2.0.0' } },
217
+ fw: { bootloader: { version: '2.0.0' } },
218
218
  se1: {},
219
219
  },
220
220
  ],
@@ -222,7 +222,7 @@ describe('DeviceStateMapper', () => {
222
222
  'romloader',
223
223
  {
224
224
  hw: { serial_no: 'SERIAL-ROM' },
225
- main_mcu: {
225
+ fw: {
226
226
  romloader: { version: '1.0.0' },
227
227
  bootloader: { version: '2.0.0' },
228
228
  },
@@ -89,7 +89,7 @@ describe('device feature selectors', () => {
89
89
  const features = buildProtocolV2FeaturesPayload({
90
90
  deviceInfo: {
91
91
  hw: { Device_type: DeviceType.PRO2, serial_no: 'P2-001' },
92
- main_mcu: {
92
+ fw: {
93
93
  application: { version: '5.0.0' },
94
94
  bootloader: { version: '2.0.0' },
95
95
  romloader: { version: '1.0.0' },
@@ -1,6 +1,7 @@
1
1
  import * as hdShared from '@onekeyfe/hd-shared';
2
2
 
3
3
  import FirmwareUpdateV3 from '../../src/api/FirmwareUpdateV3';
4
+ import { DataManager } from '../../src/data-manager';
4
5
  import { DevicePool } from '../../src/device/DevicePool';
5
6
 
6
7
  import type { Device } from '../../src/device/Device';
@@ -112,10 +113,125 @@ describe('FirmwareUpdateV3 reconnect', () => {
112
113
  })
113
114
  ).rejects.toBe(reconnectError);
114
115
 
116
+ expect(commands.typedCall).toHaveBeenCalledWith('GetFeatures', 'Features', {});
115
117
  expect(waitForDeviceReconnect).toHaveBeenCalledWith(3 * 60 * 1000);
116
118
  } finally {
117
119
  jest.clearAllTimers();
118
120
  jest.useRealTimers();
119
121
  }
120
122
  });
123
+
124
+ it('lets Browser WebUSB clean up a timed out install probe before reconnecting', async () => {
125
+ jest.spyOn(hdShared, 'wait').mockResolvedValue(undefined);
126
+ jest.spyOn(DataManager, 'getSettings').mockReturnValue('webusb' as never);
127
+ jest.spyOn(DataManager, 'isBrowserWebUsb').mockReturnValue(true);
128
+ const method = new FirmwareUpdateV3({
129
+ id: 1,
130
+ payload: {
131
+ method: 'firmwareUpdateV3',
132
+ connectId: 'webusb-device',
133
+ },
134
+ });
135
+ const reconnectError = new Error('stop after WebUSB reconnect assertion');
136
+ const waitForDeviceReconnect = jest
137
+ .spyOn(method, 'waitForDeviceReconnect')
138
+ .mockRejectedValue(reconnectError);
139
+ method.isBleReconnect = jest.fn(() => false);
140
+ method.params = {
141
+ platform: 'web',
142
+ } as any;
143
+ const commands = {
144
+ typedCall: jest.fn().mockRejectedValue(new Error('Protocol V1 read timeout after 3000ms')),
145
+ };
146
+ method.device = {
147
+ features: {},
148
+ getCommands: () => commands,
149
+ } as unknown as Device;
150
+ (method as any).createUpdatesFolderIfNotExists = jest.fn().mockResolvedValue(undefined);
151
+ (method as any).startEmmcFirmwareUpdate = jest.fn().mockResolvedValue(undefined);
152
+ method.postTipMessage = jest.fn();
153
+ method.postProcessingMessage = jest.fn();
154
+ method.postProgressMessage = jest.fn();
155
+
156
+ await expect(
157
+ (method as any).executeUpdate({
158
+ resourceBinary: null,
159
+ resourceEntries: [],
160
+ fwSources: [],
161
+ bootloaderSource: null,
162
+ })
163
+ ).rejects.toBe(reconnectError);
164
+
165
+ expect(commands.typedCall).toHaveBeenCalledWith(
166
+ 'GetFeatures',
167
+ 'Features',
168
+ {},
169
+ {
170
+ timeoutMs: 3000,
171
+ }
172
+ );
173
+ expect(waitForDeviceReconnect).toHaveBeenCalledWith(60 * 1000);
174
+ });
175
+
176
+ it('does not treat cached firmware versions as Browser WebUSB install completion', async () => {
177
+ jest.spyOn(hdShared, 'wait').mockResolvedValue(undefined);
178
+ jest.spyOn(DataManager, 'getSettings').mockReturnValue('webusb' as never);
179
+ jest.spyOn(DataManager, 'isBrowserWebUsb').mockReturnValue(true);
180
+ const method = new FirmwareUpdateV3({
181
+ id: 1,
182
+ payload: {
183
+ method: 'firmwareUpdateV3',
184
+ connectId: 'webusb-device',
185
+ },
186
+ });
187
+ method.params = {
188
+ platform: 'web',
189
+ } as any;
190
+ const commands = {
191
+ typedCall: jest
192
+ .fn()
193
+ .mockResolvedValueOnce({
194
+ type: 'Features',
195
+ message: {
196
+ major_version: 0,
197
+ minor_version: 0,
198
+ patch_version: 0,
199
+ },
200
+ })
201
+ .mockResolvedValueOnce({
202
+ type: 'Features',
203
+ message: {
204
+ major_version: 4,
205
+ minor_version: 21,
206
+ patch_version: 0,
207
+ onekey_ble_version: '2.3.7',
208
+ },
209
+ }),
210
+ };
211
+ method.device = {
212
+ features: {
213
+ firmwareVersion: '4.21.0',
214
+ bleVersion: '2.3.7',
215
+ },
216
+ getCommands: () => commands,
217
+ } as unknown as Device;
218
+ (method as any).createUpdatesFolderIfNotExists = jest.fn().mockResolvedValue(undefined);
219
+ (method as any).startEmmcFirmwareUpdate = jest.fn().mockResolvedValue(undefined);
220
+ method.postTipMessage = jest.fn();
221
+ method.postProcessingMessage = jest.fn();
222
+ method.postProgressMessage = jest.fn();
223
+
224
+ const result = await (method as any).executeUpdate({
225
+ resourceBinary: null,
226
+ resourceEntries: [],
227
+ fwSources: [],
228
+ bootloaderSource: null,
229
+ });
230
+
231
+ expect(commands.typedCall).toHaveBeenCalledTimes(2);
232
+ expect(result).toMatchObject({
233
+ firmwareVersion: '4.21.0',
234
+ bleVersion: '2.3.7',
235
+ });
236
+ });
121
237
  });
@@ -1,6 +1,7 @@
1
- import { HardwareErrorCode } from '@onekeyfe/hd-shared';
1
+ import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
2
2
 
3
3
  import FirmwareUpdateV4 from '../../src/api/FirmwareUpdateV4';
4
+ import { DataManager } from '../../src/data-manager';
4
5
 
5
6
  import type { Device } from '../../src/device/Device';
6
7
 
@@ -10,11 +11,19 @@ jest.mock('../../src/data/config', () => ({
10
11
  }));
11
12
 
12
13
  describe('FirmwareUpdateV4 install polling', () => {
14
+ let getSettingsSpy: jest.SpyInstance;
15
+
16
+ beforeEach(() => {
17
+ getSettingsSpy = jest
18
+ .spyOn(DataManager, 'getSettings')
19
+ .mockReturnValue('react-native' as never);
20
+ });
21
+
13
22
  afterEach(() => {
14
23
  jest.restoreAllMocks();
15
24
  });
16
25
 
17
- test('uses Stage, an empty write-only Request, then status polling', async () => {
26
+ test('polls status instead of replaying install after React Native BLE releases', async () => {
18
27
  const method = new FirmwareUpdateV4({
19
28
  id: 1,
20
29
  payload: {
@@ -25,9 +34,8 @@ describe('FirmwareUpdateV4 install polling', () => {
25
34
  const targets = [{ target_id: 4, path: 'vol0:/application_p1.bin' }];
26
35
  const typedCall = jest
27
36
  .fn()
28
- .mockResolvedValueOnce({ type: 'Success', message: {} })
37
+ .mockRejectedValueOnce(new Error('React Native BLE transport released'))
29
38
  .mockResolvedValueOnce({
30
- type: 'DeviceFirmwareUpdateStatus',
31
39
  message: {
32
40
  records: [
33
41
  {
@@ -38,16 +46,15 @@ describe('FirmwareUpdateV4 install polling', () => {
38
46
  ],
39
47
  },
40
48
  });
41
- const call = jest.fn().mockResolvedValue({ type: 'WriteCompleted', message: {} });
42
49
 
43
50
  method.device = {
44
- getCommands: () => ({ typedCall, call }),
51
+ getCommands: () => ({ typedCall }),
45
52
  } as unknown as Device;
46
53
  method.postTipMessage = jest.fn();
47
54
  method.postProgressMessage = jest.fn();
48
55
 
49
56
  const firmwareUpdate = method as unknown as {
50
- protocolV2StartFirmwareUpdate: (params: { targets: typeof targets }) => Promise<void>;
57
+ protocolV2StartFirmwareUpdate: (params: { targets: typeof targets }) => Promise<unknown>;
51
58
  waitForProtocolV2FirmwareUpdateComplete: (value: typeof targets) => Promise<void>;
52
59
  reconnectProtocolV2Device: () => Promise<void>;
53
60
  verifyProtocolV2ReconnectIdentity: () => Promise<Record<string, never>>;
@@ -55,20 +62,19 @@ describe('FirmwareUpdateV4 install polling', () => {
55
62
  firmwareUpdate.reconnectProtocolV2Device = jest.fn().mockResolvedValue(undefined);
56
63
  firmwareUpdate.verifyProtocolV2ReconnectIdentity = jest.fn().mockResolvedValue({});
57
64
 
58
- await firmwareUpdate.protocolV2StartFirmwareUpdate({ targets });
59
- await firmwareUpdate.waitForProtocolV2FirmwareUpdateComplete(targets);
65
+ await expect(
66
+ firmwareUpdate.protocolV2StartFirmwareUpdate({ targets })
67
+ ).resolves.toBeUndefined();
68
+ await expect(
69
+ firmwareUpdate.waitForProtocolV2FirmwareUpdateComplete(targets)
70
+ ).resolves.toBeUndefined();
60
71
 
61
- expect(typedCall.mock.calls[0]).toEqual(['DeviceFirmwareUpdateStage', 'Success', { targets }]);
62
- expect(call).toHaveBeenCalledWith(
63
- 'DeviceFirmwareUpdateRequest',
64
- {},
65
- { returnAfterWrite: true }
66
- );
72
+ expect(typedCall).toHaveBeenCalledTimes(2);
73
+ expect(typedCall.mock.calls[0]?.[0]).toBe('DeviceFirmwareUpdateRequest');
67
74
  expect(typedCall.mock.calls[1]?.[0]).toBe('DeviceFirmwareUpdateStatusGet');
68
- expect(call.mock.invocationCallOrder[0]).toBeLessThan(typedCall.mock.invocationCallOrder[1]);
69
75
  });
70
76
 
71
- test('does not send Request when Stage is rejected', async () => {
77
+ test('does not treat an explicit workflow cancellation as an install reboot', async () => {
72
78
  const method = new FirmwareUpdateV4({
73
79
  id: 1,
74
80
  payload: {
@@ -77,25 +83,34 @@ describe('FirmwareUpdateV4 install polling', () => {
77
83
  },
78
84
  });
79
85
  const targets = [{ target_id: 4, path: 'vol0:/application_p1.bin' }];
80
- const typedCall = jest.fn().mockRejectedValue(new Error('stage rejected'));
81
- const call = jest.fn();
86
+ const typedCall = jest.fn().mockRejectedValue(new Error('React Native BLE transport released'));
87
+ const abortController = new AbortController();
88
+ abortController.abort();
82
89
 
90
+ method.abortSignal = abortController.signal;
83
91
  method.device = {
84
- getCommands: () => ({ typedCall, call }),
92
+ getCommands: () => ({ typedCall }),
85
93
  } as unknown as Device;
86
94
 
87
95
  await expect(
88
96
  (
89
97
  method as unknown as {
90
- protocolV2StartFirmwareUpdate: (params: { targets: typeof targets }) => Promise<void>;
98
+ protocolV2StartFirmwareUpdate: (params: { targets: typeof targets }) => Promise<unknown>;
91
99
  }
92
100
  ).protocolV2StartFirmwareUpdate({ targets })
93
- ).rejects.toThrow('stage rejected');
101
+ ).rejects.toMatchObject({
102
+ errorCode: HardwareErrorCode.CallQueueActionCancelled,
103
+ });
94
104
 
95
- expect(call).not.toHaveBeenCalled();
105
+ expect(typedCall).toHaveBeenCalledTimes(1);
96
106
  });
97
107
 
98
- test('does not hide an explicit workflow cancellation during status polling', async () => {
108
+ test.each([
109
+ HardwareErrorCode.BleConnectedError,
110
+ HardwareErrorCode.BleCharacteristicNotifyError,
111
+ HardwareErrorCode.BleForceCleanRunPromise,
112
+ HardwareErrorCode.BleDeviceDisconnected,
113
+ ])('continues install polling after BLE interruption error %s', async errorCode => {
99
114
  const method = new FirmwareUpdateV4({
100
115
  id: 1,
101
116
  payload: {
@@ -103,23 +118,61 @@ describe('FirmwareUpdateV4 install polling', () => {
103
118
  connectId: 'pro2-ble',
104
119
  },
105
120
  });
106
- const abortController = new AbortController();
107
- abortController.abort();
121
+ const targets = [{ target_id: 4, path: 'vol0:/application_p1.bin' }];
122
+ const typedCall = jest.fn().mockRejectedValue(ERRORS.TypedError(errorCode));
108
123
 
109
- method.abortSignal = abortController.signal;
124
+ method.device = {
125
+ getCommands: () => ({ typedCall }),
126
+ } as unknown as Device;
127
+ method.postTipMessage = jest.fn();
128
+ method.postProgressMessage = jest.fn();
110
129
 
111
130
  await expect(
112
131
  (
113
132
  method as unknown as {
114
- waitForProtocolV2FirmwareUpdateComplete: (
115
- targets: Array<{ target_id: number; path: string }>
116
- ) => Promise<void>;
133
+ protocolV2StartFirmwareUpdate: (params: { targets: typeof targets }) => Promise<unknown>;
117
134
  }
118
- ).waitForProtocolV2FirmwareUpdateComplete([
119
- { target_id: 4, path: 'vol0:/application_p1.bin' },
120
- ])
121
- ).rejects.toMatchObject({
122
- errorCode: HardwareErrorCode.CallQueueActionCancelled,
123
- });
135
+ ).protocolV2StartFirmwareUpdate({ targets })
136
+ ).resolves.toBeUndefined();
137
+
138
+ expect(typedCall).toHaveBeenCalledTimes(1);
124
139
  });
140
+
141
+ test.each([
142
+ ['webusb', 'React Native BLE transport released'],
143
+ ['react-native', 'Unrelated transport failure'],
144
+ ])(
145
+ 'requires both a BLE environment and an explicit transport release signal: %s / %s',
146
+ async (env, message) => {
147
+ getSettingsSpy.mockReturnValue(env);
148
+ const method = new FirmwareUpdateV4({
149
+ id: 1,
150
+ payload: {
151
+ method: 'firmwareUpdateV4',
152
+ connectId: 'pro2-device',
153
+ },
154
+ });
155
+ const targets = [{ target_id: 4, path: 'vol0:/application_p1.bin' }];
156
+ const typedCall = jest.fn().mockRejectedValue(new Error(message));
157
+
158
+ method.device = {
159
+ getCommands: () => ({ typedCall }),
160
+ } as unknown as Device;
161
+ method.postTipMessage = jest.fn();
162
+ method.postProgressMessage = jest.fn();
163
+
164
+ await expect(
165
+ (
166
+ method as unknown as {
167
+ protocolV2StartFirmwareUpdate: (params: {
168
+ targets: typeof targets;
169
+ }) => Promise<unknown>;
170
+ }
171
+ ).protocolV2StartFirmwareUpdate({ targets })
172
+ ).rejects.toThrow(message);
173
+
174
+ expect(method.postTipMessage).not.toHaveBeenCalled();
175
+ expect(method.postProgressMessage).not.toHaveBeenCalled();
176
+ }
177
+ );
125
178
  });
@@ -123,7 +123,7 @@ describe('getDeviceState', () => {
123
123
  message: {
124
124
  protocol_version: 2,
125
125
  hw: { serial_no: 'SERIAL-1' },
126
- main_mcu: { application: { version: '5.0.0' } },
126
+ fw: { application: { version: '5.0.0' } },
127
127
  },
128
128
  };
129
129
  }
@@ -150,7 +150,7 @@ describe('getDeviceState', () => {
150
150
  message: {
151
151
  protocol_version: 2,
152
152
  hw: { serial_no: 'SERIAL-1' },
153
- main_mcu: { application: { version: '5.0.0', hash: 'HASH-1' } },
153
+ fw: { application: { version: '5.0.0', hash: 'HASH-1' } },
154
154
  },
155
155
  };
156
156
  }
@@ -182,7 +182,7 @@ describe('getDeviceState', () => {
182
182
  message: {
183
183
  protocol_version: 2,
184
184
  hw: { serial_no: 'SERIAL-1' },
185
- main_mcu: { application: { version: '5.0.0' } },
185
+ fw: { application: { version: '5.0.0' } },
186
186
  se1: { application: { version: '1.0.0' } },
187
187
  },
188
188
  });
@@ -230,7 +230,7 @@ describe('getDeviceState', () => {
230
230
  raw: {
231
231
  protocolV2DeviceInfo: {
232
232
  hw: { Device_type: protocolV2DeviceType, serial_no: 'SERIAL-1' },
233
- main_mcu:
233
+ fw:
234
234
  mode === 'romloader'
235
235
  ? { romloader: { version: '1.0.0' } }
236
236
  : { bootloader: { version: '1.0.0' } },
@@ -285,7 +285,7 @@ describe('getDeviceState', () => {
285
285
  return {
286
286
  message: {
287
287
  hw: { serial_no: 'SERIAL-1' },
288
- main_mcu: { application: { version: '5.0.0' } },
288
+ fw: { application: { version: '5.0.0' } },
289
289
  },
290
290
  };
291
291
  }
@@ -354,7 +354,7 @@ describe('getDeviceState', () => {
354
354
  return {
355
355
  message: {
356
356
  hw: { serial_no: 'SERIAL-1' },
357
- main_mcu: { application: { version: '5.0.0' } },
357
+ fw: { application: { version: '5.0.0' } },
358
358
  },
359
359
  };
360
360
  }
@@ -397,7 +397,7 @@ describe('getDeviceState', () => {
397
397
  return {
398
398
  message: {
399
399
  hw: { serial_no: 'SERIAL-1' },
400
- main_mcu: { application: { version: '5.0.0' } },
400
+ fw: { application: { version: '5.0.0' } },
401
401
  },
402
402
  };
403
403
  }
@@ -582,7 +582,7 @@ describe('getDeviceState', () => {
582
582
  message: {
583
583
  protocol_version: 1,
584
584
  hw: { serial_no: 'SERIAL-1' },
585
- main_mcu: { bootloader: { version: '1.0.0' } },
585
+ fw: { bootloader: { version: '1.0.0' } },
586
586
  se1: {},
587
587
  },
588
588
  };
@@ -126,26 +126,6 @@ describe('Pro2 resource configuration', () => {
126
126
  expect(parseProtocolV2ResourceManifest(resourceManifest).files).toHaveLength(9);
127
127
  });
128
128
 
129
- test('uses only the resource file list from a manifest', () => {
130
- expect(
131
- parseProtocolV2ResourceManifest({
132
- files: resourceManifest.files,
133
- })
134
- ).toEqual({ files: resourceManifest.files });
135
- });
136
-
137
- test('ignores release metadata and accepts a partial resource file set', () => {
138
- expect(
139
- parseProtocolV2ResourceManifest({
140
- schema: 1,
141
- variant: 'dev_resource',
142
- restore_mode: 'bootloader_update',
143
- trees: [],
144
- files: [resourceManifest.files[0]],
145
- })
146
- ).toEqual({ files: [resourceManifest.files[0]] });
147
- });
148
-
149
129
  test('rejects missing source and malformed manifest paths', () => {
150
130
  expect(() => parseProtocolV2Resources({})).toThrow('source is required');
151
131
  expect(() =>