@korajs/auth 0.3.2 → 0.4.0
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/{chunk-FSU4SK32.js → chunk-IO2MCCG2.js} +35 -40
- package/dist/chunk-IO2MCCG2.js.map +1 -0
- package/dist/{chunk-HOZXDR6Y.js → chunk-L7GXPS74.js} +3 -9
- package/dist/chunk-L7GXPS74.js.map +1 -0
- package/dist/index.cjs +102 -122
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +69 -84
- package/dist/index.js.map +1 -1
- package/dist/{password-hash-HDH6VQCQ.js → password-hash-QRBG6BNJ.js} +2 -2
- package/dist/react.cjs.map +1 -1
- package/dist/react.js.map +1 -1
- package/dist/server.cjs +1486 -1519
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1332 -1356
- package/dist/server.js.map +1 -1
- package/package.json +5 -5
- package/dist/chunk-FSU4SK32.js.map +0 -1
- package/dist/chunk-HOZXDR6Y.js.map +0 -1
- /package/dist/{password-hash-HDH6VQCQ.js.map → password-hash-QRBG6BNJ.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -86,18 +86,18 @@ function decodeJwtPayload(token) {
|
|
|
86
86
|
}
|
|
87
87
|
function isTokenExpired(token) {
|
|
88
88
|
const payload = decodeJwtPayload(token);
|
|
89
|
-
if (!payload || typeof payload
|
|
89
|
+
if (!payload || typeof payload.exp !== "number") {
|
|
90
90
|
return true;
|
|
91
91
|
}
|
|
92
92
|
const nowSeconds = Math.floor(Date.now() / 1e3);
|
|
93
|
-
return payload
|
|
93
|
+
return payload.exp <= nowSeconds + EXPIRY_BUFFER_SECONDS;
|
|
94
94
|
}
|
|
95
95
|
function getUserIdFromToken(token) {
|
|
96
96
|
const payload = decodeJwtPayload(token);
|
|
97
|
-
if (!payload || typeof payload
|
|
97
|
+
if (!payload || typeof payload.sub !== "string") {
|
|
98
98
|
return null;
|
|
99
99
|
}
|
|
100
|
-
return payload
|
|
100
|
+
return payload.sub;
|
|
101
101
|
}
|
|
102
102
|
function createTokenStorage(prefix) {
|
|
103
103
|
let useLocalStorage = false;
|
|
@@ -445,7 +445,7 @@ var AuthClient = class {
|
|
|
445
445
|
headers["Content-Type"] = "application/json";
|
|
446
446
|
}
|
|
447
447
|
if (options.token) {
|
|
448
|
-
headers
|
|
448
|
+
headers.Authorization = `Bearer ${options.token}`;
|
|
449
449
|
}
|
|
450
450
|
let response;
|
|
451
451
|
try {
|
|
@@ -466,23 +466,23 @@ var AuthClient = class {
|
|
|
466
466
|
let serverError;
|
|
467
467
|
try {
|
|
468
468
|
const body = await response.json();
|
|
469
|
-
if (typeof body
|
|
470
|
-
errorMessage = body
|
|
469
|
+
if (typeof body.error === "string") {
|
|
470
|
+
errorMessage = body.error;
|
|
471
471
|
serverError = errorMessage;
|
|
472
|
-
} else if (typeof body
|
|
473
|
-
errorMessage = body
|
|
472
|
+
} else if (typeof body.message === "string") {
|
|
473
|
+
errorMessage = body.message;
|
|
474
474
|
serverError = errorMessage;
|
|
475
475
|
}
|
|
476
476
|
} catch {
|
|
477
477
|
}
|
|
478
|
-
throw new AuthError(
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
);
|
|
478
|
+
throw new AuthError(errorMessage, "AUTH_SERVER_ERROR", {
|
|
479
|
+
path,
|
|
480
|
+
status: response.status,
|
|
481
|
+
serverError
|
|
482
|
+
});
|
|
483
483
|
}
|
|
484
484
|
const json = await response.json();
|
|
485
|
-
const data = json
|
|
485
|
+
const data = json.data !== void 0 ? json.data : json;
|
|
486
486
|
return data;
|
|
487
487
|
}
|
|
488
488
|
};
|
|
@@ -668,10 +668,9 @@ var OrgClient = class {
|
|
|
668
668
|
* List pending invitations for the current user's email.
|
|
669
669
|
*/
|
|
670
670
|
async listMyInvitations(email) {
|
|
671
|
-
return this.request(
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
);
|
|
671
|
+
return this.request(`/invitations?email=${encodeURIComponent(email)}`, {
|
|
672
|
+
method: "GET"
|
|
673
|
+
});
|
|
675
674
|
}
|
|
676
675
|
// --- Subscriptions ---
|
|
677
676
|
/**
|
|
@@ -716,18 +715,17 @@ var OrgClient = class {
|
|
|
716
715
|
body: options.body ? JSON.stringify(options.body) : void 0
|
|
717
716
|
});
|
|
718
717
|
} catch (cause) {
|
|
719
|
-
throw new OrgClientError(
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
);
|
|
718
|
+
throw new OrgClientError(`Network request to ${path} failed.`, "ORG_NETWORK_ERROR", {
|
|
719
|
+
path,
|
|
720
|
+
cause: cause instanceof Error ? cause.message : String(cause)
|
|
721
|
+
});
|
|
724
722
|
}
|
|
725
723
|
if (!response.ok) {
|
|
726
724
|
let errorMessage = `Server returned HTTP ${response.status}`;
|
|
727
725
|
try {
|
|
728
726
|
const body = await response.json();
|
|
729
|
-
if (typeof body
|
|
730
|
-
errorMessage = body
|
|
727
|
+
if (typeof body.error === "string") {
|
|
728
|
+
errorMessage = body.error;
|
|
731
729
|
}
|
|
732
730
|
} catch {
|
|
733
731
|
}
|
|
@@ -905,10 +903,9 @@ async function computePublicKeyThumbprint(publicKeyJwk) {
|
|
|
905
903
|
const hashBuffer = await globalThis.crypto.subtle.digest("SHA-256", encoded);
|
|
906
904
|
return toBase64Url(hashBuffer);
|
|
907
905
|
} catch (cause) {
|
|
908
|
-
throw new DeviceIdentityError(
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
);
|
|
906
|
+
throw new DeviceIdentityError("Failed to compute SHA-256 thumbprint of the public key JWK.", {
|
|
907
|
+
cause: cause instanceof Error ? cause.message : String(cause)
|
|
908
|
+
});
|
|
912
909
|
}
|
|
913
910
|
}
|
|
914
911
|
|
|
@@ -962,10 +959,9 @@ var IndexedDBDeviceKeyStore = class {
|
|
|
962
959
|
request.onerror = () => {
|
|
963
960
|
this.dbPromise = null;
|
|
964
961
|
reject(
|
|
965
|
-
new DeviceKeyStoreError(
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
)
|
|
962
|
+
new DeviceKeyStoreError("Failed to open IndexedDB database for device key storage.", {
|
|
963
|
+
error: request.error?.message
|
|
964
|
+
})
|
|
969
965
|
);
|
|
970
966
|
};
|
|
971
967
|
request.onblocked = () => {
|
|
@@ -992,21 +988,18 @@ var IndexedDBDeviceKeyStore = class {
|
|
|
992
988
|
};
|
|
993
989
|
tx.onerror = () => {
|
|
994
990
|
reject(
|
|
995
|
-
new DeviceKeyStoreError(
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
)
|
|
991
|
+
new DeviceKeyStoreError(`Failed to save key pair for device "${deviceId}".`, {
|
|
992
|
+
deviceId,
|
|
993
|
+
error: tx.error?.message
|
|
994
|
+
})
|
|
999
995
|
);
|
|
1000
996
|
};
|
|
1001
997
|
} catch (cause) {
|
|
1002
998
|
reject(
|
|
1003
|
-
new DeviceKeyStoreError(
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
cause: cause instanceof Error ? cause.message : String(cause)
|
|
1008
|
-
}
|
|
1009
|
-
)
|
|
999
|
+
new DeviceKeyStoreError(`Failed to save key pair for device "${deviceId}".`, {
|
|
1000
|
+
deviceId,
|
|
1001
|
+
cause: cause instanceof Error ? cause.message : String(cause)
|
|
1002
|
+
})
|
|
1010
1003
|
);
|
|
1011
1004
|
}
|
|
1012
1005
|
});
|
|
@@ -1025,21 +1018,18 @@ var IndexedDBDeviceKeyStore = class {
|
|
|
1025
1018
|
};
|
|
1026
1019
|
request.onerror = () => {
|
|
1027
1020
|
reject(
|
|
1028
|
-
new DeviceKeyStoreError(
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
)
|
|
1021
|
+
new DeviceKeyStoreError(`Failed to load key pair for device "${deviceId}".`, {
|
|
1022
|
+
deviceId,
|
|
1023
|
+
error: request.error?.message
|
|
1024
|
+
})
|
|
1032
1025
|
);
|
|
1033
1026
|
};
|
|
1034
1027
|
} catch (cause) {
|
|
1035
1028
|
reject(
|
|
1036
|
-
new DeviceKeyStoreError(
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
cause: cause instanceof Error ? cause.message : String(cause)
|
|
1041
|
-
}
|
|
1042
|
-
)
|
|
1029
|
+
new DeviceKeyStoreError(`Failed to load key pair for device "${deviceId}".`, {
|
|
1030
|
+
deviceId,
|
|
1031
|
+
cause: cause instanceof Error ? cause.message : String(cause)
|
|
1032
|
+
})
|
|
1043
1033
|
);
|
|
1044
1034
|
}
|
|
1045
1035
|
});
|
|
@@ -1057,21 +1047,18 @@ var IndexedDBDeviceKeyStore = class {
|
|
|
1057
1047
|
};
|
|
1058
1048
|
tx.onerror = () => {
|
|
1059
1049
|
reject(
|
|
1060
|
-
new DeviceKeyStoreError(
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
)
|
|
1050
|
+
new DeviceKeyStoreError(`Failed to delete key pair for device "${deviceId}".`, {
|
|
1051
|
+
deviceId,
|
|
1052
|
+
error: tx.error?.message
|
|
1053
|
+
})
|
|
1064
1054
|
);
|
|
1065
1055
|
};
|
|
1066
1056
|
} catch (cause) {
|
|
1067
1057
|
reject(
|
|
1068
|
-
new DeviceKeyStoreError(
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
cause: cause instanceof Error ? cause.message : String(cause)
|
|
1073
|
-
}
|
|
1074
|
-
)
|
|
1058
|
+
new DeviceKeyStoreError(`Failed to delete key pair for device "${deviceId}".`, {
|
|
1059
|
+
deviceId,
|
|
1060
|
+
cause: cause instanceof Error ? cause.message : String(cause)
|
|
1061
|
+
})
|
|
1075
1062
|
);
|
|
1076
1063
|
}
|
|
1077
1064
|
});
|
|
@@ -1097,13 +1084,10 @@ var IndexedDBDeviceKeyStore = class {
|
|
|
1097
1084
|
};
|
|
1098
1085
|
} catch (cause) {
|
|
1099
1086
|
reject(
|
|
1100
|
-
new DeviceKeyStoreError(
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
cause: cause instanceof Error ? cause.message : String(cause)
|
|
1105
|
-
}
|
|
1106
|
-
)
|
|
1087
|
+
new DeviceKeyStoreError(`Failed to check if key pair exists for device "${deviceId}".`, {
|
|
1088
|
+
deviceId,
|
|
1089
|
+
cause: cause instanceof Error ? cause.message : String(cause)
|
|
1090
|
+
})
|
|
1107
1091
|
);
|
|
1108
1092
|
}
|
|
1109
1093
|
});
|
|
@@ -1210,15 +1194,15 @@ var TokenStore = class {
|
|
|
1210
1194
|
return null;
|
|
1211
1195
|
}
|
|
1212
1196
|
const record = parsed;
|
|
1213
|
-
if (typeof record
|
|
1197
|
+
if (typeof record.accessToken !== "string" || typeof record.refreshToken !== "string") {
|
|
1214
1198
|
return null;
|
|
1215
1199
|
}
|
|
1216
1200
|
const tokens = {
|
|
1217
|
-
accessToken: record
|
|
1218
|
-
refreshToken: record
|
|
1201
|
+
accessToken: record.accessToken,
|
|
1202
|
+
refreshToken: record.refreshToken
|
|
1219
1203
|
};
|
|
1220
|
-
if (typeof record
|
|
1221
|
-
tokens.deviceCredential = record
|
|
1204
|
+
if (typeof record.deviceCredential === "string") {
|
|
1205
|
+
tokens.deviceCredential = record.deviceCredential;
|
|
1222
1206
|
}
|
|
1223
1207
|
return tokens;
|
|
1224
1208
|
} catch {
|
|
@@ -1507,11 +1491,11 @@ var EncryptedTokenStore = class {
|
|
|
1507
1491
|
return null;
|
|
1508
1492
|
}
|
|
1509
1493
|
const record = parsed;
|
|
1510
|
-
if (typeof record
|
|
1494
|
+
if (typeof record.iv !== "string" || typeof record.data !== "string") {
|
|
1511
1495
|
return null;
|
|
1512
1496
|
}
|
|
1513
|
-
const iv = fromBase64Url2(record
|
|
1514
|
-
const ciphertext = fromBase64Url2(record
|
|
1497
|
+
const iv = fromBase64Url2(record.iv);
|
|
1498
|
+
const ciphertext = fromBase64Url2(record.data);
|
|
1515
1499
|
const plaintextBytes = await decryptData(this.key, ciphertext, iv);
|
|
1516
1500
|
const json = new TextDecoder().decode(plaintextBytes);
|
|
1517
1501
|
const tokenData = JSON.parse(json);
|
|
@@ -1519,15 +1503,15 @@ var EncryptedTokenStore = class {
|
|
|
1519
1503
|
return null;
|
|
1520
1504
|
}
|
|
1521
1505
|
const tokenRecord = tokenData;
|
|
1522
|
-
if (typeof tokenRecord
|
|
1506
|
+
if (typeof tokenRecord.accessToken !== "string" || typeof tokenRecord.refreshToken !== "string") {
|
|
1523
1507
|
return null;
|
|
1524
1508
|
}
|
|
1525
1509
|
const tokens = {
|
|
1526
|
-
accessToken: tokenRecord
|
|
1527
|
-
refreshToken: tokenRecord
|
|
1510
|
+
accessToken: tokenRecord.accessToken,
|
|
1511
|
+
refreshToken: tokenRecord.refreshToken
|
|
1528
1512
|
};
|
|
1529
|
-
if (typeof tokenRecord
|
|
1530
|
-
tokens.deviceCredential = tokenRecord
|
|
1513
|
+
if (typeof tokenRecord.deviceCredential === "string") {
|
|
1514
|
+
tokens.deviceCredential = tokenRecord.deviceCredential;
|
|
1531
1515
|
}
|
|
1532
1516
|
return tokens;
|
|
1533
1517
|
} catch {
|
|
@@ -1753,9 +1737,7 @@ function extractPublicKeyFromAttestationObject(attestationObject) {
|
|
|
1753
1737
|
const topMap = decoded.value;
|
|
1754
1738
|
const authData = topMap.get("authData");
|
|
1755
1739
|
if (!(authData instanceof Uint8Array)) {
|
|
1756
|
-
throw new PasskeyError(
|
|
1757
|
-
"Invalid attestation object: authData is missing or not a byte string."
|
|
1758
|
-
);
|
|
1740
|
+
throw new PasskeyError("Invalid attestation object: authData is missing or not a byte string.");
|
|
1759
1741
|
}
|
|
1760
1742
|
let offset = 0;
|
|
1761
1743
|
offset += 32;
|
|
@@ -1777,53 +1759,54 @@ function extractPublicKeyFromAttestationObject(attestationObject) {
|
|
|
1777
1759
|
return authData.slice(offset, offset + coseKeyLength);
|
|
1778
1760
|
}
|
|
1779
1761
|
function decodeCbor(data, offset) {
|
|
1780
|
-
|
|
1762
|
+
let pos = offset;
|
|
1763
|
+
if (pos >= data.length) {
|
|
1781
1764
|
throw new PasskeyError("CBOR decode error: unexpected end of data.");
|
|
1782
1765
|
}
|
|
1783
|
-
const initialByte = data[
|
|
1766
|
+
const initialByte = data[pos];
|
|
1784
1767
|
const majorType = initialByte >> 5;
|
|
1785
1768
|
const additionalInfo = initialByte & 31;
|
|
1786
|
-
|
|
1769
|
+
pos += 1;
|
|
1787
1770
|
let argument;
|
|
1788
1771
|
if (additionalInfo < 24) {
|
|
1789
1772
|
argument = additionalInfo;
|
|
1790
1773
|
} else if (additionalInfo === 24) {
|
|
1791
|
-
argument = data[
|
|
1792
|
-
|
|
1774
|
+
argument = data[pos];
|
|
1775
|
+
pos += 1;
|
|
1793
1776
|
} else if (additionalInfo === 25) {
|
|
1794
|
-
argument = data[
|
|
1795
|
-
|
|
1777
|
+
argument = data[pos] << 8 | data[pos + 1];
|
|
1778
|
+
pos += 2;
|
|
1796
1779
|
} else if (additionalInfo === 26) {
|
|
1797
|
-
argument = data[
|
|
1780
|
+
argument = data[pos] << 24 | data[pos + 1] << 16 | data[pos + 2] << 8 | data[pos + 3];
|
|
1798
1781
|
argument = argument >>> 0;
|
|
1799
|
-
|
|
1782
|
+
pos += 4;
|
|
1800
1783
|
} else {
|
|
1801
1784
|
throw new PasskeyError(
|
|
1802
|
-
`CBOR decode error: unsupported additional info ${additionalInfo} at byte ${
|
|
1785
|
+
`CBOR decode error: unsupported additional info ${additionalInfo} at byte ${pos - 1}. This CBOR decoder only supports definite-length encodings.`
|
|
1803
1786
|
);
|
|
1804
1787
|
}
|
|
1805
1788
|
switch (majorType) {
|
|
1806
1789
|
// Major type 0: Unsigned integer
|
|
1807
1790
|
case 0:
|
|
1808
|
-
return { value: argument, offset };
|
|
1791
|
+
return { value: argument, offset: pos };
|
|
1809
1792
|
// Major type 1: Negative integer (-1 - argument)
|
|
1810
1793
|
case 1:
|
|
1811
|
-
return { value: -1 - argument, offset };
|
|
1794
|
+
return { value: -1 - argument, offset: pos };
|
|
1812
1795
|
// Major type 2: Byte string
|
|
1813
1796
|
case 2: {
|
|
1814
|
-
const bytes = data.slice(
|
|
1815
|
-
return { value: bytes, offset:
|
|
1797
|
+
const bytes = data.slice(pos, pos + argument);
|
|
1798
|
+
return { value: bytes, offset: pos + argument };
|
|
1816
1799
|
}
|
|
1817
1800
|
// Major type 3: Text string (UTF-8)
|
|
1818
1801
|
case 3: {
|
|
1819
|
-
const textBytes = data.slice(
|
|
1802
|
+
const textBytes = data.slice(pos, pos + argument);
|
|
1820
1803
|
const text = new TextDecoder().decode(textBytes);
|
|
1821
|
-
return { value: text, offset:
|
|
1804
|
+
return { value: text, offset: pos + argument };
|
|
1822
1805
|
}
|
|
1823
1806
|
// Major type 4: Array
|
|
1824
1807
|
case 4: {
|
|
1825
1808
|
const arr = [];
|
|
1826
|
-
let currentOffset =
|
|
1809
|
+
let currentOffset = pos;
|
|
1827
1810
|
for (let i = 0; i < argument; i++) {
|
|
1828
1811
|
const item = decodeCbor(data, currentOffset);
|
|
1829
1812
|
arr.push(item.value);
|
|
@@ -1834,7 +1817,7 @@ function decodeCbor(data, offset) {
|
|
|
1834
1817
|
// Major type 5: Map
|
|
1835
1818
|
case 5: {
|
|
1836
1819
|
const map = /* @__PURE__ */ new Map();
|
|
1837
|
-
let currentOffset =
|
|
1820
|
+
let currentOffset = pos;
|
|
1838
1821
|
for (let i = 0; i < argument; i++) {
|
|
1839
1822
|
const keyResult = decodeCbor(data, currentOffset);
|
|
1840
1823
|
const valResult = decodeCbor(data, keyResult.offset);
|
|
@@ -1845,7 +1828,7 @@ function decodeCbor(data, offset) {
|
|
|
1845
1828
|
}
|
|
1846
1829
|
default:
|
|
1847
1830
|
throw new PasskeyError(
|
|
1848
|
-
`CBOR decode error: unsupported major type ${majorType} at byte ${
|
|
1831
|
+
`CBOR decode error: unsupported major type ${majorType} at byte ${pos - 1}. This CBOR decoder only supports types 0-5 (integers, byte/text strings, arrays, maps).`
|
|
1849
1832
|
);
|
|
1850
1833
|
}
|
|
1851
1834
|
}
|
|
@@ -2170,14 +2153,11 @@ var OperationEncryptor = class {
|
|
|
2170
2153
|
if (cause instanceof OperationEncryptionError) {
|
|
2171
2154
|
throw cause;
|
|
2172
2155
|
}
|
|
2173
|
-
throw new OperationEncryptionError(
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
cause: cause instanceof Error ? cause.message : String(cause)
|
|
2179
|
-
}
|
|
2180
|
-
);
|
|
2156
|
+
throw new OperationEncryptionError(`Failed to encrypt operation ${fieldName} field.`, {
|
|
2157
|
+
operationId,
|
|
2158
|
+
fieldName,
|
|
2159
|
+
cause: cause instanceof Error ? cause.message : String(cause)
|
|
2160
|
+
});
|
|
2181
2161
|
}
|
|
2182
2162
|
}
|
|
2183
2163
|
async decryptField(field, operationId, fieldName) {
|
|
@@ -2201,10 +2181,10 @@ var OperationEncryptor = class {
|
|
|
2201
2181
|
const json = new TextDecoder().decode(plaintextBytes);
|
|
2202
2182
|
const parsed = JSON.parse(json);
|
|
2203
2183
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
2204
|
-
throw new OperationEncryptionError(
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
);
|
|
2184
|
+
throw new OperationEncryptionError(`Decrypted ${fieldName} is not a valid record object.`, {
|
|
2185
|
+
operationId,
|
|
2186
|
+
fieldName
|
|
2187
|
+
});
|
|
2208
2188
|
}
|
|
2209
2189
|
return parsed;
|
|
2210
2190
|
} catch (cause) {
|
|
@@ -2229,7 +2209,7 @@ function isEncryptedEnvelope(field) {
|
|
|
2229
2209
|
if (field === null || typeof field !== "object") {
|
|
2230
2210
|
return false;
|
|
2231
2211
|
}
|
|
2232
|
-
return field[ENCRYPTED_MARKER] === true && typeof field
|
|
2212
|
+
return field[ENCRYPTED_MARKER] === true && typeof field.ciphertext === "string" && typeof field.iv === "string" && field.algorithm === "AES-256-GCM";
|
|
2233
2213
|
}
|
|
2234
2214
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2235
2215
|
0 && (module.exports = {
|