@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/README.md +10 -0
- package/dist/crypto.cjs +184 -252
- package/dist/crypto.d.mts +50 -103
- package/dist/crypto.d.ts +50 -103
- package/dist/crypto.js +170 -238
- package/dist/{index-DchqyDBQ.d.mts → index-j5EqxJaC.d.mts} +8 -2
- package/dist/{index-DchqyDBQ.d.ts → index-j5EqxJaC.d.ts} +8 -2
- package/dist/index.cjs +488 -1008
- package/dist/index.d.mts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +479 -986
- package/dist/services.cjs +82 -60
- package/dist/services.d.mts +1 -1
- package/dist/services.d.ts +1 -1
- package/dist/services.js +82 -60
- package/dist/storage.cjs +328 -894
- package/dist/storage.d.mts +69 -127
- package/dist/storage.d.ts +69 -127
- package/dist/storage.js +320 -887
- package/dist/upload.cjs +56 -14
- package/dist/upload.d.mts +1 -1
- package/dist/upload.d.ts +1 -1
- package/dist/upload.js +56 -14
- package/package.json +11 -3
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
|
|
773
|
-
return `${protocol}//${host}${pathname}${search}${
|
|
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);
|
|
@@ -1144,6 +1148,8 @@ var ChunkUploader = class {
|
|
|
1144
1148
|
this.uploadedChunks = /* @__PURE__ */ new Set();
|
|
1145
1149
|
this.status = "pending" /* PENDING */;
|
|
1146
1150
|
this.abortController = null;
|
|
1151
|
+
/** 进度流专用 AbortController,用于在上传结束/取消时关闭流 */
|
|
1152
|
+
this.progressStreamAbortController = null;
|
|
1147
1153
|
this.file = file;
|
|
1148
1154
|
this.options = {
|
|
1149
1155
|
chunkSize: options.chunkSize || 2 * 1024 * 1024,
|
|
@@ -1251,7 +1257,6 @@ var ChunkUploader = class {
|
|
|
1251
1257
|
});
|
|
1252
1258
|
if (response.code === 200 && response.data.success) {
|
|
1253
1259
|
this.uploadedChunks.add(chunkInfo.index);
|
|
1254
|
-
await this.updateProgress();
|
|
1255
1260
|
} else {
|
|
1256
1261
|
throw new Error(response.message || "\u5206\u7247\u4E0A\u4F20\u5931\u8D25");
|
|
1257
1262
|
}
|
|
@@ -1286,23 +1291,60 @@ var ChunkUploader = class {
|
|
|
1286
1291
|
await Promise.all(tasks);
|
|
1287
1292
|
}
|
|
1288
1293
|
/**
|
|
1289
|
-
*
|
|
1294
|
+
* 启动进度流式接口(单次请求,通过 ReadableStream 消费多条 SSE 事件)
|
|
1290
1295
|
*/
|
|
1291
|
-
|
|
1296
|
+
startProgressStream() {
|
|
1292
1297
|
if (!this.taskId) return;
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1298
|
+
this.progressStreamAbortController = new AbortController();
|
|
1299
|
+
const url = `${this.options.baseURL}/api/files/common/progress/${this.taskId}`;
|
|
1300
|
+
fetch(url, {
|
|
1301
|
+
method: "GET",
|
|
1302
|
+
headers: {
|
|
1303
|
+
Accept: "text/event-stream",
|
|
1304
|
+
...this.options.headers
|
|
1305
|
+
},
|
|
1306
|
+
signal: this.progressStreamAbortController.signal
|
|
1307
|
+
}).then(async (response) => {
|
|
1308
|
+
if (!response.ok || !response.body) return;
|
|
1309
|
+
const reader = response.body.getReader();
|
|
1310
|
+
const decoder = new TextDecoder();
|
|
1311
|
+
let buffer = "";
|
|
1312
|
+
try {
|
|
1313
|
+
while (true) {
|
|
1314
|
+
const { done, value } = await reader.read();
|
|
1315
|
+
if (done) break;
|
|
1316
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1317
|
+
const lines = buffer.split("\n");
|
|
1318
|
+
buffer = lines.pop() ?? "";
|
|
1319
|
+
for (const line of lines) {
|
|
1320
|
+
if (line.startsWith("data: ")) {
|
|
1321
|
+
const jsonStr = line.slice(6).trim();
|
|
1322
|
+
if (!jsonStr) continue;
|
|
1323
|
+
try {
|
|
1324
|
+
const apiRes = JSON.parse(jsonStr);
|
|
1325
|
+
if (apiRes.code === 200 && apiRes.data) {
|
|
1326
|
+
this.options.onProgress(apiRes.data);
|
|
1327
|
+
}
|
|
1328
|
+
} catch {
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1299
1332
|
}
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
this.options.onProgress(response.data);
|
|
1333
|
+
} finally {
|
|
1334
|
+
reader.releaseLock();
|
|
1303
1335
|
}
|
|
1304
|
-
}
|
|
1305
|
-
|
|
1336
|
+
}).catch((error) => {
|
|
1337
|
+
if (error?.name === "AbortError") return;
|
|
1338
|
+
console.warn("\u8FDB\u5EA6\u6D41\u5F0F\u63A5\u53E3\u5F02\u5E38:", error);
|
|
1339
|
+
});
|
|
1340
|
+
}
|
|
1341
|
+
/**
|
|
1342
|
+
* 关闭进度流
|
|
1343
|
+
*/
|
|
1344
|
+
closeProgressStream() {
|
|
1345
|
+
if (this.progressStreamAbortController) {
|
|
1346
|
+
this.progressStreamAbortController.abort();
|
|
1347
|
+
this.progressStreamAbortController = null;
|
|
1306
1348
|
}
|
|
1307
1349
|
}
|
|
1308
1350
|
/**
|
|
@@ -1375,6 +1417,7 @@ var ChunkUploader = class {
|
|
|
1375
1417
|
this.options.onComplete(result);
|
|
1376
1418
|
return result;
|
|
1377
1419
|
}
|
|
1420
|
+
this.startProgressStream();
|
|
1378
1421
|
console.log("[\u4E0A\u4F20\u6D41\u7A0B] 2. \u51C6\u5907\u5206\u7247");
|
|
1379
1422
|
this.prepareChunks();
|
|
1380
1423
|
const totalChunks = this.chunks.length;
|
|
@@ -1418,6 +1461,8 @@ var ChunkUploader = class {
|
|
|
1418
1461
|
this.options.onError(err);
|
|
1419
1462
|
console.error("[\u4E0A\u4F20\u6D41\u7A0B] \u274C \u4E0A\u4F20\u5931\u8D25:", err);
|
|
1420
1463
|
throw err;
|
|
1464
|
+
} finally {
|
|
1465
|
+
this.closeProgressStream();
|
|
1421
1466
|
}
|
|
1422
1467
|
}
|
|
1423
1468
|
/**
|
|
@@ -1444,6 +1489,7 @@ var ChunkUploader = class {
|
|
|
1444
1489
|
* 取消上传
|
|
1445
1490
|
*/
|
|
1446
1491
|
async cancel() {
|
|
1492
|
+
this.closeProgressStream();
|
|
1447
1493
|
if (this.taskId && this.status === "uploading" /* UPLOADING */) {
|
|
1448
1494
|
try {
|
|
1449
1495
|
await this.request(`/api/files/common/cancel/${this.taskId}`, {
|
|
@@ -1500,14 +1546,6 @@ async function uploadFile(file, options) {
|
|
|
1500
1546
|
const uploader = createUploader(file, options);
|
|
1501
1547
|
return uploader.upload();
|
|
1502
1548
|
}
|
|
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
1549
|
function base64Encode(data) {
|
|
1512
1550
|
if (typeof data === "string") {
|
|
1513
1551
|
return btoa(unescape(encodeURIComponent(data)));
|
|
@@ -1519,1009 +1557,444 @@ function base64Encode(data) {
|
|
|
1519
1557
|
}
|
|
1520
1558
|
return btoa(binary);
|
|
1521
1559
|
}
|
|
1522
|
-
function
|
|
1523
|
-
|
|
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
|
-
});
|
|
1538
|
-
}
|
|
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));
|
|
1543
|
-
}
|
|
1544
|
-
return result;
|
|
1560
|
+
function generateRandomAESKeyString() {
|
|
1561
|
+
return CryptoJS__default.default.lib.WordArray.random(256 / 8).toString();
|
|
1545
1562
|
}
|
|
1546
|
-
function
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1563
|
+
function encryptJsonWithAES(data, aesKey) {
|
|
1564
|
+
try {
|
|
1565
|
+
const jsonString = JSON.stringify(data);
|
|
1566
|
+
return CryptoJS__default.default.AES.encrypt(jsonString, aesKey).toString();
|
|
1567
|
+
} catch (error) {
|
|
1568
|
+
throw new Error(
|
|
1569
|
+
"[encryption] AES encryptJsonWithAES failed: " + (error instanceof Error ? error.message : String(error))
|
|
1570
|
+
);
|
|
1552
1571
|
}
|
|
1553
|
-
return Math.abs(hashValue);
|
|
1554
1572
|
}
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1573
|
+
function decryptJsonWithAES(encryptedData, aesKey) {
|
|
1574
|
+
try {
|
|
1575
|
+
const decrypted = CryptoJS__default.default.AES.decrypt(encryptedData, aesKey);
|
|
1576
|
+
const jsonString = decrypted.toString(CryptoJS__default.default.enc.Utf8);
|
|
1577
|
+
if (!jsonString) {
|
|
1578
|
+
throw new Error("decrypted JSON string is empty");
|
|
1579
|
+
}
|
|
1580
|
+
return JSON.parse(jsonString);
|
|
1581
|
+
} catch (error) {
|
|
1582
|
+
throw new Error(
|
|
1583
|
+
"[encryption] AES decryptJsonWithAES failed: " + (error instanceof Error ? error.message : String(error))
|
|
1584
|
+
);
|
|
1558
1585
|
}
|
|
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
1586
|
}
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
const dataBuffer = encoder.encode(data);
|
|
1580
|
-
const algo = publicKey.algorithm;
|
|
1581
|
-
const modulusLength = typeof algo.modulusLength === "number" ? algo.modulusLength : 2048;
|
|
1582
|
-
const hashName = typeof algo.hash === "object" && "name" in algo.hash ? algo.hash.name : "SHA-256";
|
|
1583
|
-
const hashLength = hashName === "SHA-1" ? 20 : 32;
|
|
1584
|
-
const maxChunkSize = Math.max(Math.floor(modulusLength / 8 - 2 * hashLength - 2), 1);
|
|
1585
|
-
const chunks = [];
|
|
1586
|
-
for (let i = 0; i < dataBuffer.length; i += maxChunkSize) {
|
|
1587
|
-
const chunk2 = dataBuffer.slice(i, i + maxChunkSize);
|
|
1588
|
-
const encrypted = await crypto.subtle.encrypt(
|
|
1589
|
-
{
|
|
1590
|
-
name: "RSA-OAEP"
|
|
1591
|
-
},
|
|
1592
|
-
publicKey,
|
|
1593
|
-
chunk2
|
|
1587
|
+
function getEnvPublicKey() {
|
|
1588
|
+
const key = undefined.VITE_RSA_PUBLIC_KEY?.trim();
|
|
1589
|
+
if (!key) {
|
|
1590
|
+
throw new Error(
|
|
1591
|
+
"[encryption] VITE_RSA_PUBLIC_KEY is not set. Please configure RSA public key in environment variables."
|
|
1594
1592
|
);
|
|
1595
|
-
chunks.push(encrypted);
|
|
1596
1593
|
}
|
|
1597
|
-
|
|
1598
|
-
const merged = new Uint8Array(totalLength);
|
|
1599
|
-
let offset = 0;
|
|
1600
|
-
for (const chunk2 of chunks) {
|
|
1601
|
-
merged.set(new Uint8Array(chunk2), offset);
|
|
1602
|
-
offset += chunk2.byteLength;
|
|
1603
|
-
}
|
|
1604
|
-
return base64Encode(merged.buffer);
|
|
1594
|
+
return key;
|
|
1605
1595
|
}
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
const encryptedArray = new Uint8Array(binaryString.length);
|
|
1612
|
-
for (let i = 0; i < binaryString.length; i++) {
|
|
1613
|
-
encryptedArray[i] = binaryString.charCodeAt(i);
|
|
1614
|
-
}
|
|
1615
|
-
const chunkSize = 256;
|
|
1616
|
-
const chunks = [];
|
|
1617
|
-
for (let i = 0; i < encryptedArray.length; i += chunkSize) {
|
|
1618
|
-
const chunk2 = encryptedArray.slice(i, i + chunkSize);
|
|
1619
|
-
const decrypted = await crypto.subtle.decrypt(
|
|
1620
|
-
{
|
|
1621
|
-
name: "RSA-OAEP"
|
|
1622
|
-
},
|
|
1623
|
-
privateKey,
|
|
1624
|
-
chunk2
|
|
1596
|
+
function getEnvPrivateKey() {
|
|
1597
|
+
const key = undefined.VITE_RSA_PRIVATE_KEY?.trim();
|
|
1598
|
+
if (!key) {
|
|
1599
|
+
throw new Error(
|
|
1600
|
+
"[encryption] VITE_RSA_PRIVATE_KEY is not set. Please configure RSA private key in environment variables."
|
|
1625
1601
|
);
|
|
1626
|
-
const decoder = new TextDecoder();
|
|
1627
|
-
chunks.push(decoder.decode(decrypted));
|
|
1628
1602
|
}
|
|
1629
|
-
return
|
|
1603
|
+
return key;
|
|
1630
1604
|
}
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1605
|
+
function base64ToPem(base64Key, type) {
|
|
1606
|
+
const chunks = [];
|
|
1607
|
+
for (let i = 0; i < base64Key.length; i += 64) {
|
|
1608
|
+
chunks.push(base64Key.slice(i, i + 64));
|
|
1634
1609
|
}
|
|
1635
|
-
const
|
|
1636
|
-
|
|
1610
|
+
const body = chunks.join("\n");
|
|
1611
|
+
const header = type === "PUBLIC" ? "-----BEGIN PUBLIC KEY-----" : "-----BEGIN PRIVATE KEY-----";
|
|
1612
|
+
const footer = type === "PUBLIC" ? "-----END PUBLIC KEY-----" : "-----END PRIVATE KEY-----";
|
|
1613
|
+
return `${header}
|
|
1614
|
+
${body}
|
|
1615
|
+
${footer}`;
|
|
1637
1616
|
}
|
|
1638
|
-
async function
|
|
1639
|
-
|
|
1640
|
-
|
|
1617
|
+
async function rsaEncrypt(plain, publicKeyPem) {
|
|
1618
|
+
try {
|
|
1619
|
+
const encrypt = new JSEncrypt__default.default();
|
|
1620
|
+
let publicKeyString;
|
|
1621
|
+
if (typeof publicKeyPem === "string") {
|
|
1622
|
+
const trimmedKey = publicKeyPem.trim();
|
|
1623
|
+
if (!trimmedKey) {
|
|
1624
|
+
throw new Error("Public key string is empty");
|
|
1625
|
+
}
|
|
1626
|
+
if (trimmedKey.includes("-----BEGIN")) {
|
|
1627
|
+
publicKeyString = trimmedKey;
|
|
1628
|
+
} else {
|
|
1629
|
+
publicKeyString = base64ToPem(trimmedKey, "PUBLIC");
|
|
1630
|
+
}
|
|
1631
|
+
} else if (publicKeyPem instanceof CryptoJS__default.default.lib.WordArray) {
|
|
1632
|
+
const base64Key = publicKeyPem.toString();
|
|
1633
|
+
publicKeyString = base64ToPem(base64Key, "PUBLIC");
|
|
1634
|
+
} else {
|
|
1635
|
+
try {
|
|
1636
|
+
const exported = await crypto.subtle.exportKey("spki", publicKeyPem);
|
|
1637
|
+
const base64Key = base64Encode(exported);
|
|
1638
|
+
publicKeyString = base64ToPem(base64Key, "PUBLIC");
|
|
1639
|
+
} catch (error) {
|
|
1640
|
+
throw new Error(
|
|
1641
|
+
"Failed to export CryptoKey. In non-HTTPS environment, use string publicKey (Base64 or PEM) directly. " + (error instanceof Error ? error.message : String(error))
|
|
1642
|
+
);
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
encrypt.setPublicKey(publicKeyString);
|
|
1646
|
+
const MAX_ENCRYPT_LENGTH = 200;
|
|
1647
|
+
if (plain.length > MAX_ENCRYPT_LENGTH) {
|
|
1648
|
+
const chunks = [];
|
|
1649
|
+
for (let i = 0; i < plain.length; i += MAX_ENCRYPT_LENGTH) {
|
|
1650
|
+
const chunk2 = plain.slice(i, i + MAX_ENCRYPT_LENGTH);
|
|
1651
|
+
const encrypted2 = encrypt.encrypt(chunk2);
|
|
1652
|
+
if (!encrypted2) {
|
|
1653
|
+
throw new Error(
|
|
1654
|
+
`RSA encryption failed for chunk ${i / MAX_ENCRYPT_LENGTH + 1}. Public key may be invalid or JSEncrypt may not be loaded correctly.`
|
|
1655
|
+
);
|
|
1656
|
+
}
|
|
1657
|
+
chunks.push(encrypted2);
|
|
1658
|
+
}
|
|
1659
|
+
return chunks.join("|");
|
|
1660
|
+
}
|
|
1661
|
+
const encrypted = encrypt.encrypt(plain);
|
|
1662
|
+
if (!encrypted) {
|
|
1663
|
+
throw new Error(
|
|
1664
|
+
`RSA encryption failed. Public key may be invalid or JSEncrypt may not be loaded correctly. Plain text length: ${plain.length} bytes.`
|
|
1665
|
+
);
|
|
1666
|
+
}
|
|
1667
|
+
return encrypted;
|
|
1668
|
+
} catch (error) {
|
|
1669
|
+
if (error instanceof Error) {
|
|
1670
|
+
if (error.message.includes("RSA encryption failed") || error.message.includes("Public key")) {
|
|
1671
|
+
throw error;
|
|
1672
|
+
}
|
|
1673
|
+
throw new Error(`[encryption] rsaEncrypt failed: ${error.message}`);
|
|
1674
|
+
}
|
|
1675
|
+
throw new Error(`[encryption] rsaEncrypt failed: ${String(error)}`);
|
|
1641
1676
|
}
|
|
1642
|
-
const exported = await crypto.subtle.exportKey("pkcs8", privateKey);
|
|
1643
|
-
return base64Encode(exported);
|
|
1644
1677
|
}
|
|
1645
|
-
async function
|
|
1646
|
-
|
|
1647
|
-
|
|
1678
|
+
async function rsaDecrypt(cipher, privateKeyPem) {
|
|
1679
|
+
const decrypt = new JSEncrypt__default.default();
|
|
1680
|
+
let privateKeyString;
|
|
1681
|
+
if (typeof privateKeyPem === "string") {
|
|
1682
|
+
if (privateKeyPem.includes("-----BEGIN")) {
|
|
1683
|
+
privateKeyString = privateKeyPem.trim();
|
|
1684
|
+
} else {
|
|
1685
|
+
const trimmed = privateKeyPem.trim();
|
|
1686
|
+
privateKeyString = base64ToPem(trimmed, "PRIVATE");
|
|
1687
|
+
}
|
|
1688
|
+
} else if (privateKeyPem instanceof CryptoJS__default.default.lib.WordArray) {
|
|
1689
|
+
const base64Key = privateKeyPem.toString();
|
|
1690
|
+
privateKeyString = base64ToPem(base64Key, "PRIVATE");
|
|
1691
|
+
} else {
|
|
1692
|
+
try {
|
|
1693
|
+
const exported = await crypto.subtle.exportKey("pkcs8", privateKeyPem);
|
|
1694
|
+
const base64Key = base64Encode(exported);
|
|
1695
|
+
privateKeyString = base64ToPem(base64Key, "PRIVATE");
|
|
1696
|
+
} catch (error) {
|
|
1697
|
+
throw new Error(
|
|
1698
|
+
"[encryption] rsaDecrypt: failed to export CryptoKey. In non-HTTPS environment, use PEM string directly."
|
|
1699
|
+
);
|
|
1700
|
+
}
|
|
1648
1701
|
}
|
|
1649
|
-
|
|
1650
|
-
const
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
{
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
true,
|
|
1659
|
-
["encrypt"]
|
|
1660
|
-
);
|
|
1661
|
-
}
|
|
1662
|
-
async function importPrivateKey(keyData) {
|
|
1663
|
-
if (typeof crypto === "undefined" || !crypto.subtle) {
|
|
1664
|
-
throw new Error("Web Crypto API is not available");
|
|
1702
|
+
decrypt.setPrivateKey(privateKeyString);
|
|
1703
|
+
const chunks = cipher.split("|");
|
|
1704
|
+
const decryptedChunks = [];
|
|
1705
|
+
for (const chunk2 of chunks) {
|
|
1706
|
+
const decrypted = decrypt.decrypt(chunk2);
|
|
1707
|
+
if (!decrypted) {
|
|
1708
|
+
throw new Error("[encryption] rsaDecrypt: RSA decryption failed");
|
|
1709
|
+
}
|
|
1710
|
+
decryptedChunks.push(decrypted);
|
|
1665
1711
|
}
|
|
1666
|
-
|
|
1667
|
-
const keyArray = new Uint8Array(keyBuffer.split("").map((char) => char.charCodeAt(0)));
|
|
1668
|
-
return crypto.subtle.importKey(
|
|
1669
|
-
"pkcs8",
|
|
1670
|
-
keyArray.buffer,
|
|
1671
|
-
{
|
|
1672
|
-
name: "RSA-OAEP",
|
|
1673
|
-
hash: "SHA-256"
|
|
1674
|
-
},
|
|
1675
|
-
true,
|
|
1676
|
-
["decrypt"]
|
|
1677
|
-
);
|
|
1712
|
+
return decryptedChunks.join("");
|
|
1678
1713
|
}
|
|
1679
|
-
async function
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
}
|
|
1683
|
-
return crypto.subtle.generateKey(
|
|
1684
|
-
{
|
|
1685
|
-
name: "HMAC",
|
|
1686
|
-
hash: "SHA-256"
|
|
1687
|
-
},
|
|
1688
|
-
true,
|
|
1689
|
-
["sign", "verify"]
|
|
1690
|
-
);
|
|
1714
|
+
async function encryptWithEnvPublicKey(plain) {
|
|
1715
|
+
const publicKey = getEnvPublicKey();
|
|
1716
|
+
return rsaEncrypt(plain, publicKey);
|
|
1691
1717
|
}
|
|
1692
|
-
async function
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
}
|
|
1696
|
-
const buffer = typeof data === "string" ? new TextEncoder().encode(data) : data;
|
|
1697
|
-
const signature = await crypto.subtle.sign("HMAC", key, buffer);
|
|
1698
|
-
return base64Encode(signature);
|
|
1718
|
+
async function decryptWithEnvPrivateKey(cipher) {
|
|
1719
|
+
const privateKey = getEnvPrivateKey();
|
|
1720
|
+
return rsaDecrypt(cipher, privateKey);
|
|
1699
1721
|
}
|
|
1700
|
-
async function
|
|
1722
|
+
async function generateRSAKeyPair(modulusLength = 2048) {
|
|
1701
1723
|
if (typeof crypto === "undefined" || !crypto.subtle) {
|
|
1702
|
-
throw new Error(
|
|
1703
|
-
|
|
1704
|
-
try {
|
|
1705
|
-
const dataBuffer = typeof data === "string" ? new TextEncoder().encode(data) : data;
|
|
1706
|
-
const signatureBuffer = new Uint8Array(
|
|
1707
|
-
atob(signature).split("").map((char) => char.charCodeAt(0))
|
|
1724
|
+
throw new Error(
|
|
1725
|
+
"[encryption] Web Crypto API is not available. Cannot generate RSA key pair. Please use fixed key pair from environment variables."
|
|
1708
1726
|
);
|
|
1709
|
-
return await crypto.subtle.verify("HMAC", key, signatureBuffer, dataBuffer);
|
|
1710
|
-
} catch {
|
|
1711
|
-
return false;
|
|
1712
1727
|
}
|
|
1713
|
-
|
|
1714
|
-
async function deriveKeyFromPassword(password, salt, iterations = 1e5, keyLength = 256) {
|
|
1715
|
-
if (typeof crypto === "undefined" || !crypto.subtle) {
|
|
1716
|
-
throw new Error("Web Crypto API is not available");
|
|
1717
|
-
}
|
|
1718
|
-
const saltBuffer = typeof salt === "string" ? new TextEncoder().encode(salt) : salt;
|
|
1719
|
-
const passwordKey = await crypto.subtle.importKey(
|
|
1720
|
-
"raw",
|
|
1721
|
-
new TextEncoder().encode(password),
|
|
1722
|
-
"PBKDF2",
|
|
1723
|
-
false,
|
|
1724
|
-
["deriveBits", "deriveKey"]
|
|
1725
|
-
);
|
|
1726
|
-
return crypto.subtle.deriveKey(
|
|
1728
|
+
const keyPair = await crypto.subtle.generateKey(
|
|
1727
1729
|
{
|
|
1728
|
-
name: "
|
|
1729
|
-
|
|
1730
|
-
|
|
1730
|
+
name: "RSA-OAEP",
|
|
1731
|
+
modulusLength,
|
|
1732
|
+
publicExponent: new Uint8Array([1, 0, 1]),
|
|
1731
1733
|
hash: "SHA-256"
|
|
1732
1734
|
},
|
|
1733
|
-
|
|
1734
|
-
{
|
|
1735
|
-
name: "AES-GCM",
|
|
1736
|
-
length: keyLength
|
|
1737
|
-
},
|
|
1738
|
-
false,
|
|
1735
|
+
true,
|
|
1739
1736
|
["encrypt", "decrypt"]
|
|
1740
1737
|
);
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
}
|
|
1746
|
-
const dataBuffer = typeof data === "string" ? new TextEncoder().encode(data) : data;
|
|
1747
|
-
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
1748
|
-
const encrypted = await crypto.subtle.encrypt(
|
|
1749
|
-
{
|
|
1750
|
-
name: "AES-GCM",
|
|
1751
|
-
iv
|
|
1752
|
-
},
|
|
1753
|
-
key,
|
|
1754
|
-
dataBuffer
|
|
1755
|
-
);
|
|
1738
|
+
const publicKeyDer = await crypto.subtle.exportKey("spki", keyPair.publicKey);
|
|
1739
|
+
const privateKeyDer = await crypto.subtle.exportKey("pkcs8", keyPair.privateKey);
|
|
1740
|
+
const publicKeyBase64 = base64Encode(publicKeyDer);
|
|
1741
|
+
const privateKeyBase64 = base64Encode(privateKeyDer);
|
|
1756
1742
|
return {
|
|
1757
|
-
|
|
1758
|
-
|
|
1743
|
+
publicKey: base64ToPem(publicKeyBase64, "PUBLIC"),
|
|
1744
|
+
privateKey: base64ToPem(privateKeyBase64, "PRIVATE")
|
|
1759
1745
|
};
|
|
1760
1746
|
}
|
|
1761
|
-
async function aesGCMDecrypt(encryptedData, iv, key) {
|
|
1762
|
-
if (typeof crypto === "undefined" || !crypto.subtle) {
|
|
1763
|
-
throw new Error("Web Crypto API is not available");
|
|
1764
|
-
}
|
|
1765
|
-
const encryptedBuffer = new Uint8Array(
|
|
1766
|
-
atob(encryptedData).split("").map((char) => char.charCodeAt(0))
|
|
1767
|
-
);
|
|
1768
|
-
const ivBuffer = new Uint8Array(
|
|
1769
|
-
atob(iv).split("").map((char) => char.charCodeAt(0))
|
|
1770
|
-
);
|
|
1771
|
-
const decrypted = await crypto.subtle.decrypt(
|
|
1772
|
-
{
|
|
1773
|
-
name: "AES-GCM",
|
|
1774
|
-
iv: ivBuffer
|
|
1775
|
-
},
|
|
1776
|
-
key,
|
|
1777
|
-
encryptedBuffer
|
|
1778
|
-
);
|
|
1779
|
-
return new TextDecoder().decode(decrypted);
|
|
1780
|
-
}
|
|
1781
1747
|
|
|
1782
1748
|
// src/browser/SecureStorage/index.ts
|
|
1783
|
-
var
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
var initOptions = {
|
|
1787
|
-
autoGenerateKeys: true,
|
|
1788
|
-
persistKeys: false,
|
|
1789
|
-
keyStorageKey: void 0,
|
|
1790
|
-
// 将自动生成随机键名
|
|
1791
|
-
keyEncryptionPassword: void 0,
|
|
1792
|
-
pbkdf2Iterations: 1e5,
|
|
1793
|
-
keyModulusLength: 2048,
|
|
1794
|
-
enableHMAC: true,
|
|
1795
|
-
enableTimestampValidation: true,
|
|
1796
|
-
timestampMaxAge: 7 * 24 * 60 * 60 * 1e3,
|
|
1797
|
-
// 7 天
|
|
1798
|
-
isProduction: false
|
|
1749
|
+
var CONFIG = {
|
|
1750
|
+
NAMESPACE: "sec_b_",
|
|
1751
|
+
DEFAULT_EXPIRE: null
|
|
1799
1752
|
};
|
|
1800
|
-
var
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
function generateKeyStorageKey() {
|
|
1805
|
-
return `_sk_${generateRandomString(32)}_${Date.now()}`;
|
|
1806
|
-
}
|
|
1807
|
-
async function initializeStorageKeys(options = {}) {
|
|
1808
|
-
if (options.forceReinitialize) {
|
|
1809
|
-
globalKeyPair = null;
|
|
1810
|
-
globalHMACKey = null;
|
|
1811
|
-
keyPairInitialized = false;
|
|
1812
|
-
actualKeyStorageKey = null;
|
|
1813
|
-
keyUsageCount = 0;
|
|
1814
|
-
initializationPromise = null;
|
|
1815
|
-
} else if (keyPairInitialized && globalKeyPair) {
|
|
1816
|
-
initOptions = { ...initOptions, ...options };
|
|
1817
|
-
return;
|
|
1818
|
-
}
|
|
1819
|
-
initOptions = { ...initOptions, ...options };
|
|
1820
|
-
if (initOptions.keyStorageKey) {
|
|
1821
|
-
actualKeyStorageKey = initOptions.keyStorageKey;
|
|
1822
|
-
} else if (!actualKeyStorageKey) {
|
|
1823
|
-
if (typeof window !== "undefined" && window.localStorage) {
|
|
1824
|
-
const savedKeyName = window.localStorage.getItem(KEY_STORAGE_KEY_REGISTRY);
|
|
1825
|
-
if (savedKeyName) {
|
|
1826
|
-
actualKeyStorageKey = savedKeyName;
|
|
1827
|
-
} else {
|
|
1828
|
-
actualKeyStorageKey = generateKeyStorageKey();
|
|
1829
|
-
window.localStorage.setItem(KEY_STORAGE_KEY_REGISTRY, actualKeyStorageKey);
|
|
1830
|
-
}
|
|
1831
|
-
} else {
|
|
1832
|
-
actualKeyStorageKey = generateKeyStorageKey();
|
|
1833
|
-
}
|
|
1834
|
-
}
|
|
1835
|
-
if (initOptions.persistKeys && typeof window !== "undefined" && window.localStorage) {
|
|
1836
|
-
try {
|
|
1837
|
-
const storedKeys = window.localStorage.getItem(actualKeyStorageKey);
|
|
1838
|
-
if (storedKeys) {
|
|
1839
|
-
const keyData = JSON.parse(storedKeys);
|
|
1840
|
-
if (keyData.encrypted && keyData.privateKeyEncrypted && keyData.salt) {
|
|
1841
|
-
if (!initOptions.keyEncryptionPassword) {
|
|
1842
|
-
if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
|
|
1843
|
-
console.error("Encrypted keys found but no password provided");
|
|
1844
|
-
}
|
|
1845
|
-
throw new Error("Password required to decrypt stored keys");
|
|
1846
|
-
}
|
|
1847
|
-
const saltArray = new Uint8Array(
|
|
1848
|
-
atob(keyData.salt).split("").map((char) => char.charCodeAt(0))
|
|
1849
|
-
);
|
|
1850
|
-
const iterations = keyData.pbkdf2Iterations || initOptions.pbkdf2Iterations;
|
|
1851
|
-
const derivedKey = await deriveKeyFromPassword(
|
|
1852
|
-
initOptions.keyEncryptionPassword,
|
|
1853
|
-
saltArray.buffer,
|
|
1854
|
-
iterations
|
|
1855
|
-
);
|
|
1856
|
-
const decryptedPrivateKey = await aesGCMDecrypt(
|
|
1857
|
-
keyData.privateKeyEncrypted,
|
|
1858
|
-
keyData.iv,
|
|
1859
|
-
derivedKey
|
|
1860
|
-
);
|
|
1861
|
-
globalKeyPair = {
|
|
1862
|
-
publicKey: await importPublicKey(keyData.publicKey),
|
|
1863
|
-
privateKey: await importPrivateKey(decryptedPrivateKey)
|
|
1864
|
-
};
|
|
1865
|
-
} else if (keyData.publicKey && keyData.privateKey) {
|
|
1866
|
-
if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
|
|
1867
|
-
console.warn(
|
|
1868
|
-
"\u26A0\uFE0F SECURITY WARNING: Loading unencrypted keys from storage. Consider re-initializing with password encryption."
|
|
1869
|
-
);
|
|
1870
|
-
}
|
|
1871
|
-
globalKeyPair = {
|
|
1872
|
-
publicKey: await importPublicKey(keyData.publicKey),
|
|
1873
|
-
privateKey: await importPrivateKey(keyData.privateKey)
|
|
1874
|
-
};
|
|
1875
|
-
}
|
|
1876
|
-
if (globalKeyPair) {
|
|
1877
|
-
keyPairInitialized = true;
|
|
1878
|
-
if (initOptions.enableHMAC && !globalHMACKey) {
|
|
1879
|
-
globalHMACKey = await generateHMACKey();
|
|
1880
|
-
}
|
|
1881
|
-
return;
|
|
1882
|
-
}
|
|
1883
|
-
}
|
|
1884
|
-
} catch (error) {
|
|
1885
|
-
if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
|
|
1886
|
-
console.warn("Failed to load persisted keys, will generate new ones");
|
|
1887
|
-
}
|
|
1888
|
-
}
|
|
1889
|
-
}
|
|
1890
|
-
if (initOptions.autoGenerateKeys && !globalKeyPair) {
|
|
1891
|
-
try {
|
|
1892
|
-
globalKeyPair = await generateRSAKeyPair(initOptions.keyModulusLength);
|
|
1893
|
-
if (initOptions.enableHMAC && !globalHMACKey) {
|
|
1894
|
-
globalHMACKey = await generateHMACKey();
|
|
1895
|
-
}
|
|
1896
|
-
keyPairInitialized = true;
|
|
1897
|
-
keyUsageCount = 0;
|
|
1898
|
-
if (initOptions.persistKeys && typeof window !== "undefined" && window.localStorage) {
|
|
1899
|
-
try {
|
|
1900
|
-
const publicKeyStr = await exportPublicKey(globalKeyPair.publicKey);
|
|
1901
|
-
if (initOptions.keyEncryptionPassword) {
|
|
1902
|
-
const privateKeyStr = await exportPrivateKey(globalKeyPair.privateKey);
|
|
1903
|
-
const salt = crypto.getRandomValues(new Uint8Array(16));
|
|
1904
|
-
const derivedKey = await deriveKeyFromPassword(
|
|
1905
|
-
initOptions.keyEncryptionPassword,
|
|
1906
|
-
salt.buffer,
|
|
1907
|
-
initOptions.pbkdf2Iterations
|
|
1908
|
-
);
|
|
1909
|
-
const { encrypted, iv } = await aesGCMEncrypt(privateKeyStr, derivedKey);
|
|
1910
|
-
const keyData = {
|
|
1911
|
-
encrypted: true,
|
|
1912
|
-
publicKey: publicKeyStr,
|
|
1913
|
-
privateKeyEncrypted: encrypted,
|
|
1914
|
-
iv,
|
|
1915
|
-
salt: base64Encode(salt.buffer),
|
|
1916
|
-
pbkdf2Iterations: initOptions.pbkdf2Iterations
|
|
1917
|
-
};
|
|
1918
|
-
window.localStorage.setItem(actualKeyStorageKey, JSON.stringify(keyData));
|
|
1919
|
-
if (!initOptions.isProduction && typeof console !== "undefined" && console.info) {
|
|
1920
|
-
console.info("\u2705 Keys encrypted and stored securely");
|
|
1921
|
-
}
|
|
1922
|
-
} else {
|
|
1923
|
-
if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
|
|
1924
|
-
console.warn(
|
|
1925
|
-
"\u26A0\uFE0F SECURITY WARNING: Storing private keys without encryption! Private keys will be stored in plain text (Base64 encoded). Provide keyEncryptionPassword for secure storage."
|
|
1926
|
-
);
|
|
1927
|
-
}
|
|
1928
|
-
const keyData = {
|
|
1929
|
-
publicKey: publicKeyStr,
|
|
1930
|
-
privateKey: await exportPrivateKey(globalKeyPair.privateKey)
|
|
1931
|
-
};
|
|
1932
|
-
window.localStorage.setItem(actualKeyStorageKey, JSON.stringify(keyData));
|
|
1933
|
-
}
|
|
1934
|
-
} catch (error) {
|
|
1935
|
-
if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
|
|
1936
|
-
console.error("Failed to persist keys");
|
|
1937
|
-
}
|
|
1938
|
-
}
|
|
1939
|
-
}
|
|
1940
|
-
} catch (error) {
|
|
1941
|
-
if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
|
|
1942
|
-
console.error("Failed to generate storage keys");
|
|
1943
|
-
}
|
|
1944
|
-
throw error;
|
|
1945
|
-
}
|
|
1946
|
-
}
|
|
1947
|
-
if (initOptions.enableHMAC && !globalHMACKey) {
|
|
1948
|
-
globalHMACKey = await generateHMACKey();
|
|
1949
|
-
}
|
|
1950
|
-
}
|
|
1951
|
-
function setStorageKeyPair(keyPair) {
|
|
1952
|
-
globalKeyPair = keyPair;
|
|
1953
|
-
keyPairInitialized = true;
|
|
1954
|
-
}
|
|
1955
|
-
function getStorageKeyPair() {
|
|
1956
|
-
return globalKeyPair;
|
|
1957
|
-
}
|
|
1958
|
-
function clearPersistedKeys() {
|
|
1959
|
-
if (typeof window !== "undefined" && window.localStorage && actualKeyStorageKey) {
|
|
1960
|
-
try {
|
|
1961
|
-
window.localStorage.removeItem(actualKeyStorageKey);
|
|
1962
|
-
actualKeyStorageKey = null;
|
|
1963
|
-
keyPairInitialized = false;
|
|
1964
|
-
globalKeyPair = null;
|
|
1965
|
-
globalHMACKey = null;
|
|
1966
|
-
keyUsageCount = 0;
|
|
1967
|
-
} catch (error) {
|
|
1968
|
-
if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
|
|
1969
|
-
console.error("Failed to clear persisted keys");
|
|
1970
|
-
}
|
|
1971
|
-
}
|
|
1972
|
-
}
|
|
1973
|
-
}
|
|
1974
|
-
function getKeyUsageCount() {
|
|
1975
|
-
return keyUsageCount;
|
|
1976
|
-
}
|
|
1977
|
-
function resetKeyUsageCount() {
|
|
1978
|
-
keyUsageCount = 0;
|
|
1979
|
-
}
|
|
1980
|
-
async function ensureKeyPair() {
|
|
1981
|
-
if (globalKeyPair) {
|
|
1982
|
-
keyUsageCount++;
|
|
1983
|
-
return;
|
|
1984
|
-
}
|
|
1985
|
-
if (!initOptions.autoGenerateKeys) {
|
|
1986
|
-
return;
|
|
1987
|
-
}
|
|
1988
|
-
if (initializationPromise) {
|
|
1989
|
-
await initializationPromise;
|
|
1990
|
-
if (globalKeyPair) {
|
|
1991
|
-
keyUsageCount++;
|
|
1753
|
+
var SecureStorage = class _SecureStorage {
|
|
1754
|
+
static get instances() {
|
|
1755
|
+
if (!this._instances) {
|
|
1756
|
+
this._instances = /* @__PURE__ */ new Map();
|
|
1992
1757
|
}
|
|
1993
|
-
return;
|
|
1994
|
-
}
|
|
1995
|
-
initializationPromise = initializeStorageKeys();
|
|
1996
|
-
try {
|
|
1997
|
-
await initializationPromise;
|
|
1998
|
-
} finally {
|
|
1999
|
-
initializationPromise = null;
|
|
1758
|
+
return this._instances;
|
|
2000
1759
|
}
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
}
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
if (expectedType === "object" && (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))) {
|
|
2009
|
-
throw new Error("Expected object but got different type");
|
|
2010
|
-
}
|
|
2011
|
-
if (expectedType === "array" && !Array.isArray(parsed)) {
|
|
2012
|
-
throw new Error("Expected array but got different type");
|
|
2013
|
-
}
|
|
2014
|
-
return parsed;
|
|
2015
|
-
} catch (error) {
|
|
2016
|
-
if (error instanceof SyntaxError) {
|
|
2017
|
-
throw new Error(`Invalid JSON format: ${error.message}`);
|
|
1760
|
+
constructor(options) {
|
|
1761
|
+
const storageType = options.storage === window.sessionStorage ? "session" : "local";
|
|
1762
|
+
const namespace = options.namespace || CONFIG.NAMESPACE;
|
|
1763
|
+
const instanceKey = `${storageType}_${namespace}`;
|
|
1764
|
+
const existingInstance = _SecureStorage.instances.get(instanceKey);
|
|
1765
|
+
if (existingInstance) {
|
|
1766
|
+
return existingInstance;
|
|
2018
1767
|
}
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
return true;
|
|
2025
|
-
}
|
|
2026
|
-
if (initOptions.timestampMaxAge === 0) {
|
|
2027
|
-
return true;
|
|
2028
|
-
}
|
|
2029
|
-
const now = Date.now();
|
|
2030
|
-
const age = now - timestamp;
|
|
2031
|
-
const maxAge = initOptions.timestampMaxAge || 7 * 24 * 60 * 60 * 1e3;
|
|
2032
|
-
if (age < -maxClockSkew) {
|
|
2033
|
-
return false;
|
|
1768
|
+
this.namespace = namespace;
|
|
1769
|
+
this.storage = options.storage || window.localStorage;
|
|
1770
|
+
this.defaultExpire = options.defaultExpire ?? CONFIG.DEFAULT_EXPIRE;
|
|
1771
|
+
this.instanceKey = instanceKey;
|
|
1772
|
+
_SecureStorage.instances.set(instanceKey, this);
|
|
2034
1773
|
}
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
const hmac = await computeHMAC(encryptedData, hmacKey);
|
|
2041
|
-
const item = {
|
|
2042
|
-
data: encryptedData,
|
|
2043
|
-
hmac,
|
|
2044
|
-
encrypted: true,
|
|
2045
|
-
timestamp: Date.now()
|
|
2046
|
-
};
|
|
2047
|
-
return JSON.stringify(item);
|
|
2048
|
-
}
|
|
2049
|
-
return encryptedData;
|
|
2050
|
-
}
|
|
2051
|
-
async function handleDecryptError(error, encryptedStr, key) {
|
|
2052
|
-
if (error instanceof Error && error.message.includes("HMAC verification failed")) {
|
|
2053
|
-
if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
|
|
2054
|
-
console.error(
|
|
2055
|
-
"\u26A0\uFE0F SECURITY ALERT: Data integrity check failed! Data may have been tampered with."
|
|
2056
|
-
);
|
|
2057
|
-
}
|
|
2058
|
-
throw error;
|
|
1774
|
+
/**
|
|
1775
|
+
* 获取单例实例(静态方法)
|
|
1776
|
+
*/
|
|
1777
|
+
static getInstance(options) {
|
|
1778
|
+
return new _SecureStorage(options);
|
|
2059
1779
|
}
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
1780
|
+
/**
|
|
1781
|
+
* 清除所有单例实例(用于测试或重置)
|
|
1782
|
+
*/
|
|
1783
|
+
static clearInstances() {
|
|
1784
|
+
_SecureStorage.instances.clear();
|
|
2065
1785
|
}
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
);
|
|
2072
|
-
}
|
|
2073
|
-
return decryptedStr;
|
|
2074
|
-
} catch {
|
|
2075
|
-
throw error;
|
|
1786
|
+
/**
|
|
1787
|
+
* 获取当前所有实例的数量(用于调试)
|
|
1788
|
+
*/
|
|
1789
|
+
static getInstanceCount() {
|
|
1790
|
+
return _SecureStorage.instances.size;
|
|
2076
1791
|
}
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
console.warn(
|
|
2083
|
-
`\u26A0\uFE0F SECURITY WARNING: Reading unencrypted data for key "${key}" without encryption keys.`
|
|
2084
|
-
);
|
|
2085
|
-
}
|
|
2086
|
-
return decryptedStr;
|
|
2087
|
-
} catch (error) {
|
|
2088
|
-
throw new Error(`Failed to decode storage value for key "${key}"`);
|
|
1792
|
+
/**
|
|
1793
|
+
* 获取完整键名(带命名空间)
|
|
1794
|
+
*/
|
|
1795
|
+
_getFullKey(key) {
|
|
1796
|
+
return `${this.namespace}${key}`;
|
|
2089
1797
|
}
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
if (
|
|
2095
|
-
|
|
2096
|
-
const isValid = await verifyHMAC(item.data, item.hmac, hmacKey);
|
|
2097
|
-
if (!isValid) {
|
|
2098
|
-
throw new Error("HMAC verification failed: data may have been tampered with");
|
|
2099
|
-
}
|
|
2100
|
-
}
|
|
2101
|
-
if (item.timestamp && !validateTimestamp(item.timestamp)) {
|
|
2102
|
-
throw new Error("Data timestamp validation failed: data may be expired or replayed");
|
|
2103
|
-
}
|
|
2104
|
-
return await rsaDecrypt(item.data, privateKey);
|
|
2105
|
-
}
|
|
2106
|
-
return await rsaDecrypt(encryptedValue, privateKey);
|
|
2107
|
-
} catch (error) {
|
|
2108
|
-
if (error instanceof Error && error.message.includes("HMAC verification failed")) {
|
|
2109
|
-
throw error;
|
|
2110
|
-
}
|
|
2111
|
-
throw new Error("Failed to decrypt storage value: invalid format or corrupted data");
|
|
1798
|
+
/**
|
|
1799
|
+
* 检查数据是否过期
|
|
1800
|
+
*/
|
|
1801
|
+
_isExpired(expire) {
|
|
1802
|
+
if (!expire) return false;
|
|
1803
|
+
return Date.now() > expire;
|
|
2112
1804
|
}
|
|
2113
|
-
}
|
|
2114
|
-
var localStorage = {
|
|
2115
1805
|
/**
|
|
2116
1806
|
* 设置值
|
|
2117
|
-
* @param key - 键
|
|
2118
|
-
* @param value - 值
|
|
2119
|
-
* @param options - 选项(过期时间、密钥等)
|
|
2120
|
-
* @returns Promise<void>
|
|
2121
1807
|
*/
|
|
2122
|
-
async set(key, value,
|
|
2123
|
-
if (typeof window === "undefined" || !window.localStorage) {
|
|
2124
|
-
throw new Error("localStorage is not available");
|
|
2125
|
-
}
|
|
2126
|
-
const { expiry, publicKey } = options;
|
|
2127
|
-
const item = {
|
|
2128
|
-
value,
|
|
2129
|
-
expiry: expiry ? Date.now() + expiry : void 0
|
|
2130
|
-
};
|
|
1808
|
+
async set(key, value, expire = this.defaultExpire) {
|
|
2131
1809
|
try {
|
|
2132
|
-
const
|
|
2133
|
-
|
|
2134
|
-
const
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
}
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
1810
|
+
const fullKey = this._getFullKey(key);
|
|
1811
|
+
const aesKey = generateRandomAESKeyString();
|
|
1812
|
+
const storageData = {
|
|
1813
|
+
value,
|
|
1814
|
+
expire: expire ? Date.now() + expire : null,
|
|
1815
|
+
timestamp: Date.now()
|
|
1816
|
+
};
|
|
1817
|
+
const encryptedData = encryptJsonWithAES(storageData, aesKey);
|
|
1818
|
+
const encryptedKey = await encryptWithEnvPublicKey(aesKey);
|
|
1819
|
+
const finalData = {
|
|
1820
|
+
data: encryptedData,
|
|
1821
|
+
key: encryptedKey,
|
|
1822
|
+
version: "1.0"
|
|
1823
|
+
};
|
|
1824
|
+
this.storage.setItem(fullKey, JSON.stringify(finalData));
|
|
1825
|
+
return true;
|
|
2147
1826
|
} catch (error) {
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
}
|
|
2151
|
-
throw error;
|
|
1827
|
+
console.error("[SecureStorage] \u4FDD\u5B58\u5931\u8D25:", error);
|
|
1828
|
+
return false;
|
|
2152
1829
|
}
|
|
2153
|
-
}
|
|
1830
|
+
}
|
|
2154
1831
|
/**
|
|
2155
1832
|
* 获取值
|
|
2156
|
-
* @param key - 键
|
|
2157
|
-
* @param options - 选项(默认值、私钥等)
|
|
2158
|
-
* @returns Promise<T | undefined> 值或默认值
|
|
2159
1833
|
*/
|
|
2160
|
-
async get(key,
|
|
2161
|
-
if (typeof window === "undefined" || !window.localStorage) {
|
|
2162
|
-
return options.defaultValue;
|
|
2163
|
-
}
|
|
2164
|
-
const { defaultValue, privateKey } = options;
|
|
1834
|
+
async get(key, defaultValue = null) {
|
|
2165
1835
|
try {
|
|
2166
|
-
const
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
const keyToUse = privateKey || globalKeyPair?.privateKey;
|
|
2171
|
-
if (keyToUse) {
|
|
2172
|
-
try {
|
|
2173
|
-
decryptedStr = await decryptValue(encryptedStr, keyToUse, globalHMACKey);
|
|
2174
|
-
} catch (error) {
|
|
2175
|
-
try {
|
|
2176
|
-
decryptedStr = await handleDecryptError(error, encryptedStr, key);
|
|
2177
|
-
} catch {
|
|
2178
|
-
return defaultValue;
|
|
2179
|
-
}
|
|
2180
|
-
}
|
|
2181
|
-
} else {
|
|
2182
|
-
try {
|
|
2183
|
-
decryptedStr = handleNoKeyDecode(encryptedStr, key);
|
|
2184
|
-
} catch {
|
|
2185
|
-
return defaultValue;
|
|
2186
|
-
}
|
|
1836
|
+
const fullKey = this._getFullKey(key);
|
|
1837
|
+
const storedData = this.storage.getItem(fullKey);
|
|
1838
|
+
if (!storedData) {
|
|
1839
|
+
return defaultValue;
|
|
2187
1840
|
}
|
|
2188
|
-
const
|
|
2189
|
-
|
|
2190
|
-
|
|
1841
|
+
const finalData = JSON.parse(storedData);
|
|
1842
|
+
const aesKey = await decryptWithEnvPrivateKey(finalData.key);
|
|
1843
|
+
const storageData = decryptJsonWithAES(finalData.data, aesKey);
|
|
1844
|
+
if (this._isExpired(storageData.expire)) {
|
|
1845
|
+
this.remove(key);
|
|
2191
1846
|
return defaultValue;
|
|
2192
1847
|
}
|
|
2193
|
-
return
|
|
1848
|
+
return storageData.value;
|
|
2194
1849
|
} catch (error) {
|
|
2195
|
-
|
|
2196
|
-
console.warn(`Failed to parse storage item for key "${key}"`);
|
|
2197
|
-
}
|
|
1850
|
+
console.error("[SecureStorage] \u8BFB\u53D6\u5931\u8D25:", error);
|
|
2198
1851
|
return defaultValue;
|
|
2199
1852
|
}
|
|
2200
|
-
}
|
|
1853
|
+
}
|
|
2201
1854
|
/**
|
|
2202
|
-
*
|
|
2203
|
-
* @param key - 键
|
|
1855
|
+
* 删除值
|
|
2204
1856
|
*/
|
|
2205
1857
|
remove(key) {
|
|
2206
|
-
if (typeof window === "undefined" || !window.localStorage) return;
|
|
2207
1858
|
try {
|
|
2208
|
-
|
|
1859
|
+
const fullKey = this._getFullKey(key);
|
|
1860
|
+
this.storage.removeItem(fullKey);
|
|
1861
|
+
return true;
|
|
2209
1862
|
} catch (error) {
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
}
|
|
1863
|
+
console.error("[SecureStorage] \u79FB\u9664\u5931\u8D25:", error);
|
|
1864
|
+
return false;
|
|
2213
1865
|
}
|
|
2214
|
-
}
|
|
1866
|
+
}
|
|
2215
1867
|
/**
|
|
2216
|
-
*
|
|
1868
|
+
* 检查键是否存在
|
|
2217
1869
|
*/
|
|
2218
|
-
|
|
2219
|
-
if (typeof window === "undefined" || !window.localStorage) return;
|
|
1870
|
+
async has(key) {
|
|
2220
1871
|
try {
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
if (!
|
|
2224
|
-
|
|
1872
|
+
const fullKey = this._getFullKey(key);
|
|
1873
|
+
const storedData = this.storage.getItem(fullKey);
|
|
1874
|
+
if (!storedData) return false;
|
|
1875
|
+
const finalData = JSON.parse(storedData);
|
|
1876
|
+
const aesKey = await decryptWithEnvPrivateKey(finalData.key);
|
|
1877
|
+
const storageData = decryptJsonWithAES(finalData.data, aesKey);
|
|
1878
|
+
if (this._isExpired(storageData.expire)) {
|
|
1879
|
+
this.remove(key);
|
|
1880
|
+
return false;
|
|
2225
1881
|
}
|
|
1882
|
+
return true;
|
|
1883
|
+
} catch (error) {
|
|
1884
|
+
return false;
|
|
2226
1885
|
}
|
|
2227
|
-
}
|
|
1886
|
+
}
|
|
2228
1887
|
/**
|
|
2229
|
-
*
|
|
2230
|
-
* @returns 键数组
|
|
1888
|
+
* 清空所有数据
|
|
2231
1889
|
*/
|
|
2232
|
-
|
|
2233
|
-
if (typeof window === "undefined" || !window.localStorage) return [];
|
|
2234
|
-
const keys2 = [];
|
|
1890
|
+
clear() {
|
|
2235
1891
|
try {
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
1892
|
+
let count = 0;
|
|
1893
|
+
const keys2 = [];
|
|
1894
|
+
for (let i = 0; i < this.storage.length; i++) {
|
|
1895
|
+
const key = this.storage.key(i);
|
|
1896
|
+
if (key && key.startsWith(this.namespace)) {
|
|
1897
|
+
keys2.push(key);
|
|
1898
|
+
}
|
|
2239
1899
|
}
|
|
1900
|
+
keys2.forEach((key) => {
|
|
1901
|
+
this.storage.removeItem(key);
|
|
1902
|
+
count++;
|
|
1903
|
+
});
|
|
1904
|
+
return count;
|
|
2240
1905
|
} catch (error) {
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
}
|
|
1906
|
+
console.error("[SecureStorage] \u6E05\u7A7A\u5931\u8D25:", error);
|
|
1907
|
+
return 0;
|
|
2244
1908
|
}
|
|
2245
|
-
return keys2;
|
|
2246
1909
|
}
|
|
2247
|
-
};
|
|
2248
|
-
var sessionStorage = {
|
|
2249
1910
|
/**
|
|
2250
|
-
*
|
|
2251
|
-
* @param key - 键
|
|
2252
|
-
* @param value - 值
|
|
2253
|
-
* @param options - 选项(公钥等)
|
|
2254
|
-
* @returns Promise<void>
|
|
1911
|
+
* 获取所有键名
|
|
2255
1912
|
*/
|
|
2256
|
-
|
|
2257
|
-
if (typeof window === "undefined" || !window.sessionStorage) {
|
|
2258
|
-
throw new Error("sessionStorage is not available");
|
|
2259
|
-
}
|
|
2260
|
-
const { publicKey } = options;
|
|
1913
|
+
keys() {
|
|
2261
1914
|
try {
|
|
2262
|
-
const
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
window.sessionStorage.setItem(key, encrypted);
|
|
2268
|
-
} else {
|
|
2269
|
-
if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
|
|
2270
|
-
console.warn(
|
|
2271
|
-
`\u26A0\uFE0F SECURITY WARNING: Storing data without encryption for key "${key}". Data is only Base64 encoded, not encrypted!`
|
|
2272
|
-
);
|
|
1915
|
+
const keys2 = [];
|
|
1916
|
+
for (let i = 0; i < this.storage.length; i++) {
|
|
1917
|
+
const key = this.storage.key(i);
|
|
1918
|
+
if (key && key.startsWith(this.namespace)) {
|
|
1919
|
+
keys2.push(key.substring(this.namespace.length));
|
|
2273
1920
|
}
|
|
2274
|
-
const encoded = base64Encode(jsonStr);
|
|
2275
|
-
window.sessionStorage.setItem(key, encoded);
|
|
2276
1921
|
}
|
|
1922
|
+
return keys2;
|
|
2277
1923
|
} catch (error) {
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
}
|
|
2281
|
-
throw error;
|
|
1924
|
+
console.error("[SecureStorage] \u83B7\u53D6\u952E\u540D\u5931\u8D25:", error);
|
|
1925
|
+
return [];
|
|
2282
1926
|
}
|
|
2283
|
-
}
|
|
1927
|
+
}
|
|
2284
1928
|
/**
|
|
2285
|
-
*
|
|
2286
|
-
* @param key - 键
|
|
2287
|
-
* @param options - 选项(默认值、私钥等)
|
|
2288
|
-
* @returns Promise<T | undefined> 值或默认值
|
|
1929
|
+
* 获取数据条数
|
|
2289
1930
|
*/
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
1931
|
+
size() {
|
|
1932
|
+
return this.keys().length;
|
|
1933
|
+
}
|
|
1934
|
+
/**
|
|
1935
|
+
* 清理过期数据
|
|
1936
|
+
*/
|
|
1937
|
+
async clearExpired() {
|
|
2295
1938
|
try {
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
const keyToUse = privateKey || globalKeyPair?.privateKey;
|
|
2301
|
-
if (keyToUse) {
|
|
1939
|
+
let count = 0;
|
|
1940
|
+
const keys2 = this.keys();
|
|
1941
|
+
for (const key of keys2) {
|
|
1942
|
+
const fullKey = this._getFullKey(key);
|
|
2302
1943
|
try {
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
1944
|
+
const storedData = this.storage.getItem(fullKey);
|
|
1945
|
+
if (storedData) {
|
|
1946
|
+
const finalData = JSON.parse(storedData);
|
|
1947
|
+
const aesKey = await decryptWithEnvPrivateKey(finalData.key);
|
|
1948
|
+
const storageData = decryptJsonWithAES(finalData.data, aesKey);
|
|
1949
|
+
if (this._isExpired(storageData.expire)) {
|
|
1950
|
+
this.remove(key);
|
|
1951
|
+
count++;
|
|
1952
|
+
}
|
|
2309
1953
|
}
|
|
1954
|
+
} catch (error) {
|
|
1955
|
+
this.remove(key);
|
|
1956
|
+
count++;
|
|
2310
1957
|
}
|
|
2311
|
-
} else {
|
|
2312
|
-
try {
|
|
2313
|
-
decryptedStr = handleNoKeyDecode(encryptedStr, key);
|
|
2314
|
-
} catch {
|
|
2315
|
-
return defaultValue;
|
|
2316
|
-
}
|
|
2317
|
-
}
|
|
2318
|
-
return safeParseJSON(decryptedStr);
|
|
2319
|
-
} catch {
|
|
2320
|
-
return defaultValue;
|
|
2321
|
-
}
|
|
2322
|
-
},
|
|
2323
|
-
/**
|
|
2324
|
-
* 移除值
|
|
2325
|
-
* @param key - 键
|
|
2326
|
-
*/
|
|
2327
|
-
remove(key) {
|
|
2328
|
-
if (typeof window === "undefined" || !window.sessionStorage) return;
|
|
2329
|
-
try {
|
|
2330
|
-
window.sessionStorage.removeItem(key);
|
|
2331
|
-
} catch (error) {
|
|
2332
|
-
if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
|
|
2333
|
-
console.error("sessionStorage.remove error");
|
|
2334
1958
|
}
|
|
2335
|
-
|
|
2336
|
-
},
|
|
2337
|
-
/**
|
|
2338
|
-
* 清空所有值
|
|
2339
|
-
*/
|
|
2340
|
-
clear() {
|
|
2341
|
-
if (typeof window === "undefined" || !window.sessionStorage) return;
|
|
2342
|
-
try {
|
|
2343
|
-
window.sessionStorage.clear();
|
|
1959
|
+
return count;
|
|
2344
1960
|
} catch (error) {
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
}
|
|
1961
|
+
console.error("[SecureStorage] \u6E05\u7406\u8FC7\u671F\u6570\u636E\u5931\u8D25:", error);
|
|
1962
|
+
return 0;
|
|
2348
1963
|
}
|
|
2349
1964
|
}
|
|
2350
|
-
};
|
|
2351
|
-
var cookie = {
|
|
2352
|
-
/**
|
|
2353
|
-
* 设置Cookie
|
|
2354
|
-
* @param key - 键
|
|
2355
|
-
* @param value - 值
|
|
2356
|
-
* @param options - Cookie选项
|
|
2357
|
-
*/
|
|
2358
|
-
set(key, value, options = {}) {
|
|
2359
|
-
if (typeof document === "undefined") {
|
|
2360
|
-
throw new Error("document is not available");
|
|
2361
|
-
}
|
|
2362
|
-
let cookieStr = `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
|
|
2363
|
-
if (options.expires) {
|
|
2364
|
-
const expiresDate = options.expires instanceof Date ? options.expires : new Date(Date.now() + options.expires * 24 * 60 * 60 * 1e3);
|
|
2365
|
-
cookieStr += `; expires=${expiresDate.toUTCString()}`;
|
|
2366
|
-
}
|
|
2367
|
-
if (options.path) cookieStr += `; path=${options.path}`;
|
|
2368
|
-
if (options.domain) cookieStr += `; domain=${options.domain}`;
|
|
2369
|
-
if (options.secure) cookieStr += "; secure";
|
|
2370
|
-
if (options.sameSite) cookieStr += `; sameSite=${options.sameSite}`;
|
|
2371
|
-
document.cookie = cookieStr;
|
|
2372
|
-
},
|
|
2373
|
-
/**
|
|
2374
|
-
* 获取Cookie
|
|
2375
|
-
* @param key - 键
|
|
2376
|
-
* @returns Cookie值
|
|
2377
|
-
*/
|
|
2378
|
-
get(key) {
|
|
2379
|
-
if (typeof document === "undefined") return void 0;
|
|
2380
|
-
const name = encodeURIComponent(key);
|
|
2381
|
-
const cookies = document.cookie.split(";");
|
|
2382
|
-
for (const cookieStr of cookies) {
|
|
2383
|
-
const trimmed = cookieStr.trim();
|
|
2384
|
-
const eqIndex = trimmed.indexOf("=");
|
|
2385
|
-
if (eqIndex === -1) continue;
|
|
2386
|
-
const cookieKey = trimmed.substring(0, eqIndex).trim();
|
|
2387
|
-
const cookieValue = trimmed.substring(eqIndex + 1).trim();
|
|
2388
|
-
if (cookieKey === name) {
|
|
2389
|
-
const decoded = decodeURIComponent(cookieValue);
|
|
2390
|
-
return decoded === "" ? void 0 : decoded;
|
|
2391
|
-
}
|
|
2392
|
-
}
|
|
2393
|
-
return void 0;
|
|
2394
|
-
},
|
|
2395
1965
|
/**
|
|
2396
|
-
*
|
|
2397
|
-
* @param key - 键
|
|
2398
|
-
* @param options - Cookie选项
|
|
1966
|
+
* 获取实例的唯一标识
|
|
2399
1967
|
*/
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
expires: /* @__PURE__ */ new Date(0)
|
|
2404
|
-
});
|
|
2405
|
-
},
|
|
1968
|
+
getInstanceKey() {
|
|
1969
|
+
return this.instanceKey;
|
|
1970
|
+
}
|
|
2406
1971
|
/**
|
|
2407
|
-
*
|
|
2408
|
-
* @returns Cookie对象
|
|
1972
|
+
* 检查是否为单例实例
|
|
2409
1973
|
*/
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
const cookies = {};
|
|
2413
|
-
document.cookie.split(";").forEach((cookieStr) => {
|
|
2414
|
-
const trimmed = cookieStr.trim();
|
|
2415
|
-
if (!trimmed) return;
|
|
2416
|
-
const eqIndex = trimmed.indexOf("=");
|
|
2417
|
-
if (eqIndex === -1) return;
|
|
2418
|
-
const key = trimmed.substring(0, eqIndex).trim();
|
|
2419
|
-
const value = trimmed.substring(eqIndex + 1).trim();
|
|
2420
|
-
if (key && value) {
|
|
2421
|
-
const decodedKey = decodeURIComponent(key);
|
|
2422
|
-
const decodedValue = decodeURIComponent(value);
|
|
2423
|
-
if (decodedValue !== "") {
|
|
2424
|
-
cookies[decodedKey] = decodedValue;
|
|
2425
|
-
}
|
|
2426
|
-
}
|
|
2427
|
-
});
|
|
2428
|
-
return cookies;
|
|
1974
|
+
isSingleton() {
|
|
1975
|
+
return _SecureStorage.instances.has(this.instanceKey);
|
|
2429
1976
|
}
|
|
2430
1977
|
};
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
async set(key, value, options) {
|
|
2436
|
-
await localStorage.set(key, value, {
|
|
2437
|
-
expiry: options?.expiry,
|
|
2438
|
-
publicKey: options?.publicKey
|
|
2439
|
-
});
|
|
2440
|
-
},
|
|
2441
|
-
async get(key, options) {
|
|
2442
|
-
return localStorage.get(key, {
|
|
2443
|
-
defaultValue: options?.defaultValue,
|
|
2444
|
-
privateKey: options?.privateKey
|
|
2445
|
-
});
|
|
2446
|
-
},
|
|
2447
|
-
remove(key) {
|
|
2448
|
-
localStorage.remove(key);
|
|
2449
|
-
},
|
|
2450
|
-
clear() {
|
|
2451
|
-
localStorage.clear();
|
|
2452
|
-
},
|
|
2453
|
-
keys() {
|
|
2454
|
-
return localStorage.keys();
|
|
2455
|
-
}
|
|
2456
|
-
};
|
|
2457
|
-
case "session":
|
|
2458
|
-
return {
|
|
2459
|
-
async set(key, value, options) {
|
|
2460
|
-
await sessionStorage.set(key, value, {
|
|
2461
|
-
publicKey: options?.publicKey
|
|
2462
|
-
});
|
|
2463
|
-
},
|
|
2464
|
-
async get(key, options) {
|
|
2465
|
-
return sessionStorage.get(key, {
|
|
2466
|
-
defaultValue: options?.defaultValue,
|
|
2467
|
-
privateKey: options?.privateKey
|
|
2468
|
-
});
|
|
2469
|
-
},
|
|
2470
|
-
remove(key) {
|
|
2471
|
-
sessionStorage.remove(key);
|
|
2472
|
-
},
|
|
2473
|
-
clear() {
|
|
2474
|
-
sessionStorage.clear();
|
|
2475
|
-
},
|
|
2476
|
-
keys() {
|
|
2477
|
-
if (typeof window === "undefined" || !window.sessionStorage) return [];
|
|
2478
|
-
const keys2 = [];
|
|
2479
|
-
try {
|
|
2480
|
-
for (let i = 0; i < window.sessionStorage.length; i++) {
|
|
2481
|
-
const key = window.sessionStorage.key(i);
|
|
2482
|
-
if (key) keys2.push(key);
|
|
2483
|
-
}
|
|
2484
|
-
} catch (error) {
|
|
2485
|
-
if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
|
|
2486
|
-
console.error("sessionStorage.keys error");
|
|
2487
|
-
}
|
|
2488
|
-
}
|
|
2489
|
-
return keys2;
|
|
2490
|
-
}
|
|
2491
|
-
};
|
|
2492
|
-
case "cookie":
|
|
2493
|
-
return {
|
|
2494
|
-
async set(key, value) {
|
|
2495
|
-
cookie.set(key, String(value));
|
|
2496
|
-
},
|
|
2497
|
-
async get(key, options) {
|
|
2498
|
-
const value = cookie.get(key);
|
|
2499
|
-
if (value === void 0) return options?.defaultValue;
|
|
2500
|
-
try {
|
|
2501
|
-
return JSON.parse(value);
|
|
2502
|
-
} catch {
|
|
2503
|
-
return value;
|
|
2504
|
-
}
|
|
2505
|
-
},
|
|
2506
|
-
remove(key) {
|
|
2507
|
-
cookie.remove(key);
|
|
2508
|
-
},
|
|
2509
|
-
clear() {
|
|
2510
|
-
const all = cookie.getAll();
|
|
2511
|
-
Object.keys(all).forEach((k) => cookie.remove(k));
|
|
2512
|
-
},
|
|
2513
|
-
keys() {
|
|
2514
|
-
return Object.keys(cookie.getAll());
|
|
2515
|
-
}
|
|
2516
|
-
};
|
|
2517
|
-
default:
|
|
2518
|
-
return createBackendAdapter("local");
|
|
1978
|
+
var _defaultSecureStorage = null;
|
|
1979
|
+
function _getDefaultSecureStorage() {
|
|
1980
|
+
if (typeof window === "undefined") {
|
|
1981
|
+
throw new Error("[SecureStorage] secureStorage is only available in browser environments.");
|
|
2519
1982
|
}
|
|
1983
|
+
if (!_defaultSecureStorage) {
|
|
1984
|
+
_defaultSecureStorage = new SecureStorage({});
|
|
1985
|
+
}
|
|
1986
|
+
return _defaultSecureStorage;
|
|
2520
1987
|
}
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
1988
|
+
var secureStorage = new Proxy({}, {
|
|
1989
|
+
get(_target, prop) {
|
|
1990
|
+
const instance = _getDefaultSecureStorage();
|
|
1991
|
+
const value = instance[prop];
|
|
1992
|
+
if (typeof value === "function") {
|
|
1993
|
+
return value.bind(instance);
|
|
1994
|
+
}
|
|
1995
|
+
return value;
|
|
1996
|
+
}
|
|
1997
|
+
});
|
|
2525
1998
|
|
|
2526
1999
|
// src/browser/network/index.ts
|
|
2527
2000
|
async function fetchWithRetry(url, options = {}, retryCount = 3, retryDelay = 1e3) {
|
|
@@ -4117,6 +3590,48 @@ function setCommonParams(params) {
|
|
|
4117
3590
|
async function flush() {
|
|
4118
3591
|
await getTracker().flush();
|
|
4119
3592
|
}
|
|
3593
|
+
function convertRes2Blob(response) {
|
|
3594
|
+
const contentDisposition = response.headers["content-disposition"];
|
|
3595
|
+
let fileName = "file.xlsx";
|
|
3596
|
+
if (contentDisposition) {
|
|
3597
|
+
const rfc5987Match = contentDisposition.match(/filename\*=UTF-8''([^;]+)/i);
|
|
3598
|
+
if (rfc5987Match) {
|
|
3599
|
+
fileName = decodeURIComponent(rfc5987Match[1]);
|
|
3600
|
+
} else {
|
|
3601
|
+
const standardMatch = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/i);
|
|
3602
|
+
if (standardMatch) {
|
|
3603
|
+
fileName = standardMatch[1];
|
|
3604
|
+
fileName = fileName.replace(/^['"]|['"]$/g, "");
|
|
3605
|
+
try {
|
|
3606
|
+
fileName = decodeURIComponent(fileName);
|
|
3607
|
+
} catch (e) {
|
|
3608
|
+
try {
|
|
3609
|
+
fileName = decodeURI(fileName);
|
|
3610
|
+
} catch (e2) {
|
|
3611
|
+
}
|
|
3612
|
+
}
|
|
3613
|
+
}
|
|
3614
|
+
}
|
|
3615
|
+
fileName = fileName.trim().replace(/^_+|_+$/g, "");
|
|
3616
|
+
}
|
|
3617
|
+
const blob = new Blob([response.data], { type: "application/vnd.ms-excel;charset=utf-8" });
|
|
3618
|
+
if (typeof window.navigator.msSaveBlob !== "undefined") {
|
|
3619
|
+
window.navigator.msSaveBlob(blob, fileName);
|
|
3620
|
+
} else {
|
|
3621
|
+
const blobURL = window.URL.createObjectURL(blob);
|
|
3622
|
+
const tempLink = document.createElement("a");
|
|
3623
|
+
tempLink.style.display = "none";
|
|
3624
|
+
tempLink.href = blobURL;
|
|
3625
|
+
tempLink.setAttribute("download", fileName);
|
|
3626
|
+
if (typeof tempLink.download === "undefined") {
|
|
3627
|
+
tempLink.setAttribute("target", "_blank");
|
|
3628
|
+
}
|
|
3629
|
+
document.body.appendChild(tempLink);
|
|
3630
|
+
tempLink.click();
|
|
3631
|
+
document.body.removeChild(tempLink);
|
|
3632
|
+
window.URL.revokeObjectURL(blobURL);
|
|
3633
|
+
}
|
|
3634
|
+
}
|
|
4120
3635
|
var serviceEventHandlers = {
|
|
4121
3636
|
message: null
|
|
4122
3637
|
};
|
|
@@ -4150,11 +3665,28 @@ function filterEmptyKey(params, emptyString) {
|
|
|
4150
3665
|
}
|
|
4151
3666
|
});
|
|
4152
3667
|
}
|
|
3668
|
+
function isFileUpload(data) {
|
|
3669
|
+
if (!data) return false;
|
|
3670
|
+
if (data instanceof FormData) return true;
|
|
3671
|
+
if (data instanceof File) return true;
|
|
3672
|
+
if (data instanceof Blob) return true;
|
|
3673
|
+
if (data instanceof ArrayBuffer) return true;
|
|
3674
|
+
if (typeof data === "object" && data !== null && !Array.isArray(data)) {
|
|
3675
|
+
const obj = data;
|
|
3676
|
+
return Object.values(obj).some(
|
|
3677
|
+
(value) => value instanceof File || value instanceof Blob || value instanceof FormData
|
|
3678
|
+
);
|
|
3679
|
+
}
|
|
3680
|
+
return false;
|
|
3681
|
+
}
|
|
4153
3682
|
function getTransformRequest(contentType) {
|
|
4154
3683
|
if (!contentType) {
|
|
4155
3684
|
return (data) => Qs__default.default.stringify(data);
|
|
4156
3685
|
}
|
|
4157
3686
|
const normalizedType = contentType.toLowerCase().trim();
|
|
3687
|
+
if (normalizedType.startsWith("multipart/form-data")) {
|
|
3688
|
+
return void 0;
|
|
3689
|
+
}
|
|
4158
3690
|
if (normalizedType.startsWith("application/json")) {
|
|
4159
3691
|
return (data) => JSON.stringify(data);
|
|
4160
3692
|
}
|
|
@@ -4175,6 +3707,15 @@ var service = axios__default.default.create({
|
|
|
4175
3707
|
});
|
|
4176
3708
|
service.interceptors.request.use(
|
|
4177
3709
|
async (config) => {
|
|
3710
|
+
const isFileUploadRequest = isFileUpload(config.data);
|
|
3711
|
+
if (isFileUploadRequest) {
|
|
3712
|
+
if (config.headers) {
|
|
3713
|
+
delete config.headers["Content-Type"];
|
|
3714
|
+
delete config.headers["content-type"];
|
|
3715
|
+
}
|
|
3716
|
+
config.transformRequest = void 0;
|
|
3717
|
+
return config;
|
|
3718
|
+
}
|
|
4178
3719
|
const contentType = config.headers?.["Content-Type"] || config.headers?.["content-type"];
|
|
4179
3720
|
if (!config.transformRequest && contentType) {
|
|
4180
3721
|
const transformRequest = getTransformRequest(
|
|
@@ -4187,9 +3728,11 @@ service.interceptors.request.use(
|
|
|
4187
3728
|
config.transformRequest = (data) => Qs__default.default.stringify(data);
|
|
4188
3729
|
}
|
|
4189
3730
|
if (config.method === "post" || config.method === "put" || config.method === "patch") {
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
3731
|
+
if (config.data && typeof config.data === "object" && !isFileUploadRequest) {
|
|
3732
|
+
const params = { ...config.data || {} };
|
|
3733
|
+
filterEmptyKey(params, config.emptyParams ?? false);
|
|
3734
|
+
config.data = params;
|
|
3735
|
+
}
|
|
4193
3736
|
} else if (config.method === "get" || config.method === "delete") {
|
|
4194
3737
|
if (!config.disableTimestamp) {
|
|
4195
3738
|
config.params = {
|
|
@@ -4273,19 +3816,7 @@ service.interceptors.response.use(
|
|
|
4273
3816
|
if (status === 401) {
|
|
4274
3817
|
handle401Error(config, error);
|
|
4275
3818
|
}
|
|
4276
|
-
if (
|
|
4277
|
-
config._retryCount = config._retryCount || 0;
|
|
4278
|
-
if (config._retryCount < 2) {
|
|
4279
|
-
config._retryCount++;
|
|
4280
|
-
console.debug(
|
|
4281
|
-
`[request] aborted, retry attempt #${config._retryCount} -> ${config.url || ""}`
|
|
4282
|
-
);
|
|
4283
|
-
return new Promise((resolve) => setTimeout(resolve, 300)).then(
|
|
4284
|
-
() => service.request(config)
|
|
4285
|
-
);
|
|
4286
|
-
}
|
|
4287
|
-
}
|
|
4288
|
-
if (!config?.hidden && !isAborted) {
|
|
3819
|
+
if (!config?.hidden && !isAborted && status !== 401) {
|
|
4289
3820
|
let friendly = messageText;
|
|
4290
3821
|
if (status === 403) friendly = "No permission";
|
|
4291
3822
|
else if (status === 502) friendly = "System is upgrading or unavailable";
|
|
@@ -4311,7 +3842,7 @@ service.json = function(url, params, config) {
|
|
|
4311
3842
|
};
|
|
4312
3843
|
return service.post(
|
|
4313
3844
|
url,
|
|
4314
|
-
|
|
3845
|
+
params,
|
|
4315
3846
|
newConfig
|
|
4316
3847
|
);
|
|
4317
3848
|
};
|
|
@@ -4320,7 +3851,13 @@ service.put = function(url, params, config) {
|
|
|
4320
3851
|
headers: { "Content-Type": "application/json", ...config?.headers },
|
|
4321
3852
|
...config
|
|
4322
3853
|
};
|
|
4323
|
-
|
|
3854
|
+
const data = Array.isArray(params) ? params : params;
|
|
3855
|
+
return service.request({
|
|
3856
|
+
url,
|
|
3857
|
+
method: "put",
|
|
3858
|
+
data,
|
|
3859
|
+
...newConfig
|
|
3860
|
+
}).then((res) => res);
|
|
4324
3861
|
};
|
|
4325
3862
|
service.arrayGet = function(url, config) {
|
|
4326
3863
|
if (config) {
|
|
@@ -4349,48 +3886,6 @@ service.download = function(url, config = {}) {
|
|
|
4349
3886
|
return service.post(url, params, newConfig).then(() => void 0);
|
|
4350
3887
|
}
|
|
4351
3888
|
};
|
|
4352
|
-
function convertRes2Blob(response) {
|
|
4353
|
-
const contentDisposition = response.headers["content-disposition"];
|
|
4354
|
-
let fileName = "file.xlsx";
|
|
4355
|
-
if (contentDisposition) {
|
|
4356
|
-
const rfc5987Match = contentDisposition.match(/filename\*=UTF-8''([^;]+)/i);
|
|
4357
|
-
if (rfc5987Match) {
|
|
4358
|
-
fileName = decodeURIComponent(rfc5987Match[1]);
|
|
4359
|
-
} else {
|
|
4360
|
-
const standardMatch = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/i);
|
|
4361
|
-
if (standardMatch) {
|
|
4362
|
-
fileName = standardMatch[1];
|
|
4363
|
-
fileName = fileName.replace(/^['"]|['"]$/g, "");
|
|
4364
|
-
try {
|
|
4365
|
-
fileName = decodeURIComponent(fileName);
|
|
4366
|
-
} catch (e) {
|
|
4367
|
-
try {
|
|
4368
|
-
fileName = decodeURI(fileName);
|
|
4369
|
-
} catch (e2) {
|
|
4370
|
-
}
|
|
4371
|
-
}
|
|
4372
|
-
}
|
|
4373
|
-
}
|
|
4374
|
-
fileName = fileName.trim().replace(/^_+|_+$/g, "");
|
|
4375
|
-
}
|
|
4376
|
-
const blob = new Blob([response.data], { type: "application/vnd.ms-excel;charset=utf-8" });
|
|
4377
|
-
if (typeof window.navigator.msSaveBlob !== "undefined") {
|
|
4378
|
-
window.navigator.msSaveBlob(blob, fileName);
|
|
4379
|
-
} else {
|
|
4380
|
-
const blobURL = window.URL.createObjectURL(blob);
|
|
4381
|
-
const tempLink = document.createElement("a");
|
|
4382
|
-
tempLink.style.display = "none";
|
|
4383
|
-
tempLink.href = blobURL;
|
|
4384
|
-
tempLink.setAttribute("download", fileName);
|
|
4385
|
-
if (typeof tempLink.download === "undefined") {
|
|
4386
|
-
tempLink.setAttribute("target", "_blank");
|
|
4387
|
-
}
|
|
4388
|
-
document.body.appendChild(tempLink);
|
|
4389
|
-
tempLink.click();
|
|
4390
|
-
document.body.removeChild(tempLink);
|
|
4391
|
-
window.URL.revokeObjectURL(blobURL);
|
|
4392
|
-
}
|
|
4393
|
-
}
|
|
4394
3889
|
|
|
4395
3890
|
exports.$ = $;
|
|
4396
3891
|
exports.$$ = $$;
|
|
@@ -4401,6 +3896,7 @@ exports.Graph = Graph;
|
|
|
4401
3896
|
exports.LRUCache = LRUCache;
|
|
4402
3897
|
exports.LinkedList = LinkedList;
|
|
4403
3898
|
exports.Queue = Queue;
|
|
3899
|
+
exports.SecureStorage = SecureStorage;
|
|
4404
3900
|
exports.Stack = Stack;
|
|
4405
3901
|
exports.Tracker = Tracker;
|
|
4406
3902
|
exports.UploadStatus = UploadStatus;
|
|
@@ -4408,9 +3904,6 @@ exports.addClass = addClass;
|
|
|
4408
3904
|
exports.addDays = addDays;
|
|
4409
3905
|
exports.addMonths = addMonths;
|
|
4410
3906
|
exports.addYears = addYears;
|
|
4411
|
-
exports.aesGCMDecrypt = aesGCMDecrypt;
|
|
4412
|
-
exports.aesGCMEncrypt = aesGCMEncrypt;
|
|
4413
|
-
exports.base64Decode = base64Decode;
|
|
4414
3907
|
exports.base64Encode = base64Encode;
|
|
4415
3908
|
exports.batch = batch;
|
|
4416
3909
|
exports.binarySearch = binarySearch;
|
|
@@ -4424,32 +3917,29 @@ exports.ceil = ceil;
|
|
|
4424
3917
|
exports.checkOnline = checkOnline;
|
|
4425
3918
|
exports.chunk = chunk;
|
|
4426
3919
|
exports.clamp = clamp;
|
|
4427
|
-
exports.clearPersistedKeys = clearPersistedKeys;
|
|
4428
3920
|
exports.compact = compact;
|
|
4429
|
-
exports.computeHMAC = computeHMAC;
|
|
4430
3921
|
exports.contrast = contrast;
|
|
4431
|
-
exports.cookie = cookie;
|
|
4432
3922
|
exports.copyToClipboard = copyToClipboard;
|
|
4433
|
-
exports.createSecureStorage = createSecureStorage;
|
|
4434
3923
|
exports.createTracker = createTracker;
|
|
4435
3924
|
exports.createTranslator = createTranslator;
|
|
4436
3925
|
exports.createUploader = createUploader;
|
|
4437
3926
|
exports.csvToJson = csvToJson;
|
|
4438
3927
|
exports.darken = darken;
|
|
4439
3928
|
exports.debounce = debounce;
|
|
3929
|
+
exports.decryptJsonWithAES = decryptJsonWithAES;
|
|
3930
|
+
exports.decryptWithEnvPrivateKey = decryptWithEnvPrivateKey;
|
|
4440
3931
|
exports.deepClone = deepClone;
|
|
4441
3932
|
exports.deepMerge = deepMerge;
|
|
4442
3933
|
exports.defaults = defaults;
|
|
4443
|
-
exports.deriveKeyFromPassword = deriveKeyFromPassword;
|
|
4444
3934
|
exports.diffDays = diffDays;
|
|
4445
3935
|
exports.difference = difference;
|
|
4446
3936
|
exports.downloadFile = downloadFile;
|
|
4447
3937
|
exports.drop = drop;
|
|
4448
3938
|
exports.dropWhile = dropWhile;
|
|
3939
|
+
exports.encryptJsonWithAES = encryptJsonWithAES;
|
|
3940
|
+
exports.encryptWithEnvPublicKey = encryptWithEnvPublicKey;
|
|
4449
3941
|
exports.endOfDay = endOfDay;
|
|
4450
3942
|
exports.escapeHtml = escapeHtml;
|
|
4451
|
-
exports.exportPrivateKey = exportPrivateKey;
|
|
4452
|
-
exports.exportPublicKey = exportPublicKey;
|
|
4453
3943
|
exports.factorial = factorial;
|
|
4454
3944
|
exports.fetchWithRetry = fetchWithRetry;
|
|
4455
3945
|
exports.fetchWithTimeout = fetchWithTimeout;
|
|
@@ -4469,35 +3959,29 @@ exports.formatNumber = formatNumber;
|
|
|
4469
3959
|
exports.formatNumberI18n = formatNumberI18n;
|
|
4470
3960
|
exports.formatRelativeTime = formatRelativeTime;
|
|
4471
3961
|
exports.gcd = gcd;
|
|
4472
|
-
exports.generateHMACKey = generateHMACKey;
|
|
4473
3962
|
exports.generateRSAKeyPair = generateRSAKeyPair;
|
|
4474
|
-
exports.
|
|
4475
|
-
exports.generateUUID = generateUUID;
|
|
3963
|
+
exports.generateRandomAESKeyString = generateRandomAESKeyString;
|
|
4476
3964
|
exports.get = get;
|
|
4477
3965
|
exports.getDateFormatByGMT = getDateFormatByGMT;
|
|
4478
3966
|
exports.getElementOffset = getElementOffset;
|
|
3967
|
+
exports.getEnvPrivateKey = getEnvPrivateKey;
|
|
3968
|
+
exports.getEnvPublicKey = getEnvPublicKey;
|
|
4479
3969
|
exports.getFileExtension = getFileExtension;
|
|
4480
3970
|
exports.getFileNameWithoutExtension = getFileNameWithoutExtension;
|
|
4481
|
-
exports.getKeyUsageCount = getKeyUsageCount;
|
|
4482
3971
|
exports.getLocale = getLocale;
|
|
4483
3972
|
exports.getQuarter = getQuarter;
|
|
4484
3973
|
exports.getQueryParams = getQueryParams;
|
|
4485
3974
|
exports.getRelativeTime = getRelativeTime;
|
|
4486
3975
|
exports.getScrollPosition = getScrollPosition;
|
|
4487
|
-
exports.getStorageKeyPair = getStorageKeyPair;
|
|
4488
3976
|
exports.getStyle = getStyle;
|
|
4489
3977
|
exports.getTimeFromGMT = getTimeFromGMT;
|
|
4490
3978
|
exports.getTracker = getTracker;
|
|
4491
3979
|
exports.getWeekNumber = getWeekNumber;
|
|
4492
3980
|
exports.groupBy = groupBy;
|
|
4493
|
-
exports.hash = hash;
|
|
4494
3981
|
exports.hexToRgb = hexToRgb;
|
|
4495
3982
|
exports.highlight = highlight;
|
|
4496
3983
|
exports.hslToRgb = hslToRgb;
|
|
4497
|
-
exports.importPrivateKey = importPrivateKey;
|
|
4498
|
-
exports.importPublicKey = importPublicKey;
|
|
4499
3984
|
exports.initTracker = initTracker;
|
|
4500
|
-
exports.initializeStorageKeys = initializeStorageKeys;
|
|
4501
3985
|
exports.intersection = intersection;
|
|
4502
3986
|
exports.invert = invert;
|
|
4503
3987
|
exports.isAbsoluteUrl = isAbsoluteUrl;
|
|
@@ -4557,7 +4041,6 @@ exports.removeAccents = removeAccents;
|
|
|
4557
4041
|
exports.removeClass = removeClass;
|
|
4558
4042
|
exports.removeQueryParams = removeQueryParams;
|
|
4559
4043
|
exports.request = request;
|
|
4560
|
-
exports.resetKeyUsageCount = resetKeyUsageCount;
|
|
4561
4044
|
exports.retry = retry;
|
|
4562
4045
|
exports.rgbToHex = rgbToHex;
|
|
4563
4046
|
exports.rgbToHsl = rgbToHsl;
|
|
@@ -4571,10 +4054,8 @@ exports.set = set;
|
|
|
4571
4054
|
exports.setCommonParams = setCommonParams;
|
|
4572
4055
|
exports.setQueryParams = setQueryParams;
|
|
4573
4056
|
exports.setServiceEventHandlers = setServiceEventHandlers;
|
|
4574
|
-
exports.setStorageKeyPair = setStorageKeyPair;
|
|
4575
4057
|
exports.setStyle = setStyle;
|
|
4576
4058
|
exports.setUserInfo = setUserInfo;
|
|
4577
|
-
exports.sha256 = sha256;
|
|
4578
4059
|
exports.shuffle = shuffle;
|
|
4579
4060
|
exports.slugify = slugify;
|
|
4580
4061
|
exports.snakeCase = snakeCase;
|
|
@@ -4602,7 +4083,6 @@ exports.unzip = unzip;
|
|
|
4602
4083
|
exports.updateQueryParams = updateQueryParams;
|
|
4603
4084
|
exports.uploadFile = uploadFile;
|
|
4604
4085
|
exports.values = values;
|
|
4605
|
-
exports.verifyHMAC = verifyHMAC;
|
|
4606
4086
|
exports.xmlToJson = xmlToJson;
|
|
4607
4087
|
exports.yamlToJson = yamlToJson;
|
|
4608
4088
|
exports.zip = zip;
|