@alibarbar/common 1.1.1 → 1.1.3

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.cjs CHANGED
@@ -1,10 +1,14 @@
1
1
  'use strict';
2
2
 
3
+ var JSEncrypt = require('jsencrypt');
4
+ var CryptoJS = require('crypto-js');
3
5
  var Qs = require('qs');
4
6
  var axios = require('axios');
5
7
 
6
8
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
7
9
 
10
+ var JSEncrypt__default = /*#__PURE__*/_interopDefault(JSEncrypt);
11
+ var CryptoJS__default = /*#__PURE__*/_interopDefault(CryptoJS);
8
12
  var Qs__default = /*#__PURE__*/_interopDefault(Qs);
9
13
  var axios__default = /*#__PURE__*/_interopDefault(axios);
10
14
 
@@ -769,8 +773,8 @@ function parseUrl(url) {
769
773
  }
770
774
  }
771
775
  function buildUrl(parts) {
772
- const { protocol = "https:", host = "", pathname = "/", search = "", hash: hash2 = "" } = parts;
773
- return `${protocol}//${host}${pathname}${search}${hash2}`;
776
+ const { protocol = "https:", host = "", pathname = "/", search = "", hash = "" } = parts;
777
+ return `${protocol}//${host}${pathname}${search}${hash}`;
774
778
  }
775
779
  function getQueryParams(url) {
776
780
  const searchParams = url ? new URL(url).searchParams : new URLSearchParams(window.location.search);
@@ -1500,14 +1504,6 @@ async function uploadFile(file, options) {
1500
1504
  const uploader = createUploader(file, options);
1501
1505
  return uploader.upload();
1502
1506
  }
1503
-
1504
- // src/helper/crypto/index.ts
1505
- async function sha256(data) {
1506
- const buffer = typeof data === "string" ? new TextEncoder().encode(data) : data;
1507
- const hashBuffer = await crypto.subtle.digest("SHA-256", buffer);
1508
- const hashArray = Array.from(new Uint8Array(hashBuffer));
1509
- return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
1510
- }
1511
1507
  function base64Encode(data) {
1512
1508
  if (typeof data === "string") {
1513
1509
  return btoa(unescape(encodeURIComponent(data)));
@@ -1519,967 +1515,444 @@ function base64Encode(data) {
1519
1515
  }
1520
1516
  return btoa(binary);
1521
1517
  }
1522
- function base64Decode(data) {
1523
- try {
1524
- return decodeURIComponent(escape(atob(data)));
1525
- } catch {
1526
- throw new Error("Invalid Base64 string");
1527
- }
1528
- }
1529
- function generateUUID() {
1530
- if (typeof crypto !== "undefined" && crypto.randomUUID) {
1531
- return crypto.randomUUID();
1532
- }
1533
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
1534
- const r = Math.random() * 16 | 0;
1535
- const v = c === "x" ? r : r & 3 | 8;
1536
- return v.toString(16);
1537
- });
1518
+ function generateRandomAESKeyString() {
1519
+ return CryptoJS__default.default.lib.WordArray.random(256 / 8).toString();
1538
1520
  }
1539
- function generateRandomString(length, charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") {
1540
- let result = "";
1541
- for (let i = 0; i < length; i++) {
1542
- result += charset.charAt(Math.floor(Math.random() * charset.length));
1521
+ function encryptJsonWithAES(data, aesKey) {
1522
+ try {
1523
+ const jsonString = JSON.stringify(data);
1524
+ return CryptoJS__default.default.AES.encrypt(jsonString, aesKey).toString();
1525
+ } catch (error) {
1526
+ throw new Error(
1527
+ "[encryption] AES encryptJsonWithAES failed: " + (error instanceof Error ? error.message : String(error))
1528
+ );
1543
1529
  }
1544
- return result;
1545
1530
  }
1546
- function hash(data) {
1547
- let hashValue = 0;
1548
- for (let i = 0; i < data.length; i++) {
1549
- const char = data.charCodeAt(i);
1550
- hashValue = (hashValue << 5) - hashValue + char;
1551
- hashValue = hashValue & hashValue;
1531
+ function decryptJsonWithAES(encryptedData, aesKey) {
1532
+ try {
1533
+ const decrypted = CryptoJS__default.default.AES.decrypt(encryptedData, aesKey);
1534
+ const jsonString = decrypted.toString(CryptoJS__default.default.enc.Utf8);
1535
+ if (!jsonString) {
1536
+ throw new Error("decrypted JSON string is empty");
1537
+ }
1538
+ return JSON.parse(jsonString);
1539
+ } catch (error) {
1540
+ throw new Error(
1541
+ "[encryption] AES decryptJsonWithAES failed: " + (error instanceof Error ? error.message : String(error))
1542
+ );
1552
1543
  }
1553
- return Math.abs(hashValue);
1554
1544
  }
1555
- async function generateRSAKeyPair(modulusLength = 2048) {
1556
- if (typeof crypto === "undefined" || !crypto.subtle) {
1557
- throw new Error("Web Crypto API is not available");
1558
- }
1559
- const keyPair = await crypto.subtle.generateKey(
1560
- {
1561
- name: "RSA-OAEP",
1562
- modulusLength,
1563
- publicExponent: new Uint8Array([1, 0, 1]),
1564
- hash: "SHA-256"
1565
- },
1566
- true,
1567
- ["encrypt", "decrypt"]
1568
- );
1569
- return {
1570
- publicKey: keyPair.publicKey,
1571
- privateKey: keyPair.privateKey
1572
- };
1573
- }
1574
- async function rsaEncrypt(data, publicKey) {
1575
- if (typeof crypto === "undefined" || !crypto.subtle) {
1576
- throw new Error("Web Crypto API is not available");
1577
- }
1578
- const encoder = new TextEncoder();
1579
- const dataBuffer = encoder.encode(data);
1580
- const maxChunkSize = 245;
1581
- const chunks = [];
1582
- for (let i = 0; i < dataBuffer.length; i += maxChunkSize) {
1583
- const chunk2 = dataBuffer.slice(i, i + maxChunkSize);
1584
- const encrypted = await crypto.subtle.encrypt(
1585
- {
1586
- name: "RSA-OAEP"
1587
- },
1588
- publicKey,
1589
- chunk2
1545
+ function getEnvPublicKey() {
1546
+ const key = undefined.VITE_RSA_PUBLIC_KEY?.trim();
1547
+ if (!key) {
1548
+ throw new Error(
1549
+ "[encryption] VITE_RSA_PUBLIC_KEY is not set. Please configure RSA public key in environment variables."
1590
1550
  );
1591
- chunks.push(encrypted);
1592
1551
  }
1593
- const totalLength = chunks.reduce((sum, chunk2) => sum + chunk2.byteLength, 0);
1594
- const merged = new Uint8Array(totalLength);
1595
- let offset = 0;
1596
- for (const chunk2 of chunks) {
1597
- merged.set(new Uint8Array(chunk2), offset);
1598
- offset += chunk2.byteLength;
1599
- }
1600
- return base64Encode(merged.buffer);
1552
+ return key;
1601
1553
  }
1602
- async function rsaDecrypt(encryptedData, privateKey) {
1603
- if (typeof crypto === "undefined" || !crypto.subtle) {
1604
- throw new Error("Web Crypto API is not available");
1605
- }
1606
- const binaryString = atob(encryptedData);
1607
- const encryptedArray = new Uint8Array(binaryString.length);
1608
- for (let i = 0; i < binaryString.length; i++) {
1609
- encryptedArray[i] = binaryString.charCodeAt(i);
1610
- }
1611
- const chunkSize = 256;
1612
- const chunks = [];
1613
- for (let i = 0; i < encryptedArray.length; i += chunkSize) {
1614
- const chunk2 = encryptedArray.slice(i, i + chunkSize);
1615
- const decrypted = await crypto.subtle.decrypt(
1616
- {
1617
- name: "RSA-OAEP"
1618
- },
1619
- privateKey,
1620
- chunk2
1554
+ function getEnvPrivateKey() {
1555
+ const key = undefined.VITE_RSA_PRIVATE_KEY?.trim();
1556
+ if (!key) {
1557
+ throw new Error(
1558
+ "[encryption] VITE_RSA_PRIVATE_KEY is not set. Please configure RSA private key in environment variables."
1621
1559
  );
1622
- const decoder = new TextDecoder();
1623
- chunks.push(decoder.decode(decrypted));
1624
1560
  }
1625
- return chunks.join("");
1561
+ return key;
1626
1562
  }
1627
- async function exportPublicKey(publicKey) {
1628
- if (typeof crypto === "undefined" || !crypto.subtle) {
1629
- throw new Error("Web Crypto API is not available");
1563
+ function base64ToPem(base64Key, type) {
1564
+ const chunks = [];
1565
+ for (let i = 0; i < base64Key.length; i += 64) {
1566
+ chunks.push(base64Key.slice(i, i + 64));
1630
1567
  }
1631
- const exported = await crypto.subtle.exportKey("spki", publicKey);
1632
- return base64Encode(exported);
1568
+ const body = chunks.join("\n");
1569
+ const header = type === "PUBLIC" ? "-----BEGIN PUBLIC KEY-----" : "-----BEGIN PRIVATE KEY-----";
1570
+ const footer = type === "PUBLIC" ? "-----END PUBLIC KEY-----" : "-----END PRIVATE KEY-----";
1571
+ return `${header}
1572
+ ${body}
1573
+ ${footer}`;
1633
1574
  }
1634
- async function exportPrivateKey(privateKey) {
1635
- if (typeof crypto === "undefined" || !crypto.subtle) {
1636
- throw new Error("Web Crypto API is not available");
1575
+ async function rsaEncrypt(plain, publicKeyPem) {
1576
+ try {
1577
+ const encrypt = new JSEncrypt__default.default();
1578
+ let publicKeyString;
1579
+ if (typeof publicKeyPem === "string") {
1580
+ const trimmedKey = publicKeyPem.trim();
1581
+ if (!trimmedKey) {
1582
+ throw new Error("Public key string is empty");
1583
+ }
1584
+ if (trimmedKey.includes("-----BEGIN")) {
1585
+ publicKeyString = trimmedKey;
1586
+ } else {
1587
+ publicKeyString = base64ToPem(trimmedKey, "PUBLIC");
1588
+ }
1589
+ } else if (publicKeyPem instanceof CryptoJS__default.default.lib.WordArray) {
1590
+ const base64Key = publicKeyPem.toString();
1591
+ publicKeyString = base64ToPem(base64Key, "PUBLIC");
1592
+ } else {
1593
+ try {
1594
+ const exported = await crypto.subtle.exportKey("spki", publicKeyPem);
1595
+ const base64Key = base64Encode(exported);
1596
+ publicKeyString = base64ToPem(base64Key, "PUBLIC");
1597
+ } catch (error) {
1598
+ throw new Error(
1599
+ "Failed to export CryptoKey. In non-HTTPS environment, use string publicKey (Base64 or PEM) directly. " + (error instanceof Error ? error.message : String(error))
1600
+ );
1601
+ }
1602
+ }
1603
+ encrypt.setPublicKey(publicKeyString);
1604
+ const MAX_ENCRYPT_LENGTH = 200;
1605
+ if (plain.length > MAX_ENCRYPT_LENGTH) {
1606
+ const chunks = [];
1607
+ for (let i = 0; i < plain.length; i += MAX_ENCRYPT_LENGTH) {
1608
+ const chunk2 = plain.slice(i, i + MAX_ENCRYPT_LENGTH);
1609
+ const encrypted2 = encrypt.encrypt(chunk2);
1610
+ if (!encrypted2) {
1611
+ throw new Error(
1612
+ `RSA encryption failed for chunk ${i / MAX_ENCRYPT_LENGTH + 1}. Public key may be invalid or JSEncrypt may not be loaded correctly.`
1613
+ );
1614
+ }
1615
+ chunks.push(encrypted2);
1616
+ }
1617
+ return chunks.join("|");
1618
+ }
1619
+ const encrypted = encrypt.encrypt(plain);
1620
+ if (!encrypted) {
1621
+ throw new Error(
1622
+ `RSA encryption failed. Public key may be invalid or JSEncrypt may not be loaded correctly. Plain text length: ${plain.length} bytes.`
1623
+ );
1624
+ }
1625
+ return encrypted;
1626
+ } catch (error) {
1627
+ if (error instanceof Error) {
1628
+ if (error.message.includes("RSA encryption failed") || error.message.includes("Public key")) {
1629
+ throw error;
1630
+ }
1631
+ throw new Error(`[encryption] rsaEncrypt failed: ${error.message}`);
1632
+ }
1633
+ throw new Error(`[encryption] rsaEncrypt failed: ${String(error)}`);
1637
1634
  }
1638
- const exported = await crypto.subtle.exportKey("pkcs8", privateKey);
1639
- return base64Encode(exported);
1640
1635
  }
1641
- async function importPublicKey(keyData) {
1642
- if (typeof crypto === "undefined" || !crypto.subtle) {
1643
- throw new Error("Web Crypto API is not available");
1636
+ async function rsaDecrypt(cipher, privateKeyPem) {
1637
+ const decrypt = new JSEncrypt__default.default();
1638
+ let privateKeyString;
1639
+ if (typeof privateKeyPem === "string") {
1640
+ if (privateKeyPem.includes("-----BEGIN")) {
1641
+ privateKeyString = privateKeyPem.trim();
1642
+ } else {
1643
+ const trimmed = privateKeyPem.trim();
1644
+ privateKeyString = base64ToPem(trimmed, "PRIVATE");
1645
+ }
1646
+ } else if (privateKeyPem instanceof CryptoJS__default.default.lib.WordArray) {
1647
+ const base64Key = privateKeyPem.toString();
1648
+ privateKeyString = base64ToPem(base64Key, "PRIVATE");
1649
+ } else {
1650
+ try {
1651
+ const exported = await crypto.subtle.exportKey("pkcs8", privateKeyPem);
1652
+ const base64Key = base64Encode(exported);
1653
+ privateKeyString = base64ToPem(base64Key, "PRIVATE");
1654
+ } catch (error) {
1655
+ throw new Error(
1656
+ "[encryption] rsaDecrypt: failed to export CryptoKey. In non-HTTPS environment, use PEM string directly."
1657
+ );
1658
+ }
1644
1659
  }
1645
- const keyBuffer = base64Decode(keyData);
1646
- const keyArray = new Uint8Array(keyBuffer.split("").map((char) => char.charCodeAt(0)));
1647
- return crypto.subtle.importKey(
1648
- "spki",
1649
- keyArray.buffer,
1650
- {
1651
- name: "RSA-OAEP",
1652
- hash: "SHA-256"
1653
- },
1654
- true,
1655
- ["encrypt"]
1656
- );
1657
- }
1658
- async function importPrivateKey(keyData) {
1659
- if (typeof crypto === "undefined" || !crypto.subtle) {
1660
- throw new Error("Web Crypto API is not available");
1660
+ decrypt.setPrivateKey(privateKeyString);
1661
+ const chunks = cipher.split("|");
1662
+ const decryptedChunks = [];
1663
+ for (const chunk2 of chunks) {
1664
+ const decrypted = decrypt.decrypt(chunk2);
1665
+ if (!decrypted) {
1666
+ throw new Error("[encryption] rsaDecrypt: RSA decryption failed");
1667
+ }
1668
+ decryptedChunks.push(decrypted);
1661
1669
  }
1662
- const keyBuffer = base64Decode(keyData);
1663
- const keyArray = new Uint8Array(keyBuffer.split("").map((char) => char.charCodeAt(0)));
1664
- return crypto.subtle.importKey(
1665
- "pkcs8",
1666
- keyArray.buffer,
1667
- {
1668
- name: "RSA-OAEP",
1669
- hash: "SHA-256"
1670
- },
1671
- true,
1672
- ["decrypt"]
1673
- );
1670
+ return decryptedChunks.join("");
1674
1671
  }
1675
- async function generateHMACKey() {
1676
- if (typeof crypto === "undefined" || !crypto.subtle) {
1677
- throw new Error("Web Crypto API is not available");
1678
- }
1679
- return crypto.subtle.generateKey(
1680
- {
1681
- name: "HMAC",
1682
- hash: "SHA-256"
1683
- },
1684
- true,
1685
- ["sign", "verify"]
1686
- );
1672
+ async function encryptWithEnvPublicKey(plain) {
1673
+ const publicKey = getEnvPublicKey();
1674
+ return rsaEncrypt(plain, publicKey);
1687
1675
  }
1688
- async function computeHMAC(data, key) {
1689
- if (typeof crypto === "undefined" || !crypto.subtle) {
1690
- throw new Error("Web Crypto API is not available");
1691
- }
1692
- const buffer = typeof data === "string" ? new TextEncoder().encode(data) : data;
1693
- const signature = await crypto.subtle.sign("HMAC", key, buffer);
1694
- return base64Encode(signature);
1676
+ async function decryptWithEnvPrivateKey(cipher) {
1677
+ const privateKey = getEnvPrivateKey();
1678
+ return rsaDecrypt(cipher, privateKey);
1695
1679
  }
1696
- async function verifyHMAC(data, signature, key) {
1680
+ async function generateRSAKeyPair(modulusLength = 2048) {
1697
1681
  if (typeof crypto === "undefined" || !crypto.subtle) {
1698
- throw new Error("Web Crypto API is not available");
1699
- }
1700
- try {
1701
- const dataBuffer = typeof data === "string" ? new TextEncoder().encode(data) : data;
1702
- const signatureBuffer = new Uint8Array(
1703
- atob(signature).split("").map((char) => char.charCodeAt(0))
1682
+ throw new Error(
1683
+ "[encryption] Web Crypto API is not available. Cannot generate RSA key pair. Please use fixed key pair from environment variables."
1704
1684
  );
1705
- return await crypto.subtle.verify("HMAC", key, signatureBuffer, dataBuffer);
1706
- } catch {
1707
- return false;
1708
1685
  }
1709
- }
1710
- async function deriveKeyFromPassword(password, salt, iterations = 1e5, keyLength = 256) {
1711
- if (typeof crypto === "undefined" || !crypto.subtle) {
1712
- throw new Error("Web Crypto API is not available");
1713
- }
1714
- const saltBuffer = typeof salt === "string" ? new TextEncoder().encode(salt) : salt;
1715
- const passwordKey = await crypto.subtle.importKey(
1716
- "raw",
1717
- new TextEncoder().encode(password),
1718
- "PBKDF2",
1719
- false,
1720
- ["deriveBits", "deriveKey"]
1721
- );
1722
- return crypto.subtle.deriveKey(
1686
+ const keyPair = await crypto.subtle.generateKey(
1723
1687
  {
1724
- name: "PBKDF2",
1725
- salt: saltBuffer,
1726
- iterations,
1688
+ name: "RSA-OAEP",
1689
+ modulusLength,
1690
+ publicExponent: new Uint8Array([1, 0, 1]),
1727
1691
  hash: "SHA-256"
1728
1692
  },
1729
- passwordKey,
1730
- {
1731
- name: "AES-GCM",
1732
- length: keyLength
1733
- },
1734
- false,
1693
+ true,
1735
1694
  ["encrypt", "decrypt"]
1736
1695
  );
1737
- }
1738
- async function aesGCMEncrypt(data, key) {
1739
- if (typeof crypto === "undefined" || !crypto.subtle) {
1740
- throw new Error("Web Crypto API is not available");
1741
- }
1742
- const dataBuffer = typeof data === "string" ? new TextEncoder().encode(data) : data;
1743
- const iv = crypto.getRandomValues(new Uint8Array(12));
1744
- const encrypted = await crypto.subtle.encrypt(
1745
- {
1746
- name: "AES-GCM",
1747
- iv
1748
- },
1749
- key,
1750
- dataBuffer
1751
- );
1696
+ const publicKeyDer = await crypto.subtle.exportKey("spki", keyPair.publicKey);
1697
+ const privateKeyDer = await crypto.subtle.exportKey("pkcs8", keyPair.privateKey);
1698
+ const publicKeyBase64 = base64Encode(publicKeyDer);
1699
+ const privateKeyBase64 = base64Encode(privateKeyDer);
1752
1700
  return {
1753
- encrypted: base64Encode(encrypted),
1754
- iv: base64Encode(iv.buffer)
1701
+ publicKey: base64ToPem(publicKeyBase64, "PUBLIC"),
1702
+ privateKey: base64ToPem(privateKeyBase64, "PRIVATE")
1755
1703
  };
1756
1704
  }
1757
- async function aesGCMDecrypt(encryptedData, iv, key) {
1758
- if (typeof crypto === "undefined" || !crypto.subtle) {
1759
- throw new Error("Web Crypto API is not available");
1760
- }
1761
- const encryptedBuffer = new Uint8Array(
1762
- atob(encryptedData).split("").map((char) => char.charCodeAt(0))
1763
- );
1764
- const ivBuffer = new Uint8Array(
1765
- atob(iv).split("").map((char) => char.charCodeAt(0))
1766
- );
1767
- const decrypted = await crypto.subtle.decrypt(
1768
- {
1769
- name: "AES-GCM",
1770
- iv: ivBuffer
1771
- },
1772
- key,
1773
- encryptedBuffer
1774
- );
1775
- return new TextDecoder().decode(decrypted);
1776
- }
1777
1705
 
1778
1706
  // src/browser/SecureStorage/index.ts
1779
- var globalKeyPair = null;
1780
- var globalHMACKey = null;
1781
- var keyPairInitialized = false;
1782
- var initOptions = {
1783
- autoGenerateKeys: true,
1784
- persistKeys: false,
1785
- keyStorageKey: void 0,
1786
- // 将自动生成随机键名
1787
- keyEncryptionPassword: void 0,
1788
- pbkdf2Iterations: 1e5,
1789
- keyModulusLength: 2048,
1790
- enableHMAC: true,
1791
- enableTimestampValidation: true,
1792
- timestampMaxAge: 7 * 24 * 60 * 60 * 1e3,
1793
- // 7 天
1794
- isProduction: false
1707
+ var CONFIG = {
1708
+ NAMESPACE: "sec_b_",
1709
+ DEFAULT_EXPIRE: null
1795
1710
  };
1796
- var actualKeyStorageKey = null;
1797
- var keyUsageCount = 0;
1798
- var initializationPromise = null;
1799
- function generateKeyStorageKey() {
1800
- return `_sk_${generateRandomString(32)}_${Date.now()}`;
1801
- }
1802
- async function initializeStorageKeys(options = {}) {
1803
- if (options.forceReinitialize) {
1804
- globalKeyPair = null;
1805
- globalHMACKey = null;
1806
- keyPairInitialized = false;
1807
- actualKeyStorageKey = null;
1808
- keyUsageCount = 0;
1809
- initializationPromise = null;
1810
- } else if (keyPairInitialized && globalKeyPair) {
1811
- initOptions = { ...initOptions, ...options };
1812
- return;
1813
- }
1814
- initOptions = { ...initOptions, ...options };
1815
- if (initOptions.keyStorageKey) {
1816
- actualKeyStorageKey = initOptions.keyStorageKey;
1817
- } else {
1818
- actualKeyStorageKey = generateKeyStorageKey();
1819
- }
1820
- if (initOptions.persistKeys && typeof window !== "undefined" && window.localStorage) {
1821
- try {
1822
- const storedKeys = window.localStorage.getItem(actualKeyStorageKey);
1823
- if (storedKeys) {
1824
- const keyData = JSON.parse(storedKeys);
1825
- if (keyData.encrypted && keyData.privateKeyEncrypted && keyData.salt) {
1826
- if (!initOptions.keyEncryptionPassword) {
1827
- if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
1828
- console.error("Encrypted keys found but no password provided");
1829
- }
1830
- throw new Error("Password required to decrypt stored keys");
1831
- }
1832
- const saltArray = new Uint8Array(
1833
- atob(keyData.salt).split("").map((char) => char.charCodeAt(0))
1834
- );
1835
- const iterations = keyData.pbkdf2Iterations || initOptions.pbkdf2Iterations;
1836
- const derivedKey = await deriveKeyFromPassword(
1837
- initOptions.keyEncryptionPassword,
1838
- saltArray.buffer,
1839
- iterations
1840
- );
1841
- const decryptedPrivateKey = await aesGCMDecrypt(
1842
- keyData.privateKeyEncrypted,
1843
- keyData.iv,
1844
- derivedKey
1845
- );
1846
- globalKeyPair = {
1847
- publicKey: await importPublicKey(keyData.publicKey),
1848
- privateKey: await importPrivateKey(decryptedPrivateKey)
1849
- };
1850
- } else if (keyData.publicKey && keyData.privateKey) {
1851
- if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
1852
- console.warn(
1853
- "\u26A0\uFE0F SECURITY WARNING: Loading unencrypted keys from storage. Consider re-initializing with password encryption."
1854
- );
1855
- }
1856
- globalKeyPair = {
1857
- publicKey: await importPublicKey(keyData.publicKey),
1858
- privateKey: await importPrivateKey(keyData.privateKey)
1859
- };
1860
- }
1861
- if (globalKeyPair) {
1862
- keyPairInitialized = true;
1863
- if (initOptions.enableHMAC && !globalHMACKey) {
1864
- globalHMACKey = await generateHMACKey();
1865
- }
1866
- return;
1867
- }
1868
- }
1869
- } catch (error) {
1870
- if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
1871
- console.warn("Failed to load persisted keys, will generate new ones");
1872
- }
1873
- }
1874
- }
1875
- if (initOptions.autoGenerateKeys && !globalKeyPair) {
1876
- try {
1877
- globalKeyPair = await generateRSAKeyPair(initOptions.keyModulusLength);
1878
- if (initOptions.enableHMAC && !globalHMACKey) {
1879
- globalHMACKey = await generateHMACKey();
1880
- }
1881
- keyPairInitialized = true;
1882
- keyUsageCount = 0;
1883
- if (initOptions.persistKeys && typeof window !== "undefined" && window.localStorage) {
1884
- try {
1885
- const publicKeyStr = await exportPublicKey(globalKeyPair.publicKey);
1886
- if (initOptions.keyEncryptionPassword) {
1887
- const privateKeyStr = await exportPrivateKey(globalKeyPair.privateKey);
1888
- const salt = crypto.getRandomValues(new Uint8Array(16));
1889
- const derivedKey = await deriveKeyFromPassword(
1890
- initOptions.keyEncryptionPassword,
1891
- salt.buffer,
1892
- initOptions.pbkdf2Iterations
1893
- );
1894
- const { encrypted, iv } = await aesGCMEncrypt(privateKeyStr, derivedKey);
1895
- const keyData = {
1896
- encrypted: true,
1897
- publicKey: publicKeyStr,
1898
- privateKeyEncrypted: encrypted,
1899
- iv,
1900
- salt: base64Encode(salt.buffer),
1901
- pbkdf2Iterations: initOptions.pbkdf2Iterations
1902
- };
1903
- window.localStorage.setItem(actualKeyStorageKey, JSON.stringify(keyData));
1904
- if (!initOptions.isProduction && typeof console !== "undefined" && console.info) {
1905
- console.info("\u2705 Keys encrypted and stored securely");
1906
- }
1907
- } else {
1908
- if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
1909
- console.warn(
1910
- "\u26A0\uFE0F SECURITY WARNING: Storing private keys without encryption! Private keys will be stored in plain text (Base64 encoded). Provide keyEncryptionPassword for secure storage."
1911
- );
1912
- }
1913
- const keyData = {
1914
- publicKey: publicKeyStr,
1915
- privateKey: await exportPrivateKey(globalKeyPair.privateKey)
1916
- };
1917
- window.localStorage.setItem(actualKeyStorageKey, JSON.stringify(keyData));
1918
- }
1919
- } catch (error) {
1920
- if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
1921
- console.error("Failed to persist keys");
1922
- }
1923
- }
1924
- }
1925
- } catch (error) {
1926
- if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
1927
- console.error("Failed to generate storage keys");
1928
- }
1929
- throw error;
1930
- }
1931
- }
1932
- if (initOptions.enableHMAC && !globalHMACKey) {
1933
- globalHMACKey = await generateHMACKey();
1934
- }
1935
- }
1936
- function setStorageKeyPair(keyPair) {
1937
- globalKeyPair = keyPair;
1938
- keyPairInitialized = true;
1939
- }
1940
- function getStorageKeyPair() {
1941
- return globalKeyPair;
1942
- }
1943
- function clearPersistedKeys() {
1944
- if (typeof window !== "undefined" && window.localStorage && actualKeyStorageKey) {
1945
- try {
1946
- window.localStorage.removeItem(actualKeyStorageKey);
1947
- actualKeyStorageKey = null;
1948
- keyPairInitialized = false;
1949
- globalKeyPair = null;
1950
- globalHMACKey = null;
1951
- keyUsageCount = 0;
1952
- } catch (error) {
1953
- if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
1954
- console.error("Failed to clear persisted keys");
1955
- }
1956
- }
1957
- }
1958
- }
1959
- function getKeyUsageCount() {
1960
- return keyUsageCount;
1961
- }
1962
- function resetKeyUsageCount() {
1963
- keyUsageCount = 0;
1964
- }
1965
- async function ensureKeyPair() {
1966
- if (globalKeyPair) {
1967
- keyUsageCount++;
1968
- return;
1969
- }
1970
- if (!initOptions.autoGenerateKeys) {
1971
- return;
1972
- }
1973
- if (initializationPromise) {
1974
- await initializationPromise;
1975
- if (globalKeyPair) {
1976
- keyUsageCount++;
1711
+ var SecureStorage = class _SecureStorage {
1712
+ static get instances() {
1713
+ if (!this._instances) {
1714
+ this._instances = /* @__PURE__ */ new Map();
1977
1715
  }
1978
- return;
1979
- }
1980
- initializationPromise = initializeStorageKeys();
1981
- try {
1982
- await initializationPromise;
1983
- } finally {
1984
- initializationPromise = null;
1985
- }
1986
- if (globalKeyPair) {
1987
- keyUsageCount++;
1716
+ return this._instances;
1988
1717
  }
1989
- }
1990
- function safeParseJSON(jsonStr, expectedType) {
1991
- try {
1992
- const parsed = JSON.parse(jsonStr);
1993
- if (expectedType === "object" && (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))) {
1994
- throw new Error("Expected object but got different type");
1995
- }
1996
- if (expectedType === "array" && !Array.isArray(parsed)) {
1997
- throw new Error("Expected array but got different type");
1998
- }
1999
- return parsed;
2000
- } catch (error) {
2001
- if (error instanceof SyntaxError) {
2002
- throw new Error(`Invalid JSON format: ${error.message}`);
1718
+ constructor(options) {
1719
+ const storageType = options.storage === window.sessionStorage ? "session" : "local";
1720
+ const namespace = options.namespace || CONFIG.NAMESPACE;
1721
+ const instanceKey = `${storageType}_${namespace}`;
1722
+ const existingInstance = _SecureStorage.instances.get(instanceKey);
1723
+ if (existingInstance) {
1724
+ return existingInstance;
2003
1725
  }
2004
- throw error;
1726
+ this.namespace = namespace;
1727
+ this.storage = options.storage || window.localStorage;
1728
+ this.defaultExpire = options.defaultExpire ?? CONFIG.DEFAULT_EXPIRE;
1729
+ this.instanceKey = instanceKey;
1730
+ _SecureStorage.instances.set(instanceKey, this);
2005
1731
  }
2006
- }
2007
- function validateTimestamp(timestamp, maxClockSkew = 5 * 60 * 1e3) {
2008
- if (!initOptions.enableTimestampValidation || !timestamp) {
2009
- return true;
2010
- }
2011
- if (initOptions.timestampMaxAge === 0) {
2012
- return true;
2013
- }
2014
- const now = Date.now();
2015
- const age = now - timestamp;
2016
- const maxAge = initOptions.timestampMaxAge || 7 * 24 * 60 * 60 * 1e3;
2017
- if (age < -maxClockSkew) {
2018
- return false;
2019
- }
2020
- return age >= -maxClockSkew && age <= maxAge;
2021
- }
2022
- async function encryptValue(value, publicKey, hmacKey) {
2023
- const encryptedData = await rsaEncrypt(value, publicKey);
2024
- if (hmacKey) {
2025
- const hmac = await computeHMAC(encryptedData, hmacKey);
2026
- const item = {
2027
- data: encryptedData,
2028
- hmac,
2029
- encrypted: true,
2030
- timestamp: Date.now()
2031
- };
2032
- return JSON.stringify(item);
2033
- }
2034
- return encryptedData;
2035
- }
2036
- async function handleDecryptError(error, encryptedStr, key) {
2037
- if (error instanceof Error && error.message.includes("HMAC verification failed")) {
2038
- if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
2039
- console.error(
2040
- "\u26A0\uFE0F SECURITY ALERT: Data integrity check failed! Data may have been tampered with."
2041
- );
2042
- }
2043
- throw error;
1732
+ /**
1733
+ * 获取单例实例(静态方法)
1734
+ */
1735
+ static getInstance(options) {
1736
+ return new _SecureStorage(options);
2044
1737
  }
2045
- if (error instanceof Error && error.message.includes("Data timestamp validation failed")) {
2046
- if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
2047
- console.warn("\u26A0\uFE0F SECURITY WARNING: Data timestamp validation failed");
2048
- }
2049
- throw error;
1738
+ /**
1739
+ * 清除所有单例实例(用于测试或重置)
1740
+ */
1741
+ static clearInstances() {
1742
+ _SecureStorage.instances.clear();
2050
1743
  }
2051
- try {
2052
- const decryptedStr = base64Decode(encryptedStr);
2053
- if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
2054
- console.warn(
2055
- `\u26A0\uFE0F SECURITY WARNING: Reading unencrypted data for key "${key}". This may be a security risk if sensitive data was stored without encryption.`
2056
- );
2057
- }
2058
- return decryptedStr;
2059
- } catch {
2060
- throw error;
1744
+ /**
1745
+ * 获取当前所有实例的数量(用于调试)
1746
+ */
1747
+ static getInstanceCount() {
1748
+ return _SecureStorage.instances.size;
2061
1749
  }
2062
- }
2063
- function handleNoKeyDecode(encryptedStr, key) {
2064
- try {
2065
- const decryptedStr = base64Decode(encryptedStr);
2066
- if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
2067
- console.warn(
2068
- `\u26A0\uFE0F SECURITY WARNING: Reading unencrypted data for key "${key}" without encryption keys.`
2069
- );
2070
- }
2071
- return decryptedStr;
2072
- } catch (error) {
2073
- throw new Error(`Failed to decode storage value for key "${key}"`);
1750
+ /**
1751
+ * 获取完整键名(带命名空间)
1752
+ */
1753
+ _getFullKey(key) {
1754
+ return `${this.namespace}${key}`;
2074
1755
  }
2075
- }
2076
- async function decryptValue(encryptedValue, privateKey, hmacKey) {
2077
- try {
2078
- const item = JSON.parse(encryptedValue);
2079
- if (item.encrypted && item.data && item.hmac) {
2080
- if (hmacKey) {
2081
- const isValid = await verifyHMAC(item.data, item.hmac, hmacKey);
2082
- if (!isValid) {
2083
- throw new Error("HMAC verification failed: data may have been tampered with");
2084
- }
2085
- }
2086
- if (item.timestamp && !validateTimestamp(item.timestamp)) {
2087
- throw new Error("Data timestamp validation failed: data may be expired or replayed");
2088
- }
2089
- return await rsaDecrypt(item.data, privateKey);
2090
- }
2091
- return await rsaDecrypt(encryptedValue, privateKey);
2092
- } catch (error) {
2093
- if (error instanceof Error && error.message.includes("HMAC verification failed")) {
2094
- throw error;
2095
- }
2096
- throw new Error("Failed to decrypt storage value: invalid format or corrupted data");
1756
+ /**
1757
+ * 检查数据是否过期
1758
+ */
1759
+ _isExpired(expire) {
1760
+ if (!expire) return false;
1761
+ return Date.now() > expire;
2097
1762
  }
2098
- }
2099
- var localStorage = {
2100
1763
  /**
2101
1764
  * 设置值
2102
- * @param key - 键
2103
- * @param value - 值
2104
- * @param options - 选项(过期时间、密钥等)
2105
- * @returns Promise<void>
2106
1765
  */
2107
- async set(key, value, options = {}) {
2108
- if (typeof window === "undefined" || !window.localStorage) {
2109
- throw new Error("localStorage is not available");
2110
- }
2111
- const { expiry, publicKey } = options;
2112
- const item = {
2113
- value,
2114
- expiry: expiry ? Date.now() + expiry : void 0
2115
- };
1766
+ async set(key, value, expire = this.defaultExpire) {
2116
1767
  try {
2117
- const jsonStr = JSON.stringify(item);
2118
- await ensureKeyPair();
2119
- const keyToUse = publicKey || globalKeyPair?.publicKey;
2120
- if (keyToUse) {
2121
- const encrypted = await encryptValue(jsonStr, keyToUse, globalHMACKey);
2122
- window.localStorage.setItem(key, encrypted);
2123
- } else {
2124
- if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
2125
- console.warn(
2126
- `\u26A0\uFE0F SECURITY WARNING: Storing data without encryption for key "${key}". Data is only Base64 encoded, not encrypted!`
2127
- );
2128
- }
2129
- const encoded = base64Encode(jsonStr);
2130
- window.localStorage.setItem(key, encoded);
2131
- }
1768
+ const fullKey = this._getFullKey(key);
1769
+ const aesKey = generateRandomAESKeyString();
1770
+ const storageData = {
1771
+ value,
1772
+ expire: expire ? Date.now() + expire : null,
1773
+ timestamp: Date.now()
1774
+ };
1775
+ const encryptedData = encryptJsonWithAES(storageData, aesKey);
1776
+ const encryptedKey = await encryptWithEnvPublicKey(aesKey);
1777
+ const finalData = {
1778
+ data: encryptedData,
1779
+ key: encryptedKey,
1780
+ version: "1.0"
1781
+ };
1782
+ this.storage.setItem(fullKey, JSON.stringify(finalData));
1783
+ return true;
2132
1784
  } catch (error) {
2133
- if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
2134
- console.error("localStorage.set error:", error);
2135
- }
2136
- throw error;
1785
+ console.error("[SecureStorage] \u4FDD\u5B58\u5931\u8D25:", error);
1786
+ return false;
2137
1787
  }
2138
- },
1788
+ }
2139
1789
  /**
2140
1790
  * 获取值
2141
- * @param key - 键
2142
- * @param options - 选项(默认值、私钥等)
2143
- * @returns Promise<T | undefined> 值或默认值
2144
1791
  */
2145
- async get(key, options = {}) {
2146
- if (typeof window === "undefined" || !window.localStorage) {
2147
- return options.defaultValue;
2148
- }
2149
- const { defaultValue, privateKey } = options;
1792
+ async get(key, defaultValue = null) {
2150
1793
  try {
2151
- const encryptedStr = window.localStorage.getItem(key);
2152
- if (!encryptedStr) return defaultValue;
2153
- await ensureKeyPair();
2154
- let decryptedStr;
2155
- const keyToUse = privateKey || globalKeyPair?.privateKey;
2156
- if (keyToUse) {
2157
- try {
2158
- decryptedStr = await decryptValue(encryptedStr, keyToUse, globalHMACKey);
2159
- } catch (error) {
2160
- try {
2161
- decryptedStr = await handleDecryptError(error, encryptedStr, key);
2162
- } catch {
2163
- return defaultValue;
2164
- }
2165
- }
2166
- } else {
2167
- try {
2168
- decryptedStr = handleNoKeyDecode(encryptedStr, key);
2169
- } catch {
2170
- return defaultValue;
2171
- }
1794
+ const fullKey = this._getFullKey(key);
1795
+ const storedData = this.storage.getItem(fullKey);
1796
+ if (!storedData) {
1797
+ return defaultValue;
2172
1798
  }
2173
- const item = safeParseJSON(decryptedStr, "object");
2174
- if (item.expiry && Date.now() > item.expiry) {
2175
- window.localStorage.removeItem(key);
1799
+ const finalData = JSON.parse(storedData);
1800
+ const aesKey = await decryptWithEnvPrivateKey(finalData.key);
1801
+ const storageData = decryptJsonWithAES(finalData.data, aesKey);
1802
+ if (this._isExpired(storageData.expire)) {
1803
+ this.remove(key);
2176
1804
  return defaultValue;
2177
1805
  }
2178
- return item.value;
1806
+ return storageData.value;
2179
1807
  } catch (error) {
2180
- if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
2181
- console.warn(`Failed to parse storage item for key "${key}"`);
2182
- }
1808
+ console.error("[SecureStorage] \u8BFB\u53D6\u5931\u8D25:", error);
2183
1809
  return defaultValue;
2184
1810
  }
2185
- },
1811
+ }
2186
1812
  /**
2187
- * 移除值
2188
- * @param key - 键
1813
+ * 删除值
2189
1814
  */
2190
1815
  remove(key) {
2191
- if (typeof window === "undefined" || !window.localStorage) return;
2192
1816
  try {
2193
- window.localStorage.removeItem(key);
2194
- } catch (error) {
2195
- if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
2196
- console.error("localStorage.remove error");
2197
- }
2198
- }
2199
- },
2200
- /**
2201
- * 清空所有值
2202
- */
2203
- clear() {
2204
- if (typeof window === "undefined" || !window.localStorage) return;
2205
- try {
2206
- window.localStorage.clear();
1817
+ const fullKey = this._getFullKey(key);
1818
+ this.storage.removeItem(fullKey);
1819
+ return true;
2207
1820
  } catch (error) {
2208
- if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
2209
- console.error("localStorage.clear error");
2210
- }
1821
+ console.error("[SecureStorage] \u79FB\u9664\u5931\u8D25:", error);
1822
+ return false;
2211
1823
  }
2212
- },
1824
+ }
2213
1825
  /**
2214
- * 获取所有键
2215
- * @returns 键数组
1826
+ * 检查键是否存在
2216
1827
  */
2217
- keys() {
2218
- if (typeof window === "undefined" || !window.localStorage) return [];
2219
- const keys2 = [];
1828
+ async has(key) {
2220
1829
  try {
2221
- for (let i = 0; i < window.localStorage.length; i++) {
2222
- const key = window.localStorage.key(i);
2223
- if (key) keys2.push(key);
1830
+ const fullKey = this._getFullKey(key);
1831
+ const storedData = this.storage.getItem(fullKey);
1832
+ if (!storedData) return false;
1833
+ const finalData = JSON.parse(storedData);
1834
+ const aesKey = await decryptWithEnvPrivateKey(finalData.key);
1835
+ const storageData = decryptJsonWithAES(finalData.data, aesKey);
1836
+ if (this._isExpired(storageData.expire)) {
1837
+ this.remove(key);
1838
+ return false;
2224
1839
  }
1840
+ return true;
2225
1841
  } catch (error) {
2226
- if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
2227
- console.error("localStorage.keys error");
2228
- }
1842
+ return false;
2229
1843
  }
2230
- return keys2;
2231
1844
  }
2232
- };
2233
- var sessionStorage = {
2234
1845
  /**
2235
- * 设置值
2236
- * @param key - 键
2237
- * @param value - 值
2238
- * @param options - 选项(公钥等)
2239
- * @returns Promise<void>
1846
+ * 清空所有数据
2240
1847
  */
2241
- async set(key, value, options = {}) {
2242
- if (typeof window === "undefined" || !window.sessionStorage) {
2243
- throw new Error("sessionStorage is not available");
2244
- }
2245
- const { publicKey } = options;
1848
+ clear() {
2246
1849
  try {
2247
- const jsonStr = JSON.stringify(value);
2248
- await ensureKeyPair();
2249
- const keyToUse = publicKey || globalKeyPair?.publicKey;
2250
- if (keyToUse) {
2251
- const encrypted = await encryptValue(jsonStr, keyToUse, globalHMACKey);
2252
- window.sessionStorage.setItem(key, encrypted);
2253
- } else {
2254
- if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
2255
- console.warn(
2256
- `\u26A0\uFE0F SECURITY WARNING: Storing data without encryption for key "${key}". Data is only Base64 encoded, not encrypted!`
2257
- );
1850
+ let count = 0;
1851
+ const keys2 = [];
1852
+ for (let i = 0; i < this.storage.length; i++) {
1853
+ const key = this.storage.key(i);
1854
+ if (key && key.startsWith(this.namespace)) {
1855
+ keys2.push(key);
2258
1856
  }
2259
- const encoded = base64Encode(jsonStr);
2260
- window.sessionStorage.setItem(key, encoded);
2261
1857
  }
1858
+ keys2.forEach((key) => {
1859
+ this.storage.removeItem(key);
1860
+ count++;
1861
+ });
1862
+ return count;
2262
1863
  } catch (error) {
2263
- if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
2264
- console.error("sessionStorage.set error:", error);
2265
- }
2266
- throw error;
1864
+ console.error("[SecureStorage] \u6E05\u7A7A\u5931\u8D25:", error);
1865
+ return 0;
2267
1866
  }
2268
- },
1867
+ }
2269
1868
  /**
2270
- * 获取值
2271
- * @param key - 键
2272
- * @param options - 选项(默认值、私钥等)
2273
- * @returns Promise<T | undefined> 值或默认值
1869
+ * 获取所有键名
2274
1870
  */
2275
- async get(key, options = {}) {
2276
- if (typeof window === "undefined" || !window.sessionStorage) {
2277
- return options.defaultValue;
2278
- }
2279
- const { defaultValue, privateKey } = options;
1871
+ keys() {
2280
1872
  try {
2281
- const encryptedStr = window.sessionStorage.getItem(key);
2282
- if (!encryptedStr) return defaultValue;
2283
- await ensureKeyPair();
2284
- let decryptedStr;
2285
- const keyToUse = privateKey || globalKeyPair?.privateKey;
2286
- if (keyToUse) {
2287
- try {
2288
- decryptedStr = await decryptValue(encryptedStr, keyToUse, globalHMACKey);
2289
- } catch (error) {
2290
- try {
2291
- decryptedStr = await handleDecryptError(error, encryptedStr, key);
2292
- } catch {
2293
- return defaultValue;
2294
- }
2295
- }
2296
- } else {
2297
- try {
2298
- decryptedStr = handleNoKeyDecode(encryptedStr, key);
2299
- } catch {
2300
- return defaultValue;
1873
+ const keys2 = [];
1874
+ for (let i = 0; i < this.storage.length; i++) {
1875
+ const key = this.storage.key(i);
1876
+ if (key && key.startsWith(this.namespace)) {
1877
+ keys2.push(key.substring(this.namespace.length));
2301
1878
  }
2302
1879
  }
2303
- return safeParseJSON(decryptedStr);
2304
- } catch {
2305
- return defaultValue;
2306
- }
2307
- },
2308
- /**
2309
- * 移除值
2310
- * @param key - 键
2311
- */
2312
- remove(key) {
2313
- if (typeof window === "undefined" || !window.sessionStorage) return;
2314
- try {
2315
- window.sessionStorage.removeItem(key);
1880
+ return keys2;
2316
1881
  } catch (error) {
2317
- if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
2318
- console.error("sessionStorage.remove error");
2319
- }
2320
- }
2321
- },
2322
- /**
2323
- * 清空所有值
2324
- */
2325
- clear() {
2326
- if (typeof window === "undefined" || !window.sessionStorage) return;
2327
- try {
2328
- window.sessionStorage.clear();
2329
- } catch (error) {
2330
- if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
2331
- console.error("sessionStorage.clear error");
2332
- }
1882
+ console.error("[SecureStorage] \u83B7\u53D6\u952E\u540D\u5931\u8D25:", error);
1883
+ return [];
2333
1884
  }
2334
1885
  }
2335
- };
2336
- var cookie = {
2337
- /**
2338
- * 设置Cookie
2339
- * @param key - 键
2340
- * @param value - 值
2341
- * @param options - Cookie选项
2342
- */
2343
- set(key, value, options = {}) {
2344
- if (typeof document === "undefined") {
2345
- throw new Error("document is not available");
2346
- }
2347
- let cookieStr = `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
2348
- if (options.expires) {
2349
- const expiresDate = options.expires instanceof Date ? options.expires : new Date(Date.now() + options.expires * 24 * 60 * 60 * 1e3);
2350
- cookieStr += `; expires=${expiresDate.toUTCString()}`;
2351
- }
2352
- if (options.path) cookieStr += `; path=${options.path}`;
2353
- if (options.domain) cookieStr += `; domain=${options.domain}`;
2354
- if (options.secure) cookieStr += "; secure";
2355
- if (options.sameSite) cookieStr += `; sameSite=${options.sameSite}`;
2356
- document.cookie = cookieStr;
2357
- },
2358
- /**
2359
- * 获取Cookie
2360
- * @param key - 键
2361
- * @returns Cookie值
2362
- */
2363
- get(key) {
2364
- if (typeof document === "undefined") return void 0;
2365
- const name = encodeURIComponent(key);
2366
- const cookies = document.cookie.split(";");
2367
- for (const cookieStr of cookies) {
2368
- const trimmed = cookieStr.trim();
2369
- const eqIndex = trimmed.indexOf("=");
2370
- if (eqIndex === -1) continue;
2371
- const cookieKey = trimmed.substring(0, eqIndex).trim();
2372
- const cookieValue = trimmed.substring(eqIndex + 1).trim();
2373
- if (cookieKey === name) {
2374
- const decoded = decodeURIComponent(cookieValue);
2375
- return decoded === "" ? void 0 : decoded;
2376
- }
2377
- }
2378
- return void 0;
2379
- },
2380
- /**
2381
- * 移除Cookie
2382
- * @param key - 键
2383
- * @param options - Cookie选项
2384
- */
2385
- remove(key, options = {}) {
2386
- cookie.set(key, "", {
2387
- ...options,
2388
- expires: /* @__PURE__ */ new Date(0)
2389
- });
2390
- },
2391
1886
  /**
2392
- * 获取所有Cookie
2393
- * @returns Cookie对象
1887
+ * 获取数据条数
2394
1888
  */
2395
- getAll() {
2396
- if (typeof document === "undefined") return {};
2397
- const cookies = {};
2398
- document.cookie.split(";").forEach((cookieStr) => {
2399
- const trimmed = cookieStr.trim();
2400
- if (!trimmed) return;
2401
- const eqIndex = trimmed.indexOf("=");
2402
- if (eqIndex === -1) return;
2403
- const key = trimmed.substring(0, eqIndex).trim();
2404
- const value = trimmed.substring(eqIndex + 1).trim();
2405
- if (key && value) {
2406
- const decodedKey = decodeURIComponent(key);
2407
- const decodedValue = decodeURIComponent(value);
2408
- if (decodedValue !== "") {
2409
- cookies[decodedKey] = decodedValue;
2410
- }
2411
- }
2412
- });
2413
- return cookies;
1889
+ size() {
1890
+ return this.keys().length;
2414
1891
  }
2415
- };
2416
- var secureStorage = {
2417
1892
  /**
2418
- * 设置值(优先使用localStorage,失败则使用sessionStorage)
2419
- * @param key - 键
2420
- * @param value - 值
2421
- * @param options - 选项(过期时间、公钥等)
2422
- * @returns Promise<void>
1893
+ * 清理过期数据
2423
1894
  */
2424
- async set(key, value, options = {}) {
1895
+ async clearExpired() {
2425
1896
  try {
2426
- await localStorage.set(key, value, options);
2427
- } catch {
2428
- try {
2429
- await sessionStorage.set(key, value, { publicKey: options.publicKey });
2430
- } catch {
2431
- if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
2432
- console.warn("Both localStorage and sessionStorage are not available");
1897
+ let count = 0;
1898
+ const keys2 = this.keys();
1899
+ for (const key of keys2) {
1900
+ const fullKey = this._getFullKey(key);
1901
+ try {
1902
+ const storedData = this.storage.getItem(fullKey);
1903
+ if (storedData) {
1904
+ const finalData = JSON.parse(storedData);
1905
+ const aesKey = await decryptWithEnvPrivateKey(finalData.key);
1906
+ const storageData = decryptJsonWithAES(finalData.data, aesKey);
1907
+ if (this._isExpired(storageData.expire)) {
1908
+ this.remove(key);
1909
+ count++;
1910
+ }
1911
+ }
1912
+ } catch (error) {
1913
+ this.remove(key);
1914
+ count++;
2433
1915
  }
2434
1916
  }
1917
+ return count;
1918
+ } catch (error) {
1919
+ console.error("[SecureStorage] \u6E05\u7406\u8FC7\u671F\u6570\u636E\u5931\u8D25:", error);
1920
+ return 0;
2435
1921
  }
2436
- },
2437
- /**
2438
- * 获取值
2439
- * @param key - 键
2440
- * @param options - 选项(默认值、私钥等)
2441
- * @returns Promise<T | undefined> 值或默认值
2442
- */
2443
- async get(key, options = {}) {
2444
- const localValue = await localStorage.get(key, options);
2445
- if (localValue !== void 0) return localValue;
2446
- const sessionValue = await sessionStorage.get(key, options);
2447
- return sessionValue !== void 0 ? sessionValue : options.defaultValue;
2448
- },
2449
- /**
2450
- * 移除值
2451
- * @param key - 键
2452
- */
2453
- remove(key) {
2454
- localStorage.remove(key);
2455
- sessionStorage.remove(key);
2456
- },
1922
+ }
2457
1923
  /**
2458
- * 清空所有值
1924
+ * 获取实例的唯一标识
2459
1925
  */
2460
- clear() {
2461
- localStorage.clear();
2462
- sessionStorage.clear();
2463
- },
1926
+ getInstanceKey() {
1927
+ return this.instanceKey;
1928
+ }
2464
1929
  /**
2465
- * 获取所有键名
2466
- * @returns 键名数组
1930
+ * 检查是否为单例实例
2467
1931
  */
2468
- keys() {
2469
- const localKeys = localStorage.keys();
2470
- const sessionKeys = [];
2471
- if (typeof window !== "undefined" && window.sessionStorage) {
2472
- try {
2473
- for (let i = 0; i < window.sessionStorage.length; i++) {
2474
- const key = window.sessionStorage.key(i);
2475
- if (key) sessionKeys.push(key);
2476
- }
2477
- } catch (error) {
2478
- }
2479
- }
2480
- return Array.from(/* @__PURE__ */ new Set([...localKeys, ...sessionKeys]));
1932
+ isSingleton() {
1933
+ return _SecureStorage.instances.has(this.instanceKey);
2481
1934
  }
2482
1935
  };
1936
+ var _defaultSecureStorage = null;
1937
+ function _getDefaultSecureStorage() {
1938
+ if (typeof window === "undefined") {
1939
+ throw new Error("[SecureStorage] secureStorage is only available in browser environments.");
1940
+ }
1941
+ if (!_defaultSecureStorage) {
1942
+ _defaultSecureStorage = new SecureStorage({});
1943
+ }
1944
+ return _defaultSecureStorage;
1945
+ }
1946
+ var secureStorage = new Proxy({}, {
1947
+ get(_target, prop) {
1948
+ const instance = _getDefaultSecureStorage();
1949
+ const value = instance[prop];
1950
+ if (typeof value === "function") {
1951
+ return value.bind(instance);
1952
+ }
1953
+ return value;
1954
+ }
1955
+ });
2483
1956
 
2484
1957
  // src/browser/network/index.ts
2485
1958
  async function fetchWithRetry(url, options = {}, retryCount = 3, retryDelay = 1e3) {
@@ -4075,6 +3548,48 @@ function setCommonParams(params) {
4075
3548
  async function flush() {
4076
3549
  await getTracker().flush();
4077
3550
  }
3551
+ function convertRes2Blob(response) {
3552
+ const contentDisposition = response.headers["content-disposition"];
3553
+ let fileName = "file.xlsx";
3554
+ if (contentDisposition) {
3555
+ const rfc5987Match = contentDisposition.match(/filename\*=UTF-8''([^;]+)/i);
3556
+ if (rfc5987Match) {
3557
+ fileName = decodeURIComponent(rfc5987Match[1]);
3558
+ } else {
3559
+ const standardMatch = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/i);
3560
+ if (standardMatch) {
3561
+ fileName = standardMatch[1];
3562
+ fileName = fileName.replace(/^['"]|['"]$/g, "");
3563
+ try {
3564
+ fileName = decodeURIComponent(fileName);
3565
+ } catch (e) {
3566
+ try {
3567
+ fileName = decodeURI(fileName);
3568
+ } catch (e2) {
3569
+ }
3570
+ }
3571
+ }
3572
+ }
3573
+ fileName = fileName.trim().replace(/^_+|_+$/g, "");
3574
+ }
3575
+ const blob = new Blob([response.data], { type: "application/vnd.ms-excel;charset=utf-8" });
3576
+ if (typeof window.navigator.msSaveBlob !== "undefined") {
3577
+ window.navigator.msSaveBlob(blob, fileName);
3578
+ } else {
3579
+ const blobURL = window.URL.createObjectURL(blob);
3580
+ const tempLink = document.createElement("a");
3581
+ tempLink.style.display = "none";
3582
+ tempLink.href = blobURL;
3583
+ tempLink.setAttribute("download", fileName);
3584
+ if (typeof tempLink.download === "undefined") {
3585
+ tempLink.setAttribute("target", "_blank");
3586
+ }
3587
+ document.body.appendChild(tempLink);
3588
+ tempLink.click();
3589
+ document.body.removeChild(tempLink);
3590
+ window.URL.revokeObjectURL(blobURL);
3591
+ }
3592
+ }
4078
3593
  var serviceEventHandlers = {
4079
3594
  message: null
4080
3595
  };
@@ -4108,11 +3623,28 @@ function filterEmptyKey(params, emptyString) {
4108
3623
  }
4109
3624
  });
4110
3625
  }
3626
+ function isFileUpload(data) {
3627
+ if (!data) return false;
3628
+ if (data instanceof FormData) return true;
3629
+ if (data instanceof File) return true;
3630
+ if (data instanceof Blob) return true;
3631
+ if (data instanceof ArrayBuffer) return true;
3632
+ if (typeof data === "object" && data !== null && !Array.isArray(data)) {
3633
+ const obj = data;
3634
+ return Object.values(obj).some(
3635
+ (value) => value instanceof File || value instanceof Blob || value instanceof FormData
3636
+ );
3637
+ }
3638
+ return false;
3639
+ }
4111
3640
  function getTransformRequest(contentType) {
4112
3641
  if (!contentType) {
4113
3642
  return (data) => Qs__default.default.stringify(data);
4114
3643
  }
4115
3644
  const normalizedType = contentType.toLowerCase().trim();
3645
+ if (normalizedType.startsWith("multipart/form-data")) {
3646
+ return void 0;
3647
+ }
4116
3648
  if (normalizedType.startsWith("application/json")) {
4117
3649
  return (data) => JSON.stringify(data);
4118
3650
  }
@@ -4133,6 +3665,15 @@ var service = axios__default.default.create({
4133
3665
  });
4134
3666
  service.interceptors.request.use(
4135
3667
  async (config) => {
3668
+ const isFileUploadRequest = isFileUpload(config.data);
3669
+ if (isFileUploadRequest) {
3670
+ if (config.headers) {
3671
+ delete config.headers["Content-Type"];
3672
+ delete config.headers["content-type"];
3673
+ }
3674
+ config.transformRequest = void 0;
3675
+ return config;
3676
+ }
4136
3677
  const contentType = config.headers?.["Content-Type"] || config.headers?.["content-type"];
4137
3678
  if (!config.transformRequest && contentType) {
4138
3679
  const transformRequest = getTransformRequest(
@@ -4145,9 +3686,11 @@ service.interceptors.request.use(
4145
3686
  config.transformRequest = (data) => Qs__default.default.stringify(data);
4146
3687
  }
4147
3688
  if (config.method === "post" || config.method === "put" || config.method === "patch") {
4148
- const params = { ...config.data || {} };
4149
- filterEmptyKey(params, config.emptyParams ?? false);
4150
- config.data = params;
3689
+ if (config.data && typeof config.data === "object" && !isFileUploadRequest) {
3690
+ const params = { ...config.data || {} };
3691
+ filterEmptyKey(params, config.emptyParams ?? false);
3692
+ config.data = params;
3693
+ }
4151
3694
  } else if (config.method === "get" || config.method === "delete") {
4152
3695
  if (!config.disableTimestamp) {
4153
3696
  config.params = {
@@ -4231,19 +3774,7 @@ service.interceptors.response.use(
4231
3774
  if (status === 401) {
4232
3775
  handle401Error(config, error);
4233
3776
  }
4234
- if (isAborted && config && !config._noAutoRetry) {
4235
- config._retryCount = config._retryCount || 0;
4236
- if (config._retryCount < 2) {
4237
- config._retryCount++;
4238
- console.debug(
4239
- `[request] aborted, retry attempt #${config._retryCount} -> ${config.url || ""}`
4240
- );
4241
- return new Promise((resolve) => setTimeout(resolve, 300)).then(
4242
- () => service.request(config)
4243
- );
4244
- }
4245
- }
4246
- if (!config?.hidden && !isAborted) {
3777
+ if (!config?.hidden && !isAborted && status !== 401) {
4247
3778
  let friendly = messageText;
4248
3779
  if (status === 403) friendly = "No permission";
4249
3780
  else if (status === 502) friendly = "System is upgrading or unavailable";
@@ -4269,7 +3800,7 @@ service.json = function(url, params, config) {
4269
3800
  };
4270
3801
  return service.post(
4271
3802
  url,
4272
- Array.isArray(params) ? params : params,
3803
+ params,
4273
3804
  newConfig
4274
3805
  );
4275
3806
  };
@@ -4278,7 +3809,13 @@ service.put = function(url, params, config) {
4278
3809
  headers: { "Content-Type": "application/json", ...config?.headers },
4279
3810
  ...config
4280
3811
  };
4281
- return service.put(url, Array.isArray(params) ? params : params, newConfig).then((res) => res);
3812
+ const data = Array.isArray(params) ? params : params;
3813
+ return service.request({
3814
+ url,
3815
+ method: "put",
3816
+ data,
3817
+ ...newConfig
3818
+ }).then((res) => res);
4282
3819
  };
4283
3820
  service.arrayGet = function(url, config) {
4284
3821
  if (config) {
@@ -4307,48 +3844,6 @@ service.download = function(url, config = {}) {
4307
3844
  return service.post(url, params, newConfig).then(() => void 0);
4308
3845
  }
4309
3846
  };
4310
- function convertRes2Blob(response) {
4311
- const contentDisposition = response.headers["content-disposition"];
4312
- let fileName = "file.xlsx";
4313
- if (contentDisposition) {
4314
- const rfc5987Match = contentDisposition.match(/filename\*=UTF-8''([^;]+)/i);
4315
- if (rfc5987Match) {
4316
- fileName = decodeURIComponent(rfc5987Match[1]);
4317
- } else {
4318
- const standardMatch = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/i);
4319
- if (standardMatch) {
4320
- fileName = standardMatch[1];
4321
- fileName = fileName.replace(/^['"]|['"]$/g, "");
4322
- try {
4323
- fileName = decodeURIComponent(fileName);
4324
- } catch (e) {
4325
- try {
4326
- fileName = decodeURI(fileName);
4327
- } catch (e2) {
4328
- }
4329
- }
4330
- }
4331
- }
4332
- fileName = fileName.trim().replace(/^_+|_+$/g, "");
4333
- }
4334
- const blob = new Blob([response.data], { type: "application/vnd.ms-excel;charset=utf-8" });
4335
- if (typeof window.navigator.msSaveBlob !== "undefined") {
4336
- window.navigator.msSaveBlob(blob, fileName);
4337
- } else {
4338
- const blobURL = window.URL.createObjectURL(blob);
4339
- const tempLink = document.createElement("a");
4340
- tempLink.style.display = "none";
4341
- tempLink.href = blobURL;
4342
- tempLink.setAttribute("download", fileName);
4343
- if (typeof tempLink.download === "undefined") {
4344
- tempLink.setAttribute("target", "_blank");
4345
- }
4346
- document.body.appendChild(tempLink);
4347
- tempLink.click();
4348
- document.body.removeChild(tempLink);
4349
- window.URL.revokeObjectURL(blobURL);
4350
- }
4351
- }
4352
3847
 
4353
3848
  exports.$ = $;
4354
3849
  exports.$$ = $$;
@@ -4359,6 +3854,7 @@ exports.Graph = Graph;
4359
3854
  exports.LRUCache = LRUCache;
4360
3855
  exports.LinkedList = LinkedList;
4361
3856
  exports.Queue = Queue;
3857
+ exports.SecureStorage = SecureStorage;
4362
3858
  exports.Stack = Stack;
4363
3859
  exports.Tracker = Tracker;
4364
3860
  exports.UploadStatus = UploadStatus;
@@ -4366,9 +3862,6 @@ exports.addClass = addClass;
4366
3862
  exports.addDays = addDays;
4367
3863
  exports.addMonths = addMonths;
4368
3864
  exports.addYears = addYears;
4369
- exports.aesGCMDecrypt = aesGCMDecrypt;
4370
- exports.aesGCMEncrypt = aesGCMEncrypt;
4371
- exports.base64Decode = base64Decode;
4372
3865
  exports.base64Encode = base64Encode;
4373
3866
  exports.batch = batch;
4374
3867
  exports.binarySearch = binarySearch;
@@ -4382,11 +3875,8 @@ exports.ceil = ceil;
4382
3875
  exports.checkOnline = checkOnline;
4383
3876
  exports.chunk = chunk;
4384
3877
  exports.clamp = clamp;
4385
- exports.clearPersistedKeys = clearPersistedKeys;
4386
3878
  exports.compact = compact;
4387
- exports.computeHMAC = computeHMAC;
4388
3879
  exports.contrast = contrast;
4389
- exports.cookie = cookie;
4390
3880
  exports.copyToClipboard = copyToClipboard;
4391
3881
  exports.createTracker = createTracker;
4392
3882
  exports.createTranslator = createTranslator;
@@ -4394,19 +3884,20 @@ exports.createUploader = createUploader;
4394
3884
  exports.csvToJson = csvToJson;
4395
3885
  exports.darken = darken;
4396
3886
  exports.debounce = debounce;
3887
+ exports.decryptJsonWithAES = decryptJsonWithAES;
3888
+ exports.decryptWithEnvPrivateKey = decryptWithEnvPrivateKey;
4397
3889
  exports.deepClone = deepClone;
4398
3890
  exports.deepMerge = deepMerge;
4399
3891
  exports.defaults = defaults;
4400
- exports.deriveKeyFromPassword = deriveKeyFromPassword;
4401
3892
  exports.diffDays = diffDays;
4402
3893
  exports.difference = difference;
4403
3894
  exports.downloadFile = downloadFile;
4404
3895
  exports.drop = drop;
4405
3896
  exports.dropWhile = dropWhile;
3897
+ exports.encryptJsonWithAES = encryptJsonWithAES;
3898
+ exports.encryptWithEnvPublicKey = encryptWithEnvPublicKey;
4406
3899
  exports.endOfDay = endOfDay;
4407
3900
  exports.escapeHtml = escapeHtml;
4408
- exports.exportPrivateKey = exportPrivateKey;
4409
- exports.exportPublicKey = exportPublicKey;
4410
3901
  exports.factorial = factorial;
4411
3902
  exports.fetchWithRetry = fetchWithRetry;
4412
3903
  exports.fetchWithTimeout = fetchWithTimeout;
@@ -4426,35 +3917,29 @@ exports.formatNumber = formatNumber;
4426
3917
  exports.formatNumberI18n = formatNumberI18n;
4427
3918
  exports.formatRelativeTime = formatRelativeTime;
4428
3919
  exports.gcd = gcd;
4429
- exports.generateHMACKey = generateHMACKey;
4430
3920
  exports.generateRSAKeyPair = generateRSAKeyPair;
4431
- exports.generateRandomString = generateRandomString;
4432
- exports.generateUUID = generateUUID;
3921
+ exports.generateRandomAESKeyString = generateRandomAESKeyString;
4433
3922
  exports.get = get;
4434
3923
  exports.getDateFormatByGMT = getDateFormatByGMT;
4435
3924
  exports.getElementOffset = getElementOffset;
3925
+ exports.getEnvPrivateKey = getEnvPrivateKey;
3926
+ exports.getEnvPublicKey = getEnvPublicKey;
4436
3927
  exports.getFileExtension = getFileExtension;
4437
3928
  exports.getFileNameWithoutExtension = getFileNameWithoutExtension;
4438
- exports.getKeyUsageCount = getKeyUsageCount;
4439
3929
  exports.getLocale = getLocale;
4440
3930
  exports.getQuarter = getQuarter;
4441
3931
  exports.getQueryParams = getQueryParams;
4442
3932
  exports.getRelativeTime = getRelativeTime;
4443
3933
  exports.getScrollPosition = getScrollPosition;
4444
- exports.getStorageKeyPair = getStorageKeyPair;
4445
3934
  exports.getStyle = getStyle;
4446
3935
  exports.getTimeFromGMT = getTimeFromGMT;
4447
3936
  exports.getTracker = getTracker;
4448
3937
  exports.getWeekNumber = getWeekNumber;
4449
3938
  exports.groupBy = groupBy;
4450
- exports.hash = hash;
4451
3939
  exports.hexToRgb = hexToRgb;
4452
3940
  exports.highlight = highlight;
4453
3941
  exports.hslToRgb = hslToRgb;
4454
- exports.importPrivateKey = importPrivateKey;
4455
- exports.importPublicKey = importPublicKey;
4456
3942
  exports.initTracker = initTracker;
4457
- exports.initializeStorageKeys = initializeStorageKeys;
4458
3943
  exports.intersection = intersection;
4459
3944
  exports.invert = invert;
4460
3945
  exports.isAbsoluteUrl = isAbsoluteUrl;
@@ -4514,7 +3999,6 @@ exports.removeAccents = removeAccents;
4514
3999
  exports.removeClass = removeClass;
4515
4000
  exports.removeQueryParams = removeQueryParams;
4516
4001
  exports.request = request;
4517
- exports.resetKeyUsageCount = resetKeyUsageCount;
4518
4002
  exports.retry = retry;
4519
4003
  exports.rgbToHex = rgbToHex;
4520
4004
  exports.rgbToHsl = rgbToHsl;
@@ -4528,10 +4012,8 @@ exports.set = set;
4528
4012
  exports.setCommonParams = setCommonParams;
4529
4013
  exports.setQueryParams = setQueryParams;
4530
4014
  exports.setServiceEventHandlers = setServiceEventHandlers;
4531
- exports.setStorageKeyPair = setStorageKeyPair;
4532
4015
  exports.setStyle = setStyle;
4533
4016
  exports.setUserInfo = setUserInfo;
4534
- exports.sha256 = sha256;
4535
4017
  exports.shuffle = shuffle;
4536
4018
  exports.slugify = slugify;
4537
4019
  exports.snakeCase = snakeCase;
@@ -4559,7 +4041,6 @@ exports.unzip = unzip;
4559
4041
  exports.updateQueryParams = updateQueryParams;
4560
4042
  exports.uploadFile = uploadFile;
4561
4043
  exports.values = values;
4562
- exports.verifyHMAC = verifyHMAC;
4563
4044
  exports.xmlToJson = xmlToJson;
4564
4045
  exports.yamlToJson = yamlToJson;
4565
4046
  exports.zip = zip;