@fgv/ts-web-extras 5.1.0-17 → 5.1.0-18

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 (59) hide show
  1. package/.rush/temp/{edc66e6a37414a0b69e52d768684c18c9d5825e3.tar.log → 680645452ebdb4d7f294f08d3b45f143d69c3f5d.tar.log} +2 -2
  2. package/.rush/temp/chunked-rush-logs/ts-web-extras.build.chunks.jsonl +12 -12
  3. package/.rush/temp/operation/build/all.log +12 -12
  4. package/.rush/temp/operation/build/log-chunks.jsonl +12 -12
  5. package/.rush/temp/operation/build/state.json +1 -1
  6. package/dist/packlets/crypto-utils/browserCryptoProvider.js +58 -17
  7. package/dist/packlets/crypto-utils/browserCryptoProvider.js.map +1 -1
  8. package/dist/ts-web-extras.d.ts +27 -6
  9. package/docs/CryptoUtils/classes/BrowserCryptoProvider.exportPublicKeyJwk.md +24 -0
  10. package/docs/CryptoUtils/classes/BrowserCryptoProvider.generateKeyPair.md +25 -0
  11. package/docs/CryptoUtils/classes/BrowserCryptoProvider.importPublicKeyJwk.md +25 -0
  12. package/docs/CryptoUtils/classes/BrowserCryptoProvider.md +39 -0
  13. package/docs/classes/BrowserCryptoProvider.exportPublicKeyJwk.md +24 -0
  14. package/docs/classes/BrowserCryptoProvider.generateKeyPair.md +25 -0
  15. package/docs/classes/BrowserCryptoProvider.importPublicKeyJwk.md +25 -0
  16. package/docs/classes/BrowserCryptoProvider.md +39 -0
  17. package/etc/ts-web-extras.api.md +5 -5
  18. package/lib/packlets/crypto-utils/browserCryptoProvider.d.ts +27 -5
  19. package/lib/packlets/crypto-utils/browserCryptoProvider.d.ts.map +1 -1
  20. package/lib/packlets/crypto-utils/browserCryptoProvider.js +57 -16
  21. package/lib/packlets/crypto-utils/browserCryptoProvider.js.map +1 -1
  22. package/package.json +10 -10
  23. package/rush-logs/ts-web-extras.build.cache.log +1 -1
  24. package/rush-logs/ts-web-extras.build.log +12 -12
  25. package/src/packlets/crypto-utils/browserCryptoProvider.ts +76 -22
  26. package/temp/build/typescript/ts_8nwakTlr.json +1 -1
  27. package/temp/coverage/crypto-utils/browserCryptoProvider.ts.html +189 -27
  28. package/temp/coverage/crypto-utils/browserHashProvider.ts.html +1 -1
  29. package/temp/coverage/crypto-utils/index.html +7 -7
  30. package/temp/coverage/file-tree/directoryHandleStore.ts.html +1 -1
  31. package/temp/coverage/file-tree/fileApiTreeAccessors.ts.html +1 -1
  32. package/temp/coverage/file-tree/fileSystemAccessTreeAccessors.ts.html +1 -1
  33. package/temp/coverage/file-tree/httpTreeAccessors.ts.html +1 -1
  34. package/temp/coverage/file-tree/index.html +1 -1
  35. package/temp/coverage/file-tree/localStorageTreeAccessors.ts.html +1 -1
  36. package/temp/coverage/helpers/fileTreeHelpers.ts.html +1 -1
  37. package/temp/coverage/helpers/index.html +1 -1
  38. package/temp/coverage/index.html +7 -7
  39. package/temp/coverage/lcov-report/crypto-utils/browserCryptoProvider.ts.html +189 -27
  40. package/temp/coverage/lcov-report/crypto-utils/browserHashProvider.ts.html +1 -1
  41. package/temp/coverage/lcov-report/crypto-utils/index.html +7 -7
  42. package/temp/coverage/lcov-report/file-tree/directoryHandleStore.ts.html +1 -1
  43. package/temp/coverage/lcov-report/file-tree/fileApiTreeAccessors.ts.html +1 -1
  44. package/temp/coverage/lcov-report/file-tree/fileSystemAccessTreeAccessors.ts.html +1 -1
  45. package/temp/coverage/lcov-report/file-tree/httpTreeAccessors.ts.html +1 -1
  46. package/temp/coverage/lcov-report/file-tree/index.html +1 -1
  47. package/temp/coverage/lcov-report/file-tree/localStorageTreeAccessors.ts.html +1 -1
  48. package/temp/coverage/lcov-report/helpers/fileTreeHelpers.ts.html +1 -1
  49. package/temp/coverage/lcov-report/helpers/index.html +1 -1
  50. package/temp/coverage/lcov-report/index.html +7 -7
  51. package/temp/coverage/lcov-report/url-utils/index.html +1 -1
  52. package/temp/coverage/lcov-report/url-utils/urlParams.ts.html +1 -1
  53. package/temp/coverage/lcov.info +75 -15
  54. package/temp/coverage/url-utils/index.html +1 -1
  55. package/temp/coverage/url-utils/urlParams.ts.html +1 -1
  56. package/temp/test/jest/haste-map-7492f1b44480e0cdd1f220078fb3afd8-c8dd6c3430605adeb2f1cadf4f75e791-8c9336785555d572065b28c111982ba4 +0 -0
  57. package/temp/test/jest/perf-cache-7492f1b44480e0cdd1f220078fb3afd8-da39a3ee5e6b4b0d3255bfef95601890 +1 -1
  58. package/temp/ts-web-extras.api.json +253 -4
  59. package/temp/ts-web-extras.api.md +5 -5
@@ -25,7 +25,7 @@
25
25
  <div class='fl pad1y space-right2'>
26
26
  <span class="strong">100% </span>
27
27
  <span class="quiet">Statements</span>
28
- <span class='fraction'>332/332</span>
28
+ <span class='fraction'>386/386</span>
29
29
  </div>
30
30
 
31
31
 
@@ -39,14 +39,14 @@
39
39
  <div class='fl pad1y space-right2'>
40
40
  <span class="strong">100% </span>
41
41
  <span class="quiet">Functions</span>
42
- <span class='fraction'>11/11</span>
42
+ <span class='fraction'>14/14</span>
43
43
  </div>
44
44
 
45
45
 
46
46
  <div class='fl pad1y space-right2'>
47
47
  <span class="strong">100% </span>
48
48
  <span class="quiet">Lines</span>
49
- <span class='fraction'>332/332</span>
49
+ <span class='fraction'>386/386</span>
50
50
  </div>
51
51
 
52
52
 
@@ -395,7 +395,115 @@
395
395
  <a name='L330'></a><a href='#L330'>330</a>
396
396
  <a name='L331'></a><a href='#L331'>331</a>
397
397
  <a name='L332'></a><a href='#L332'>332</a>
398
- <a name='L333'></a><a href='#L333'>333</a></td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</span>
398
+ <a name='L333'></a><a href='#L333'>333</a>
399
+ <a name='L334'></a><a href='#L334'>334</a>
400
+ <a name='L335'></a><a href='#L335'>335</a>
401
+ <a name='L336'></a><a href='#L336'>336</a>
402
+ <a name='L337'></a><a href='#L337'>337</a>
403
+ <a name='L338'></a><a href='#L338'>338</a>
404
+ <a name='L339'></a><a href='#L339'>339</a>
405
+ <a name='L340'></a><a href='#L340'>340</a>
406
+ <a name='L341'></a><a href='#L341'>341</a>
407
+ <a name='L342'></a><a href='#L342'>342</a>
408
+ <a name='L343'></a><a href='#L343'>343</a>
409
+ <a name='L344'></a><a href='#L344'>344</a>
410
+ <a name='L345'></a><a href='#L345'>345</a>
411
+ <a name='L346'></a><a href='#L346'>346</a>
412
+ <a name='L347'></a><a href='#L347'>347</a>
413
+ <a name='L348'></a><a href='#L348'>348</a>
414
+ <a name='L349'></a><a href='#L349'>349</a>
415
+ <a name='L350'></a><a href='#L350'>350</a>
416
+ <a name='L351'></a><a href='#L351'>351</a>
417
+ <a name='L352'></a><a href='#L352'>352</a>
418
+ <a name='L353'></a><a href='#L353'>353</a>
419
+ <a name='L354'></a><a href='#L354'>354</a>
420
+ <a name='L355'></a><a href='#L355'>355</a>
421
+ <a name='L356'></a><a href='#L356'>356</a>
422
+ <a name='L357'></a><a href='#L357'>357</a>
423
+ <a name='L358'></a><a href='#L358'>358</a>
424
+ <a name='L359'></a><a href='#L359'>359</a>
425
+ <a name='L360'></a><a href='#L360'>360</a>
426
+ <a name='L361'></a><a href='#L361'>361</a>
427
+ <a name='L362'></a><a href='#L362'>362</a>
428
+ <a name='L363'></a><a href='#L363'>363</a>
429
+ <a name='L364'></a><a href='#L364'>364</a>
430
+ <a name='L365'></a><a href='#L365'>365</a>
431
+ <a name='L366'></a><a href='#L366'>366</a>
432
+ <a name='L367'></a><a href='#L367'>367</a>
433
+ <a name='L368'></a><a href='#L368'>368</a>
434
+ <a name='L369'></a><a href='#L369'>369</a>
435
+ <a name='L370'></a><a href='#L370'>370</a>
436
+ <a name='L371'></a><a href='#L371'>371</a>
437
+ <a name='L372'></a><a href='#L372'>372</a>
438
+ <a name='L373'></a><a href='#L373'>373</a>
439
+ <a name='L374'></a><a href='#L374'>374</a>
440
+ <a name='L375'></a><a href='#L375'>375</a>
441
+ <a name='L376'></a><a href='#L376'>376</a>
442
+ <a name='L377'></a><a href='#L377'>377</a>
443
+ <a name='L378'></a><a href='#L378'>378</a>
444
+ <a name='L379'></a><a href='#L379'>379</a>
445
+ <a name='L380'></a><a href='#L380'>380</a>
446
+ <a name='L381'></a><a href='#L381'>381</a>
447
+ <a name='L382'></a><a href='#L382'>382</a>
448
+ <a name='L383'></a><a href='#L383'>383</a>
449
+ <a name='L384'></a><a href='#L384'>384</a>
450
+ <a name='L385'></a><a href='#L385'>385</a>
451
+ <a name='L386'></a><a href='#L386'>386</a>
452
+ <a name='L387'></a><a href='#L387'>387</a></td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</span>
453
+ <span class="cline-any cline-yes">1x</span>
454
+ <span class="cline-any cline-yes">1x</span>
455
+ <span class="cline-any cline-yes">1x</span>
456
+ <span class="cline-any cline-yes">1x</span>
457
+ <span class="cline-any cline-yes">1x</span>
458
+ <span class="cline-any cline-yes">1x</span>
459
+ <span class="cline-any cline-yes">1x</span>
460
+ <span class="cline-any cline-yes">1x</span>
461
+ <span class="cline-any cline-yes">1x</span>
462
+ <span class="cline-any cline-yes">1x</span>
463
+ <span class="cline-any cline-yes">1x</span>
464
+ <span class="cline-any cline-yes">1x</span>
465
+ <span class="cline-any cline-yes">1x</span>
466
+ <span class="cline-any cline-yes">1x</span>
467
+ <span class="cline-any cline-yes">1x</span>
468
+ <span class="cline-any cline-yes">1x</span>
469
+ <span class="cline-any cline-yes">1x</span>
470
+ <span class="cline-any cline-yes">1x</span>
471
+ <span class="cline-any cline-yes">1x</span>
472
+ <span class="cline-any cline-yes">1x</span>
473
+ <span class="cline-any cline-yes">1x</span>
474
+ <span class="cline-any cline-yes">1x</span>
475
+ <span class="cline-any cline-yes">1x</span>
476
+ <span class="cline-any cline-yes">1x</span>
477
+ <span class="cline-any cline-yes">1x</span>
478
+ <span class="cline-any cline-yes">1x</span>
479
+ <span class="cline-any cline-yes">1x</span>
480
+ <span class="cline-any cline-yes">1x</span>
481
+ <span class="cline-any cline-yes">1x</span>
482
+ <span class="cline-any cline-yes">1x</span>
483
+ <span class="cline-any cline-yes">1x</span>
484
+ <span class="cline-any cline-yes">1x</span>
485
+ <span class="cline-any cline-yes">1x</span>
486
+ <span class="cline-any cline-yes">1x</span>
487
+ <span class="cline-any cline-yes">1x</span>
488
+ <span class="cline-any cline-yes">1x</span>
489
+ <span class="cline-any cline-yes">1x</span>
490
+ <span class="cline-any cline-yes">1x</span>
491
+ <span class="cline-any cline-yes">1x</span>
492
+ <span class="cline-any cline-yes">1x</span>
493
+ <span class="cline-any cline-yes">1x</span>
494
+ <span class="cline-any cline-yes">1x</span>
495
+ <span class="cline-any cline-yes">1x</span>
496
+ <span class="cline-any cline-yes">1x</span>
497
+ <span class="cline-any cline-yes">1x</span>
498
+ <span class="cline-any cline-yes">1x</span>
499
+ <span class="cline-any cline-yes">1x</span>
500
+ <span class="cline-any cline-yes">1x</span>
501
+ <span class="cline-any cline-yes">1x</span>
502
+ <span class="cline-any cline-yes">1x</span>
503
+ <span class="cline-any cline-yes">1x</span>
504
+ <span class="cline-any cline-yes">1x</span>
505
+ <span class="cline-any cline-yes">1x</span>
506
+ <span class="cline-any cline-yes">1x</span>
399
507
  <span class="cline-any cline-yes">1x</span>
400
508
  <span class="cline-any cline-yes">1x</span>
401
509
  <span class="cline-any cline-yes">1x</span>
@@ -748,13 +856,9 @@
748
856
  // SOFTWARE.
749
857
  &nbsp;
750
858
  /* c8 ignore start - Browser-only implementation cannot be tested in Node.js environment */
751
- import { captureResult, fail, Failure, Result, succeed, Success } from '@fgv/ts-utils';
859
+ import { captureAsyncResult, captureResult, fail, Failure, Result, succeed, Success } from '@fgv/ts-utils';
752
860
  import { CryptoUtils } from '@fgv/ts-extras';
753
861
  &nbsp;
754
- type ICryptoProvider = CryptoUtils.ICryptoProvider;
755
- type IEncryptionResult = CryptoUtils.IEncryptionResult;
756
- const CryptoConstants = CryptoUtils.Constants;
757
- &nbsp;
758
862
  /**
759
863
  * Extracts an `ArrayBuffer` from a Uint8Array, handling the potential SharedArrayBuffer case.
760
864
  * @param arr - The Uint8Array to extract from
@@ -776,7 +880,7 @@ function toArrayBuffer(arr: Uint8Array): ArrayBuffer {
776
880
  *
777
881
  * @public
778
882
  */
779
- export class BrowserCryptoProvider implements ICryptoProvider {
883
+ export class BrowserCryptoProvider implements CryptoUtils.ICryptoProvider {
780
884
  private readonly _crypto: Crypto;
781
885
  &nbsp;
782
886
  /**
@@ -801,14 +905,14 @@ export class BrowserCryptoProvider implements ICryptoProvider {
801
905
  * @param key - 32-byte encryption key
802
906
  * @returns `Success` with encryption result, or `Failure` with an error.
803
907
  */
804
- public async encrypt(plaintext: string, key: Uint8Array): Promise&lt;Result&lt;IEncryptionResult&gt;&gt; {
805
- if (key.length !== CryptoConstants.AES_256_KEY_SIZE) {
806
- return Failure.with(`Key must be ${CryptoConstants.AES_256_KEY_SIZE} bytes, got ${key.length}`);
908
+ public async encrypt(plaintext: string, key: Uint8Array): Promise&lt;Result&lt;CryptoUtils.IEncryptionResult&gt;&gt; {
909
+ if (key.length !== CryptoUtils.Constants.AES_256_KEY_SIZE) {
910
+ return Failure.with(`Key must be ${CryptoUtils.Constants.AES_256_KEY_SIZE} bytes, got ${key.length}`);
807
911
  }
808
912
  &nbsp;
809
913
  try {
810
914
  // Generate random IV
811
- const iv = this._crypto.getRandomValues(new Uint8Array(CryptoConstants.GCM_IV_SIZE));
915
+ const iv = this._crypto.getRandomValues(new Uint8Array(CryptoUtils.Constants.GCM_IV_SIZE));
812
916
  &nbsp;
813
917
  // Import the key
814
918
  const cryptoKey = await this._crypto.subtle.importKey(
@@ -828,7 +932,7 @@ export class BrowserCryptoProvider implements ICryptoProvider {
828
932
  {
829
933
  name: 'AES-GCM',
830
934
  iv: iv,
831
- tagLength: CryptoConstants.GCM_AUTH_TAG_SIZE * 8 // bits
935
+ tagLength: CryptoUtils.Constants.GCM_AUTH_TAG_SIZE * 8 // bits
832
936
  },
833
937
  cryptoKey,
834
938
  plaintextBytes
@@ -838,9 +942,9 @@ export class BrowserCryptoProvider implements ICryptoProvider {
838
942
  const encryptedArray = new Uint8Array(encryptedWithTag);
839
943
  const encryptedData = encryptedArray.slice(
840
944
  0,
841
- encryptedArray.length - CryptoConstants.GCM_AUTH_TAG_SIZE
945
+ encryptedArray.length - CryptoUtils.Constants.GCM_AUTH_TAG_SIZE
842
946
  );
843
- const authTag = encryptedArray.slice(encryptedArray.length - CryptoConstants.GCM_AUTH_TAG_SIZE);
947
+ const authTag = encryptedArray.slice(encryptedArray.length - CryptoUtils.Constants.GCM_AUTH_TAG_SIZE);
844
948
  return Success.with({
845
949
  iv,
846
950
  authTag,
@@ -866,15 +970,15 @@ export class BrowserCryptoProvider implements ICryptoProvider {
866
970
  iv: Uint8Array,
867
971
  authTag: Uint8Array
868
972
  ): Promise&lt;Result&lt;string&gt;&gt; {
869
- if (key.length !== CryptoConstants.AES_256_KEY_SIZE) {
870
- return Failure.with(`Key must be ${CryptoConstants.AES_256_KEY_SIZE} bytes, got ${key.length}`);
973
+ if (key.length !== CryptoUtils.Constants.AES_256_KEY_SIZE) {
974
+ return Failure.with(`Key must be ${CryptoUtils.Constants.AES_256_KEY_SIZE} bytes, got ${key.length}`);
871
975
  }
872
- if (iv.length !== CryptoConstants.GCM_IV_SIZE) {
873
- return Failure.with(`IV must be ${CryptoConstants.GCM_IV_SIZE} bytes, got ${iv.length}`);
976
+ if (iv.length !== CryptoUtils.Constants.GCM_IV_SIZE) {
977
+ return Failure.with(`IV must be ${CryptoUtils.Constants.GCM_IV_SIZE} bytes, got ${iv.length}`);
874
978
  }
875
- if (authTag.length !== CryptoConstants.GCM_AUTH_TAG_SIZE) {
979
+ if (authTag.length !== CryptoUtils.Constants.GCM_AUTH_TAG_SIZE) {
876
980
  return Failure.with(
877
- `Auth tag must be ${CryptoConstants.GCM_AUTH_TAG_SIZE} bytes, got ${authTag.length}`
981
+ `Auth tag must be ${CryptoUtils.Constants.GCM_AUTH_TAG_SIZE} bytes, got ${authTag.length}`
878
982
  );
879
983
  }
880
984
  &nbsp;
@@ -898,7 +1002,7 @@ export class BrowserCryptoProvider implements ICryptoProvider {
898
1002
  {
899
1003
  name: 'AES-GCM',
900
1004
  iv: toArrayBuffer(iv),
901
- tagLength: CryptoConstants.GCM_AUTH_TAG_SIZE * 8 // bits
1005
+ tagLength: CryptoUtils.Constants.GCM_AUTH_TAG_SIZE * 8 // bits
902
1006
  },
903
1007
  cryptoKey,
904
1008
  encryptedWithTag
@@ -919,7 +1023,9 @@ export class BrowserCryptoProvider implements ICryptoProvider {
919
1023
  */
920
1024
  public async generateKey(): Promise&lt;Result&lt;Uint8Array&gt;&gt; {
921
1025
  try {
922
- return Success.with(this._crypto.getRandomValues(new Uint8Array(CryptoConstants.AES_256_KEY_SIZE)));
1026
+ return Success.with(
1027
+ this._crypto.getRandomValues(new Uint8Array(CryptoUtils.Constants.AES_256_KEY_SIZE))
1028
+ );
923
1029
  } catch (e) {
924
1030
  const message = e instanceof Error ? e.message : String(e);
925
1031
  return Failure.with(`Key generation failed: ${message}`);
@@ -964,7 +1070,7 @@ export class BrowserCryptoProvider implements ICryptoProvider {
964
1070
  hash: 'SHA-256'
965
1071
  },
966
1072
  keyMaterial,
967
- CryptoConstants.AES_256_KEY_SIZE * 8 // bits
1073
+ CryptoUtils.Constants.AES_256_KEY_SIZE * 8 // bits
968
1074
  );
969
1075
  &nbsp;
970
1076
  return Success.with(new Uint8Array(derivedBits));
@@ -1047,6 +1153,62 @@ export class BrowserCryptoProvider implements ICryptoProvider {
1047
1153
  return Failure.with('Invalid base64 string');
1048
1154
  }
1049
1155
  }
1156
+ &nbsp;
1157
+ // ============================================================================
1158
+ // Asymmetric Key Operations
1159
+ // ============================================================================
1160
+ &nbsp;
1161
+ /**
1162
+ * Generates a new asymmetric keypair via Web Crypto.
1163
+ * @param algorithm - The algorithm to use.
1164
+ * @param extractable - Whether the resulting keys may be exported.
1165
+ * @returns `Success` with the generated `CryptoKeyPair`, or `Failure` with an error.
1166
+ */
1167
+ public async generateKeyPair(
1168
+ algorithm: CryptoUtils.KeyPairAlgorithm,
1169
+ extractable: boolean
1170
+ ): Promise&lt;Result&lt;CryptoKeyPair&gt;&gt; {
1171
+ const params = CryptoUtils.keyPairAlgorithmParams[algorithm];
1172
+ const result = await captureAsyncResult(() =&gt;
1173
+ this._crypto.subtle.generateKey(params.generateKey, extractable, params.keyPairUsages)
1174
+ );
1175
+ return result.withErrorFormat((e) =&gt; `Failed to generate ${algorithm} keypair: ${e}`);
1176
+ }
1177
+ &nbsp;
1178
+ /**
1179
+ * Exports a public `CryptoKey` as a JSON Web Key.
1180
+ * @remarks
1181
+ * Rejects non-public keys at runtime. WebCrypto's `exportKey('jwk', ...)`
1182
+ * does not enforce public-vs-private; without this guard a caller that
1183
+ * passed an extractable private key would receive its private fields
1184
+ * (`d`, `p`, `q`, ...) as JWK, defeating the method's name.
1185
+ * @param publicKey - Extractable public key to export.
1186
+ * @returns `Success` with the JWK, or `Failure` if not a public key or if export fails.
1187
+ */
1188
+ public async exportPublicKeyJwk(publicKey: CryptoKey): Promise&lt;Result&lt;JsonWebKey&gt;&gt; {
1189
+ if (publicKey.type !== 'public') {
1190
+ return Failure.with(`exportPublicKeyJwk requires a public CryptoKey, got '${publicKey.type}'`);
1191
+ }
1192
+ const result = await captureAsyncResult(() =&gt; this._crypto.subtle.exportKey('jwk', publicKey));
1193
+ return result.withErrorFormat((e) =&gt; `Failed to export public key as JWK: ${e}`);
1194
+ }
1195
+ &nbsp;
1196
+ /**
1197
+ * Imports a public-key JWK as a `CryptoKey` for the requested algorithm.
1198
+ * @param jwk - The JSON Web Key produced by a prior export.
1199
+ * @param algorithm - The algorithm the key was generated for.
1200
+ * @returns `Success` with the imported public `CryptoKey`, or `Failure` with an error.
1201
+ */
1202
+ public async importPublicKeyJwk(
1203
+ jwk: JsonWebKey,
1204
+ algorithm: CryptoUtils.KeyPairAlgorithm
1205
+ ): Promise&lt;Result&lt;CryptoKey&gt;&gt; {
1206
+ const params = CryptoUtils.keyPairAlgorithmParams[algorithm];
1207
+ const result = await captureAsyncResult(() =&gt;
1208
+ this._crypto.subtle.importKey('jwk', jwk, params.importPublicKey, true, params.publicKeyUsages)
1209
+ );
1210
+ return result.withErrorFormat((e) =&gt; `Failed to import ${algorithm} public key from JWK: ${e}`);
1211
+ }
1050
1212
  }
1051
1213
  &nbsp;
1052
1214
  /**
@@ -1066,7 +1228,7 @@ export function createBrowserCryptoProvider(): Result&lt;BrowserCryptoProvider&g
1066
1228
  <div class='footer quiet pad2 space-top1 center small'>
1067
1229
  Code coverage generated by
1068
1230
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
1069
- at 2026-04-27T03:46:28.725Z
1231
+ at 2026-04-27T05:38:37.090Z
1070
1232
  </div>
1071
1233
  <script src="../prettify.js"></script>
1072
1234
  <script>
@@ -289,7 +289,7 @@ export class BrowserHashProvider {
289
289
  <div class='footer quiet pad2 space-top1 center small'>
290
290
  Code coverage generated by
291
291
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
292
- at 2026-04-27T03:46:28.725Z
292
+ at 2026-04-27T05:38:37.090Z
293
293
  </div>
294
294
  <script src="../prettify.js"></script>
295
295
  <script>
@@ -25,7 +25,7 @@
25
25
  <div class='fl pad1y space-right2'>
26
26
  <span class="strong">100% </span>
27
27
  <span class="quiet">Statements</span>
28
- <span class='fraction'>405/405</span>
28
+ <span class='fraction'>459/459</span>
29
29
  </div>
30
30
 
31
31
 
@@ -39,14 +39,14 @@
39
39
  <div class='fl pad1y space-right2'>
40
40
  <span class="strong">100% </span>
41
41
  <span class="quiet">Functions</span>
42
- <span class='fraction'>13/13</span>
42
+ <span class='fraction'>16/16</span>
43
43
  </div>
44
44
 
45
45
 
46
46
  <div class='fl pad1y space-right2'>
47
47
  <span class="strong">100% </span>
48
48
  <span class="quiet">Lines</span>
49
- <span class='fraction'>405/405</span>
49
+ <span class='fraction'>459/459</span>
50
50
  </div>
51
51
 
52
52
 
@@ -84,13 +84,13 @@
84
84
  <div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
85
85
  </td>
86
86
  <td data-value="100" class="pct high">100%</td>
87
- <td data-value="332" class="abs high">332/332</td>
87
+ <td data-value="386" class="abs high">386/386</td>
88
88
  <td data-value="100" class="pct high">100%</td>
89
89
  <td data-value="0" class="abs high">0/0</td>
90
90
  <td data-value="100" class="pct high">100%</td>
91
- <td data-value="11" class="abs high">11/11</td>
91
+ <td data-value="14" class="abs high">14/14</td>
92
92
  <td data-value="100" class="pct high">100%</td>
93
- <td data-value="332" class="abs high">332/332</td>
93
+ <td data-value="386" class="abs high">386/386</td>
94
94
  </tr>
95
95
 
96
96
  <tr>
@@ -116,7 +116,7 @@
116
116
  <div class='footer quiet pad2 space-top1 center small'>
117
117
  Code coverage generated by
118
118
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
119
- at 2026-04-27T03:46:28.725Z
119
+ at 2026-04-27T05:38:37.090Z
120
120
  </div>
121
121
  <script src="../prettify.js"></script>
122
122
  <script>
@@ -478,7 +478,7 @@ export class DirectoryHandleStore {
478
478
  <div class='footer quiet pad2 space-top1 center small'>
479
479
  Code coverage generated by
480
480
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
481
- at 2026-04-27T03:46:28.725Z
481
+ at 2026-04-27T05:38:37.090Z
482
482
  </div>
483
483
  <script src="../prettify.js"></script>
484
484
  <script>
@@ -1654,7 +1654,7 @@ export class FileApiTreeAccessors&lt;TCT extends string = string&gt; {
1654
1654
  <div class='footer quiet pad2 space-top1 center small'>
1655
1655
  Code coverage generated by
1656
1656
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
1657
- at 2026-04-27T03:46:28.725Z
1657
+ at 2026-04-27T05:38:37.090Z
1658
1658
  </div>
1659
1659
  <script src="../prettify.js"></script>
1660
1660
  <script>
@@ -1627,7 +1627,7 @@ export class FileSystemAccessTreeAccessors&lt;TCT extends string = string&gt;
1627
1627
  <div class='footer quiet pad2 space-top1 center small'>
1628
1628
  Code coverage generated by
1629
1629
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
1630
- at 2026-04-27T03:46:28.725Z
1630
+ at 2026-04-27T05:38:37.090Z
1631
1631
  </div>
1632
1632
  <script src="../prettify.js"></script>
1633
1633
  <script>
@@ -1504,7 +1504,7 @@ export class HttpTreeAccessors&lt;TCT extends string = string&gt;
1504
1504
  <div class='footer quiet pad2 space-top1 center small'>
1505
1505
  Code coverage generated by
1506
1506
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
1507
- at 2026-04-27T03:46:28.725Z
1507
+ at 2026-04-27T05:38:37.090Z
1508
1508
  </div>
1509
1509
  <script src="../prettify.js"></script>
1510
1510
  <script>
@@ -161,7 +161,7 @@
161
161
  <div class='footer quiet pad2 space-top1 center small'>
162
162
  Code coverage generated by
163
163
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
164
- at 2026-04-27T03:46:28.725Z
164
+ at 2026-04-27T05:38:37.090Z
165
165
  </div>
166
166
  <script src="../prettify.js"></script>
167
167
  <script>
@@ -1360,7 +1360,7 @@ export class LocalStorageTreeAccessors&lt;TCT extends string = string&gt;
1360
1360
  <div class='footer quiet pad2 space-top1 center small'>
1361
1361
  Code coverage generated by
1362
1362
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
1363
- at 2026-04-27T03:46:28.725Z
1363
+ at 2026-04-27T05:38:37.090Z
1364
1364
  </div>
1365
1365
  <script src="../prettify.js"></script>
1366
1366
  <script>
@@ -391,7 +391,7 @@ export function extractFileMetadata(file: File): IFileMetadata {
391
391
  <div class='footer quiet pad2 space-top1 center small'>
392
392
  Code coverage generated by
393
393
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
394
- at 2026-04-27T03:46:28.725Z
394
+ at 2026-04-27T05:38:37.090Z
395
395
  </div>
396
396
  <script src="../prettify.js"></script>
397
397
  <script>
@@ -101,7 +101,7 @@
101
101
  <div class='footer quiet pad2 space-top1 center small'>
102
102
  Code coverage generated by
103
103
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
104
- at 2026-04-27T03:46:28.725Z
104
+ at 2026-04-27T05:38:37.090Z
105
105
  </div>
106
106
  <script src="../prettify.js"></script>
107
107
  <script>
@@ -25,7 +25,7 @@
25
25
  <div class='fl pad1y space-right2'>
26
26
  <span class="strong">100% </span>
27
27
  <span class="quiet">Statements</span>
28
- <span class='fraction'>2848/2848</span>
28
+ <span class='fraction'>2902/2902</span>
29
29
  </div>
30
30
 
31
31
 
@@ -39,14 +39,14 @@
39
39
  <div class='fl pad1y space-right2'>
40
40
  <span class="strong">100% </span>
41
41
  <span class="quiet">Functions</span>
42
- <span class='fraction'>95/95</span>
42
+ <span class='fraction'>98/98</span>
43
43
  </div>
44
44
 
45
45
 
46
46
  <div class='fl pad1y space-right2'>
47
47
  <span class="strong">100% </span>
48
48
  <span class="quiet">Lines</span>
49
- <span class='fraction'>2848/2848</span>
49
+ <span class='fraction'>2902/2902</span>
50
50
  </div>
51
51
 
52
52
 
@@ -84,13 +84,13 @@
84
84
  <div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
85
85
  </td>
86
86
  <td data-value="100" class="pct high">100%</td>
87
- <td data-value="405" class="abs high">405/405</td>
87
+ <td data-value="459" class="abs high">459/459</td>
88
88
  <td data-value="100" class="pct high">100%</td>
89
89
  <td data-value="9" class="abs high">9/9</td>
90
90
  <td data-value="100" class="pct high">100%</td>
91
- <td data-value="13" class="abs high">13/13</td>
91
+ <td data-value="16" class="abs high">16/16</td>
92
92
  <td data-value="100" class="pct high">100%</td>
93
- <td data-value="405" class="abs high">405/405</td>
93
+ <td data-value="459" class="abs high">459/459</td>
94
94
  </tr>
95
95
 
96
96
  <tr>
@@ -146,7 +146,7 @@
146
146
  <div class='footer quiet pad2 space-top1 center small'>
147
147
  Code coverage generated by
148
148
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
149
- at 2026-04-27T03:46:28.725Z
149
+ at 2026-04-27T05:38:37.090Z
150
150
  </div>
151
151
  <script src="prettify.js"></script>
152
152
  <script>
@@ -101,7 +101,7 @@
101
101
  <div class='footer quiet pad2 space-top1 center small'>
102
102
  Code coverage generated by
103
103
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
104
- at 2026-04-27T03:46:28.725Z
104
+ at 2026-04-27T05:38:37.090Z
105
105
  </div>
106
106
  <script src="../prettify.js"></script>
107
107
  <script>
@@ -805,7 +805,7 @@ export function isFilePath(path: string): boolean {
805
805
  <div class='footer quiet pad2 space-top1 center small'>
806
806
  Code coverage generated by
807
807
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
808
- at 2026-04-27T03:46:28.725Z
808
+ at 2026-04-27T05:38:37.090Z
809
809
  </div>
810
810
  <script src="../prettify.js"></script>
811
811
  <script>