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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/__tests__/check-all-firmware-release-protocol-v2.test.ts +411 -86
  2. package/__tests__/check-firmware-release-protocol-v2.test.ts +199 -0
  3. package/__tests__/firmware-memory-host.test.ts +126 -0
  4. package/__tests__/firmware-update/device-update-bootloader.test.ts +14 -2
  5. package/__tests__/firmware-update/firmware-host-binding.test.ts +9 -0
  6. package/__tests__/firmware-update/firmware-prepared-host-digest.test.ts +573 -0
  7. package/__tests__/firmware-update/firmware-prepared-resource-archive.test.ts +150 -0
  8. package/__tests__/firmware-update/firmware-update-bootloader-poll.test.ts +65 -2
  9. package/__tests__/firmware-update/firmware-update-plan-bootloader-identity.test.ts +80 -0
  10. package/__tests__/firmware-update/firmware-update-plan.test.ts +293 -71
  11. package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +53 -1
  12. package/__tests__/firmware-update/firmware-update-prepared-resource-executors.test.ts +326 -0
  13. package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +2 -2
  14. package/__tests__/protocol-v2-firmware-targets.test.ts +16 -0
  15. package/__tests__/protocol-v2-resources.test.ts +148 -232
  16. package/__tests__/protocol-v2.test.ts +1326 -510
  17. package/dist/api/CheckAllFirmwareRelease.d.ts +2 -3
  18. package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
  19. package/dist/api/CheckBLEFirmwareRelease.d.ts +3 -2
  20. package/dist/api/CheckBLEFirmwareRelease.d.ts.map +1 -1
  21. package/dist/api/CheckBootloaderRelease.d.ts +2 -1
  22. package/dist/api/CheckBootloaderRelease.d.ts.map +1 -1
  23. package/dist/api/CheckFirmwareRelease.d.ts +3 -2
  24. package/dist/api/CheckFirmwareRelease.d.ts.map +1 -1
  25. package/dist/api/FirmwareUpdateV2.d.ts +1 -0
  26. package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
  27. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  28. package/dist/api/FirmwareUpdateV4.d.ts +11 -9
  29. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  30. package/dist/api/UploadPortfolio.d.ts.map +1 -1
  31. package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
  32. package/dist/api/firmware/FirmwareHostBinding.d.ts +1 -1
  33. package/dist/api/firmware/FirmwareHostBinding.d.ts.map +1 -1
  34. package/dist/api/firmware/FirmwareMemoryHost.d.ts +22 -0
  35. package/dist/api/firmware/FirmwareMemoryHost.d.ts.map +1 -0
  36. package/dist/api/firmware/FirmwarePreparedResourceArchive.d.ts +11 -0
  37. package/dist/api/firmware/FirmwarePreparedResourceArchive.d.ts.map +1 -0
  38. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  39. package/dist/api/firmware/FirmwareUpdatePlan.d.ts +48 -1
  40. package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -1
  41. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +7 -1
  42. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -1
  43. package/dist/api/firmware/getBinary.d.ts +0 -1
  44. package/dist/api/firmware/getBinary.d.ts.map +1 -1
  45. package/dist/api/firmware/protocolV2Release.d.ts +33 -0
  46. package/dist/api/firmware/protocolV2Release.d.ts.map +1 -0
  47. package/dist/data-manager/DataManager.d.ts +4 -3
  48. package/dist/data-manager/DataManager.d.ts.map +1 -1
  49. package/dist/index.d.ts +108 -114
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +1825 -1006
  52. package/dist/protocols/protocol-v2/resources.d.ts +10 -28
  53. package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
  54. package/dist/types/api/checkAllFirmwareRelease.d.ts +20 -8
  55. package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
  56. package/dist/types/api/checkBLEFirmwareRelease.d.ts +2 -13
  57. package/dist/types/api/checkBLEFirmwareRelease.d.ts.map +1 -1
  58. package/dist/types/api/checkBootloaderRelease.d.ts +2 -6
  59. package/dist/types/api/checkBootloaderRelease.d.ts.map +1 -1
  60. package/dist/types/api/checkFirmwareRelease.d.ts +2 -13
  61. package/dist/types/api/checkFirmwareRelease.d.ts.map +1 -1
  62. package/dist/types/api/checkFirmwareTypeAvailable.d.ts +2 -2
  63. package/dist/types/api/checkFirmwareTypeAvailable.d.ts.map +1 -1
  64. package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
  65. package/dist/types/api/export.d.ts +1 -1
  66. package/dist/types/api/export.d.ts.map +1 -1
  67. package/dist/types/api/firmwareUpdate.d.ts +4 -12
  68. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  69. package/dist/types/api/firmwareUpdatePlan.d.ts +2 -2
  70. package/dist/types/api/firmwareUpdatePlan.d.ts.map +1 -1
  71. package/dist/types/settings.d.ts +35 -36
  72. package/dist/types/settings.d.ts.map +1 -1
  73. package/package.json +4 -4
  74. package/src/api/CheckAllFirmwareRelease.ts +113 -80
  75. package/src/api/CheckBLEFirmwareRelease.ts +37 -5
  76. package/src/api/CheckBootloaderRelease.ts +35 -3
  77. package/src/api/CheckFirmwareRelease.ts +35 -7
  78. package/src/api/FirmwareUpdateV2.ts +231 -138
  79. package/src/api/FirmwareUpdateV3.ts +101 -56
  80. package/src/api/FirmwareUpdateV4.ts +936 -408
  81. package/src/api/UploadPortfolio.ts +18 -0
  82. package/src/api/device/DeviceUpdateBootloader.ts +37 -11
  83. package/src/api/firmware/FirmwareHostBinding.ts +26 -3
  84. package/src/api/firmware/FirmwareMemoryHost.ts +143 -0
  85. package/src/api/firmware/FirmwarePreparedResourceArchive.ts +207 -0
  86. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +122 -73
  87. package/src/api/firmware/FirmwareUpdatePlan.ts +295 -122
  88. package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +49 -12
  89. package/src/api/firmware/protocolV2Release.ts +168 -0
  90. package/src/data-manager/DataManager.ts +35 -19
  91. package/src/index.ts +10 -0
  92. package/src/protocols/protocol-v2/resources.ts +157 -341
  93. package/src/types/api/checkAllFirmwareRelease.ts +27 -12
  94. package/src/types/api/checkBLEFirmwareRelease.ts +4 -13
  95. package/src/types/api/checkBootloaderRelease.ts +2 -6
  96. package/src/types/api/checkFirmwareRelease.ts +2 -13
  97. package/src/types/api/checkFirmwareTypeAvailable.ts +2 -2
  98. package/src/types/api/deviceUpdateBootloader.ts +1 -0
  99. package/src/types/api/export.ts +6 -1
  100. package/src/types/api/firmwareUpdate.ts +12 -17
  101. package/src/types/api/firmwareUpdatePlan.ts +2 -2
  102. package/src/types/settings.ts +35 -61
  103. package/src/utils/deviceFeaturesUtils.ts +2 -2
@@ -0,0 +1,326 @@
1
+ import { sha256 } from '@noble/hashes/sha256';
2
+ import { bytesToHex } from '@noble/hashes/utils';
3
+ import { EDeviceType, EFirmwareType } from '@onekeyfe/hd-shared';
4
+ import JSZip from 'jszip';
5
+
6
+ import FirmwareUpdateV2 from '../../src/api/FirmwareUpdateV2';
7
+ import FirmwareUpdateV3 from '../../src/api/FirmwareUpdateV3';
8
+ import { DataManager } from '../../src/data-manager';
9
+ import {
10
+ registerFirmwareUpdateHostBinding,
11
+ unregisterFirmwareUpdateHostBinding,
12
+ } from '../../src/api/firmware/FirmwareHostBinding';
13
+ import { prepareFirmwareUpdatePlan } from '../../src/api/firmware/FirmwareUpdatePreparedPlan';
14
+ import { digestFirmwareUpdateContract } from '../../src/api/firmware/FirmwareUpdatePlan';
15
+ import {
16
+ updateResourcesFromSources,
17
+ uploadFirmwareFromSource,
18
+ } from '../../src/api/firmware/uploadFirmware';
19
+ import * as utils from '../../src/utils';
20
+
21
+ import type {
22
+ FirmwareArtifactReader,
23
+ FirmwareArtifactReference,
24
+ } from '../../src/types/api/firmwareUpdate';
25
+ import type { FirmwareUpdatePlan } from '../../src/types/api/firmwareUpdatePlan';
26
+
27
+ jest.mock('../../src/data/config', () => ({
28
+ getSDKVersion: jest.fn(() => '1.0.0'),
29
+ DEFAULT_DOMAIN: 'https://jssdk.onekey.so/1.0.0/',
30
+ }));
31
+
32
+ jest.mock('../../src/api/firmware/getBinary', () => ({
33
+ getBinary: jest.fn(),
34
+ getInfo: jest.fn(),
35
+ getSysResourceBinary: jest.fn(),
36
+ }));
37
+
38
+ jest.mock('../../src/api/firmware/uploadFirmware', () => ({
39
+ updateResources: jest.fn(),
40
+ updateResourcesFromSources: jest.fn(),
41
+ uploadFirmwareFromSource: jest.fn(),
42
+ }));
43
+
44
+ jest.mock('../../src/device/DevicePool', () => ({
45
+ DevicePool: {
46
+ clearDeviceCache: jest.fn(),
47
+ devicesCache: {},
48
+ },
49
+ }));
50
+
51
+ const mockUpdateResourcesFromSources = updateResourcesFromSources as jest.MockedFunction<
52
+ typeof updateResourcesFromSources
53
+ >;
54
+ const mockUploadFirmwareFromSource = uploadFirmwareFromSource as jest.MockedFunction<
55
+ typeof uploadFirmwareFromSource
56
+ >;
57
+
58
+ const createReference = (artifactRef: string, binary: ArrayBuffer): FirmwareArtifactReference => ({
59
+ artifactRef,
60
+ size: binary.byteLength,
61
+ sha256: bytesToHex(sha256(new Uint8Array(binary))),
62
+ });
63
+
64
+ const createReader = (artifacts: Map<string, ArrayBuffer>): FirmwareArtifactReader => {
65
+ const opened = new Map<string, ArrayBuffer>();
66
+ let sequence = 0;
67
+ return {
68
+ open({ artifactRef }) {
69
+ const binary = artifacts.get(artifactRef);
70
+ if (!binary) throw new Error(`missing artifact: ${artifactRef}`);
71
+ sequence += 1;
72
+ const readerId = `reader-${sequence}`;
73
+ opened.set(readerId, binary);
74
+ return Promise.resolve({ readerId, size: binary.byteLength });
75
+ },
76
+ read({ readerId, offset, length }) {
77
+ const binary = opened.get(readerId);
78
+ if (!binary) throw new Error(`missing reader: ${readerId}`);
79
+ const data = binary.slice(offset, offset + length);
80
+ return Promise.resolve({
81
+ data,
82
+ bytesRead: data.byteLength,
83
+ eof: offset + length === binary.byteLength,
84
+ });
85
+ },
86
+ close({ readerId }) {
87
+ opened.delete(readerId);
88
+ return Promise.resolve();
89
+ },
90
+ };
91
+ };
92
+
93
+ const createPreparedPlan = async (
94
+ executor: 'v2' | 'v3',
95
+ componentTarget: 'firmware' | 'ble' = 'firmware'
96
+ ) => {
97
+ const firmwareBinary = Uint8Array.from([5, 6, 7, 8]).buffer;
98
+ const resourceBinary = Uint8Array.from([1, 2, 3, 4]).buffer;
99
+ const zip = new JSZip();
100
+ zip.file('images/logo.bin', resourceBinary);
101
+ const archiveBinary = await zip.generateAsync({ type: 'arraybuffer' });
102
+ const firmwareArtifact = createReference('firmware-artifact', firmwareBinary);
103
+ const archiveArtifact = createReference('resource-archive', archiveBinary);
104
+ const resourceArtifact = createReference('resource-entry', resourceBinary);
105
+ const planWithoutDigest: Omit<FirmwareUpdatePlan, 'planDigest'> = {
106
+ schemaVersion: 2,
107
+ executor,
108
+ deviceIdentity: executor === 'v2' ? 'touch-device' : 'pro-device',
109
+ deviceModel: String(executor === 'v2' ? EDeviceType.Touch : EDeviceType.Pro),
110
+ firmwareType: EFirmwareType.Universal,
111
+ platform: 'desktop',
112
+ artifacts: [
113
+ {
114
+ artifactId: componentTarget,
115
+ role: componentTarget,
116
+ target: componentTarget,
117
+ url: `https://firmware.onekey.so/${componentTarget}.bin`,
118
+ container: 'raw',
119
+ expectedSize: firmwareArtifact.size,
120
+ expectedSha256: firmwareArtifact.sha256,
121
+ },
122
+ {
123
+ artifactId: 'resource',
124
+ role: 'resource',
125
+ target: 'resource',
126
+ url: 'https://firmware.onekey.so/resource.zip',
127
+ container: 'zip',
128
+ expectedSize: archiveArtifact.size,
129
+ expectedSha256: archiveArtifact.sha256,
130
+ },
131
+ ],
132
+ targetsToUpdate: [componentTarget, 'resource'],
133
+ };
134
+ const plan: FirmwareUpdatePlan = {
135
+ ...planWithoutDigest,
136
+ planDigest: digestFirmwareUpdateContract(planWithoutDigest),
137
+ };
138
+ const preparedPlan = prepareFirmwareUpdatePlan({
139
+ plan,
140
+ leaseRef: `${executor}-resource-executor`,
141
+ artifacts: [
142
+ { artifactId: componentTarget, artifact: firmwareArtifact },
143
+ {
144
+ artifactId: 'resource',
145
+ artifact: archiveArtifact,
146
+ materializedEntries: [{ entryName: 'images/logo.bin', artifact: resourceArtifact }],
147
+ },
148
+ ],
149
+ });
150
+ return {
151
+ firmwareArtifact,
152
+ firmwareBinary,
153
+ archiveArtifact,
154
+ archiveBinary,
155
+ resourceBinary,
156
+ preparedPlan,
157
+ };
158
+ };
159
+
160
+ describe('prepared resource executors', () => {
161
+ afterEach(() => {
162
+ unregisterFirmwareUpdateHostBinding();
163
+ jest.restoreAllMocks();
164
+ jest.clearAllMocks();
165
+ });
166
+
167
+ test('FirmwareUpdateV3 derives resource bytes from the approved ZIP without duplicate entries', async () => {
168
+ const prepared = await createPreparedPlan('v3');
169
+ const artifactReader = createReader(
170
+ new Map([
171
+ [prepared.firmwareArtifact.artifactRef, prepared.firmwareBinary],
172
+ [prepared.archiveArtifact.artifactRef, prepared.archiveBinary],
173
+ ])
174
+ );
175
+ const hostBindingGeneration = registerFirmwareUpdateHostBinding({
176
+ preparedPlanDigest: prepared.preparedPlan.preparedPlanDigest,
177
+ artifactReader,
178
+ });
179
+ const method = new FirmwareUpdateV3({
180
+ id: 1,
181
+ payload: {
182
+ method: 'firmwareUpdateV3',
183
+ platform: 'desktop',
184
+ preparedPlan: prepared.preparedPlan,
185
+ hostBindingGeneration,
186
+ artifacts: { firmware: prepared.firmwareArtifact },
187
+ },
188
+ });
189
+ method.init();
190
+
191
+ const result = await (method as any).prepareResourceInput(EFirmwareType.Universal);
192
+ expect(result.resourceEntries).toHaveLength(1);
193
+ expect(result.resourceEntries[0].entryName).toBe('logo.bin');
194
+ await expect(result.resourceEntries[0].source.readAt(0, 4)).resolves.toEqual(
195
+ prepared.resourceBinary
196
+ );
197
+ await (method as any).closeArtifactSources();
198
+ });
199
+
200
+ test('FirmwareUpdateV2 executes prepared resources without a live release record', async () => {
201
+ const prepared = await createPreparedPlan('v2');
202
+ const artifactReader = createReader(
203
+ new Map([
204
+ [prepared.firmwareArtifact.artifactRef, prepared.firmwareBinary],
205
+ [prepared.archiveArtifact.artifactRef, prepared.archiveBinary],
206
+ ])
207
+ );
208
+ const hostBindingGeneration = registerFirmwareUpdateHostBinding({
209
+ preparedPlanDigest: prepared.preparedPlan.preparedPlanDigest,
210
+ artifactReader,
211
+ });
212
+ const method = new FirmwareUpdateV2({
213
+ id: 1,
214
+ payload: {
215
+ method: 'firmwareUpdateV2',
216
+ connectId: 'touch-connect-id',
217
+ platform: 'desktop',
218
+ updateType: 'firmware',
219
+ artifact: prepared.firmwareArtifact,
220
+ preparedPlan: prepared.preparedPlan,
221
+ hostBindingGeneration,
222
+ },
223
+ });
224
+ method.init();
225
+
226
+ const typedCall = jest.fn().mockResolvedValue({ type: 'Success' });
227
+ const commands = { typedCall, checkDisposed: jest.fn(), disposed: false };
228
+ method.device = {
229
+ features: {
230
+ deviceType: EDeviceType.Touch,
231
+ serialNo: 'touch-device',
232
+ firmwareVersion: '3.2.0',
233
+ bootloaderVersion: '2.8.0',
234
+ },
235
+ commands,
236
+ getCommands: () => commands,
237
+ getCurrentDeviceType: () => EDeviceType.Touch,
238
+ getCurrentFirmwareType: () => EFirmwareType.Universal,
239
+ getCurrentFirmwareVersionString: () => '3.2.0',
240
+ getCurrentBootloaderVersionString: () => '2.8.0',
241
+ getCurrentSerialNo: () => 'touch-device',
242
+ isBootloader: () => false,
243
+ isProtocolV2: () => false,
244
+ acquire: jest.fn().mockResolvedValue(undefined),
245
+ toMessageObject: jest.fn(() => ({})),
246
+ } as any;
247
+ method.postMessage = jest.fn();
248
+ jest.spyOn(method, 'checkDeviceToBootloader').mockImplementation(() => {
249
+ method.checkPromise = { promise: Promise.resolve(true) } as any;
250
+ });
251
+ jest.spyOn(utils, 'wait').mockResolvedValue(undefined);
252
+ const liveReleaseSpy = jest
253
+ .spyOn(DataManager, 'getSysResourcesLatestRelease')
254
+ .mockReturnValue(undefined);
255
+ mockUpdateResourcesFromSources.mockImplementation(async (_call, _post, _device, entries) => {
256
+ expect(entries).toHaveLength(1);
257
+ expect(entries[0].entryName).toBe('logo.bin');
258
+ await expect(entries[0].source.readAt(0, 4)).resolves.toEqual(prepared.resourceBinary);
259
+ return true;
260
+ });
261
+ mockUploadFirmwareFromSource.mockResolvedValue({ success: true } as any);
262
+
263
+ await expect(method.run()).resolves.toEqual({ success: true });
264
+ expect(liveReleaseSpy).not.toHaveBeenCalled();
265
+ expect(mockUpdateResourcesFromSources).toHaveBeenCalledTimes(1);
266
+ });
267
+
268
+ test('FirmwareUpdateV2 does not replay a plan resource during its BLE component phase', async () => {
269
+ const prepared = await createPreparedPlan('v2', 'ble');
270
+ const artifactReader = createReader(
271
+ new Map([
272
+ [prepared.firmwareArtifact.artifactRef, prepared.firmwareBinary],
273
+ [prepared.archiveArtifact.artifactRef, prepared.archiveBinary],
274
+ ])
275
+ );
276
+ const hostBindingGeneration = registerFirmwareUpdateHostBinding({
277
+ preparedPlanDigest: prepared.preparedPlan.preparedPlanDigest,
278
+ artifactReader,
279
+ });
280
+ const method = new FirmwareUpdateV2({
281
+ id: 1,
282
+ payload: {
283
+ method: 'firmwareUpdateV2',
284
+ connectId: 'touch-connect-id',
285
+ platform: 'desktop',
286
+ updateType: 'ble',
287
+ preparedPlan: prepared.preparedPlan,
288
+ hostBindingGeneration,
289
+ },
290
+ });
291
+ method.init();
292
+
293
+ const typedCall = jest.fn().mockResolvedValue({ type: 'Success' });
294
+ const commands = { typedCall, checkDisposed: jest.fn(), disposed: false };
295
+ method.device = {
296
+ features: {
297
+ deviceType: EDeviceType.Touch,
298
+ serialNo: 'touch-device',
299
+ firmwareVersion: '3.2.0',
300
+ bootloaderVersion: '2.8.0',
301
+ },
302
+ commands,
303
+ getCommands: () => commands,
304
+ getCurrentDeviceType: () => EDeviceType.Touch,
305
+ getCurrentFirmwareType: () => EFirmwareType.Universal,
306
+ getCurrentFirmwareVersionString: () => '3.2.0',
307
+ getCurrentBootloaderVersionString: () => '2.8.0',
308
+ getCurrentSerialNo: () => 'touch-device',
309
+ isBootloader: () => false,
310
+ isProtocolV2: () => false,
311
+ acquire: jest.fn().mockResolvedValue(undefined),
312
+ toMessageObject: jest.fn(() => ({})),
313
+ } as any;
314
+ method.postMessage = jest.fn();
315
+ jest.spyOn(method, 'checkDeviceToBootloader').mockImplementation(() => {
316
+ method.checkPromise = { promise: Promise.resolve(true) } as any;
317
+ });
318
+ jest.spyOn(utils, 'wait').mockResolvedValue(undefined);
319
+ const liveReleaseSpy = jest.spyOn(DataManager, 'getSysResourcesLatestRelease');
320
+ mockUploadFirmwareFromSource.mockResolvedValue({ success: true } as any);
321
+
322
+ await expect(method.run()).resolves.toEqual({ success: true });
323
+ expect(liveReleaseSpy).not.toHaveBeenCalled();
324
+ expect(mockUpdateResourcesFromSources).not.toHaveBeenCalled();
325
+ });
326
+ });
@@ -151,7 +151,7 @@ describe('FirmwareUpdateV2 download-before-reboot safety', () => {
151
151
  jest.restoreAllMocks();
152
152
  });
153
153
 
154
- it('rejects mixed binary and prepared artifact inputs during initialization', () => {
154
+ it('rejects a prepared plan combined with legacy inputs during initialization', () => {
155
155
  const method = new FirmwareUpdateV2({
156
156
  id: 1,
157
157
  payload: {
@@ -173,7 +173,7 @@ describe('FirmwareUpdateV2 download-before-reboot safety', () => {
173
173
  expect(() => method.init()).toThrow(
174
174
  expect.objectContaining({
175
175
  errorCode: HardwareErrorCode.CallMethodInvalidParameter,
176
- message: 'Firmware update binary and artifact are mutually exclusive',
176
+ message: 'Prepared firmware plans cannot be combined with legacy firmware inputs',
177
177
  })
178
178
  );
179
179
  });
@@ -57,6 +57,22 @@ describe('Protocol V2 firmware target contract', () => {
57
57
  ).toThrow('Neo only supports SE01 and SE02; unsupported firmware targets: se03, se04');
58
58
  });
59
59
 
60
+ test('ignores unselected SE binaries when the caller supplied a target subset', () => {
61
+ const params = {
62
+ platform: 'web' as const,
63
+ targetsToUpdate: ['app_v1'] as Array<'app_v1'>,
64
+ se03Binary: new ArrayBuffer(1),
65
+ se04Binary: new ArrayBuffer(1),
66
+ };
67
+
68
+ expect(() =>
69
+ assertProtocolV2FirmwareTargetsSupported(EDeviceType.Neo, params, true)
70
+ ).not.toThrow();
71
+ expect(() => assertProtocolV2FirmwareTargetsSupported(EDeviceType.Neo, params)).toThrow(
72
+ 'Neo only supports SE01 and SE02; unsupported firmware targets: se03, se04'
73
+ );
74
+ });
75
+
60
76
  test('keeps all four SE firmware targets available on Pro2', () => {
61
77
  expect(() =>
62
78
  assertProtocolV2FirmwareTargetsSupported(EDeviceType.Pro2, {