@h-ai/crypto 0.1.0-alpha.16 → 0.1.0-alpha.19

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.
package/dist/index.js CHANGED
@@ -33,7 +33,21 @@ var en_US_default = {
33
33
  crypto_passwordHashEmpty: "Password and hash cannot be empty",
34
34
  crypto_passwordHashFailed: "Password hashing failed",
35
35
  crypto_hashFormatInvalid: "Invalid hash format",
36
- crypto_passwordVerifyFailed: "Password verification failed"
36
+ crypto_passwordVerifyFailed: "Password verification failed",
37
+ crypto_transportClientKeyGenerateFailed: "Failed to generate transport client key pair",
38
+ crypto_transportServerKeyGenerateFailed: "Failed to generate transport server key pair",
39
+ crypto_transportKeyExchangeRequestFailed: "Transport key exchange request failed",
40
+ crypto_transportKeyExchangeHttpFailed: "Transport key exchange returned HTTP {status}",
41
+ crypto_transportKeyExchangeInvalidJson: "Transport key exchange response is not valid JSON",
42
+ crypto_transportKeyExchangeMissingFields: "Transport key exchange response is missing required fields",
43
+ crypto_transportClientNotInitialized: "Transport client has not completed key exchange",
44
+ crypto_transportClientNotRegistered: "Transport client is not registered or has expired",
45
+ crypto_transportRequestEncryptFailed: "Failed to encrypt transport request body",
46
+ crypto_transportRequestDecryptFailed: "Failed to decrypt transport request body",
47
+ crypto_transportResponseEncryptFailed: "Failed to encrypt transport response body",
48
+ crypto_transportResponseDecryptFailed: "Failed to decrypt transport response body",
49
+ crypto_transportSessionKeyEncryptFailed: "Failed to encrypt transport session key",
50
+ crypto_transportSessionKeyDecryptFailed: "Failed to decrypt transport session key"
37
51
  };
38
52
 
39
53
  // messages/zh-CN.json
@@ -66,7 +80,21 @@ var zh_CN_default = {
66
80
  crypto_passwordHashEmpty: "\u5BC6\u7801\u548C\u54C8\u5E0C\u503C\u4E0D\u80FD\u4E3A\u7A7A",
67
81
  crypto_passwordHashFailed: "\u5BC6\u7801\u54C8\u5E0C\u5931\u8D25",
68
82
  crypto_hashFormatInvalid: "\u65E0\u6548\u7684\u54C8\u5E0C\u683C\u5F0F",
69
- crypto_passwordVerifyFailed: "\u5BC6\u7801\u9A8C\u8BC1\u5931\u8D25"
83
+ crypto_passwordVerifyFailed: "\u5BC6\u7801\u9A8C\u8BC1\u5931\u8D25",
84
+ crypto_transportClientKeyGenerateFailed: "\u4F20\u8F93\u52A0\u5BC6\u5BA2\u6237\u7AEF\u5BC6\u94A5\u751F\u6210\u5931\u8D25",
85
+ crypto_transportServerKeyGenerateFailed: "\u4F20\u8F93\u52A0\u5BC6\u670D\u52A1\u7AEF\u5BC6\u94A5\u751F\u6210\u5931\u8D25",
86
+ crypto_transportKeyExchangeRequestFailed: "\u4F20\u8F93\u52A0\u5BC6\u5BC6\u94A5\u534F\u5546\u8BF7\u6C42\u5931\u8D25",
87
+ crypto_transportKeyExchangeHttpFailed: "\u4F20\u8F93\u52A0\u5BC6\u5BC6\u94A5\u534F\u5546\u8FD4\u56DE HTTP {status}",
88
+ crypto_transportKeyExchangeInvalidJson: "\u4F20\u8F93\u52A0\u5BC6\u5BC6\u94A5\u534F\u5546\u54CD\u5E94\u4E0D\u662F\u6709\u6548 JSON",
89
+ crypto_transportKeyExchangeMissingFields: "\u4F20\u8F93\u52A0\u5BC6\u5BC6\u94A5\u534F\u5546\u54CD\u5E94\u7F3A\u5C11\u5FC5\u8981\u5B57\u6BB5",
90
+ crypto_transportClientNotInitialized: "\u4F20\u8F93\u52A0\u5BC6\u5BA2\u6237\u7AEF\u5C1A\u672A\u5B8C\u6210\u5BC6\u94A5\u534F\u5546",
91
+ crypto_transportClientNotRegistered: "\u4F20\u8F93\u52A0\u5BC6\u5BA2\u6237\u7AEF\u672A\u6CE8\u518C\u6216\u5DF2\u8FC7\u671F",
92
+ crypto_transportRequestEncryptFailed: "\u4F20\u8F93\u52A0\u5BC6\u8BF7\u6C42\u4F53\u52A0\u5BC6\u5931\u8D25",
93
+ crypto_transportRequestDecryptFailed: "\u4F20\u8F93\u52A0\u5BC6\u8BF7\u6C42\u4F53\u89E3\u5BC6\u5931\u8D25",
94
+ crypto_transportResponseEncryptFailed: "\u4F20\u8F93\u52A0\u5BC6\u54CD\u5E94\u4F53\u52A0\u5BC6\u5931\u8D25",
95
+ crypto_transportResponseDecryptFailed: "\u4F20\u8F93\u52A0\u5BC6\u54CD\u5E94\u4F53\u89E3\u5BC6\u5931\u8D25",
96
+ crypto_transportSessionKeyEncryptFailed: "\u4F20\u8F93\u52A0\u5BC6\u4F1A\u8BDD\u5BC6\u94A5\u52A0\u5BC6\u5931\u8D25",
97
+ crypto_transportSessionKeyDecryptFailed: "\u4F20\u8F93\u52A0\u5BC6\u4F1A\u8BDD\u5BC6\u94A5\u89E3\u5BC6\u5931\u8D25"
70
98
  };
71
99
 
72
100
  // src/crypto-i18n.ts
@@ -560,49 +588,44 @@ function createSM4() {
560
588
  /**
561
589
  * SM4 对称加密
562
590
  *
563
- * 支持 ECB(默认)和 CBC 两种模式,使用 PKCS#7 填充。
564
- * CBC 模式需提供合法 IV。
591
+ * 默认使用 CBC 并自动生成随机 IV,返回结构化字段。
565
592
  *
566
- * ⚠️ 安全警告:**ECB 默认模式不安全**—相同明文块产生相同密文块,泄漏结构信息。
567
- * 生产场景请优先使用 `encryptWithIV()`(自动随机 IV CBC)或显式传入
568
- * `{ mode: 'cbc', iv }`。ECB 模式仅保留兼容性,未来版本可能移除默认值。
593
+ * ⚠️ 安全警告:ECB 模式会让相同明文块产生相同密文块,泄漏结构信息。
594
+ * 生产场景请使用默认 CBC,或显式传入 `{ mode: 'cbc', iv }`。
569
595
  *
570
596
  * @param data - 待加密明文
571
597
  * @param key - 密钥(32 字符十六进制)
572
598
  * @param options - 加密模式/IV/输出格式
573
- * @returns 成功时返回密文;失败时返回 INVALID_KEY/INVALID_IV/ENCRYPTION_FAILED
599
+ * @returns 成功时返回结构化密文;失败时返回 INVALID_KEY/INVALID_IV/ENCRYPTION_FAILED
574
600
  */
575
601
  encrypt(data, key, options = {}) {
576
602
  const {
577
- mode = "ecb",
603
+ mode = "cbc",
578
604
  iv,
579
605
  outputFormat = "hex"
580
606
  } = options;
607
+ const actualIv = mode === "cbc" ? iv ?? this.generateIV() : void 0;
581
608
  if (!this.isValidKey(key)) {
582
609
  return err(
583
610
  HaiCryptoError.INVALID_KEY,
584
611
  cryptoM("crypto_sm4KeyInvalid")
585
612
  );
586
613
  }
587
- if (mode === "cbc" && !iv) {
588
- return err(
589
- HaiCryptoError.INVALID_IV,
590
- cryptoM("crypto_sm4CbcNeedIv")
591
- );
592
- }
593
- if (mode === "cbc" && iv && !this.isValidIV(iv)) {
594
- return err(
595
- HaiCryptoError.INVALID_IV,
596
- cryptoM("crypto_sm4IvInvalid")
597
- );
614
+ if (mode === "cbc") {
615
+ if (!actualIv || !this.isValidIV(actualIv)) {
616
+ return err(
617
+ HaiCryptoError.INVALID_IV,
618
+ cryptoM("crypto_sm4IvInvalid")
619
+ );
620
+ }
598
621
  }
599
622
  try {
600
623
  const sm4Options = {
601
624
  mode,
602
625
  padding: "pkcs#7"
603
626
  };
604
- if (mode === "cbc" && iv) {
605
- sm4Options.iv = iv;
627
+ if (mode === "cbc" && actualIv) {
628
+ sm4Options.iv = actualIv;
606
629
  }
607
630
  const encrypted = sm4.encrypt(data, key, sm4Options);
608
631
  if (!encrypted) {
@@ -611,10 +634,12 @@ function createSM4() {
611
634
  cryptoM("crypto_sm4EncryptEmpty")
612
635
  );
613
636
  }
614
- if (outputFormat === "base64") {
615
- return ok(hexToBase64(encrypted));
616
- }
617
- return ok(encrypted);
637
+ return ok({
638
+ mode,
639
+ ciphertext: encodeCiphertext(encrypted, outputFormat),
640
+ ...actualIv ? { iv: actualIv } : {},
641
+ encoding: outputFormat
642
+ });
618
643
  } catch (error) {
619
644
  return err(
620
645
  HaiCryptoError.ENCRYPTION_FAILED,
@@ -626,33 +651,28 @@ function createSM4() {
626
651
  /**
627
652
  * SM4 对称解密
628
653
  *
629
- * 自动检测 base64 格式输入并转换为 hex
630
- * 解密模式和 IV 需与加密时一致。
654
+ * 使用结构化字段解密;解密模式、IV 与密文编码均来自 payload
631
655
  *
632
- * @param ciphertext - 密文(hex 或 base64)
656
+ * @param payload - 结构化密文
633
657
  * @param key - 密钥(32 字符十六进制)
634
- * @param options - 解密模式/IV
635
658
  * @returns 成功时返回明文;失败时返回 INVALID_KEY/INVALID_IV/DECRYPTION_FAILED
636
659
  */
637
- decrypt(ciphertext, key, options = {}) {
638
- const { mode = "ecb", iv } = options;
660
+ decrypt(payload, key) {
661
+ const { mode, iv } = payload;
639
662
  if (!this.isValidKey(key)) {
640
663
  return err(
641
664
  HaiCryptoError.INVALID_KEY,
642
665
  cryptoM("crypto_sm4KeyInvalid")
643
666
  );
644
667
  }
645
- if (mode === "cbc" && !iv) {
668
+ if (mode === "cbc" && (!iv || !this.isValidIV(iv))) {
646
669
  return err(
647
670
  HaiCryptoError.INVALID_IV,
648
- cryptoM("crypto_sm4CbcNeedIv")
671
+ cryptoM("crypto_sm4IvInvalid")
649
672
  );
650
673
  }
651
674
  try {
652
- let input = ciphertext;
653
- if (isBase64(ciphertext)) {
654
- input = base64ToHex(ciphertext);
655
- }
675
+ const input = decodeCiphertext(payload);
656
676
  const sm4Options = {
657
677
  mode,
658
678
  padding: "pkcs#7"
@@ -689,7 +709,12 @@ function createSM4() {
689
709
  if (!result.success) {
690
710
  return result;
691
711
  }
692
- return ok({ ciphertext: result.data, iv });
712
+ return ok({
713
+ mode: "cbc",
714
+ ciphertext: result.data.ciphertext,
715
+ iv,
716
+ encoding: result.data.encoding
717
+ });
693
718
  },
694
719
  /**
695
720
  * 带 IV 解密(CBC 模式)
@@ -700,7 +725,7 @@ function createSM4() {
700
725
  * @returns 成功时返回明文;失败时同 decrypt
701
726
  */
702
727
  decryptWithIV(ciphertext, key, iv) {
703
- return this.decrypt(ciphertext, key, { mode: "cbc", iv });
728
+ return this.decrypt({ mode: "cbc", ciphertext, iv, encoding: "hex" }, key);
704
729
  },
705
730
  /**
706
731
  * 从密码和盐值派生密钥
@@ -737,6 +762,12 @@ function generateRandomHex(byteLength) {
737
762
  crypto.getRandomValues(bytes);
738
763
  return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
739
764
  }
765
+ function encodeCiphertext(ciphertext, encoding) {
766
+ return encoding === "base64" ? hexToBase64(ciphertext) : ciphertext;
767
+ }
768
+ function decodeCiphertext(payload) {
769
+ return payload.encoding === "base64" ? base64ToHex(payload.ciphertext) : payload.ciphertext;
770
+ }
740
771
 
741
772
  // src/transport/crypto-transport-types.ts
742
773
  var TRANSPORT_PROTOCOL = {
@@ -760,7 +791,7 @@ function createTransportClient(options) {
760
791
  async function doInit() {
761
792
  const kpResult = options.crypto.asymmetric.generateKeyPair();
762
793
  if (!kpResult.success)
763
- return err(HaiCommonError.INTERNAL_ERROR, "Failed to generate client key pair", kpResult.error);
794
+ return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportClientKeyGenerateFailed"), kpResult.error);
764
795
  const localKeyPair = kpResult.data;
765
796
  let response;
766
797
  try {
@@ -770,18 +801,18 @@ function createTransportClient(options) {
770
801
  body: JSON.stringify({ clientPublicKey: localKeyPair.publicKey })
771
802
  });
772
803
  } catch (cause) {
773
- return err(HaiCommonError.INTERNAL_ERROR, "Key exchange request failed", cause);
804
+ return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportKeyExchangeRequestFailed"), cause);
774
805
  }
775
806
  if (!response.ok)
776
- return err(HaiCommonError.INTERNAL_ERROR, `Key exchange returned HTTP ${response.status}`);
807
+ return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportKeyExchangeHttpFailed", { params: { status: response.status } }));
777
808
  let body;
778
809
  try {
779
810
  body = await response.json();
780
811
  } catch (cause) {
781
- return err(HaiCommonError.INTERNAL_ERROR, "Key exchange response is not valid JSON", cause);
812
+ return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportKeyExchangeInvalidJson"), cause);
782
813
  }
783
814
  if (!body.serverPublicKey || !body.clientId)
784
- return err(HaiCommonError.INTERNAL_ERROR, "Key exchange response missing fields");
815
+ return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportKeyExchangeMissingFields"));
785
816
  keyPair = localKeyPair;
786
817
  serverPublicKey = body.serverPublicKey;
787
818
  clientId = body.clientId;
@@ -801,14 +832,14 @@ function createTransportClient(options) {
801
832
  }
802
833
  function encryptBody(plaintext) {
803
834
  if (!serverPublicKey)
804
- return err(HaiCommonError.INTERNAL_ERROR, "Transport client not initialized");
835
+ return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportClientNotInitialized"));
805
836
  const symKey = options.crypto.symmetric.generateKey();
806
837
  const encResult = options.crypto.symmetric.encryptWithIV(plaintext, symKey);
807
838
  if (!encResult.success)
808
- return err(HaiCommonError.INTERNAL_ERROR, "Failed to encrypt request body", encResult.error);
839
+ return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportRequestEncryptFailed"), encResult.error);
809
840
  const keyEncResult = options.crypto.asymmetric.encrypt(symKey, serverPublicKey);
810
841
  if (!keyEncResult.success)
811
- return err(HaiCommonError.INTERNAL_ERROR, "Failed to encrypt session key", keyEncResult.error);
842
+ return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportSessionKeyEncryptFailed"), keyEncResult.error);
812
843
  return ok({
813
844
  encryptedKey: keyEncResult.data,
814
845
  ciphertext: encResult.data.ciphertext,
@@ -817,13 +848,13 @@ function createTransportClient(options) {
817
848
  }
818
849
  function decryptPayload(payload) {
819
850
  if (!keyPair)
820
- return err(HaiCommonError.INTERNAL_ERROR, "Transport client not initialized");
851
+ return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportClientNotInitialized"));
821
852
  const keyDec = options.crypto.asymmetric.decrypt(payload.encryptedKey, keyPair.privateKey);
822
853
  if (!keyDec.success)
823
- return err(HaiCommonError.INTERNAL_ERROR, "Failed to decrypt session key", keyDec.error);
854
+ return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportSessionKeyDecryptFailed"), keyDec.error);
824
855
  const dec = options.crypto.symmetric.decryptWithIV(payload.ciphertext, keyDec.data, payload.iv);
825
856
  if (!dec.success)
826
- return err(HaiCommonError.INTERNAL_ERROR, "Failed to decrypt response body", dec.error);
857
+ return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportResponseDecryptFailed"), dec.error);
827
858
  return ok(dec.data);
828
859
  }
829
860
  const encryptedFetch = async (input, init) => {
@@ -933,14 +964,27 @@ function isEncryptedPayload(payload) {
933
964
  const p = payload;
934
965
  return typeof p.encryptedKey === "string" && typeof p.ciphertext === "string" && typeof p.iv === "string";
935
966
  }
936
- function createInMemoryKeyStore(maxClients = 1e4) {
967
+
968
+ // src/transport/store-provider/crypto-transport-store-memory.ts
969
+ var DEFAULT_MAX_CLIENTS = 1e4;
970
+ function normalizeMaxClients(maxClients) {
971
+ if (!Number.isFinite(maxClients))
972
+ return DEFAULT_MAX_CLIENTS;
973
+ return Math.max(1, Math.floor(maxClients));
974
+ }
975
+ function createClientId(counter) {
976
+ const entropy = typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID().replace(/-/g, "") : Math.random().toString(36).slice(2, 12);
977
+ return `c_${counter}_${Date.now()}_${entropy}`;
978
+ }
979
+ function createInMemoryKeyStore(maxClients = DEFAULT_MAX_CLIENTS) {
937
980
  const clientKeys = /* @__PURE__ */ new Map();
981
+ const capacity = normalizeMaxClients(maxClients);
938
982
  let counter = 0;
939
983
  return {
940
984
  async register(publicKey) {
941
985
  counter++;
942
- const clientId = `c_${counter}_${Date.now()}`;
943
- if (clientKeys.size >= maxClients) {
986
+ const clientId = createClientId(counter);
987
+ if (clientKeys.size >= capacity) {
944
988
  const oldest = clientKeys.keys().next().value;
945
989
  if (oldest !== void 0)
946
990
  clientKeys.delete(oldest);
@@ -959,10 +1003,12 @@ function createInMemoryKeyStore(maxClients = 1e4) {
959
1003
  }
960
1004
  };
961
1005
  }
1006
+
1007
+ // src/transport/crypto-transport-server.ts
962
1008
  function createTransportEncryption(cryptoService, options = {}) {
963
1009
  const keyPairResult = cryptoService.asymmetric.generateKeyPair();
964
1010
  if (!keyPairResult.success)
965
- return err(HaiCommonError.INTERNAL_ERROR, "Failed to generate transport key pair", keyPairResult.error);
1011
+ return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportServerKeyGenerateFailed"), keyPairResult.error);
966
1012
  const serverKeyPair = keyPairResult.data;
967
1013
  const keyStore = options.keyStore ?? createInMemoryKeyStore(options.maxClients);
968
1014
  const manager = {
@@ -978,14 +1024,14 @@ function createTransportEncryption(cryptoService, options = {}) {
978
1024
  async encryptResponse(clientId, data) {
979
1025
  const clientPublicKey = await keyStore.get(clientId);
980
1026
  if (!clientPublicKey)
981
- return err(HaiCommonError.NOT_FOUND, `Unknown transport client: ${clientId}`);
1027
+ return err(HaiCommonError.NOT_FOUND, cryptoM("crypto_transportClientNotRegistered"));
982
1028
  const symmetricKey = cryptoService.symmetric.generateKey();
983
1029
  const encResult = cryptoService.symmetric.encryptWithIV(data, symmetricKey);
984
1030
  if (!encResult.success)
985
- return err(HaiCommonError.INTERNAL_ERROR, "Failed to encrypt response payload", encResult.error);
1031
+ return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportResponseEncryptFailed"), encResult.error);
986
1032
  const keyEncResult = cryptoService.asymmetric.encrypt(symmetricKey, clientPublicKey);
987
1033
  if (!keyEncResult.success)
988
- return err(HaiCommonError.INTERNAL_ERROR, "Failed to encrypt session key", keyEncResult.error);
1034
+ return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportSessionKeyEncryptFailed"), keyEncResult.error);
989
1035
  const payload = {
990
1036
  encryptedKey: keyEncResult.data,
991
1037
  ciphertext: encResult.data.ciphertext,
@@ -996,10 +1042,10 @@ function createTransportEncryption(cryptoService, options = {}) {
996
1042
  decryptRequest(payload) {
997
1043
  const keyDecResult = cryptoService.asymmetric.decrypt(payload.encryptedKey, serverKeyPair.privateKey);
998
1044
  if (!keyDecResult.success)
999
- return err(HaiCommonError.INTERNAL_ERROR, "Failed to decrypt session key", keyDecResult.error);
1045
+ return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportSessionKeyDecryptFailed"), keyDecResult.error);
1000
1046
  const decResult = cryptoService.symmetric.decryptWithIV(payload.ciphertext, keyDecResult.data, payload.iv);
1001
1047
  if (!decResult.success)
1002
- return err(HaiCommonError.INTERNAL_ERROR, "Failed to decrypt request payload", decResult.error);
1048
+ return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportRequestDecryptFailed"), decResult.error);
1003
1049
  return ok(decResult.data);
1004
1050
  },
1005
1051
  async close() {
@@ -1052,6 +1098,7 @@ var crypto2 = {
1052
1098
  }
1053
1099
  initInProgress = true;
1054
1100
  try {
1101
+ await Promise.resolve();
1055
1102
  if (initialized) {
1056
1103
  logger.warn("Crypto module is already initialized, reinitializing");
1057
1104
  await crypto2.close();
@@ -1129,6 +1176,148 @@ var crypto2 = {
1129
1176
  }
1130
1177
  };
1131
1178
 
1132
- export { HaiCryptoError, TRANSPORT_PROTOCOL, crypto2 as crypto };
1179
+ // src/transport/store-provider/crypto-transport-store-redis.ts
1180
+ var TRANSPORT_KEY_CACHE_PREFIX = "hai:crypto:transport:client";
1181
+ function normalizeTtlSeconds(ttlSeconds) {
1182
+ if (ttlSeconds === void 0 || !Number.isFinite(ttlSeconds) || ttlSeconds <= 0)
1183
+ return void 0;
1184
+ return Math.floor(ttlSeconds);
1185
+ }
1186
+ function createClientId2() {
1187
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function")
1188
+ return `c_${crypto.randomUUID()}`;
1189
+ return `c_${Date.now()}_${Math.random().toString(36).slice(2, 12)}`;
1190
+ }
1191
+ function toOperationError(operation, error) {
1192
+ const wrapped = new Error(`${operation} failed: ${error.message}`, { cause: error });
1193
+ wrapped.code = error.code;
1194
+ return wrapped;
1195
+ }
1196
+ function unwrapResult(operation, result) {
1197
+ if (result.success)
1198
+ return result.data;
1199
+ throw toOperationError(operation, result.error);
1200
+ }
1201
+ function createRedisTransportKeyStore(options) {
1202
+ const ttlSeconds = normalizeTtlSeconds(options.ttlSeconds);
1203
+ function buildCacheKey(clientId) {
1204
+ return `${TRANSPORT_KEY_CACHE_PREFIX}:${clientId}`;
1205
+ }
1206
+ return {
1207
+ async register(publicKey) {
1208
+ const clientId = createClientId2();
1209
+ unwrapResult(
1210
+ "cache.kv.set",
1211
+ await options.cache.kv.set(
1212
+ buildCacheKey(clientId),
1213
+ publicKey,
1214
+ ttlSeconds === void 0 ? void 0 : { ex: ttlSeconds }
1215
+ )
1216
+ );
1217
+ return clientId;
1218
+ },
1219
+ async get(clientId) {
1220
+ const publicKey = unwrapResult(
1221
+ "cache.kv.get",
1222
+ await options.cache.kv.get(buildCacheKey(clientId))
1223
+ );
1224
+ return publicKey ?? void 0;
1225
+ },
1226
+ async delete(clientId) {
1227
+ unwrapResult("cache.kv.del", await options.cache.kv.del(buildCacheKey(clientId)));
1228
+ }
1229
+ };
1230
+ }
1231
+
1232
+ // src/transport/store-provider/crypto-transport-store-reldb.ts
1233
+ var TRANSPORT_KEY_TABLE = "hai_crypto_transport_client_keys";
1234
+ function normalizeTtlSeconds2(ttlSeconds) {
1235
+ if (ttlSeconds === void 0 || !Number.isFinite(ttlSeconds) || ttlSeconds <= 0)
1236
+ return void 0;
1237
+ return Math.floor(ttlSeconds);
1238
+ }
1239
+ function createClientId3() {
1240
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function")
1241
+ return `c_${crypto.randomUUID()}`;
1242
+ return `c_${Date.now()}_${Math.random().toString(36).slice(2, 12)}`;
1243
+ }
1244
+ function toOperationError2(operation, error) {
1245
+ const wrapped = new Error(`${operation} failed: ${error.message}`, { cause: error });
1246
+ wrapped.code = error.code;
1247
+ return wrapped;
1248
+ }
1249
+ function unwrapResult2(operation, result) {
1250
+ if (result.success)
1251
+ return result.data;
1252
+ throw toOperationError2(operation, result.error);
1253
+ }
1254
+ function createReldbTransportKeyStore(options) {
1255
+ const ttlSeconds = normalizeTtlSeconds2(options.ttlSeconds);
1256
+ let ensureTablePromise = null;
1257
+ async function ensureTable() {
1258
+ if (!ensureTablePromise) {
1259
+ ensureTablePromise = (async () => {
1260
+ unwrapResult2(
1261
+ "reldb.ddl.createTable",
1262
+ await options.reldb.ddl.createTable(TRANSPORT_KEY_TABLE, {
1263
+ client_id: { type: "TEXT", primaryKey: true },
1264
+ public_key: { type: "TEXT", notNull: true },
1265
+ created_at: { type: "TIMESTAMP", notNull: true },
1266
+ expires_at: { type: "TIMESTAMP" }
1267
+ }, true)
1268
+ );
1269
+ })();
1270
+ }
1271
+ return ensureTablePromise;
1272
+ }
1273
+ async function deleteClientKey(clientId) {
1274
+ unwrapResult2(
1275
+ "reldb.sql.execute",
1276
+ await options.reldb.sql.execute(
1277
+ `DELETE FROM ${TRANSPORT_KEY_TABLE} WHERE client_id = ?`,
1278
+ [clientId]
1279
+ )
1280
+ );
1281
+ }
1282
+ return {
1283
+ async register(publicKey) {
1284
+ await ensureTable();
1285
+ const clientId = createClientId3();
1286
+ const createdAt = (/* @__PURE__ */ new Date()).toISOString();
1287
+ const expiresAt = ttlSeconds === void 0 ? null : new Date(Date.now() + ttlSeconds * 1e3).toISOString();
1288
+ unwrapResult2(
1289
+ "reldb.sql.execute",
1290
+ await options.reldb.sql.execute(
1291
+ `INSERT INTO ${TRANSPORT_KEY_TABLE} (client_id, public_key, created_at, expires_at) VALUES (?, ?, ?, ?)`,
1292
+ [clientId, publicKey, createdAt, expiresAt]
1293
+ )
1294
+ );
1295
+ return clientId;
1296
+ },
1297
+ async get(clientId) {
1298
+ await ensureTable();
1299
+ const row = unwrapResult2(
1300
+ "reldb.sql.get",
1301
+ await options.reldb.sql.get(
1302
+ `SELECT public_key, expires_at FROM ${TRANSPORT_KEY_TABLE} WHERE client_id = ?`,
1303
+ [clientId]
1304
+ )
1305
+ );
1306
+ if (!row)
1307
+ return void 0;
1308
+ if (row.expires_at && Date.parse(row.expires_at) <= Date.now()) {
1309
+ await deleteClientKey(clientId);
1310
+ return void 0;
1311
+ }
1312
+ return row.public_key;
1313
+ },
1314
+ async delete(clientId) {
1315
+ await ensureTable();
1316
+ await deleteClientKey(clientId);
1317
+ }
1318
+ };
1319
+ }
1320
+
1321
+ export { HaiCryptoError, TRANSPORT_PROTOCOL, createInMemoryKeyStore, createRedisTransportKeyStore, createReldbTransportKeyStore, crypto2 as crypto };
1133
1322
  //# sourceMappingURL=index.js.map
1134
1323
  //# sourceMappingURL=index.js.map