@alibarbar/common 1.1.2 → 1.1.4

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
@@ -1,3 +1,5 @@
1
+ import JSEncrypt from 'jsencrypt';
2
+ import CryptoJS from 'crypto-js';
1
3
  import Qs from 'qs';
2
4
  import axios from 'axios';
3
5
 
@@ -762,8 +764,8 @@ function parseUrl(url) {
762
764
  }
763
765
  }
764
766
  function buildUrl(parts) {
765
- const { protocol = "https:", host = "", pathname = "/", search = "", hash: hash2 = "" } = parts;
766
- return `${protocol}//${host}${pathname}${search}${hash2}`;
767
+ const { protocol = "https:", host = "", pathname = "/", search = "", hash = "" } = parts;
768
+ return `${protocol}//${host}${pathname}${search}${hash}`;
767
769
  }
768
770
  function getQueryParams(url) {
769
771
  const searchParams = url ? new URL(url).searchParams : new URLSearchParams(window.location.search);
@@ -1137,6 +1139,8 @@ var ChunkUploader = class {
1137
1139
  this.uploadedChunks = /* @__PURE__ */ new Set();
1138
1140
  this.status = "pending" /* PENDING */;
1139
1141
  this.abortController = null;
1142
+ /** 进度流专用 AbortController,用于在上传结束/取消时关闭流 */
1143
+ this.progressStreamAbortController = null;
1140
1144
  this.file = file;
1141
1145
  this.options = {
1142
1146
  chunkSize: options.chunkSize || 2 * 1024 * 1024,
@@ -1244,7 +1248,6 @@ var ChunkUploader = class {
1244
1248
  });
1245
1249
  if (response.code === 200 && response.data.success) {
1246
1250
  this.uploadedChunks.add(chunkInfo.index);
1247
- await this.updateProgress();
1248
1251
  } else {
1249
1252
  throw new Error(response.message || "\u5206\u7247\u4E0A\u4F20\u5931\u8D25");
1250
1253
  }
@@ -1279,23 +1282,60 @@ var ChunkUploader = class {
1279
1282
  await Promise.all(tasks);
1280
1283
  }
1281
1284
  /**
1282
- * 更新上传进度(仅用于回调前端显示)
1285
+ * 启动进度流式接口(单次请求,通过 ReadableStream 消费多条 SSE 事件)
1283
1286
  */
1284
- async updateProgress() {
1287
+ startProgressStream() {
1285
1288
  if (!this.taskId) return;
1286
- try {
1287
- const response = await this.request(
1288
- `/api/files/common/progress/${this.taskId}`,
1289
- {
1290
- method: "GET",
1291
- headers: this.options.headers
1289
+ this.progressStreamAbortController = new AbortController();
1290
+ const url = `${this.options.baseURL}/api/files/common/progress/${this.taskId}`;
1291
+ fetch(url, {
1292
+ method: "GET",
1293
+ headers: {
1294
+ Accept: "text/event-stream",
1295
+ ...this.options.headers
1296
+ },
1297
+ signal: this.progressStreamAbortController.signal
1298
+ }).then(async (response) => {
1299
+ if (!response.ok || !response.body) return;
1300
+ const reader = response.body.getReader();
1301
+ const decoder = new TextDecoder();
1302
+ let buffer = "";
1303
+ try {
1304
+ while (true) {
1305
+ const { done, value } = await reader.read();
1306
+ if (done) break;
1307
+ buffer += decoder.decode(value, { stream: true });
1308
+ const lines = buffer.split("\n");
1309
+ buffer = lines.pop() ?? "";
1310
+ for (const line of lines) {
1311
+ if (line.startsWith("data: ")) {
1312
+ const jsonStr = line.slice(6).trim();
1313
+ if (!jsonStr) continue;
1314
+ try {
1315
+ const apiRes = JSON.parse(jsonStr);
1316
+ if (apiRes.code === 200 && apiRes.data) {
1317
+ this.options.onProgress(apiRes.data);
1318
+ }
1319
+ } catch {
1320
+ }
1321
+ }
1322
+ }
1292
1323
  }
1293
- );
1294
- if (response.code === 200 && response.data) {
1295
- this.options.onProgress(response.data);
1324
+ } finally {
1325
+ reader.releaseLock();
1296
1326
  }
1297
- } catch (error) {
1298
- console.warn("\u83B7\u53D6\u4E0A\u4F20\u8FDB\u5EA6\u5931\u8D25:", error);
1327
+ }).catch((error) => {
1328
+ if (error?.name === "AbortError") return;
1329
+ console.warn("\u8FDB\u5EA6\u6D41\u5F0F\u63A5\u53E3\u5F02\u5E38:", error);
1330
+ });
1331
+ }
1332
+ /**
1333
+ * 关闭进度流
1334
+ */
1335
+ closeProgressStream() {
1336
+ if (this.progressStreamAbortController) {
1337
+ this.progressStreamAbortController.abort();
1338
+ this.progressStreamAbortController = null;
1299
1339
  }
1300
1340
  }
1301
1341
  /**
@@ -1368,6 +1408,7 @@ var ChunkUploader = class {
1368
1408
  this.options.onComplete(result);
1369
1409
  return result;
1370
1410
  }
1411
+ this.startProgressStream();
1371
1412
  console.log("[\u4E0A\u4F20\u6D41\u7A0B] 2. \u51C6\u5907\u5206\u7247");
1372
1413
  this.prepareChunks();
1373
1414
  const totalChunks = this.chunks.length;
@@ -1411,6 +1452,8 @@ var ChunkUploader = class {
1411
1452
  this.options.onError(err);
1412
1453
  console.error("[\u4E0A\u4F20\u6D41\u7A0B] \u274C \u4E0A\u4F20\u5931\u8D25:", err);
1413
1454
  throw err;
1455
+ } finally {
1456
+ this.closeProgressStream();
1414
1457
  }
1415
1458
  }
1416
1459
  /**
@@ -1437,6 +1480,7 @@ var ChunkUploader = class {
1437
1480
  * 取消上传
1438
1481
  */
1439
1482
  async cancel() {
1483
+ this.closeProgressStream();
1440
1484
  if (this.taskId && this.status === "uploading" /* UPLOADING */) {
1441
1485
  try {
1442
1486
  await this.request(`/api/files/common/cancel/${this.taskId}`, {
@@ -1493,14 +1537,6 @@ async function uploadFile(file, options) {
1493
1537
  const uploader = createUploader(file, options);
1494
1538
  return uploader.upload();
1495
1539
  }
1496
-
1497
- // src/helper/crypto/index.ts
1498
- async function sha256(data) {
1499
- const buffer = typeof data === "string" ? new TextEncoder().encode(data) : data;
1500
- const hashBuffer = await crypto.subtle.digest("SHA-256", buffer);
1501
- const hashArray = Array.from(new Uint8Array(hashBuffer));
1502
- return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
1503
- }
1504
1540
  function base64Encode(data) {
1505
1541
  if (typeof data === "string") {
1506
1542
  return btoa(unescape(encodeURIComponent(data)));
@@ -1512,1009 +1548,444 @@ function base64Encode(data) {
1512
1548
  }
1513
1549
  return btoa(binary);
1514
1550
  }
1515
- function base64Decode(data) {
1516
- try {
1517
- return decodeURIComponent(escape(atob(data)));
1518
- } catch {
1519
- throw new Error("Invalid Base64 string");
1520
- }
1521
- }
1522
- function generateUUID() {
1523
- if (typeof crypto !== "undefined" && crypto.randomUUID) {
1524
- return crypto.randomUUID();
1525
- }
1526
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
1527
- const r = Math.random() * 16 | 0;
1528
- const v = c === "x" ? r : r & 3 | 8;
1529
- return v.toString(16);
1530
- });
1531
- }
1532
- function generateRandomString(length, charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") {
1533
- let result = "";
1534
- for (let i = 0; i < length; i++) {
1535
- result += charset.charAt(Math.floor(Math.random() * charset.length));
1536
- }
1537
- return result;
1551
+ function generateRandomAESKeyString() {
1552
+ return CryptoJS.lib.WordArray.random(256 / 8).toString();
1538
1553
  }
1539
- function hash(data) {
1540
- let hashValue = 0;
1541
- for (let i = 0; i < data.length; i++) {
1542
- const char = data.charCodeAt(i);
1543
- hashValue = (hashValue << 5) - hashValue + char;
1544
- hashValue = hashValue & hashValue;
1554
+ function encryptJsonWithAES(data, aesKey) {
1555
+ try {
1556
+ const jsonString = JSON.stringify(data);
1557
+ return CryptoJS.AES.encrypt(jsonString, aesKey).toString();
1558
+ } catch (error) {
1559
+ throw new Error(
1560
+ "[encryption] AES encryptJsonWithAES failed: " + (error instanceof Error ? error.message : String(error))
1561
+ );
1545
1562
  }
1546
- return Math.abs(hashValue);
1547
1563
  }
1548
- async function generateRSAKeyPair(modulusLength = 2048) {
1549
- if (typeof crypto === "undefined" || !crypto.subtle) {
1550
- throw new Error("Web Crypto API is not available");
1564
+ function decryptJsonWithAES(encryptedData, aesKey) {
1565
+ try {
1566
+ const decrypted = CryptoJS.AES.decrypt(encryptedData, aesKey);
1567
+ const jsonString = decrypted.toString(CryptoJS.enc.Utf8);
1568
+ if (!jsonString) {
1569
+ throw new Error("decrypted JSON string is empty");
1570
+ }
1571
+ return JSON.parse(jsonString);
1572
+ } catch (error) {
1573
+ throw new Error(
1574
+ "[encryption] AES decryptJsonWithAES failed: " + (error instanceof Error ? error.message : String(error))
1575
+ );
1551
1576
  }
1552
- const keyPair = await crypto.subtle.generateKey(
1553
- {
1554
- name: "RSA-OAEP",
1555
- modulusLength,
1556
- publicExponent: new Uint8Array([1, 0, 1]),
1557
- hash: "SHA-256"
1558
- },
1559
- true,
1560
- ["encrypt", "decrypt"]
1561
- );
1562
- return {
1563
- publicKey: keyPair.publicKey,
1564
- privateKey: keyPair.privateKey
1565
- };
1566
1577
  }
1567
- async function rsaEncrypt(data, publicKey) {
1568
- if (typeof crypto === "undefined" || !crypto.subtle) {
1569
- throw new Error("Web Crypto API is not available");
1570
- }
1571
- const encoder = new TextEncoder();
1572
- const dataBuffer = encoder.encode(data);
1573
- const algo = publicKey.algorithm;
1574
- const modulusLength = typeof algo.modulusLength === "number" ? algo.modulusLength : 2048;
1575
- const hashName = typeof algo.hash === "object" && "name" in algo.hash ? algo.hash.name : "SHA-256";
1576
- const hashLength = hashName === "SHA-1" ? 20 : 32;
1577
- const maxChunkSize = Math.max(Math.floor(modulusLength / 8 - 2 * hashLength - 2), 1);
1578
- const chunks = [];
1579
- for (let i = 0; i < dataBuffer.length; i += maxChunkSize) {
1580
- const chunk2 = dataBuffer.slice(i, i + maxChunkSize);
1581
- const encrypted = await crypto.subtle.encrypt(
1582
- {
1583
- name: "RSA-OAEP"
1584
- },
1585
- publicKey,
1586
- chunk2
1578
+ function getEnvPublicKey() {
1579
+ const key = import.meta.env.VITE_RSA_PUBLIC_KEY?.trim();
1580
+ if (!key) {
1581
+ throw new Error(
1582
+ "[encryption] VITE_RSA_PUBLIC_KEY is not set. Please configure RSA public key in environment variables."
1587
1583
  );
1588
- chunks.push(encrypted);
1589
1584
  }
1590
- const totalLength = chunks.reduce((sum, chunk2) => sum + chunk2.byteLength, 0);
1591
- const merged = new Uint8Array(totalLength);
1592
- let offset = 0;
1593
- for (const chunk2 of chunks) {
1594
- merged.set(new Uint8Array(chunk2), offset);
1595
- offset += chunk2.byteLength;
1596
- }
1597
- return base64Encode(merged.buffer);
1585
+ return key;
1598
1586
  }
1599
- async function rsaDecrypt(encryptedData, privateKey) {
1600
- if (typeof crypto === "undefined" || !crypto.subtle) {
1601
- throw new Error("Web Crypto API is not available");
1602
- }
1603
- const binaryString = atob(encryptedData);
1604
- const encryptedArray = new Uint8Array(binaryString.length);
1605
- for (let i = 0; i < binaryString.length; i++) {
1606
- encryptedArray[i] = binaryString.charCodeAt(i);
1607
- }
1608
- const chunkSize = 256;
1609
- const chunks = [];
1610
- for (let i = 0; i < encryptedArray.length; i += chunkSize) {
1611
- const chunk2 = encryptedArray.slice(i, i + chunkSize);
1612
- const decrypted = await crypto.subtle.decrypt(
1613
- {
1614
- name: "RSA-OAEP"
1615
- },
1616
- privateKey,
1617
- chunk2
1587
+ function getEnvPrivateKey() {
1588
+ const key = import.meta.env.VITE_RSA_PRIVATE_KEY?.trim();
1589
+ if (!key) {
1590
+ throw new Error(
1591
+ "[encryption] VITE_RSA_PRIVATE_KEY is not set. Please configure RSA private key in environment variables."
1618
1592
  );
1619
- const decoder = new TextDecoder();
1620
- chunks.push(decoder.decode(decrypted));
1621
1593
  }
1622
- return chunks.join("");
1594
+ return key;
1623
1595
  }
1624
- async function exportPublicKey(publicKey) {
1625
- if (typeof crypto === "undefined" || !crypto.subtle) {
1626
- throw new Error("Web Crypto API is not available");
1596
+ function base64ToPem(base64Key, type) {
1597
+ const chunks = [];
1598
+ for (let i = 0; i < base64Key.length; i += 64) {
1599
+ chunks.push(base64Key.slice(i, i + 64));
1627
1600
  }
1628
- const exported = await crypto.subtle.exportKey("spki", publicKey);
1629
- return base64Encode(exported);
1601
+ const body = chunks.join("\n");
1602
+ const header = type === "PUBLIC" ? "-----BEGIN PUBLIC KEY-----" : "-----BEGIN PRIVATE KEY-----";
1603
+ const footer = type === "PUBLIC" ? "-----END PUBLIC KEY-----" : "-----END PRIVATE KEY-----";
1604
+ return `${header}
1605
+ ${body}
1606
+ ${footer}`;
1630
1607
  }
1631
- async function exportPrivateKey(privateKey) {
1632
- if (typeof crypto === "undefined" || !crypto.subtle) {
1633
- throw new Error("Web Crypto API is not available");
1608
+ async function rsaEncrypt(plain, publicKeyPem) {
1609
+ try {
1610
+ const encrypt = new JSEncrypt();
1611
+ let publicKeyString;
1612
+ if (typeof publicKeyPem === "string") {
1613
+ const trimmedKey = publicKeyPem.trim();
1614
+ if (!trimmedKey) {
1615
+ throw new Error("Public key string is empty");
1616
+ }
1617
+ if (trimmedKey.includes("-----BEGIN")) {
1618
+ publicKeyString = trimmedKey;
1619
+ } else {
1620
+ publicKeyString = base64ToPem(trimmedKey, "PUBLIC");
1621
+ }
1622
+ } else if (publicKeyPem instanceof CryptoJS.lib.WordArray) {
1623
+ const base64Key = publicKeyPem.toString();
1624
+ publicKeyString = base64ToPem(base64Key, "PUBLIC");
1625
+ } else {
1626
+ try {
1627
+ const exported = await crypto.subtle.exportKey("spki", publicKeyPem);
1628
+ const base64Key = base64Encode(exported);
1629
+ publicKeyString = base64ToPem(base64Key, "PUBLIC");
1630
+ } catch (error) {
1631
+ throw new Error(
1632
+ "Failed to export CryptoKey. In non-HTTPS environment, use string publicKey (Base64 or PEM) directly. " + (error instanceof Error ? error.message : String(error))
1633
+ );
1634
+ }
1635
+ }
1636
+ encrypt.setPublicKey(publicKeyString);
1637
+ const MAX_ENCRYPT_LENGTH = 200;
1638
+ if (plain.length > MAX_ENCRYPT_LENGTH) {
1639
+ const chunks = [];
1640
+ for (let i = 0; i < plain.length; i += MAX_ENCRYPT_LENGTH) {
1641
+ const chunk2 = plain.slice(i, i + MAX_ENCRYPT_LENGTH);
1642
+ const encrypted2 = encrypt.encrypt(chunk2);
1643
+ if (!encrypted2) {
1644
+ throw new Error(
1645
+ `RSA encryption failed for chunk ${i / MAX_ENCRYPT_LENGTH + 1}. Public key may be invalid or JSEncrypt may not be loaded correctly.`
1646
+ );
1647
+ }
1648
+ chunks.push(encrypted2);
1649
+ }
1650
+ return chunks.join("|");
1651
+ }
1652
+ const encrypted = encrypt.encrypt(plain);
1653
+ if (!encrypted) {
1654
+ throw new Error(
1655
+ `RSA encryption failed. Public key may be invalid or JSEncrypt may not be loaded correctly. Plain text length: ${plain.length} bytes.`
1656
+ );
1657
+ }
1658
+ return encrypted;
1659
+ } catch (error) {
1660
+ if (error instanceof Error) {
1661
+ if (error.message.includes("RSA encryption failed") || error.message.includes("Public key")) {
1662
+ throw error;
1663
+ }
1664
+ throw new Error(`[encryption] rsaEncrypt failed: ${error.message}`);
1665
+ }
1666
+ throw new Error(`[encryption] rsaEncrypt failed: ${String(error)}`);
1634
1667
  }
1635
- const exported = await crypto.subtle.exportKey("pkcs8", privateKey);
1636
- return base64Encode(exported);
1637
1668
  }
1638
- async function importPublicKey(keyData) {
1639
- if (typeof crypto === "undefined" || !crypto.subtle) {
1640
- throw new Error("Web Crypto API is not available");
1669
+ async function rsaDecrypt(cipher, privateKeyPem) {
1670
+ const decrypt = new JSEncrypt();
1671
+ let privateKeyString;
1672
+ if (typeof privateKeyPem === "string") {
1673
+ if (privateKeyPem.includes("-----BEGIN")) {
1674
+ privateKeyString = privateKeyPem.trim();
1675
+ } else {
1676
+ const trimmed = privateKeyPem.trim();
1677
+ privateKeyString = base64ToPem(trimmed, "PRIVATE");
1678
+ }
1679
+ } else if (privateKeyPem instanceof CryptoJS.lib.WordArray) {
1680
+ const base64Key = privateKeyPem.toString();
1681
+ privateKeyString = base64ToPem(base64Key, "PRIVATE");
1682
+ } else {
1683
+ try {
1684
+ const exported = await crypto.subtle.exportKey("pkcs8", privateKeyPem);
1685
+ const base64Key = base64Encode(exported);
1686
+ privateKeyString = base64ToPem(base64Key, "PRIVATE");
1687
+ } catch (error) {
1688
+ throw new Error(
1689
+ "[encryption] rsaDecrypt: failed to export CryptoKey. In non-HTTPS environment, use PEM string directly."
1690
+ );
1691
+ }
1641
1692
  }
1642
- const keyBuffer = base64Decode(keyData);
1643
- const keyArray = new Uint8Array(keyBuffer.split("").map((char) => char.charCodeAt(0)));
1644
- return crypto.subtle.importKey(
1645
- "spki",
1646
- keyArray.buffer,
1647
- {
1648
- name: "RSA-OAEP",
1649
- hash: "SHA-256"
1650
- },
1651
- true,
1652
- ["encrypt"]
1653
- );
1654
- }
1655
- async function importPrivateKey(keyData) {
1656
- if (typeof crypto === "undefined" || !crypto.subtle) {
1657
- throw new Error("Web Crypto API is not available");
1693
+ decrypt.setPrivateKey(privateKeyString);
1694
+ const chunks = cipher.split("|");
1695
+ const decryptedChunks = [];
1696
+ for (const chunk2 of chunks) {
1697
+ const decrypted = decrypt.decrypt(chunk2);
1698
+ if (!decrypted) {
1699
+ throw new Error("[encryption] rsaDecrypt: RSA decryption failed");
1700
+ }
1701
+ decryptedChunks.push(decrypted);
1658
1702
  }
1659
- const keyBuffer = base64Decode(keyData);
1660
- const keyArray = new Uint8Array(keyBuffer.split("").map((char) => char.charCodeAt(0)));
1661
- return crypto.subtle.importKey(
1662
- "pkcs8",
1663
- keyArray.buffer,
1664
- {
1665
- name: "RSA-OAEP",
1666
- hash: "SHA-256"
1667
- },
1668
- true,
1669
- ["decrypt"]
1670
- );
1703
+ return decryptedChunks.join("");
1671
1704
  }
1672
- async function generateHMACKey() {
1673
- if (typeof crypto === "undefined" || !crypto.subtle) {
1674
- throw new Error("Web Crypto API is not available");
1675
- }
1676
- return crypto.subtle.generateKey(
1677
- {
1678
- name: "HMAC",
1679
- hash: "SHA-256"
1680
- },
1681
- true,
1682
- ["sign", "verify"]
1683
- );
1705
+ async function encryptWithEnvPublicKey(plain) {
1706
+ const publicKey = getEnvPublicKey();
1707
+ return rsaEncrypt(plain, publicKey);
1684
1708
  }
1685
- async function computeHMAC(data, key) {
1686
- if (typeof crypto === "undefined" || !crypto.subtle) {
1687
- throw new Error("Web Crypto API is not available");
1688
- }
1689
- const buffer = typeof data === "string" ? new TextEncoder().encode(data) : data;
1690
- const signature = await crypto.subtle.sign("HMAC", key, buffer);
1691
- return base64Encode(signature);
1709
+ async function decryptWithEnvPrivateKey(cipher) {
1710
+ const privateKey = getEnvPrivateKey();
1711
+ return rsaDecrypt(cipher, privateKey);
1692
1712
  }
1693
- async function verifyHMAC(data, signature, key) {
1713
+ async function generateRSAKeyPair(modulusLength = 2048) {
1694
1714
  if (typeof crypto === "undefined" || !crypto.subtle) {
1695
- throw new Error("Web Crypto API is not available");
1696
- }
1697
- try {
1698
- const dataBuffer = typeof data === "string" ? new TextEncoder().encode(data) : data;
1699
- const signatureBuffer = new Uint8Array(
1700
- atob(signature).split("").map((char) => char.charCodeAt(0))
1715
+ throw new Error(
1716
+ "[encryption] Web Crypto API is not available. Cannot generate RSA key pair. Please use fixed key pair from environment variables."
1701
1717
  );
1702
- return await crypto.subtle.verify("HMAC", key, signatureBuffer, dataBuffer);
1703
- } catch {
1704
- return false;
1705
1718
  }
1706
- }
1707
- async function deriveKeyFromPassword(password, salt, iterations = 1e5, keyLength = 256) {
1708
- if (typeof crypto === "undefined" || !crypto.subtle) {
1709
- throw new Error("Web Crypto API is not available");
1710
- }
1711
- const saltBuffer = typeof salt === "string" ? new TextEncoder().encode(salt) : salt;
1712
- const passwordKey = await crypto.subtle.importKey(
1713
- "raw",
1714
- new TextEncoder().encode(password),
1715
- "PBKDF2",
1716
- false,
1717
- ["deriveBits", "deriveKey"]
1718
- );
1719
- return crypto.subtle.deriveKey(
1719
+ const keyPair = await crypto.subtle.generateKey(
1720
1720
  {
1721
- name: "PBKDF2",
1722
- salt: saltBuffer,
1723
- iterations,
1721
+ name: "RSA-OAEP",
1722
+ modulusLength,
1723
+ publicExponent: new Uint8Array([1, 0, 1]),
1724
1724
  hash: "SHA-256"
1725
1725
  },
1726
- passwordKey,
1727
- {
1728
- name: "AES-GCM",
1729
- length: keyLength
1730
- },
1731
- false,
1726
+ true,
1732
1727
  ["encrypt", "decrypt"]
1733
1728
  );
1734
- }
1735
- async function aesGCMEncrypt(data, key) {
1736
- if (typeof crypto === "undefined" || !crypto.subtle) {
1737
- throw new Error("Web Crypto API is not available");
1738
- }
1739
- const dataBuffer = typeof data === "string" ? new TextEncoder().encode(data) : data;
1740
- const iv = crypto.getRandomValues(new Uint8Array(12));
1741
- const encrypted = await crypto.subtle.encrypt(
1742
- {
1743
- name: "AES-GCM",
1744
- iv
1745
- },
1746
- key,
1747
- dataBuffer
1748
- );
1729
+ const publicKeyDer = await crypto.subtle.exportKey("spki", keyPair.publicKey);
1730
+ const privateKeyDer = await crypto.subtle.exportKey("pkcs8", keyPair.privateKey);
1731
+ const publicKeyBase64 = base64Encode(publicKeyDer);
1732
+ const privateKeyBase64 = base64Encode(privateKeyDer);
1749
1733
  return {
1750
- encrypted: base64Encode(encrypted),
1751
- iv: base64Encode(iv.buffer)
1734
+ publicKey: base64ToPem(publicKeyBase64, "PUBLIC"),
1735
+ privateKey: base64ToPem(privateKeyBase64, "PRIVATE")
1752
1736
  };
1753
1737
  }
1754
- async function aesGCMDecrypt(encryptedData, iv, key) {
1755
- if (typeof crypto === "undefined" || !crypto.subtle) {
1756
- throw new Error("Web Crypto API is not available");
1757
- }
1758
- const encryptedBuffer = new Uint8Array(
1759
- atob(encryptedData).split("").map((char) => char.charCodeAt(0))
1760
- );
1761
- const ivBuffer = new Uint8Array(
1762
- atob(iv).split("").map((char) => char.charCodeAt(0))
1763
- );
1764
- const decrypted = await crypto.subtle.decrypt(
1765
- {
1766
- name: "AES-GCM",
1767
- iv: ivBuffer
1768
- },
1769
- key,
1770
- encryptedBuffer
1771
- );
1772
- return new TextDecoder().decode(decrypted);
1773
- }
1774
1738
 
1775
1739
  // src/browser/SecureStorage/index.ts
1776
- var globalKeyPair = null;
1777
- var globalHMACKey = null;
1778
- var keyPairInitialized = false;
1779
- var initOptions = {
1780
- autoGenerateKeys: true,
1781
- persistKeys: false,
1782
- keyStorageKey: void 0,
1783
- // 将自动生成随机键名
1784
- keyEncryptionPassword: void 0,
1785
- pbkdf2Iterations: 1e5,
1786
- keyModulusLength: 2048,
1787
- enableHMAC: true,
1788
- enableTimestampValidation: true,
1789
- timestampMaxAge: 7 * 24 * 60 * 60 * 1e3,
1790
- // 7 天
1791
- isProduction: false
1740
+ var CONFIG = {
1741
+ NAMESPACE: "sec_b_",
1742
+ DEFAULT_EXPIRE: null
1792
1743
  };
1793
- var actualKeyStorageKey = null;
1794
- var keyUsageCount = 0;
1795
- var initializationPromise = null;
1796
- var KEY_STORAGE_KEY_REGISTRY = "__SECURE_STORAGE_KEY__";
1797
- function generateKeyStorageKey() {
1798
- return `_sk_${generateRandomString(32)}_${Date.now()}`;
1799
- }
1800
- async function initializeStorageKeys(options = {}) {
1801
- if (options.forceReinitialize) {
1802
- globalKeyPair = null;
1803
- globalHMACKey = null;
1804
- keyPairInitialized = false;
1805
- actualKeyStorageKey = null;
1806
- keyUsageCount = 0;
1807
- initializationPromise = null;
1808
- } else if (keyPairInitialized && globalKeyPair) {
1809
- initOptions = { ...initOptions, ...options };
1810
- return;
1811
- }
1812
- initOptions = { ...initOptions, ...options };
1813
- if (initOptions.keyStorageKey) {
1814
- actualKeyStorageKey = initOptions.keyStorageKey;
1815
- } else if (!actualKeyStorageKey) {
1816
- if (typeof window !== "undefined" && window.localStorage) {
1817
- const savedKeyName = window.localStorage.getItem(KEY_STORAGE_KEY_REGISTRY);
1818
- if (savedKeyName) {
1819
- actualKeyStorageKey = savedKeyName;
1820
- } else {
1821
- actualKeyStorageKey = generateKeyStorageKey();
1822
- window.localStorage.setItem(KEY_STORAGE_KEY_REGISTRY, actualKeyStorageKey);
1823
- }
1824
- } else {
1825
- actualKeyStorageKey = generateKeyStorageKey();
1826
- }
1827
- }
1828
- if (initOptions.persistKeys && typeof window !== "undefined" && window.localStorage) {
1829
- try {
1830
- const storedKeys = window.localStorage.getItem(actualKeyStorageKey);
1831
- if (storedKeys) {
1832
- const keyData = JSON.parse(storedKeys);
1833
- if (keyData.encrypted && keyData.privateKeyEncrypted && keyData.salt) {
1834
- if (!initOptions.keyEncryptionPassword) {
1835
- if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
1836
- console.error("Encrypted keys found but no password provided");
1837
- }
1838
- throw new Error("Password required to decrypt stored keys");
1839
- }
1840
- const saltArray = new Uint8Array(
1841
- atob(keyData.salt).split("").map((char) => char.charCodeAt(0))
1842
- );
1843
- const iterations = keyData.pbkdf2Iterations || initOptions.pbkdf2Iterations;
1844
- const derivedKey = await deriveKeyFromPassword(
1845
- initOptions.keyEncryptionPassword,
1846
- saltArray.buffer,
1847
- iterations
1848
- );
1849
- const decryptedPrivateKey = await aesGCMDecrypt(
1850
- keyData.privateKeyEncrypted,
1851
- keyData.iv,
1852
- derivedKey
1853
- );
1854
- globalKeyPair = {
1855
- publicKey: await importPublicKey(keyData.publicKey),
1856
- privateKey: await importPrivateKey(decryptedPrivateKey)
1857
- };
1858
- } else if (keyData.publicKey && keyData.privateKey) {
1859
- if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
1860
- console.warn(
1861
- "\u26A0\uFE0F SECURITY WARNING: Loading unencrypted keys from storage. Consider re-initializing with password encryption."
1862
- );
1863
- }
1864
- globalKeyPair = {
1865
- publicKey: await importPublicKey(keyData.publicKey),
1866
- privateKey: await importPrivateKey(keyData.privateKey)
1867
- };
1868
- }
1869
- if (globalKeyPair) {
1870
- keyPairInitialized = true;
1871
- if (initOptions.enableHMAC && !globalHMACKey) {
1872
- globalHMACKey = await generateHMACKey();
1873
- }
1874
- return;
1875
- }
1876
- }
1877
- } catch (error) {
1878
- if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
1879
- console.warn("Failed to load persisted keys, will generate new ones");
1880
- }
1881
- }
1882
- }
1883
- if (initOptions.autoGenerateKeys && !globalKeyPair) {
1884
- try {
1885
- globalKeyPair = await generateRSAKeyPair(initOptions.keyModulusLength);
1886
- if (initOptions.enableHMAC && !globalHMACKey) {
1887
- globalHMACKey = await generateHMACKey();
1888
- }
1889
- keyPairInitialized = true;
1890
- keyUsageCount = 0;
1891
- if (initOptions.persistKeys && typeof window !== "undefined" && window.localStorage) {
1892
- try {
1893
- const publicKeyStr = await exportPublicKey(globalKeyPair.publicKey);
1894
- if (initOptions.keyEncryptionPassword) {
1895
- const privateKeyStr = await exportPrivateKey(globalKeyPair.privateKey);
1896
- const salt = crypto.getRandomValues(new Uint8Array(16));
1897
- const derivedKey = await deriveKeyFromPassword(
1898
- initOptions.keyEncryptionPassword,
1899
- salt.buffer,
1900
- initOptions.pbkdf2Iterations
1901
- );
1902
- const { encrypted, iv } = await aesGCMEncrypt(privateKeyStr, derivedKey);
1903
- const keyData = {
1904
- encrypted: true,
1905
- publicKey: publicKeyStr,
1906
- privateKeyEncrypted: encrypted,
1907
- iv,
1908
- salt: base64Encode(salt.buffer),
1909
- pbkdf2Iterations: initOptions.pbkdf2Iterations
1910
- };
1911
- window.localStorage.setItem(actualKeyStorageKey, JSON.stringify(keyData));
1912
- if (!initOptions.isProduction && typeof console !== "undefined" && console.info) {
1913
- console.info("\u2705 Keys encrypted and stored securely");
1914
- }
1915
- } else {
1916
- if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
1917
- console.warn(
1918
- "\u26A0\uFE0F SECURITY WARNING: Storing private keys without encryption! Private keys will be stored in plain text (Base64 encoded). Provide keyEncryptionPassword for secure storage."
1919
- );
1920
- }
1921
- const keyData = {
1922
- publicKey: publicKeyStr,
1923
- privateKey: await exportPrivateKey(globalKeyPair.privateKey)
1924
- };
1925
- window.localStorage.setItem(actualKeyStorageKey, JSON.stringify(keyData));
1926
- }
1927
- } catch (error) {
1928
- if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
1929
- console.error("Failed to persist keys");
1930
- }
1931
- }
1932
- }
1933
- } catch (error) {
1934
- if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
1935
- console.error("Failed to generate storage keys");
1936
- }
1937
- throw error;
1938
- }
1939
- }
1940
- if (initOptions.enableHMAC && !globalHMACKey) {
1941
- globalHMACKey = await generateHMACKey();
1942
- }
1943
- }
1944
- function setStorageKeyPair(keyPair) {
1945
- globalKeyPair = keyPair;
1946
- keyPairInitialized = true;
1947
- }
1948
- function getStorageKeyPair() {
1949
- return globalKeyPair;
1950
- }
1951
- function clearPersistedKeys() {
1952
- if (typeof window !== "undefined" && window.localStorage && actualKeyStorageKey) {
1953
- try {
1954
- window.localStorage.removeItem(actualKeyStorageKey);
1955
- actualKeyStorageKey = null;
1956
- keyPairInitialized = false;
1957
- globalKeyPair = null;
1958
- globalHMACKey = null;
1959
- keyUsageCount = 0;
1960
- } catch (error) {
1961
- if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
1962
- console.error("Failed to clear persisted keys");
1963
- }
1964
- }
1965
- }
1966
- }
1967
- function getKeyUsageCount() {
1968
- return keyUsageCount;
1969
- }
1970
- function resetKeyUsageCount() {
1971
- keyUsageCount = 0;
1972
- }
1973
- async function ensureKeyPair() {
1974
- if (globalKeyPair) {
1975
- keyUsageCount++;
1976
- return;
1977
- }
1978
- if (!initOptions.autoGenerateKeys) {
1979
- return;
1980
- }
1981
- if (initializationPromise) {
1982
- await initializationPromise;
1983
- if (globalKeyPair) {
1984
- keyUsageCount++;
1744
+ var SecureStorage = class _SecureStorage {
1745
+ static get instances() {
1746
+ if (!this._instances) {
1747
+ this._instances = /* @__PURE__ */ new Map();
1985
1748
  }
1986
- return;
1987
- }
1988
- initializationPromise = initializeStorageKeys();
1989
- try {
1990
- await initializationPromise;
1991
- } finally {
1992
- initializationPromise = null;
1749
+ return this._instances;
1993
1750
  }
1994
- if (globalKeyPair) {
1995
- keyUsageCount++;
1996
- }
1997
- }
1998
- function safeParseJSON(jsonStr, expectedType) {
1999
- try {
2000
- const parsed = JSON.parse(jsonStr);
2001
- if (expectedType === "object" && (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))) {
2002
- throw new Error("Expected object but got different type");
2003
- }
2004
- if (expectedType === "array" && !Array.isArray(parsed)) {
2005
- throw new Error("Expected array but got different type");
2006
- }
2007
- return parsed;
2008
- } catch (error) {
2009
- if (error instanceof SyntaxError) {
2010
- throw new Error(`Invalid JSON format: ${error.message}`);
1751
+ constructor(options) {
1752
+ const storageType = options.storage === window.sessionStorage ? "session" : "local";
1753
+ const namespace = options.namespace || CONFIG.NAMESPACE;
1754
+ const instanceKey = `${storageType}_${namespace}`;
1755
+ const existingInstance = _SecureStorage.instances.get(instanceKey);
1756
+ if (existingInstance) {
1757
+ return existingInstance;
2011
1758
  }
2012
- throw error;
2013
- }
2014
- }
2015
- function validateTimestamp(timestamp, maxClockSkew = 5 * 60 * 1e3) {
2016
- if (!initOptions.enableTimestampValidation || !timestamp) {
2017
- return true;
2018
- }
2019
- if (initOptions.timestampMaxAge === 0) {
2020
- return true;
2021
- }
2022
- const now = Date.now();
2023
- const age = now - timestamp;
2024
- const maxAge = initOptions.timestampMaxAge || 7 * 24 * 60 * 60 * 1e3;
2025
- if (age < -maxClockSkew) {
2026
- return false;
1759
+ this.namespace = namespace;
1760
+ this.storage = options.storage || window.localStorage;
1761
+ this.defaultExpire = options.defaultExpire ?? CONFIG.DEFAULT_EXPIRE;
1762
+ this.instanceKey = instanceKey;
1763
+ _SecureStorage.instances.set(instanceKey, this);
2027
1764
  }
2028
- return age >= -maxClockSkew && age <= maxAge;
2029
- }
2030
- async function encryptValue(value, publicKey, hmacKey) {
2031
- const encryptedData = await rsaEncrypt(value, publicKey);
2032
- if (hmacKey) {
2033
- const hmac = await computeHMAC(encryptedData, hmacKey);
2034
- const item = {
2035
- data: encryptedData,
2036
- hmac,
2037
- encrypted: true,
2038
- timestamp: Date.now()
2039
- };
2040
- return JSON.stringify(item);
2041
- }
2042
- return encryptedData;
2043
- }
2044
- async function handleDecryptError(error, encryptedStr, key) {
2045
- if (error instanceof Error && error.message.includes("HMAC verification failed")) {
2046
- if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
2047
- console.error(
2048
- "\u26A0\uFE0F SECURITY ALERT: Data integrity check failed! Data may have been tampered with."
2049
- );
2050
- }
2051
- throw error;
1765
+ /**
1766
+ * 获取单例实例(静态方法)
1767
+ */
1768
+ static getInstance(options) {
1769
+ return new _SecureStorage(options);
2052
1770
  }
2053
- if (error instanceof Error && error.message.includes("Data timestamp validation failed")) {
2054
- if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
2055
- console.warn("\u26A0\uFE0F SECURITY WARNING: Data timestamp validation failed");
2056
- }
2057
- throw error;
1771
+ /**
1772
+ * 清除所有单例实例(用于测试或重置)
1773
+ */
1774
+ static clearInstances() {
1775
+ _SecureStorage.instances.clear();
2058
1776
  }
2059
- try {
2060
- const decryptedStr = base64Decode(encryptedStr);
2061
- if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
2062
- console.warn(
2063
- `\u26A0\uFE0F SECURITY WARNING: Reading unencrypted data for key "${key}". This may be a security risk if sensitive data was stored without encryption.`
2064
- );
2065
- }
2066
- return decryptedStr;
2067
- } catch {
2068
- throw error;
1777
+ /**
1778
+ * 获取当前所有实例的数量(用于调试)
1779
+ */
1780
+ static getInstanceCount() {
1781
+ return _SecureStorage.instances.size;
2069
1782
  }
2070
- }
2071
- function handleNoKeyDecode(encryptedStr, key) {
2072
- try {
2073
- const decryptedStr = base64Decode(encryptedStr);
2074
- if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
2075
- console.warn(
2076
- `\u26A0\uFE0F SECURITY WARNING: Reading unencrypted data for key "${key}" without encryption keys.`
2077
- );
2078
- }
2079
- return decryptedStr;
2080
- } catch (error) {
2081
- throw new Error(`Failed to decode storage value for key "${key}"`);
1783
+ /**
1784
+ * 获取完整键名(带命名空间)
1785
+ */
1786
+ _getFullKey(key) {
1787
+ return `${this.namespace}${key}`;
2082
1788
  }
2083
- }
2084
- async function decryptValue(encryptedValue, privateKey, hmacKey) {
2085
- try {
2086
- const item = JSON.parse(encryptedValue);
2087
- if (item.encrypted && item.data && item.hmac) {
2088
- if (hmacKey) {
2089
- const isValid = await verifyHMAC(item.data, item.hmac, hmacKey);
2090
- if (!isValid) {
2091
- throw new Error("HMAC verification failed: data may have been tampered with");
2092
- }
2093
- }
2094
- if (item.timestamp && !validateTimestamp(item.timestamp)) {
2095
- throw new Error("Data timestamp validation failed: data may be expired or replayed");
2096
- }
2097
- return await rsaDecrypt(item.data, privateKey);
2098
- }
2099
- return await rsaDecrypt(encryptedValue, privateKey);
2100
- } catch (error) {
2101
- if (error instanceof Error && error.message.includes("HMAC verification failed")) {
2102
- throw error;
2103
- }
2104
- throw new Error("Failed to decrypt storage value: invalid format or corrupted data");
1789
+ /**
1790
+ * 检查数据是否过期
1791
+ */
1792
+ _isExpired(expire) {
1793
+ if (!expire) return false;
1794
+ return Date.now() > expire;
2105
1795
  }
2106
- }
2107
- var localStorage = {
2108
1796
  /**
2109
1797
  * 设置值
2110
- * @param key - 键
2111
- * @param value - 值
2112
- * @param options - 选项(过期时间、密钥等)
2113
- * @returns Promise<void>
2114
1798
  */
2115
- async set(key, value, options = {}) {
2116
- if (typeof window === "undefined" || !window.localStorage) {
2117
- throw new Error("localStorage is not available");
2118
- }
2119
- const { expiry, publicKey } = options;
2120
- const item = {
2121
- value,
2122
- expiry: expiry ? Date.now() + expiry : void 0
2123
- };
1799
+ async set(key, value, expire = this.defaultExpire) {
2124
1800
  try {
2125
- const jsonStr = JSON.stringify(item);
2126
- await ensureKeyPair();
2127
- const keyToUse = publicKey || globalKeyPair?.publicKey;
2128
- if (keyToUse) {
2129
- const encrypted = await encryptValue(jsonStr, keyToUse, globalHMACKey);
2130
- window.localStorage.setItem(key, encrypted);
2131
- } else {
2132
- if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
2133
- console.warn(
2134
- `\u26A0\uFE0F SECURITY WARNING: Storing data without encryption for key "${key}". Data is only Base64 encoded, not encrypted!`
2135
- );
2136
- }
2137
- const encoded = base64Encode(jsonStr);
2138
- window.localStorage.setItem(key, encoded);
2139
- }
1801
+ const fullKey = this._getFullKey(key);
1802
+ const aesKey = generateRandomAESKeyString();
1803
+ const storageData = {
1804
+ value,
1805
+ expire: expire ? Date.now() + expire : null,
1806
+ timestamp: Date.now()
1807
+ };
1808
+ const encryptedData = encryptJsonWithAES(storageData, aesKey);
1809
+ const encryptedKey = await encryptWithEnvPublicKey(aesKey);
1810
+ const finalData = {
1811
+ data: encryptedData,
1812
+ key: encryptedKey,
1813
+ version: "1.0"
1814
+ };
1815
+ this.storage.setItem(fullKey, JSON.stringify(finalData));
1816
+ return true;
2140
1817
  } catch (error) {
2141
- if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
2142
- console.error("localStorage.set error:", error);
2143
- }
2144
- throw error;
1818
+ console.error("[SecureStorage] \u4FDD\u5B58\u5931\u8D25:", error);
1819
+ return false;
2145
1820
  }
2146
- },
1821
+ }
2147
1822
  /**
2148
1823
  * 获取值
2149
- * @param key - 键
2150
- * @param options - 选项(默认值、私钥等)
2151
- * @returns Promise<T | undefined> 值或默认值
2152
1824
  */
2153
- async get(key, options = {}) {
2154
- if (typeof window === "undefined" || !window.localStorage) {
2155
- return options.defaultValue;
2156
- }
2157
- const { defaultValue, privateKey } = options;
1825
+ async get(key, defaultValue = null) {
2158
1826
  try {
2159
- const encryptedStr = window.localStorage.getItem(key);
2160
- if (!encryptedStr) return defaultValue;
2161
- await ensureKeyPair();
2162
- let decryptedStr;
2163
- const keyToUse = privateKey || globalKeyPair?.privateKey;
2164
- if (keyToUse) {
2165
- try {
2166
- decryptedStr = await decryptValue(encryptedStr, keyToUse, globalHMACKey);
2167
- } catch (error) {
2168
- try {
2169
- decryptedStr = await handleDecryptError(error, encryptedStr, key);
2170
- } catch {
2171
- return defaultValue;
2172
- }
2173
- }
2174
- } else {
2175
- try {
2176
- decryptedStr = handleNoKeyDecode(encryptedStr, key);
2177
- } catch {
2178
- return defaultValue;
2179
- }
1827
+ const fullKey = this._getFullKey(key);
1828
+ const storedData = this.storage.getItem(fullKey);
1829
+ if (!storedData) {
1830
+ return defaultValue;
2180
1831
  }
2181
- const item = safeParseJSON(decryptedStr, "object");
2182
- if (item.expiry && Date.now() > item.expiry) {
2183
- window.localStorage.removeItem(key);
1832
+ const finalData = JSON.parse(storedData);
1833
+ const aesKey = await decryptWithEnvPrivateKey(finalData.key);
1834
+ const storageData = decryptJsonWithAES(finalData.data, aesKey);
1835
+ if (this._isExpired(storageData.expire)) {
1836
+ this.remove(key);
2184
1837
  return defaultValue;
2185
1838
  }
2186
- return item.value;
1839
+ return storageData.value;
2187
1840
  } catch (error) {
2188
- if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
2189
- console.warn(`Failed to parse storage item for key "${key}"`);
2190
- }
1841
+ console.error("[SecureStorage] \u8BFB\u53D6\u5931\u8D25:", error);
2191
1842
  return defaultValue;
2192
1843
  }
2193
- },
1844
+ }
2194
1845
  /**
2195
- * 移除值
2196
- * @param key - 键
1846
+ * 删除值
2197
1847
  */
2198
1848
  remove(key) {
2199
- if (typeof window === "undefined" || !window.localStorage) return;
2200
1849
  try {
2201
- window.localStorage.removeItem(key);
1850
+ const fullKey = this._getFullKey(key);
1851
+ this.storage.removeItem(fullKey);
1852
+ return true;
2202
1853
  } catch (error) {
2203
- if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
2204
- console.error("localStorage.remove error");
2205
- }
1854
+ console.error("[SecureStorage] \u79FB\u9664\u5931\u8D25:", error);
1855
+ return false;
2206
1856
  }
2207
- },
1857
+ }
2208
1858
  /**
2209
- * 清空所有值
1859
+ * 检查键是否存在
2210
1860
  */
2211
- clear() {
2212
- if (typeof window === "undefined" || !window.localStorage) return;
1861
+ async has(key) {
2213
1862
  try {
2214
- window.localStorage.clear();
2215
- } catch (error) {
2216
- if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
2217
- console.error("localStorage.clear error");
1863
+ const fullKey = this._getFullKey(key);
1864
+ const storedData = this.storage.getItem(fullKey);
1865
+ if (!storedData) return false;
1866
+ const finalData = JSON.parse(storedData);
1867
+ const aesKey = await decryptWithEnvPrivateKey(finalData.key);
1868
+ const storageData = decryptJsonWithAES(finalData.data, aesKey);
1869
+ if (this._isExpired(storageData.expire)) {
1870
+ this.remove(key);
1871
+ return false;
2218
1872
  }
1873
+ return true;
1874
+ } catch (error) {
1875
+ return false;
2219
1876
  }
2220
- },
1877
+ }
2221
1878
  /**
2222
- * 获取所有键
2223
- * @returns 键数组
1879
+ * 清空所有数据
2224
1880
  */
2225
- keys() {
2226
- if (typeof window === "undefined" || !window.localStorage) return [];
2227
- const keys2 = [];
1881
+ clear() {
2228
1882
  try {
2229
- for (let i = 0; i < window.localStorage.length; i++) {
2230
- const key = window.localStorage.key(i);
2231
- if (key) keys2.push(key);
1883
+ let count = 0;
1884
+ const keys2 = [];
1885
+ for (let i = 0; i < this.storage.length; i++) {
1886
+ const key = this.storage.key(i);
1887
+ if (key && key.startsWith(this.namespace)) {
1888
+ keys2.push(key);
1889
+ }
2232
1890
  }
1891
+ keys2.forEach((key) => {
1892
+ this.storage.removeItem(key);
1893
+ count++;
1894
+ });
1895
+ return count;
2233
1896
  } catch (error) {
2234
- if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
2235
- console.error("localStorage.keys error");
2236
- }
1897
+ console.error("[SecureStorage] \u6E05\u7A7A\u5931\u8D25:", error);
1898
+ return 0;
2237
1899
  }
2238
- return keys2;
2239
1900
  }
2240
- };
2241
- var sessionStorage = {
2242
1901
  /**
2243
- * 设置值
2244
- * @param key - 键
2245
- * @param value - 值
2246
- * @param options - 选项(公钥等)
2247
- * @returns Promise<void>
1902
+ * 获取所有键名
2248
1903
  */
2249
- async set(key, value, options = {}) {
2250
- if (typeof window === "undefined" || !window.sessionStorage) {
2251
- throw new Error("sessionStorage is not available");
2252
- }
2253
- const { publicKey } = options;
1904
+ keys() {
2254
1905
  try {
2255
- const jsonStr = JSON.stringify(value);
2256
- await ensureKeyPair();
2257
- const keyToUse = publicKey || globalKeyPair?.publicKey;
2258
- if (keyToUse) {
2259
- const encrypted = await encryptValue(jsonStr, keyToUse, globalHMACKey);
2260
- window.sessionStorage.setItem(key, encrypted);
2261
- } else {
2262
- if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
2263
- console.warn(
2264
- `\u26A0\uFE0F SECURITY WARNING: Storing data without encryption for key "${key}". Data is only Base64 encoded, not encrypted!`
2265
- );
1906
+ const keys2 = [];
1907
+ for (let i = 0; i < this.storage.length; i++) {
1908
+ const key = this.storage.key(i);
1909
+ if (key && key.startsWith(this.namespace)) {
1910
+ keys2.push(key.substring(this.namespace.length));
2266
1911
  }
2267
- const encoded = base64Encode(jsonStr);
2268
- window.sessionStorage.setItem(key, encoded);
2269
1912
  }
1913
+ return keys2;
2270
1914
  } catch (error) {
2271
- if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
2272
- console.error("sessionStorage.set error:", error);
2273
- }
2274
- throw error;
1915
+ console.error("[SecureStorage] \u83B7\u53D6\u952E\u540D\u5931\u8D25:", error);
1916
+ return [];
2275
1917
  }
2276
- },
1918
+ }
2277
1919
  /**
2278
- * 获取值
2279
- * @param key - 键
2280
- * @param options - 选项(默认值、私钥等)
2281
- * @returns Promise<T | undefined> 值或默认值
1920
+ * 获取数据条数
2282
1921
  */
2283
- async get(key, options = {}) {
2284
- if (typeof window === "undefined" || !window.sessionStorage) {
2285
- return options.defaultValue;
2286
- }
2287
- const { defaultValue, privateKey } = options;
1922
+ size() {
1923
+ return this.keys().length;
1924
+ }
1925
+ /**
1926
+ * 清理过期数据
1927
+ */
1928
+ async clearExpired() {
2288
1929
  try {
2289
- const encryptedStr = window.sessionStorage.getItem(key);
2290
- if (!encryptedStr) return defaultValue;
2291
- await ensureKeyPair();
2292
- let decryptedStr;
2293
- const keyToUse = privateKey || globalKeyPair?.privateKey;
2294
- if (keyToUse) {
1930
+ let count = 0;
1931
+ const keys2 = this.keys();
1932
+ for (const key of keys2) {
1933
+ const fullKey = this._getFullKey(key);
2295
1934
  try {
2296
- decryptedStr = await decryptValue(encryptedStr, keyToUse, globalHMACKey);
2297
- } catch (error) {
2298
- try {
2299
- decryptedStr = await handleDecryptError(error, encryptedStr, key);
2300
- } catch {
2301
- return defaultValue;
1935
+ const storedData = this.storage.getItem(fullKey);
1936
+ if (storedData) {
1937
+ const finalData = JSON.parse(storedData);
1938
+ const aesKey = await decryptWithEnvPrivateKey(finalData.key);
1939
+ const storageData = decryptJsonWithAES(finalData.data, aesKey);
1940
+ if (this._isExpired(storageData.expire)) {
1941
+ this.remove(key);
1942
+ count++;
1943
+ }
2302
1944
  }
1945
+ } catch (error) {
1946
+ this.remove(key);
1947
+ count++;
2303
1948
  }
2304
- } else {
2305
- try {
2306
- decryptedStr = handleNoKeyDecode(encryptedStr, key);
2307
- } catch {
2308
- return defaultValue;
2309
- }
2310
- }
2311
- return safeParseJSON(decryptedStr);
2312
- } catch {
2313
- return defaultValue;
2314
- }
2315
- },
2316
- /**
2317
- * 移除值
2318
- * @param key - 键
2319
- */
2320
- remove(key) {
2321
- if (typeof window === "undefined" || !window.sessionStorage) return;
2322
- try {
2323
- window.sessionStorage.removeItem(key);
2324
- } catch (error) {
2325
- if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
2326
- console.error("sessionStorage.remove error");
2327
1949
  }
2328
- }
2329
- },
2330
- /**
2331
- * 清空所有值
2332
- */
2333
- clear() {
2334
- if (typeof window === "undefined" || !window.sessionStorage) return;
2335
- try {
2336
- window.sessionStorage.clear();
1950
+ return count;
2337
1951
  } catch (error) {
2338
- if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
2339
- console.error("sessionStorage.clear error");
2340
- }
1952
+ console.error("[SecureStorage] \u6E05\u7406\u8FC7\u671F\u6570\u636E\u5931\u8D25:", error);
1953
+ return 0;
2341
1954
  }
2342
1955
  }
2343
- };
2344
- var cookie = {
2345
- /**
2346
- * 设置Cookie
2347
- * @param key - 键
2348
- * @param value - 值
2349
- * @param options - Cookie选项
2350
- */
2351
- set(key, value, options = {}) {
2352
- if (typeof document === "undefined") {
2353
- throw new Error("document is not available");
2354
- }
2355
- let cookieStr = `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
2356
- if (options.expires) {
2357
- const expiresDate = options.expires instanceof Date ? options.expires : new Date(Date.now() + options.expires * 24 * 60 * 60 * 1e3);
2358
- cookieStr += `; expires=${expiresDate.toUTCString()}`;
2359
- }
2360
- if (options.path) cookieStr += `; path=${options.path}`;
2361
- if (options.domain) cookieStr += `; domain=${options.domain}`;
2362
- if (options.secure) cookieStr += "; secure";
2363
- if (options.sameSite) cookieStr += `; sameSite=${options.sameSite}`;
2364
- document.cookie = cookieStr;
2365
- },
2366
- /**
2367
- * 获取Cookie
2368
- * @param key - 键
2369
- * @returns Cookie值
2370
- */
2371
- get(key) {
2372
- if (typeof document === "undefined") return void 0;
2373
- const name = encodeURIComponent(key);
2374
- const cookies = document.cookie.split(";");
2375
- for (const cookieStr of cookies) {
2376
- const trimmed = cookieStr.trim();
2377
- const eqIndex = trimmed.indexOf("=");
2378
- if (eqIndex === -1) continue;
2379
- const cookieKey = trimmed.substring(0, eqIndex).trim();
2380
- const cookieValue = trimmed.substring(eqIndex + 1).trim();
2381
- if (cookieKey === name) {
2382
- const decoded = decodeURIComponent(cookieValue);
2383
- return decoded === "" ? void 0 : decoded;
2384
- }
2385
- }
2386
- return void 0;
2387
- },
2388
1956
  /**
2389
- * 移除Cookie
2390
- * @param key - 键
2391
- * @param options - Cookie选项
1957
+ * 获取实例的唯一标识
2392
1958
  */
2393
- remove(key, options = {}) {
2394
- cookie.set(key, "", {
2395
- ...options,
2396
- expires: /* @__PURE__ */ new Date(0)
2397
- });
2398
- },
1959
+ getInstanceKey() {
1960
+ return this.instanceKey;
1961
+ }
2399
1962
  /**
2400
- * 获取所有Cookie
2401
- * @returns Cookie对象
1963
+ * 检查是否为单例实例
2402
1964
  */
2403
- getAll() {
2404
- if (typeof document === "undefined") return {};
2405
- const cookies = {};
2406
- document.cookie.split(";").forEach((cookieStr) => {
2407
- const trimmed = cookieStr.trim();
2408
- if (!trimmed) return;
2409
- const eqIndex = trimmed.indexOf("=");
2410
- if (eqIndex === -1) return;
2411
- const key = trimmed.substring(0, eqIndex).trim();
2412
- const value = trimmed.substring(eqIndex + 1).trim();
2413
- if (key && value) {
2414
- const decodedKey = decodeURIComponent(key);
2415
- const decodedValue = decodeURIComponent(value);
2416
- if (decodedValue !== "") {
2417
- cookies[decodedKey] = decodedValue;
2418
- }
2419
- }
2420
- });
2421
- return cookies;
1965
+ isSingleton() {
1966
+ return _SecureStorage.instances.has(this.instanceKey);
2422
1967
  }
2423
1968
  };
2424
- function createBackendAdapter(type) {
2425
- switch (type) {
2426
- case "local":
2427
- return {
2428
- async set(key, value, options) {
2429
- await localStorage.set(key, value, {
2430
- expiry: options?.expiry,
2431
- publicKey: options?.publicKey
2432
- });
2433
- },
2434
- async get(key, options) {
2435
- return localStorage.get(key, {
2436
- defaultValue: options?.defaultValue,
2437
- privateKey: options?.privateKey
2438
- });
2439
- },
2440
- remove(key) {
2441
- localStorage.remove(key);
2442
- },
2443
- clear() {
2444
- localStorage.clear();
2445
- },
2446
- keys() {
2447
- return localStorage.keys();
2448
- }
2449
- };
2450
- case "session":
2451
- return {
2452
- async set(key, value, options) {
2453
- await sessionStorage.set(key, value, {
2454
- publicKey: options?.publicKey
2455
- });
2456
- },
2457
- async get(key, options) {
2458
- return sessionStorage.get(key, {
2459
- defaultValue: options?.defaultValue,
2460
- privateKey: options?.privateKey
2461
- });
2462
- },
2463
- remove(key) {
2464
- sessionStorage.remove(key);
2465
- },
2466
- clear() {
2467
- sessionStorage.clear();
2468
- },
2469
- keys() {
2470
- if (typeof window === "undefined" || !window.sessionStorage) return [];
2471
- const keys2 = [];
2472
- try {
2473
- for (let i = 0; i < window.sessionStorage.length; i++) {
2474
- const key = window.sessionStorage.key(i);
2475
- if (key) keys2.push(key);
2476
- }
2477
- } catch (error) {
2478
- if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
2479
- console.error("sessionStorage.keys error");
2480
- }
2481
- }
2482
- return keys2;
2483
- }
2484
- };
2485
- case "cookie":
2486
- return {
2487
- async set(key, value) {
2488
- cookie.set(key, String(value));
2489
- },
2490
- async get(key, options) {
2491
- const value = cookie.get(key);
2492
- if (value === void 0) return options?.defaultValue;
2493
- try {
2494
- return JSON.parse(value);
2495
- } catch {
2496
- return value;
2497
- }
2498
- },
2499
- remove(key) {
2500
- cookie.remove(key);
2501
- },
2502
- clear() {
2503
- const all = cookie.getAll();
2504
- Object.keys(all).forEach((k) => cookie.remove(k));
2505
- },
2506
- keys() {
2507
- return Object.keys(cookie.getAll());
2508
- }
2509
- };
2510
- default:
2511
- return createBackendAdapter("local");
1969
+ var _defaultSecureStorage = null;
1970
+ function _getDefaultSecureStorage() {
1971
+ if (typeof window === "undefined") {
1972
+ throw new Error("[SecureStorage] secureStorage is only available in browser environments.");
2512
1973
  }
1974
+ if (!_defaultSecureStorage) {
1975
+ _defaultSecureStorage = new SecureStorage({});
1976
+ }
1977
+ return _defaultSecureStorage;
2513
1978
  }
2514
- function createSecureStorage(type = "local") {
2515
- return createBackendAdapter(type);
2516
- }
2517
- var secureStorage = createSecureStorage("local");
1979
+ var secureStorage = new Proxy({}, {
1980
+ get(_target, prop) {
1981
+ const instance = _getDefaultSecureStorage();
1982
+ const value = instance[prop];
1983
+ if (typeof value === "function") {
1984
+ return value.bind(instance);
1985
+ }
1986
+ return value;
1987
+ }
1988
+ });
2518
1989
 
2519
1990
  // src/browser/network/index.ts
2520
1991
  async function fetchWithRetry(url, options = {}, retryCount = 3, retryDelay = 1e3) {
@@ -4110,6 +3581,48 @@ function setCommonParams(params) {
4110
3581
  async function flush() {
4111
3582
  await getTracker().flush();
4112
3583
  }
3584
+ function convertRes2Blob(response) {
3585
+ const contentDisposition = response.headers["content-disposition"];
3586
+ let fileName = "file.xlsx";
3587
+ if (contentDisposition) {
3588
+ const rfc5987Match = contentDisposition.match(/filename\*=UTF-8''([^;]+)/i);
3589
+ if (rfc5987Match) {
3590
+ fileName = decodeURIComponent(rfc5987Match[1]);
3591
+ } else {
3592
+ const standardMatch = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/i);
3593
+ if (standardMatch) {
3594
+ fileName = standardMatch[1];
3595
+ fileName = fileName.replace(/^['"]|['"]$/g, "");
3596
+ try {
3597
+ fileName = decodeURIComponent(fileName);
3598
+ } catch (e) {
3599
+ try {
3600
+ fileName = decodeURI(fileName);
3601
+ } catch (e2) {
3602
+ }
3603
+ }
3604
+ }
3605
+ }
3606
+ fileName = fileName.trim().replace(/^_+|_+$/g, "");
3607
+ }
3608
+ const blob = new Blob([response.data], { type: "application/vnd.ms-excel;charset=utf-8" });
3609
+ if (typeof window.navigator.msSaveBlob !== "undefined") {
3610
+ window.navigator.msSaveBlob(blob, fileName);
3611
+ } else {
3612
+ const blobURL = window.URL.createObjectURL(blob);
3613
+ const tempLink = document.createElement("a");
3614
+ tempLink.style.display = "none";
3615
+ tempLink.href = blobURL;
3616
+ tempLink.setAttribute("download", fileName);
3617
+ if (typeof tempLink.download === "undefined") {
3618
+ tempLink.setAttribute("target", "_blank");
3619
+ }
3620
+ document.body.appendChild(tempLink);
3621
+ tempLink.click();
3622
+ document.body.removeChild(tempLink);
3623
+ window.URL.revokeObjectURL(blobURL);
3624
+ }
3625
+ }
4113
3626
  var serviceEventHandlers = {
4114
3627
  message: null
4115
3628
  };
@@ -4143,11 +3656,28 @@ function filterEmptyKey(params, emptyString) {
4143
3656
  }
4144
3657
  });
4145
3658
  }
3659
+ function isFileUpload(data) {
3660
+ if (!data) return false;
3661
+ if (data instanceof FormData) return true;
3662
+ if (data instanceof File) return true;
3663
+ if (data instanceof Blob) return true;
3664
+ if (data instanceof ArrayBuffer) return true;
3665
+ if (typeof data === "object" && data !== null && !Array.isArray(data)) {
3666
+ const obj = data;
3667
+ return Object.values(obj).some(
3668
+ (value) => value instanceof File || value instanceof Blob || value instanceof FormData
3669
+ );
3670
+ }
3671
+ return false;
3672
+ }
4146
3673
  function getTransformRequest(contentType) {
4147
3674
  if (!contentType) {
4148
3675
  return (data) => Qs.stringify(data);
4149
3676
  }
4150
3677
  const normalizedType = contentType.toLowerCase().trim();
3678
+ if (normalizedType.startsWith("multipart/form-data")) {
3679
+ return void 0;
3680
+ }
4151
3681
  if (normalizedType.startsWith("application/json")) {
4152
3682
  return (data) => JSON.stringify(data);
4153
3683
  }
@@ -4168,6 +3698,15 @@ var service = axios.create({
4168
3698
  });
4169
3699
  service.interceptors.request.use(
4170
3700
  async (config) => {
3701
+ const isFileUploadRequest = isFileUpload(config.data);
3702
+ if (isFileUploadRequest) {
3703
+ if (config.headers) {
3704
+ delete config.headers["Content-Type"];
3705
+ delete config.headers["content-type"];
3706
+ }
3707
+ config.transformRequest = void 0;
3708
+ return config;
3709
+ }
4171
3710
  const contentType = config.headers?.["Content-Type"] || config.headers?.["content-type"];
4172
3711
  if (!config.transformRequest && contentType) {
4173
3712
  const transformRequest = getTransformRequest(
@@ -4180,9 +3719,11 @@ service.interceptors.request.use(
4180
3719
  config.transformRequest = (data) => Qs.stringify(data);
4181
3720
  }
4182
3721
  if (config.method === "post" || config.method === "put" || config.method === "patch") {
4183
- const params = { ...config.data || {} };
4184
- filterEmptyKey(params, config.emptyParams ?? false);
4185
- config.data = params;
3722
+ if (config.data && typeof config.data === "object" && !isFileUploadRequest) {
3723
+ const params = { ...config.data || {} };
3724
+ filterEmptyKey(params, config.emptyParams ?? false);
3725
+ config.data = params;
3726
+ }
4186
3727
  } else if (config.method === "get" || config.method === "delete") {
4187
3728
  if (!config.disableTimestamp) {
4188
3729
  config.params = {
@@ -4266,19 +3807,7 @@ service.interceptors.response.use(
4266
3807
  if (status === 401) {
4267
3808
  handle401Error(config, error);
4268
3809
  }
4269
- if (isAborted && config && !config._noAutoRetry) {
4270
- config._retryCount = config._retryCount || 0;
4271
- if (config._retryCount < 2) {
4272
- config._retryCount++;
4273
- console.debug(
4274
- `[request] aborted, retry attempt #${config._retryCount} -> ${config.url || ""}`
4275
- );
4276
- return new Promise((resolve) => setTimeout(resolve, 300)).then(
4277
- () => service.request(config)
4278
- );
4279
- }
4280
- }
4281
- if (!config?.hidden && !isAborted) {
3810
+ if (!config?.hidden && !isAborted && status !== 401) {
4282
3811
  let friendly = messageText;
4283
3812
  if (status === 403) friendly = "No permission";
4284
3813
  else if (status === 502) friendly = "System is upgrading or unavailable";
@@ -4304,7 +3833,7 @@ service.json = function(url, params, config) {
4304
3833
  };
4305
3834
  return service.post(
4306
3835
  url,
4307
- Array.isArray(params) ? params : params,
3836
+ params,
4308
3837
  newConfig
4309
3838
  );
4310
3839
  };
@@ -4313,7 +3842,13 @@ service.put = function(url, params, config) {
4313
3842
  headers: { "Content-Type": "application/json", ...config?.headers },
4314
3843
  ...config
4315
3844
  };
4316
- return service.put(url, Array.isArray(params) ? params : params, newConfig).then((res) => res);
3845
+ const data = Array.isArray(params) ? params : params;
3846
+ return service.request({
3847
+ url,
3848
+ method: "put",
3849
+ data,
3850
+ ...newConfig
3851
+ }).then((res) => res);
4317
3852
  };
4318
3853
  service.arrayGet = function(url, config) {
4319
3854
  if (config) {
@@ -4342,47 +3877,5 @@ service.download = function(url, config = {}) {
4342
3877
  return service.post(url, params, newConfig).then(() => void 0);
4343
3878
  }
4344
3879
  };
4345
- function convertRes2Blob(response) {
4346
- const contentDisposition = response.headers["content-disposition"];
4347
- let fileName = "file.xlsx";
4348
- if (contentDisposition) {
4349
- const rfc5987Match = contentDisposition.match(/filename\*=UTF-8''([^;]+)/i);
4350
- if (rfc5987Match) {
4351
- fileName = decodeURIComponent(rfc5987Match[1]);
4352
- } else {
4353
- const standardMatch = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/i);
4354
- if (standardMatch) {
4355
- fileName = standardMatch[1];
4356
- fileName = fileName.replace(/^['"]|['"]$/g, "");
4357
- try {
4358
- fileName = decodeURIComponent(fileName);
4359
- } catch (e) {
4360
- try {
4361
- fileName = decodeURI(fileName);
4362
- } catch (e2) {
4363
- }
4364
- }
4365
- }
4366
- }
4367
- fileName = fileName.trim().replace(/^_+|_+$/g, "");
4368
- }
4369
- const blob = new Blob([response.data], { type: "application/vnd.ms-excel;charset=utf-8" });
4370
- if (typeof window.navigator.msSaveBlob !== "undefined") {
4371
- window.navigator.msSaveBlob(blob, fileName);
4372
- } else {
4373
- const blobURL = window.URL.createObjectURL(blob);
4374
- const tempLink = document.createElement("a");
4375
- tempLink.style.display = "none";
4376
- tempLink.href = blobURL;
4377
- tempLink.setAttribute("download", fileName);
4378
- if (typeof tempLink.download === "undefined") {
4379
- tempLink.setAttribute("target", "_blank");
4380
- }
4381
- document.body.appendChild(tempLink);
4382
- tempLink.click();
4383
- document.body.removeChild(tempLink);
4384
- window.URL.revokeObjectURL(blobURL);
4385
- }
4386
- }
4387
3880
 
4388
- export { $, $$, BinaryTree, ChunkUploader, DataQueue, Graph, LRUCache, LinkedList, Queue, Stack, Tracker, UploadStatus, addClass, addDays, addMonths, addYears, aesGCMDecrypt, aesGCMEncrypt, base64Decode, base64Encode, batch, binarySearch, bubbleSort, buildUrl, calculateBlobMD5, calculateFileMD5, camelCase, capitalize, ceil, checkOnline, chunk, clamp, clearPersistedKeys, compact, computeHMAC, contrast, cookie, copyToClipboard, createSecureStorage, createTracker, createTranslator, createUploader, csvToJson, darken, debounce, deepClone, deepMerge, defaults, deriveKeyFromPassword, diffDays, difference, downloadFile, drop, dropWhile, endOfDay, escapeHtml, exportPrivateKey, exportPublicKey, factorial, fetchWithRetry, fetchWithTimeout, fibonacci, fibonacciSequence, findIndexBy, flatten, floor, flush, formatBytes, formatCurrency, formatCurrencyI18n, formatDate, formatDateI18n, formatFileSize, formatNumber, formatNumberI18n, formatRelativeTime, gcd, generateHMACKey, generateRSAKeyPair, generateRandomString, generateUUID, get, getDateFormatByGMT, getElementOffset, getFileExtension, getFileNameWithoutExtension, getKeyUsageCount, getLocale, getQuarter, getQueryParams, getRelativeTime, getScrollPosition, getStorageKeyPair, getStyle, getTimeFromGMT, getTracker, getWeekNumber, groupBy, hash, hexToRgb, highlight, hslToRgb, importPrivateKey, importPublicKey, initTracker, initializeStorageKeys, intersection, invert, isAbsoluteUrl, isBetween, isEmpty, isEmptyObject, isEqual, isFloat, isInViewport, isInteger, isNumeric, isToday, isValidDomain, isValidEmail, isValidHexColor, isValidIP, isValidIdCard, isValidJSON, isValidPhone, isValidUUID, isValidUrl, isWeekday, isWeekend, isYesterday, joinUrl, jsonToCsv, jsonToXml, jsonToYaml, kebabCase, keys, lcm, lighten, mapKeys, mapValues, mask, maskEmail, maskPhone, memoize, merge, mergeSort, mix, normalizeUrl, omit, omitBy, once, parseNumber, parseUrl, partition, pascalCase, percent, pick, pickBy, pluralize, quickSort, random, removeAccents, removeClass, removeQueryParams, request, resetKeyUsageCount, retry, rgbToHex, rgbToHsl, round, rsaDecrypt, rsaEncrypt, sample, scrollTo, secureStorage, set, setCommonParams, setQueryParams, setServiceEventHandlers, setStorageKeyPair, setStyle, setUserInfo, sha256, shuffle, slugify, snakeCase, sortBy, splitFileIntoChunks, startOfDay, take, takeWhile, template, throttle, timeout, toFixed, toggleClass, trackClick, trackEvent, trackExposure, trackPageView, transform, translate, truncate, unescapeHtml, union, unique, unzip, updateQueryParams, uploadFile, values, verifyHMAC, xmlToJson, yamlToJson, zip };
3881
+ export { $, $$, BinaryTree, ChunkUploader, DataQueue, Graph, LRUCache, LinkedList, Queue, SecureStorage, Stack, Tracker, UploadStatus, addClass, addDays, addMonths, addYears, base64Encode, batch, binarySearch, bubbleSort, buildUrl, calculateBlobMD5, calculateFileMD5, camelCase, capitalize, ceil, checkOnline, chunk, clamp, compact, contrast, copyToClipboard, createTracker, createTranslator, createUploader, csvToJson, darken, debounce, decryptJsonWithAES, decryptWithEnvPrivateKey, deepClone, deepMerge, defaults, diffDays, difference, downloadFile, drop, dropWhile, encryptJsonWithAES, encryptWithEnvPublicKey, endOfDay, escapeHtml, factorial, fetchWithRetry, fetchWithTimeout, fibonacci, fibonacciSequence, findIndexBy, flatten, floor, flush, formatBytes, formatCurrency, formatCurrencyI18n, formatDate, formatDateI18n, formatFileSize, formatNumber, formatNumberI18n, formatRelativeTime, gcd, generateRSAKeyPair, generateRandomAESKeyString, get, getDateFormatByGMT, getElementOffset, getEnvPrivateKey, getEnvPublicKey, getFileExtension, getFileNameWithoutExtension, getLocale, getQuarter, getQueryParams, getRelativeTime, getScrollPosition, getStyle, getTimeFromGMT, getTracker, getWeekNumber, groupBy, hexToRgb, highlight, hslToRgb, initTracker, intersection, invert, isAbsoluteUrl, isBetween, isEmpty, isEmptyObject, isEqual, isFloat, isInViewport, isInteger, isNumeric, isToday, isValidDomain, isValidEmail, isValidHexColor, isValidIP, isValidIdCard, isValidJSON, isValidPhone, isValidUUID, isValidUrl, isWeekday, isWeekend, isYesterday, joinUrl, jsonToCsv, jsonToXml, jsonToYaml, kebabCase, keys, lcm, lighten, mapKeys, mapValues, mask, maskEmail, maskPhone, memoize, merge, mergeSort, mix, normalizeUrl, omit, omitBy, once, parseNumber, parseUrl, partition, pascalCase, percent, pick, pickBy, pluralize, quickSort, random, removeAccents, removeClass, removeQueryParams, request, retry, rgbToHex, rgbToHsl, round, rsaDecrypt, rsaEncrypt, sample, scrollTo, secureStorage, set, setCommonParams, setQueryParams, setServiceEventHandlers, setStyle, setUserInfo, shuffle, slugify, snakeCase, sortBy, splitFileIntoChunks, startOfDay, take, takeWhile, template, throttle, timeout, toFixed, toggleClass, trackClick, trackEvent, trackExposure, trackPageView, transform, translate, truncate, unescapeHtml, union, unique, unzip, updateQueryParams, uploadFile, values, xmlToJson, yamlToJson, zip };