@learncard/sss-key-manager 0.1.19 → 0.1.21

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 (39) hide show
  1. package/dist/api-client.d.ts +49 -0
  2. package/dist/api-client.d.ts.map +1 -0
  3. package/dist/atomic-operations.d.ts +101 -0
  4. package/dist/atomic-operations.d.ts.map +1 -0
  5. package/dist/auth-coordinator.d.ts +10 -0
  6. package/dist/auth-coordinator.d.ts.map +1 -0
  7. package/dist/crypto.d.ts +31 -0
  8. package/dist/crypto.d.ts.map +1 -0
  9. package/dist/index.d.ts +26 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/key-manager.d.ts +30 -0
  12. package/dist/key-manager.d.ts.map +1 -0
  13. package/dist/passkey.d.ts +23 -0
  14. package/dist/passkey.d.ts.map +1 -0
  15. package/dist/qr-crypto.d.ts +56 -0
  16. package/dist/qr-crypto.d.ts.map +1 -0
  17. package/dist/qr-login.d.ts +122 -0
  18. package/dist/qr-login.d.ts.map +1 -0
  19. package/dist/recovery-phrase.d.ts +14 -0
  20. package/dist/recovery-phrase.d.ts.map +1 -0
  21. package/dist/sss-key-manager.cjs.development.js +39 -3
  22. package/dist/sss-key-manager.cjs.development.js.map +2 -2
  23. package/dist/sss-key-manager.cjs.production.min.js +20 -20
  24. package/dist/sss-key-manager.cjs.production.min.js.map +3 -3
  25. package/dist/sss-key-manager.esm.js +39 -3
  26. package/dist/sss-key-manager.esm.js.map +2 -2
  27. package/dist/sss-strategy.d.ts +82 -0
  28. package/dist/sss-strategy.d.ts.map +1 -0
  29. package/dist/sss.d.ts +15 -0
  30. package/dist/sss.d.ts.map +1 -0
  31. package/dist/storage.d.ts +46 -0
  32. package/dist/storage.d.ts.map +1 -0
  33. package/dist/types.d.ts +155 -0
  34. package/dist/types.d.ts.map +1 -0
  35. package/package.json +8 -8
  36. package/src/crypto.ts +9 -14
  37. package/src/recovery-phrase.ts +7 -4
  38. package/src/sss-strategy.test.ts +404 -246
  39. package/dist/sss-key-manager.d.ts +0 -898
@@ -17,7 +17,11 @@
17
17
 
18
18
  import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
19
19
 
20
- import { createSSSStrategy, formatVersionedEmailShare, parseVersionedEmailShare } from './sss-strategy';
20
+ import {
21
+ createSSSStrategy,
22
+ formatVersionedEmailShare,
23
+ parseVersionedEmailShare,
24
+ } from './sss-strategy';
21
25
  import { reconstructFromShares } from './sss';
22
26
  import { splitAndVerify } from './atomic-operations';
23
27
  import { shareToRecoveryPhrase, recoveryPhraseToShare } from './recovery-phrase';
@@ -31,7 +35,10 @@ import type { SSSKeyDerivationStrategy } from './types';
31
35
 
32
36
  const DEFAULT_KEY = 'device';
33
37
 
34
- const createMemoryStorage = (): SSSStorageFunctions & { _store: Map<string, string>; _versions: Map<string, number> } => {
38
+ const createMemoryStorage = (): SSSStorageFunctions & {
39
+ _store: Map<string, string>;
40
+ _versions: Map<string, number>;
41
+ } => {
35
42
  const store = new Map<string, string>();
36
43
  const versions = new Map<string, number>();
37
44
 
@@ -322,13 +329,16 @@ describe('createSSSStrategy', () => {
322
329
  describe('fetchServerKeyStatus shareVersion', () => {
323
330
  it('returns shareVersion when server includes it', async () => {
324
331
  vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce(
325
- new Response(JSON.stringify({
326
- authShare: 'share-data',
327
- keyProvider: 'sss',
328
- primaryDid: 'did:key:z123',
329
- recoveryMethods: [],
330
- shareVersion: 5,
331
- }), { status: 200 })
332
+ new Response(
333
+ JSON.stringify({
334
+ authShare: 'share-data',
335
+ keyProvider: 'sss',
336
+ primaryDid: 'did:key:z123',
337
+ recoveryMethods: [],
338
+ shareVersion: 5,
339
+ }),
340
+ { status: 200 }
341
+ )
332
342
  );
333
343
 
334
344
  const status = await strategy.fetchServerKeyStatus('token', 'firebase');
@@ -338,12 +348,15 @@ describe('createSSSStrategy', () => {
338
348
 
339
349
  it('returns null shareVersion when server omits it', async () => {
340
350
  vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce(
341
- new Response(JSON.stringify({
342
- authShare: 'share-data',
343
- keyProvider: 'sss',
344
- primaryDid: 'did:key:z123',
345
- recoveryMethods: [],
346
- }), { status: 200 })
351
+ new Response(
352
+ JSON.stringify({
353
+ authShare: 'share-data',
354
+ keyProvider: 'sss',
355
+ primaryDid: 'did:key:z123',
356
+ recoveryMethods: [],
357
+ }),
358
+ { status: 200 }
359
+ )
347
360
  );
348
361
 
349
362
  const status = await strategy.fetchServerKeyStatus('token', 'firebase');
@@ -358,13 +371,16 @@ describe('createSSSStrategy', () => {
358
371
  expect(await strategy.getLocalShareVersion!()).toBeNull();
359
372
 
360
373
  vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce(
361
- new Response(JSON.stringify({
362
- authShare: 'share-data',
363
- keyProvider: 'sss',
364
- primaryDid: 'did:key:z123',
365
- recoveryMethods: [],
366
- shareVersion: 3,
367
- }), { status: 200 })
374
+ new Response(
375
+ JSON.stringify({
376
+ authShare: 'share-data',
377
+ keyProvider: 'sss',
378
+ primaryDid: 'did:key:z123',
379
+ recoveryMethods: [],
380
+ shareVersion: 3,
381
+ }),
382
+ { status: 200 }
383
+ )
368
384
  );
369
385
 
370
386
  const status = await strategy.fetchServerKeyStatus('token', 'firebase');
@@ -374,7 +390,10 @@ describe('createSSSStrategy', () => {
374
390
  // Wait for the fire-and-forget backfill to complete
375
391
  await new Promise(r => setTimeout(r, 10));
376
392
 
377
- expect(storage.storeShareVersion).toHaveBeenCalledWith(3, 'sss-device-share:legacy-user');
393
+ expect(storage.storeShareVersion).toHaveBeenCalledWith(
394
+ 3,
395
+ 'sss-device-share:legacy-user'
396
+ );
378
397
  expect(await strategy.getLocalShareVersion!()).toBe(3);
379
398
  });
380
399
 
@@ -385,13 +404,16 @@ describe('createSSSStrategy', () => {
385
404
  storage.storeShareVersion.mockClear();
386
405
 
387
406
  vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce(
388
- new Response(JSON.stringify({
389
- authShare: 'share-data',
390
- keyProvider: 'sss',
391
- primaryDid: 'did:key:z123',
392
- recoveryMethods: [],
393
- shareVersion: 5,
394
- }), { status: 200 })
407
+ new Response(
408
+ JSON.stringify({
409
+ authShare: 'share-data',
410
+ keyProvider: 'sss',
411
+ primaryDid: 'did:key:z123',
412
+ recoveryMethods: [],
413
+ shareVersion: 5,
414
+ }),
415
+ { status: 200 }
416
+ )
395
417
  );
396
418
 
397
419
  await strategy.fetchServerKeyStatus('token', 'firebase');
@@ -459,13 +481,16 @@ describe('createSSSStrategy', () => {
459
481
  const method = (init?.method ?? 'GET').toUpperCase();
460
482
 
461
483
  if (urlStr.includes('/keys/auth-share') && method === 'POST') {
462
- return new Response(JSON.stringify({
463
- authShare: { encryptedData: remoteKey, encryptedDek: '', iv: '' },
464
- primaryDid: 'did:key:zCorrect',
465
- recoveryMethods: [],
466
- keyProvider: 'sss',
467
- shareVersion: 42,
468
- }), { status: 200 });
484
+ return new Response(
485
+ JSON.stringify({
486
+ authShare: { encryptedData: remoteKey, encryptedDek: '', iv: '' },
487
+ primaryDid: 'did:key:zCorrect',
488
+ recoveryMethods: [],
489
+ keyProvider: 'sss',
490
+ shareVersion: 42,
491
+ }),
492
+ { status: 200 }
493
+ );
469
494
  }
470
495
 
471
496
  return new Response(null, { status: 200 });
@@ -492,13 +517,16 @@ describe('createSSSStrategy', () => {
492
517
  const method = (init?.method ?? 'GET').toUpperCase();
493
518
 
494
519
  if (urlStr.includes('/keys/auth-share') && method === 'POST') {
495
- return new Response(JSON.stringify({
496
- authShare: { encryptedData: remoteKey, encryptedDek: '', iv: '' },
497
- primaryDid: 'did:key:zCorrect',
498
- recoveryMethods: [],
499
- keyProvider: 'sss',
500
- // no shareVersion field
501
- }), { status: 200 });
520
+ return new Response(
521
+ JSON.stringify({
522
+ authShare: { encryptedData: remoteKey, encryptedDek: '', iv: '' },
523
+ primaryDid: 'did:key:zCorrect',
524
+ recoveryMethods: [],
525
+ keyProvider: 'sss',
526
+ // no shareVersion field
527
+ }),
528
+ { status: 200 }
529
+ );
502
530
  }
503
531
 
504
532
  return new Response(null, { status: 200 });
@@ -564,12 +592,15 @@ describe('createSSSStrategy', () => {
564
592
 
565
593
  it('parses server response with string authShare', async () => {
566
594
  vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce(
567
- new Response(JSON.stringify({
568
- authShare: 'raw-auth-share-string',
569
- keyProvider: 'sss',
570
- primaryDid: 'did:key:z123',
571
- recoveryMethods: [{ type: 'passkey', createdAt: '2024-01-01' }],
572
- }), { status: 200 })
595
+ new Response(
596
+ JSON.stringify({
597
+ authShare: 'raw-auth-share-string',
598
+ keyProvider: 'sss',
599
+ primaryDid: 'did:key:z123',
600
+ recoveryMethods: [{ type: 'passkey', createdAt: '2024-01-01' }],
601
+ }),
602
+ { status: 200 }
603
+ )
573
604
  );
574
605
 
575
606
  const status = await strategy.fetchServerKeyStatus('token', 'firebase');
@@ -584,12 +615,19 @@ describe('createSSSStrategy', () => {
584
615
 
585
616
  it('parses server response with object authShare (encrypted envelope)', async () => {
586
617
  vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce(
587
- new Response(JSON.stringify({
588
- authShare: { encryptedData: 'encrypted-share', iv: 'iv-value', encryptedDek: 'dek' },
589
- keyProvider: 'sss',
590
- primaryDid: 'did:key:z456',
591
- recoveryMethods: [],
592
- }), { status: 200 })
618
+ new Response(
619
+ JSON.stringify({
620
+ authShare: {
621
+ encryptedData: 'encrypted-share',
622
+ iv: 'iv-value',
623
+ encryptedDek: 'dek',
624
+ },
625
+ keyProvider: 'sss',
626
+ primaryDid: 'did:key:z456',
627
+ recoveryMethods: [],
628
+ }),
629
+ { status: 200 }
630
+ )
593
631
  );
594
632
 
595
633
  const status = await strategy.fetchServerKeyStatus('token', 'firebase');
@@ -600,12 +638,15 @@ describe('createSSSStrategy', () => {
600
638
 
601
639
  it('detects web3auth migration', async () => {
602
640
  vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce(
603
- new Response(JSON.stringify({
604
- authShare: 'share',
605
- keyProvider: 'web3auth',
606
- primaryDid: 'did:key:zOld',
607
- recoveryMethods: [],
608
- }), { status: 200 })
641
+ new Response(
642
+ JSON.stringify({
643
+ authShare: 'share',
644
+ keyProvider: 'web3auth',
645
+ primaryDid: 'did:key:zOld',
646
+ recoveryMethods: [],
647
+ }),
648
+ { status: 200 }
649
+ )
609
650
  );
610
651
 
611
652
  const status = await strategy.fetchServerKeyStatus('token', 'firebase');
@@ -618,8 +659,9 @@ describe('createSSSStrategy', () => {
618
659
  new Response(null, { status: 500, statusText: 'Internal Server Error' })
619
660
  );
620
661
 
621
- await expect(strategy.fetchServerKeyStatus('token', 'firebase'))
622
- .rejects.toThrow('Failed to fetch key status');
662
+ await expect(strategy.fetchServerKeyStatus('token', 'firebase')).rejects.toThrow(
663
+ 'Failed to fetch key status'
664
+ );
623
665
  });
624
666
  });
625
667
 
@@ -630,7 +672,9 @@ describe('createSSSStrategy', () => {
630
672
  describe('storeAuthShare', () => {
631
673
  it('sends PUT request to the server and stores returned shareVersion', async () => {
632
674
  const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce(
633
- new Response(JSON.stringify({ success: true, shareVersion: 3 }), { status: 200 })
675
+ new Response(JSON.stringify({ success: true, shareVersion: 3 }), {
676
+ status: 200,
677
+ })
634
678
  );
635
679
 
636
680
  await strategy.storeAuthShare('token', 'firebase', 'share-data', 'did:key:z1');
@@ -652,8 +696,9 @@ describe('createSSSStrategy', () => {
652
696
  new Response(null, { status: 500, statusText: 'Server Error' })
653
697
  );
654
698
 
655
- await expect(strategy.storeAuthShare('token', 'firebase', 'share', 'did'))
656
- .rejects.toThrow('Failed to store auth share');
699
+ await expect(
700
+ strategy.storeAuthShare('token', 'firebase', 'share', 'did')
701
+ ).rejects.toThrow('Failed to store auth share');
657
702
  });
658
703
  });
659
704
 
@@ -663,9 +708,9 @@ describe('createSSSStrategy', () => {
663
708
 
664
709
  describe('markMigrated', () => {
665
710
  it('sends POST to /keys/migrate', async () => {
666
- const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce(
667
- new Response(null, { status: 200 })
668
- );
711
+ const fetchSpy = vi
712
+ .spyOn(globalThis, 'fetch')
713
+ .mockResolvedValueOnce(new Response(null, { status: 200 }));
669
714
 
670
715
  await strategy.markMigrated!('token', 'firebase');
671
716
 
@@ -682,8 +727,9 @@ describe('createSSSStrategy', () => {
682
727
  new Response(null, { status: 500, statusText: 'Fail' })
683
728
  );
684
729
 
685
- await expect(strategy.markMigrated!('token', 'firebase'))
686
- .rejects.toThrow('Failed to mark migrated');
730
+ await expect(strategy.markMigrated!('token', 'firebase')).rejects.toThrow(
731
+ 'Failed to mark migrated'
732
+ );
687
733
  });
688
734
  });
689
735
 
@@ -728,17 +774,20 @@ describe('createSSSStrategy', () => {
728
774
  fetchCalls.push({
729
775
  url: urlStr,
730
776
  method,
731
- body: init?.body as string ?? '',
777
+ body: (init?.body as string) ?? '',
732
778
  });
733
779
 
734
780
  if (urlStr.includes('/keys/auth-share') && method === 'POST') {
735
- return new Response(JSON.stringify({
736
- authShare: { encryptedData: remoteKey, encryptedDek: '', iv: '' },
737
- primaryDid: 'did:key:zCorrect',
738
- recoveryMethods: [],
739
- keyProvider: 'sss',
740
- shareVersion: 1,
741
- }), { status: 200 });
781
+ return new Response(
782
+ JSON.stringify({
783
+ authShare: { encryptedData: remoteKey, encryptedDek: '', iv: '' },
784
+ primaryDid: 'did:key:zCorrect',
785
+ recoveryMethods: [],
786
+ keyProvider: 'sss',
787
+ shareVersion: 1,
788
+ }),
789
+ { status: 200 }
790
+ );
742
791
  }
743
792
 
744
793
  return new Response(null, { status: 200 });
@@ -854,13 +903,16 @@ describe('createSSSStrategy', () => {
854
903
  await strategy.storeLocalShareVersion!(2);
855
904
 
856
905
  const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce(
857
- new Response(JSON.stringify({
858
- authShare: 'v2-auth-share',
859
- keyProvider: 'sss',
860
- primaryDid: 'did:key:z123',
861
- recoveryMethods: [],
862
- shareVersion: 3,
863
- }), { status: 200 })
906
+ new Response(
907
+ JSON.stringify({
908
+ authShare: 'v2-auth-share',
909
+ keyProvider: 'sss',
910
+ primaryDid: 'did:key:z123',
911
+ recoveryMethods: [],
912
+ shareVersion: 3,
913
+ }),
914
+ { status: 200 }
915
+ )
864
916
  );
865
917
 
866
918
  await strategy.fetchServerKeyStatus('token', 'firebase');
@@ -874,13 +926,16 @@ describe('createSSSStrategy', () => {
874
926
  strategy.setActiveUser!('new-device');
875
927
 
876
928
  const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce(
877
- new Response(JSON.stringify({
878
- authShare: 'current-share',
879
- keyProvider: 'sss',
880
- primaryDid: 'did:key:z123',
881
- recoveryMethods: [],
882
- shareVersion: 3,
883
- }), { status: 200 })
929
+ new Response(
930
+ JSON.stringify({
931
+ authShare: 'current-share',
932
+ keyProvider: 'sss',
933
+ primaryDid: 'did:key:z123',
934
+ recoveryMethods: [],
935
+ shareVersion: 3,
936
+ }),
937
+ { status: 200 }
938
+ )
884
939
  );
885
940
 
886
941
  await strategy.fetchServerKeyStatus('token', 'firebase');
@@ -893,7 +948,9 @@ describe('createSSSStrategy', () => {
893
948
  it('full flow: v2 device share from another device reconstructs with v2 auth share from server', async () => {
894
949
  // --- Setup: split a key to get a real v2 device/auth pair ---
895
950
  const originalKey = 'deadbeef1234'.padEnd(64, '0');
896
- const { localKey: v2DeviceShare, remoteKey: v2AuthShare } = await strategy.splitKey(originalKey);
951
+ const { localKey: v2DeviceShare, remoteKey: v2AuthShare } = await strategy.splitKey(
952
+ originalKey
953
+ );
897
954
 
898
955
  // --- Simulate: Device A receives v2 share via QR recovery ---
899
956
  strategy.setActiveUser!('recovering-user');
@@ -902,13 +959,16 @@ describe('createSSSStrategy', () => {
902
959
 
903
960
  // --- Mock server: returns the v2 auth share when version 2 is requested ---
904
961
  vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce(
905
- new Response(JSON.stringify({
906
- authShare: { encryptedData: v2AuthShare, encryptedDek: '', iv: '' },
907
- keyProvider: 'sss',
908
- primaryDid: 'did:key:zRecoveredUser',
909
- recoveryMethods: [{ type: 'passkey', createdAt: '2024-01-01' }],
910
- shareVersion: 3, // server is at v3 but returns v2 auth share
911
- }), { status: 200 })
962
+ new Response(
963
+ JSON.stringify({
964
+ authShare: { encryptedData: v2AuthShare, encryptedDek: '', iv: '' },
965
+ keyProvider: 'sss',
966
+ primaryDid: 'did:key:zRecoveredUser',
967
+ recoveryMethods: [{ type: 'passkey', createdAt: '2024-01-01' }],
968
+ shareVersion: 3, // server is at v3 but returns v2 auth share
969
+ }),
970
+ { status: 200 }
971
+ )
912
972
  );
913
973
 
914
974
  const status = await strategy.fetchServerKeyStatus('token', 'firebase');
@@ -927,14 +987,17 @@ describe('createSSSStrategy', () => {
927
987
  strategy.setActiveUser!('overwrite-user');
928
988
 
929
989
  // Step 1: First initialize — no local key, server backfills v3
930
- vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce(
931
- new Response(JSON.stringify({
932
- authShare: 'v3-auth-share',
933
- keyProvider: 'sss',
934
- primaryDid: 'did:key:z123',
935
- recoveryMethods: [],
936
- shareVersion: 3,
937
- }), { status: 200 })
990
+ const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce(
991
+ new Response(
992
+ JSON.stringify({
993
+ authShare: 'v3-auth-share',
994
+ keyProvider: 'sss',
995
+ primaryDid: 'did:key:z123',
996
+ recoveryMethods: [],
997
+ shareVersion: 3,
998
+ }),
999
+ { status: 200 }
1000
+ )
938
1001
  );
939
1002
 
940
1003
  await strategy.fetchServerKeyStatus('token', 'firebase');
@@ -950,14 +1013,17 @@ describe('createSSSStrategy', () => {
950
1013
  expect(await strategy.getLocalShareVersion!()).toBe(2);
951
1014
 
952
1015
  // Step 3: Second fetchServerKeyStatus — should send version 2
953
- const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce(
954
- new Response(JSON.stringify({
955
- authShare: 'v2-auth-share-from-previous',
956
- keyProvider: 'sss',
957
- primaryDid: 'did:key:z123',
958
- recoveryMethods: [],
959
- shareVersion: 3,
960
- }), { status: 200 })
1016
+ fetchSpy.mockClear().mockResolvedValueOnce(
1017
+ new Response(
1018
+ JSON.stringify({
1019
+ authShare: 'v2-auth-share-from-previous',
1020
+ keyProvider: 'sss',
1021
+ primaryDid: 'did:key:z123',
1022
+ recoveryMethods: [],
1023
+ shareVersion: 3,
1024
+ }),
1025
+ { status: 200 }
1026
+ )
961
1027
  );
962
1028
 
963
1029
  const status = await strategy.fetchServerKeyStatus('token', 'firebase');
@@ -975,20 +1041,25 @@ describe('createSSSStrategy', () => {
975
1041
  // 3. coordinator.initialize() → fetchServerKeyStatus sends v2
976
1042
 
977
1043
  const originalKey = 'cafe0123babe'.padEnd(64, '0');
978
- const { localKey: v2DeviceShare, remoteKey: v2AuthShare } = await strategy.splitKey(originalKey);
1044
+ const { localKey: v2DeviceShare, remoteKey: v2AuthShare } = await strategy.splitKey(
1045
+ originalKey
1046
+ );
979
1047
 
980
1048
  strategy.setActiveUser!('device-recovery-user');
981
1049
  await strategy.storeLocalKey(v2DeviceShare);
982
1050
  await strategy.storeLocalShareVersion!(2);
983
1051
 
984
1052
  const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce(
985
- new Response(JSON.stringify({
986
- authShare: { encryptedData: v2AuthShare, encryptedDek: '', iv: '' },
987
- keyProvider: 'sss',
988
- primaryDid: 'did:key:zUser',
989
- recoveryMethods: [],
990
- shareVersion: 3,
991
- }), { status: 200 })
1053
+ new Response(
1054
+ JSON.stringify({
1055
+ authShare: { encryptedData: v2AuthShare, encryptedDek: '', iv: '' },
1056
+ keyProvider: 'sss',
1057
+ primaryDid: 'did:key:zUser',
1058
+ recoveryMethods: [],
1059
+ shareVersion: 3,
1060
+ }),
1061
+ { status: 200 }
1062
+ )
992
1063
  );
993
1064
 
994
1065
  const status = await strategy.fetchServerKeyStatus('token', 'firebase');
@@ -1038,8 +1109,11 @@ describe('createSSSStrategy', () => {
1038
1109
  const { remoteKey } = await emailStrategy.splitKey(originalKey);
1039
1110
 
1040
1111
  // Step 1b: storeAuthShare so the version is cached for email send
1041
- vi.spyOn(globalThis, 'fetch').mockImplementationOnce(async () =>
1042
- new Response(JSON.stringify({ success: true, shareVersion: 5 }), { status: 200 })
1112
+ vi.spyOn(globalThis, 'fetch').mockImplementationOnce(
1113
+ async () =>
1114
+ new Response(JSON.stringify({ success: true, shareVersion: 5 }), {
1115
+ status: 200,
1116
+ })
1043
1117
  );
1044
1118
 
1045
1119
  await emailStrategy.storeAuthShare('token', 'firebase', remoteKey, 'did:key:z1');
@@ -1055,7 +1129,10 @@ describe('createSSSStrategy', () => {
1055
1129
  });
1056
1130
 
1057
1131
  await emailStrategy.sendEmailBackupShare!(
1058
- 'token', 'firebase', originalKey, 'user@test.com'
1132
+ 'token',
1133
+ 'firebase',
1134
+ originalKey,
1135
+ 'user@test.com'
1059
1136
  );
1060
1137
 
1061
1138
  expect(capturedPayload).toBeDefined();
@@ -1078,8 +1155,11 @@ describe('createSSSStrategy', () => {
1078
1155
  const { localKey, remoteKey } = await emailStrategy.splitKey(originalKey);
1079
1156
 
1080
1157
  // storeAuthShare so version is cached
1081
- vi.spyOn(globalThis, 'fetch').mockImplementationOnce(async () =>
1082
- new Response(JSON.stringify({ success: true, shareVersion: 1 }), { status: 200 })
1158
+ vi.spyOn(globalThis, 'fetch').mockImplementationOnce(
1159
+ async () =>
1160
+ new Response(JSON.stringify({ success: true, shareVersion: 1 }), {
1161
+ status: 200,
1162
+ })
1083
1163
  );
1084
1164
 
1085
1165
  await emailStrategy.storeAuthShare('token', 'firebase', remoteKey, 'did:key:z1');
@@ -1095,7 +1175,10 @@ describe('createSSSStrategy', () => {
1095
1175
  });
1096
1176
 
1097
1177
  await emailStrategy.sendEmailBackupShare!(
1098
- 'token', 'firebase', originalKey, 'user@test.com'
1178
+ 'token',
1179
+ 'firebase',
1180
+ originalKey,
1181
+ 'user@test.com'
1099
1182
  );
1100
1183
 
1101
1184
  // Strip 4-char hex version prefix
@@ -1117,7 +1200,10 @@ describe('createSSSStrategy', () => {
1117
1200
 
1118
1201
  // Call sendEmailBackupShare without calling splitKey first
1119
1202
  await emailStrategy.sendEmailBackupShare!(
1120
- 'token', 'firebase', 'some-key', 'user@test.com'
1203
+ 'token',
1204
+ 'firebase',
1205
+ 'some-key',
1206
+ 'user@test.com'
1121
1207
  );
1122
1208
 
1123
1209
  expect(warnSpy).toHaveBeenCalledWith(
@@ -1133,8 +1219,11 @@ describe('createSSSStrategy', () => {
1133
1219
  const { remoteKey } = await emailStrategy.splitKey(originalKey);
1134
1220
 
1135
1221
  // storeAuthShare to cache version
1136
- vi.spyOn(globalThis, 'fetch').mockImplementationOnce(async () =>
1137
- new Response(JSON.stringify({ success: true, shareVersion: 2 }), { status: 200 })
1222
+ vi.spyOn(globalThis, 'fetch').mockImplementationOnce(
1223
+ async () =>
1224
+ new Response(JSON.stringify({ success: true, shareVersion: 2 }), {
1225
+ status: 200,
1226
+ })
1138
1227
  );
1139
1228
 
1140
1229
  await emailStrategy.storeAuthShare('token', 'firebase', remoteKey, 'did:key:z1');
@@ -1144,14 +1233,17 @@ describe('createSSSStrategy', () => {
1144
1233
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (url, init) => {
1145
1234
  fetchCalls.push({
1146
1235
  url: typeof url === 'string' ? url : url.toString(),
1147
- body: init?.body as string ?? '',
1236
+ body: (init?.body as string) ?? '',
1148
1237
  });
1149
1238
 
1150
1239
  return new Response(null, { status: 200 });
1151
1240
  });
1152
1241
 
1153
1242
  await emailStrategy.sendEmailBackupShare!(
1154
- 'token', 'firebase', originalKey, 'user@test.com'
1243
+ 'token',
1244
+ 'firebase',
1245
+ originalKey,
1246
+ 'user@test.com'
1155
1247
  );
1156
1248
 
1157
1249
  // Only one fetch call should have been made — to the email relay
@@ -1194,22 +1286,27 @@ describe('createSSSStrategy', () => {
1194
1286
 
1195
1287
  fetchCalls.push({
1196
1288
  url: urlStr,
1197
- body: init?.body as string ?? '',
1289
+ body: (init?.body as string) ?? '',
1198
1290
  });
1199
1291
 
1200
1292
  // fetchAuthShareRaw needs to return server data
1201
1293
  if (urlStr.includes('/keys/auth-share') && method === 'POST') {
1202
- return new Response(JSON.stringify({
1203
- authShare: 'existing-auth-share',
1204
- primaryDid: 'did:key:z123',
1205
- recoveryMethods: [],
1206
- shareVersion: 2,
1207
- }), { status: 200 });
1294
+ return new Response(
1295
+ JSON.stringify({
1296
+ authShare: 'existing-auth-share',
1297
+ primaryDid: 'did:key:z123',
1298
+ recoveryMethods: [],
1299
+ shareVersion: 2,
1300
+ }),
1301
+ { status: 200 }
1302
+ );
1208
1303
  }
1209
1304
 
1210
1305
  // putAuthShare returns shareVersion
1211
1306
  if (urlStr.includes('/keys/auth-share') && method === 'PUT') {
1212
- return new Response(JSON.stringify({ success: true, shareVersion: 3 }), { status: 200 });
1307
+ return new Response(JSON.stringify({ success: true, shareVersion: 3 }), {
1308
+ status: 200,
1309
+ });
1213
1310
  }
1214
1311
 
1215
1312
  return new Response(JSON.stringify({ success: true }), { status: 200 });
@@ -1250,8 +1347,11 @@ describe('createSSSStrategy', () => {
1250
1347
  await emailStrategy.splitKey(originalKey);
1251
1348
 
1252
1349
  // storeAuthShare returns version 7
1253
- vi.spyOn(globalThis, 'fetch').mockImplementationOnce(async () =>
1254
- new Response(JSON.stringify({ success: true, shareVersion: 7 }), { status: 200 })
1350
+ vi.spyOn(globalThis, 'fetch').mockImplementationOnce(
1351
+ async () =>
1352
+ new Response(JSON.stringify({ success: true, shareVersion: 7 }), {
1353
+ status: 200,
1354
+ })
1255
1355
  );
1256
1356
 
1257
1357
  await emailStrategy.storeAuthShare('token', 'firebase', 'auth-share', 'did:key:z1');
@@ -1267,7 +1367,10 @@ describe('createSSSStrategy', () => {
1267
1367
  });
1268
1368
 
1269
1369
  await emailStrategy.sendEmailBackupShare!(
1270
- 'token', 'firebase', originalKey, 'user@test.com'
1370
+ 'token',
1371
+ 'firebase',
1372
+ originalKey,
1373
+ 'user@test.com'
1271
1374
  );
1272
1375
 
1273
1376
  // New format: 4-char hex prefix (version 7 = "0007")
@@ -1287,20 +1390,25 @@ describe('createSSSStrategy', () => {
1287
1390
 
1288
1391
  fetchCalls.push({
1289
1392
  url: urlStr,
1290
- body: init?.body as string ?? '',
1393
+ body: (init?.body as string) ?? '',
1291
1394
  });
1292
1395
 
1293
1396
  if (urlStr.includes('/keys/auth-share') && method === 'POST') {
1294
- return new Response(JSON.stringify({
1295
- authShare: 'existing',
1296
- primaryDid: 'did:key:z1',
1297
- recoveryMethods: [],
1298
- shareVersion: 4,
1299
- }), { status: 200 });
1397
+ return new Response(
1398
+ JSON.stringify({
1399
+ authShare: 'existing',
1400
+ primaryDid: 'did:key:z1',
1401
+ recoveryMethods: [],
1402
+ shareVersion: 4,
1403
+ }),
1404
+ { status: 200 }
1405
+ );
1300
1406
  }
1301
1407
 
1302
1408
  if (urlStr.includes('/keys/auth-share') && method === 'PUT') {
1303
- return new Response(JSON.stringify({ success: true, shareVersion: 5 }), { status: 200 });
1409
+ return new Response(JSON.stringify({ success: true, shareVersion: 5 }), {
1410
+ status: 200,
1411
+ });
1304
1412
  }
1305
1413
 
1306
1414
  return new Response(JSON.stringify({ success: true }), { status: 200 });
@@ -1345,12 +1453,15 @@ describe('createSSSStrategy', () => {
1345
1453
  const body = JSON.parse(init?.body as string);
1346
1454
  capturedVersion = body.shareVersion;
1347
1455
 
1348
- return new Response(JSON.stringify({
1349
- authShare: shares.authShare,
1350
- primaryDid: 'did:key:z1',
1351
- recoveryMethods: [],
1352
- shareVersion: 2,
1353
- }), { status: 200 });
1456
+ return new Response(
1457
+ JSON.stringify({
1458
+ authShare: shares.authShare,
1459
+ primaryDid: 'did:key:z1',
1460
+ recoveryMethods: [],
1461
+ shareVersion: 2,
1462
+ }),
1463
+ { status: 200 }
1464
+ );
1354
1465
  }
1355
1466
 
1356
1467
  return new Response(JSON.stringify({ success: true }), { status: 200 });
@@ -1383,12 +1494,15 @@ describe('createSSSStrategy', () => {
1383
1494
  const body = JSON.parse(init?.body as string);
1384
1495
  capturedVersion = body.shareVersion;
1385
1496
 
1386
- return new Response(JSON.stringify({
1387
- authShare: shares.authShare,
1388
- primaryDid: 'did:key:z1',
1389
- recoveryMethods: [],
1390
- shareVersion: 255,
1391
- }), { status: 200 });
1497
+ return new Response(
1498
+ JSON.stringify({
1499
+ authShare: shares.authShare,
1500
+ primaryDid: 'did:key:z1',
1501
+ recoveryMethods: [],
1502
+ shareVersion: 255,
1503
+ }),
1504
+ { status: 200 }
1505
+ );
1392
1506
  }
1393
1507
 
1394
1508
  return new Response(JSON.stringify({ success: true }), { status: 200 });
@@ -1418,22 +1532,27 @@ describe('createSSSStrategy', () => {
1418
1532
  fetchCalls.push({
1419
1533
  url: urlStr,
1420
1534
  method,
1421
- body: init?.body as string ?? '',
1535
+ body: (init?.body as string) ?? '',
1422
1536
  });
1423
1537
 
1424
1538
  // fetchAuthShareRaw
1425
1539
  if (urlStr.includes('/keys/auth-share') && method === 'POST') {
1426
- return new Response(JSON.stringify({
1427
- authShare: 'existing',
1428
- primaryDid: 'did:key:z1',
1429
- recoveryMethods: [],
1430
- shareVersion: 5,
1431
- }), { status: 200 });
1540
+ return new Response(
1541
+ JSON.stringify({
1542
+ authShare: 'existing',
1543
+ primaryDid: 'did:key:z1',
1544
+ recoveryMethods: [],
1545
+ shareVersion: 5,
1546
+ }),
1547
+ { status: 200 }
1548
+ );
1432
1549
  }
1433
1550
 
1434
1551
  // putAuthShare
1435
1552
  if (urlStr.includes('/keys/auth-share') && method === 'PUT') {
1436
- return new Response(JSON.stringify({ success: true, shareVersion: 6 }), { status: 200 });
1553
+ return new Response(JSON.stringify({ success: true, shareVersion: 6 }), {
1554
+ status: 200,
1555
+ });
1437
1556
  }
1438
1557
 
1439
1558
  return new Response(JSON.stringify({ success: true }), { status: 200 });
@@ -1489,9 +1608,12 @@ describe('createSSSStrategy', () => {
1489
1608
 
1490
1609
  // getRecoveryShare for phrase — returns shareVersion only (no encryptedShare)
1491
1610
  if (urlStr.includes('/keys/recovery') && method === 'GET') {
1492
- return new Response(JSON.stringify({
1493
- shareVersion: 3,
1494
- }), { status: 200 });
1611
+ return new Response(
1612
+ JSON.stringify({
1613
+ shareVersion: 3,
1614
+ }),
1615
+ { status: 200 }
1616
+ );
1495
1617
  }
1496
1618
 
1497
1619
  // fetchAuthShareRaw — capture requested shareVersion
@@ -1499,12 +1621,17 @@ describe('createSSSStrategy', () => {
1499
1621
  const body = JSON.parse(init?.body as string);
1500
1622
  authShareRequestVersion = body.shareVersion;
1501
1623
 
1502
- return new Response(JSON.stringify({
1503
- authShare: shares.authShare,
1504
- primaryDid: 'did:key:z1',
1505
- recoveryMethods: [{ type: 'phrase', createdAt: new Date().toISOString() }],
1506
- shareVersion: 3,
1507
- }), { status: 200 });
1624
+ return new Response(
1625
+ JSON.stringify({
1626
+ authShare: shares.authShare,
1627
+ primaryDid: 'did:key:z1',
1628
+ recoveryMethods: [
1629
+ { type: 'phrase', createdAt: new Date().toISOString() },
1630
+ ],
1631
+ shareVersion: 3,
1632
+ }),
1633
+ { status: 200 }
1634
+ );
1508
1635
  }
1509
1636
 
1510
1637
  return new Response(JSON.stringify({ success: true }), { status: 200 });
@@ -1536,22 +1663,27 @@ describe('createSSSStrategy', () => {
1536
1663
  fetchCalls.push({
1537
1664
  url: urlStr,
1538
1665
  method,
1539
- body: init?.body as string ?? '',
1666
+ body: (init?.body as string) ?? '',
1540
1667
  });
1541
1668
 
1542
1669
  // fetchAuthShareRaw
1543
1670
  if (urlStr.includes('/keys/auth-share') && method === 'POST') {
1544
- return new Response(JSON.stringify({
1545
- authShare: 'existing',
1546
- primaryDid: 'did:key:z1',
1547
- recoveryMethods: [],
1548
- shareVersion: 7,
1549
- }), { status: 200 });
1671
+ return new Response(
1672
+ JSON.stringify({
1673
+ authShare: 'existing',
1674
+ primaryDid: 'did:key:z1',
1675
+ recoveryMethods: [],
1676
+ shareVersion: 7,
1677
+ }),
1678
+ { status: 200 }
1679
+ );
1550
1680
  }
1551
1681
 
1552
1682
  // putAuthShare
1553
1683
  if (urlStr.includes('/keys/auth-share') && method === 'PUT') {
1554
- return new Response(JSON.stringify({ success: true, shareVersion: 8 }), { status: 200 });
1684
+ return new Response(JSON.stringify({ success: true, shareVersion: 8 }), {
1685
+ status: 200,
1686
+ });
1555
1687
  }
1556
1688
 
1557
1689
  return new Response(JSON.stringify({ success: true }), { status: 200 });
@@ -1594,7 +1726,6 @@ describe('createSSSStrategy', () => {
1594
1726
  // -----------------------------------------------------------------------
1595
1727
 
1596
1728
  describe('email routing — recovery email vs primary', () => {
1597
-
1598
1729
  it('setupRecoveryMethod("email") sends ONLY to recovery email, never to primary', async () => {
1599
1730
  const strat = createSSSStrategy({
1600
1731
  serverUrl: 'http://test-server:5100/api',
@@ -1612,21 +1743,26 @@ describe('createSSSStrategy', () => {
1612
1743
  const urlStr = typeof url === 'string' ? url : url.toString();
1613
1744
  const method = (init?.method ?? 'GET').toUpperCase();
1614
1745
 
1615
- fetchCalls.push({ url: urlStr, method, body: init?.body as string ?? '' });
1746
+ fetchCalls.push({ url: urlStr, method, body: (init?.body as string) ?? '' });
1616
1747
 
1617
1748
  // fetchAuthShareRaw
1618
1749
  if (urlStr.includes('/keys/auth-share') && method === 'POST') {
1619
- return new Response(JSON.stringify({
1620
- authShare: 'existing',
1621
- primaryDid: 'did:key:z1',
1622
- recoveryMethods: [],
1623
- shareVersion: 10,
1624
- }), { status: 200 });
1750
+ return new Response(
1751
+ JSON.stringify({
1752
+ authShare: 'existing',
1753
+ primaryDid: 'did:key:z1',
1754
+ recoveryMethods: [],
1755
+ shareVersion: 10,
1756
+ }),
1757
+ { status: 200 }
1758
+ );
1625
1759
  }
1626
1760
 
1627
1761
  // putAuthShare
1628
1762
  if (urlStr.includes('/keys/auth-share') && method === 'PUT') {
1629
- return new Response(JSON.stringify({ success: true, shareVersion: 11 }), { status: 200 });
1763
+ return new Response(JSON.stringify({ success: true, shareVersion: 11 }), {
1764
+ status: 200,
1765
+ });
1630
1766
  }
1631
1767
 
1632
1768
  return new Response(JSON.stringify({ success: true }), { status: 200 });
@@ -1663,15 +1799,19 @@ describe('createSSSStrategy', () => {
1663
1799
  const originalKey = 'f1f2f3f4f5f6'.padEnd(64, '0');
1664
1800
 
1665
1801
  // First: fetchServerKeyStatus to set hasRecoveryEmail = true
1666
- vi.spyOn(globalThis, 'fetch').mockImplementationOnce(async () =>
1667
- new Response(JSON.stringify({
1668
- authShare: 'existing-share',
1669
- primaryDid: 'did:key:z1',
1670
- recoveryMethods: [],
1671
- shareVersion: 1,
1672
- keyProvider: 'sss',
1673
- maskedRecoveryEmail: 'r****@personal.com',
1674
- }), { status: 200 })
1802
+ vi.spyOn(globalThis, 'fetch').mockImplementationOnce(
1803
+ async () =>
1804
+ new Response(
1805
+ JSON.stringify({
1806
+ authShare: 'existing-share',
1807
+ primaryDid: 'did:key:z1',
1808
+ recoveryMethods: [],
1809
+ shareVersion: 1,
1810
+ keyProvider: 'sss',
1811
+ maskedRecoveryEmail: 'r****@personal.com',
1812
+ }),
1813
+ { status: 200 }
1814
+ )
1675
1815
  );
1676
1816
 
1677
1817
  await stratWithRecovery.fetchServerKeyStatus('token', 'firebase');
@@ -1687,20 +1827,25 @@ describe('createSSSStrategy', () => {
1687
1827
  const urlStr = typeof url === 'string' ? url : url.toString();
1688
1828
  const method = (init?.method ?? 'GET').toUpperCase();
1689
1829
 
1690
- fetchCalls.push({ url: urlStr, method, body: init?.body as string ?? '' });
1830
+ fetchCalls.push({ url: urlStr, method, body: (init?.body as string) ?? '' });
1691
1831
 
1692
1832
  if (urlStr.includes('/keys/auth-share') && method === 'POST') {
1693
- return new Response(JSON.stringify({
1694
- authShare: 'existing',
1695
- primaryDid: 'did:key:z1',
1696
- recoveryMethods: [],
1697
- shareVersion: 1,
1698
- maskedRecoveryEmail: 'r****@personal.com',
1699
- }), { status: 200 });
1833
+ return new Response(
1834
+ JSON.stringify({
1835
+ authShare: 'existing',
1836
+ primaryDid: 'did:key:z1',
1837
+ recoveryMethods: [],
1838
+ shareVersion: 1,
1839
+ maskedRecoveryEmail: 'r****@personal.com',
1840
+ }),
1841
+ { status: 200 }
1842
+ );
1700
1843
  }
1701
1844
 
1702
1845
  if (urlStr.includes('/keys/auth-share') && method === 'PUT') {
1703
- return new Response(JSON.stringify({ success: true, shareVersion: 2 }), { status: 200 });
1846
+ return new Response(JSON.stringify({ success: true, shareVersion: 2 }), {
1847
+ status: 200,
1848
+ });
1704
1849
  }
1705
1850
 
1706
1851
  return new Response(JSON.stringify({ success: true }), { status: 200 });
@@ -1735,15 +1880,19 @@ describe('createSSSStrategy', () => {
1735
1880
  const originalKey = 'd1d2d3d4d5d6'.padEnd(64, '0');
1736
1881
 
1737
1882
  // Prime hasRecoveryEmail via fetchServerKeyStatus
1738
- vi.spyOn(globalThis, 'fetch').mockImplementationOnce(async () =>
1739
- new Response(JSON.stringify({
1740
- authShare: 'existing-share',
1741
- primaryDid: 'did:key:z1',
1742
- recoveryMethods: [],
1743
- shareVersion: 3,
1744
- keyProvider: 'sss',
1745
- maskedRecoveryEmail: 'r****@personal.com',
1746
- }), { status: 200 })
1883
+ vi.spyOn(globalThis, 'fetch').mockImplementationOnce(
1884
+ async () =>
1885
+ new Response(
1886
+ JSON.stringify({
1887
+ authShare: 'existing-share',
1888
+ primaryDid: 'did:key:z1',
1889
+ recoveryMethods: [],
1890
+ shareVersion: 3,
1891
+ keyProvider: 'sss',
1892
+ maskedRecoveryEmail: 'r****@personal.com',
1893
+ }),
1894
+ { status: 200 }
1895
+ )
1747
1896
  );
1748
1897
 
1749
1898
  await stratWithRecovery.fetchServerKeyStatus('token', 'firebase');
@@ -1754,8 +1903,11 @@ describe('createSSSStrategy', () => {
1754
1903
  const { remoteKey } = await stratWithRecovery.splitKey(originalKey);
1755
1904
 
1756
1905
  // storeAuthShare to cache version
1757
- vi.spyOn(globalThis, 'fetch').mockImplementationOnce(async () =>
1758
- new Response(JSON.stringify({ success: true, shareVersion: 4 }), { status: 200 })
1906
+ vi.spyOn(globalThis, 'fetch').mockImplementationOnce(
1907
+ async () =>
1908
+ new Response(JSON.stringify({ success: true, shareVersion: 4 }), {
1909
+ status: 200,
1910
+ })
1759
1911
  );
1760
1912
 
1761
1913
  await stratWithRecovery.storeAuthShare('token', 'firebase', remoteKey, 'did:key:z1');
@@ -1769,7 +1921,10 @@ describe('createSSSStrategy', () => {
1769
1921
  });
1770
1922
 
1771
1923
  await stratWithRecovery.sendEmailBackupShare!(
1772
- 'token', 'firebase', originalKey, 'primary@test.com'
1924
+ 'token',
1925
+ 'firebase',
1926
+ originalKey,
1927
+ 'primary@test.com'
1773
1928
  );
1774
1929
 
1775
1930
  // Should route to recovery email, not primary
@@ -1790,8 +1945,11 @@ describe('createSSSStrategy', () => {
1790
1945
  const { remoteKey } = await strat.splitKey(originalKey);
1791
1946
 
1792
1947
  // storeAuthShare to cache version
1793
- vi.spyOn(globalThis, 'fetch').mockImplementationOnce(async () =>
1794
- new Response(JSON.stringify({ success: true, shareVersion: 1 }), { status: 200 })
1948
+ vi.spyOn(globalThis, 'fetch').mockImplementationOnce(
1949
+ async () =>
1950
+ new Response(JSON.stringify({ success: true, shareVersion: 1 }), {
1951
+ status: 200,
1952
+ })
1795
1953
  );
1796
1954
 
1797
1955
  await strat.storeAuthShare('token', 'firebase', remoteKey, 'did:key:z1');
@@ -1803,9 +1961,7 @@ describe('createSSSStrategy', () => {
1803
1961
  return new Response(null, { status: 200 });
1804
1962
  });
1805
1963
 
1806
- await strat.sendEmailBackupShare!(
1807
- 'token', 'firebase', originalKey, 'primary@test.com'
1808
- );
1964
+ await strat.sendEmailBackupShare!('token', 'firebase', originalKey, 'primary@test.com');
1809
1965
 
1810
1966
  // Should send to the explicit primary email
1811
1967
  expect(capturedBody).toBeDefined();
@@ -1830,14 +1986,17 @@ describe('createSSSStrategy', () => {
1830
1986
 
1831
1987
  // Step 1: fetchServerKeyStatus returns a user WITH a recovery email
1832
1988
  vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce(
1833
- new Response(JSON.stringify({
1834
- authShare: 'share',
1835
- keyProvider: 'sss',
1836
- primaryDid: 'did:key:z1',
1837
- recoveryMethods: [{ type: 'email', createdAt: new Date().toISOString() }],
1838
- maskedRecoveryEmail: 'r***@test.com',
1839
- shareVersion: 1,
1840
- }), { status: 200 })
1989
+ new Response(
1990
+ JSON.stringify({
1991
+ authShare: 'share',
1992
+ keyProvider: 'sss',
1993
+ primaryDid: 'did:key:z1',
1994
+ recoveryMethods: [{ type: 'email', createdAt: new Date().toISOString() }],
1995
+ maskedRecoveryEmail: 'r***@test.com',
1996
+ shareVersion: 1,
1997
+ }),
1998
+ { status: 200 }
1999
+ )
1841
2000
  );
1842
2001
 
1843
2002
  await strat.fetchServerKeyStatus('token', 'firebase');
@@ -1885,7 +2044,6 @@ describe('createSSSStrategy', () => {
1885
2044
  // -----------------------------------------------------------------------
1886
2045
 
1887
2046
  describe('formatVersionedEmailShare / parseVersionedEmailShare', () => {
1888
-
1889
2047
  it('round-trip: format then parse recovers the original share and version', () => {
1890
2048
  const share = 'abcdef1234567890';
1891
2049
  const version = 5;