@stackframe/stack-shared 2.8.35 → 2.8.39
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/CHANGELOG.md +24 -0
- package/dist/config/schema.d.mts +240 -104
- package/dist/config/schema.d.ts +240 -104
- package/dist/config/schema.js +61 -27
- package/dist/config/schema.js.map +1 -1
- package/dist/esm/config/schema.js +61 -27
- package/dist/esm/config/schema.js.map +1 -1
- package/dist/esm/helpers/vault/client-side.js +46 -0
- package/dist/esm/helpers/vault/client-side.js.map +1 -0
- package/dist/esm/helpers/vault/server-side.js +96 -0
- package/dist/esm/helpers/vault/server-side.js.map +1 -0
- package/dist/esm/hooks/use-hover.js +71 -0
- package/dist/esm/hooks/use-hover.js.map +1 -0
- package/dist/esm/interface/admin-interface.js +74 -5
- package/dist/esm/interface/admin-interface.js.map +1 -1
- package/dist/esm/interface/client-interface.js +29 -26
- package/dist/esm/interface/client-interface.js.map +1 -1
- package/dist/esm/interface/crud/oauth-providers.js +2 -0
- package/dist/esm/interface/crud/oauth-providers.js.map +1 -1
- package/dist/esm/interface/crud/transactions.js +21 -0
- package/dist/esm/interface/crud/transactions.js.map +1 -0
- package/dist/esm/interface/server-interface.js +43 -7
- package/dist/esm/interface/server-interface.js.map +1 -1
- package/dist/esm/known-errors.js +58 -1
- package/dist/esm/known-errors.js.map +1 -1
- package/dist/esm/schema-fields.js +6 -3
- package/dist/esm/schema-fields.js.map +1 -1
- package/dist/esm/utils/arrays.js +25 -0
- package/dist/esm/utils/arrays.js.map +1 -1
- package/dist/esm/utils/bytes.js +1 -2
- package/dist/esm/utils/bytes.js.map +1 -1
- package/dist/esm/utils/crypto.js +83 -2
- package/dist/esm/utils/crypto.js.map +1 -1
- package/dist/esm/utils/esbuild.js +63 -6
- package/dist/esm/utils/esbuild.js.map +1 -1
- package/dist/esm/utils/jwt.js +16 -1
- package/dist/esm/utils/jwt.js.map +1 -1
- package/dist/esm/utils/numbers.js +14 -9
- package/dist/esm/utils/numbers.js.map +1 -1
- package/dist/esm/utils/promises.js +2 -1
- package/dist/esm/utils/promises.js.map +1 -1
- package/dist/esm/utils/react.js +7 -3
- package/dist/esm/utils/react.js.map +1 -1
- package/dist/esm/utils/types.js.map +1 -1
- package/dist/helpers/password.d.mts +4 -4
- package/dist/helpers/password.d.ts +4 -4
- package/dist/helpers/vault/client-side.d.mts +14 -0
- package/dist/helpers/vault/client-side.d.ts +14 -0
- package/dist/helpers/vault/client-side.js +73 -0
- package/dist/helpers/vault/client-side.js.map +1 -0
- package/dist/helpers/vault/server-side.d.mts +7 -0
- package/dist/helpers/vault/server-side.d.ts +7 -0
- package/dist/helpers/vault/server-side.js +115 -0
- package/dist/helpers/vault/server-side.js.map +1 -0
- package/dist/hooks/use-hover.d.mts +6 -0
- package/dist/hooks/use-hover.d.ts +6 -0
- package/dist/hooks/use-hover.js +96 -0
- package/dist/hooks/use-hover.js.map +1 -0
- package/dist/index.d.mts +6 -4
- package/dist/index.d.ts +6 -4
- package/dist/interface/admin-interface.d.mts +45 -8
- package/dist/interface/admin-interface.d.ts +45 -8
- package/dist/interface/admin-interface.js +74 -5
- package/dist/interface/admin-interface.js.map +1 -1
- package/dist/interface/client-interface.d.mts +5 -21
- package/dist/interface/client-interface.d.ts +5 -21
- package/dist/interface/client-interface.js +29 -26
- package/dist/interface/client-interface.js.map +1 -1
- package/dist/interface/crud/current-user.d.mts +1 -1
- package/dist/interface/crud/current-user.d.ts +1 -1
- package/dist/interface/crud/oauth-providers.d.mts +8 -0
- package/dist/interface/crud/oauth-providers.d.ts +8 -0
- package/dist/interface/crud/oauth-providers.js +1 -0
- package/dist/interface/crud/oauth-providers.js.map +1 -1
- package/dist/interface/crud/project-api-keys.d.mts +4 -4
- package/dist/interface/crud/project-api-keys.d.ts +4 -4
- package/dist/interface/crud/team-member-profiles.d.mts +2 -2
- package/dist/interface/crud/team-member-profiles.d.ts +2 -2
- package/dist/interface/crud/transactions.d.mts +56 -0
- package/dist/interface/crud/transactions.d.ts +56 -0
- package/dist/interface/crud/transactions.js +46 -0
- package/dist/interface/crud/transactions.js.map +1 -0
- package/dist/interface/crud/users.d.mts +2 -2
- package/dist/interface/crud/users.d.ts +2 -2
- package/dist/interface/server-interface.d.mts +10 -43
- package/dist/interface/server-interface.d.ts +10 -43
- package/dist/interface/server-interface.js +43 -7
- package/dist/interface/server-interface.js.map +1 -1
- package/dist/known-errors.d.mts +15 -0
- package/dist/known-errors.d.ts +15 -0
- package/dist/known-errors.js +58 -1
- package/dist/known-errors.js.map +1 -1
- package/dist/schema-fields.d.mts +4 -3
- package/dist/schema-fields.d.ts +4 -3
- package/dist/schema-fields.js +7 -3
- package/dist/schema-fields.js.map +1 -1
- package/dist/utils/arrays.d.mts +6 -1
- package/dist/utils/arrays.d.ts +6 -1
- package/dist/utils/arrays.js +30 -0
- package/dist/utils/arrays.js.map +1 -1
- package/dist/utils/bytes.js +1 -2
- package/dist/utils/bytes.js.map +1 -1
- package/dist/utils/crypto.d.mts +31 -1
- package/dist/utils/crypto.d.ts +31 -1
- package/dist/utils/crypto.js +87 -2
- package/dist/utils/crypto.js.map +1 -1
- package/dist/utils/esbuild.d.mts +1 -0
- package/dist/utils/esbuild.d.ts +1 -0
- package/dist/utils/esbuild.js +63 -6
- package/dist/utils/esbuild.js.map +1 -1
- package/dist/utils/jwt.d.mts +34 -1
- package/dist/utils/jwt.d.ts +34 -1
- package/dist/utils/jwt.js +16 -0
- package/dist/utils/jwt.js.map +1 -1
- package/dist/utils/numbers.js +14 -9
- package/dist/utils/numbers.js.map +1 -1
- package/dist/utils/promises.d.mts +1 -1
- package/dist/utils/promises.d.ts +1 -1
- package/dist/utils/promises.js +2 -1
- package/dist/utils/promises.js.map +1 -1
- package/dist/utils/react.js +7 -3
- package/dist/utils/react.js.map +1 -1
- package/dist/utils/types.d.mts +10 -1
- package/dist/utils/types.d.ts +10 -1
- package/dist/utils/types.js.map +1 -1
- package/package.json +4 -2
package/dist/utils/crypto.d.mts
CHANGED
|
@@ -4,5 +4,35 @@ declare function generateRandomValues(array: Uint8Array): typeof array;
|
|
|
4
4
|
* random number generator.
|
|
5
5
|
*/
|
|
6
6
|
declare function generateSecureRandomString(minBitsOfEntropy?: number): string;
|
|
7
|
+
declare function encrypt({ purpose, secret, value }: {
|
|
8
|
+
purpose: string;
|
|
9
|
+
secret: string | Uint8Array;
|
|
10
|
+
value: Uint8Array;
|
|
11
|
+
}): Promise<Uint8Array>;
|
|
12
|
+
declare function decrypt({ purpose, secret, cipher }: {
|
|
13
|
+
purpose: string;
|
|
14
|
+
secret: string | Uint8Array;
|
|
15
|
+
cipher: Uint8Array;
|
|
16
|
+
}): Promise<({
|
|
17
|
+
status: "ok";
|
|
18
|
+
data: Uint8Array;
|
|
19
|
+
} & {
|
|
20
|
+
status: "ok";
|
|
21
|
+
}) | ({
|
|
22
|
+
status: "error";
|
|
23
|
+
error: Error;
|
|
24
|
+
} & {
|
|
25
|
+
status: "error";
|
|
26
|
+
})>;
|
|
27
|
+
type HashOptions = {
|
|
28
|
+
purpose: string;
|
|
29
|
+
salt?: string | Uint8Array;
|
|
30
|
+
extra?: string | Uint8Array;
|
|
31
|
+
value: string | Uint8Array;
|
|
32
|
+
};
|
|
33
|
+
declare function hash(options: HashOptions): Promise<Uint8Array>;
|
|
34
|
+
declare function iteratedHash(options: HashOptions & {
|
|
35
|
+
iterations: number;
|
|
36
|
+
}): Promise<Uint8Array>;
|
|
7
37
|
|
|
8
|
-
export { generateRandomValues, generateSecureRandomString };
|
|
38
|
+
export { type HashOptions, decrypt, encrypt, generateRandomValues, generateSecureRandomString, hash, iteratedHash };
|
package/dist/utils/crypto.d.ts
CHANGED
|
@@ -4,5 +4,35 @@ declare function generateRandomValues(array: Uint8Array): typeof array;
|
|
|
4
4
|
* random number generator.
|
|
5
5
|
*/
|
|
6
6
|
declare function generateSecureRandomString(minBitsOfEntropy?: number): string;
|
|
7
|
+
declare function encrypt({ purpose, secret, value }: {
|
|
8
|
+
purpose: string;
|
|
9
|
+
secret: string | Uint8Array;
|
|
10
|
+
value: Uint8Array;
|
|
11
|
+
}): Promise<Uint8Array>;
|
|
12
|
+
declare function decrypt({ purpose, secret, cipher }: {
|
|
13
|
+
purpose: string;
|
|
14
|
+
secret: string | Uint8Array;
|
|
15
|
+
cipher: Uint8Array;
|
|
16
|
+
}): Promise<({
|
|
17
|
+
status: "ok";
|
|
18
|
+
data: Uint8Array;
|
|
19
|
+
} & {
|
|
20
|
+
status: "ok";
|
|
21
|
+
}) | ({
|
|
22
|
+
status: "error";
|
|
23
|
+
error: Error;
|
|
24
|
+
} & {
|
|
25
|
+
status: "error";
|
|
26
|
+
})>;
|
|
27
|
+
type HashOptions = {
|
|
28
|
+
purpose: string;
|
|
29
|
+
salt?: string | Uint8Array;
|
|
30
|
+
extra?: string | Uint8Array;
|
|
31
|
+
value: string | Uint8Array;
|
|
32
|
+
};
|
|
33
|
+
declare function hash(options: HashOptions): Promise<Uint8Array>;
|
|
34
|
+
declare function iteratedHash(options: HashOptions & {
|
|
35
|
+
iterations: number;
|
|
36
|
+
}): Promise<Uint8Array>;
|
|
7
37
|
|
|
8
|
-
export { generateRandomValues, generateSecureRandomString };
|
|
38
|
+
export { type HashOptions, decrypt, encrypt, generateRandomValues, generateSecureRandomString, hash, iteratedHash };
|
package/dist/utils/crypto.js
CHANGED
|
@@ -20,13 +20,18 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/utils/crypto.tsx
|
|
21
21
|
var crypto_exports = {};
|
|
22
22
|
__export(crypto_exports, {
|
|
23
|
+
decrypt: () => decrypt,
|
|
24
|
+
encrypt: () => encrypt,
|
|
23
25
|
generateRandomValues: () => generateRandomValues,
|
|
24
|
-
generateSecureRandomString: () => generateSecureRandomString
|
|
26
|
+
generateSecureRandomString: () => generateSecureRandomString,
|
|
27
|
+
hash: () => hash,
|
|
28
|
+
iteratedHash: () => iteratedHash
|
|
25
29
|
});
|
|
26
30
|
module.exports = __toCommonJS(crypto_exports);
|
|
27
31
|
var import_bytes = require("./bytes.js");
|
|
28
32
|
var import_errors = require("./errors.js");
|
|
29
33
|
var import_globals = require("./globals.js");
|
|
34
|
+
var import_results = require("./results.js");
|
|
30
35
|
function generateRandomValues(array) {
|
|
31
36
|
if (!import_globals.globalVar.crypto) {
|
|
32
37
|
throw new import_errors.StackAssertionError("Crypto API is not available in this environment. Are you using an old browser?");
|
|
@@ -43,9 +48,89 @@ function generateSecureRandomString(minBitsOfEntropy = 224) {
|
|
|
43
48
|
const str = (0, import_bytes.encodeBase32)(randomBytes);
|
|
44
49
|
return str.slice(str.length - base32CharactersCount).toLowerCase();
|
|
45
50
|
}
|
|
51
|
+
async function getDerivedSymmetricKey(purpose, secret, salt) {
|
|
52
|
+
const originalSecretKey = await crypto.subtle.importKey("raw", typeof secret === "string" ? new TextEncoder().encode(secret) : secret, "HKDF", false, ["deriveKey"]);
|
|
53
|
+
return await crypto.subtle.deriveKey(
|
|
54
|
+
{
|
|
55
|
+
name: "HKDF",
|
|
56
|
+
salt,
|
|
57
|
+
hash: "SHA-256",
|
|
58
|
+
info: new TextEncoder().encode(JSON.stringify([
|
|
59
|
+
"stack-crypto-helper-derived-symmetric-key",
|
|
60
|
+
purpose,
|
|
61
|
+
typeof secret === "string" ? "string-key" : "binary-key",
|
|
62
|
+
(0, import_bytes.encodeBase64)(salt)
|
|
63
|
+
]))
|
|
64
|
+
},
|
|
65
|
+
originalSecretKey,
|
|
66
|
+
{ name: "AES-GCM", length: 256 },
|
|
67
|
+
false,
|
|
68
|
+
["encrypt", "decrypt"]
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
async function encrypt({ purpose, secret, value }) {
|
|
72
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
73
|
+
const salt = crypto.getRandomValues(new Uint8Array(16));
|
|
74
|
+
const derivedSecretKey = await getDerivedSymmetricKey(purpose, secret, salt);
|
|
75
|
+
const cipher = await crypto.subtle.encrypt({
|
|
76
|
+
name: "AES-GCM",
|
|
77
|
+
iv
|
|
78
|
+
}, derivedSecretKey, value);
|
|
79
|
+
const version = [1, 0];
|
|
80
|
+
return new Uint8Array([...version, ...salt, ...iv, ...new Uint8Array(cipher)]);
|
|
81
|
+
}
|
|
82
|
+
async function decrypt({ purpose, secret, cipher }) {
|
|
83
|
+
const version = cipher.slice(0, 2);
|
|
84
|
+
if (version[0] !== 1 || version[1] !== 0) throw new import_errors.StackAssertionError("Invalid ciphertext version in decrypt(...); expected 0x0100", { purpose });
|
|
85
|
+
const salt = cipher.slice(2, 18);
|
|
86
|
+
const iv = cipher.slice(18, 30);
|
|
87
|
+
const cipherBytes = cipher.slice(30);
|
|
88
|
+
const derivedSecretKey = await getDerivedSymmetricKey(purpose, secret, salt);
|
|
89
|
+
try {
|
|
90
|
+
const plaintext = await crypto.subtle.decrypt({
|
|
91
|
+
name: "AES-GCM",
|
|
92
|
+
iv
|
|
93
|
+
}, derivedSecretKey, cipherBytes);
|
|
94
|
+
return import_results.Result.ok(new Uint8Array(plaintext));
|
|
95
|
+
} catch (e) {
|
|
96
|
+
if (e instanceof DOMException && e.name === "OperationError") {
|
|
97
|
+
return import_results.Result.error(new Error("Invalid ciphertext or secret when decrypting encrypted value", { cause: e }));
|
|
98
|
+
}
|
|
99
|
+
throw e;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async function hash(options) {
|
|
103
|
+
return await iteratedHash({ ...options, iterations: 1 });
|
|
104
|
+
}
|
|
105
|
+
async function iteratedHash(options) {
|
|
106
|
+
const stringOrUint8ArrayToUint8Array = (value) => typeof value === "string" ? new TextEncoder().encode(value) : value;
|
|
107
|
+
const stringOrUint8ArrayToBase64 = (value) => (0, import_bytes.encodeBase64)(stringOrUint8ArrayToUint8Array(value));
|
|
108
|
+
const input = await crypto.subtle.importKey(
|
|
109
|
+
"raw",
|
|
110
|
+
stringOrUint8ArrayToUint8Array(options.value),
|
|
111
|
+
"PBKDF2",
|
|
112
|
+
false,
|
|
113
|
+
["deriveBits"]
|
|
114
|
+
);
|
|
115
|
+
return new Uint8Array(await crypto.subtle.deriveBits({
|
|
116
|
+
name: "PBKDF2",
|
|
117
|
+
salt: new TextEncoder().encode(JSON.stringify([
|
|
118
|
+
"stack-crypto-helper-iterated-hash",
|
|
119
|
+
options.purpose,
|
|
120
|
+
stringOrUint8ArrayToBase64(options.salt ?? ""),
|
|
121
|
+
stringOrUint8ArrayToBase64(options.extra ?? "")
|
|
122
|
+
])),
|
|
123
|
+
iterations: options.iterations,
|
|
124
|
+
hash: "SHA-256"
|
|
125
|
+
}, input, 256));
|
|
126
|
+
}
|
|
46
127
|
// Annotate the CommonJS export names for ESM import in node:
|
|
47
128
|
0 && (module.exports = {
|
|
129
|
+
decrypt,
|
|
130
|
+
encrypt,
|
|
48
131
|
generateRandomValues,
|
|
49
|
-
generateSecureRandomString
|
|
132
|
+
generateSecureRandomString,
|
|
133
|
+
hash,
|
|
134
|
+
iteratedHash
|
|
50
135
|
});
|
|
51
136
|
//# sourceMappingURL=crypto.js.map
|
package/dist/utils/crypto.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/crypto.tsx"],"sourcesContent":["import { encodeBase32 } from \"./bytes\";\nimport { StackAssertionError } from \"./errors\";\nimport { globalVar } from \"./globals\";\n\nexport function generateRandomValues(array: Uint8Array): typeof array {\n if (!globalVar.crypto) {\n throw new StackAssertionError(\"Crypto API is not available in this environment. Are you using an old browser?\");\n }\n if (!globalVar.crypto.getRandomValues) {\n throw new StackAssertionError(\"crypto.getRandomValues is not available in this environment. Are you using an old browser?\");\n }\n return globalVar.crypto.getRandomValues(array);\n}\n\n/**\n * Generates a secure alphanumeric string using the system's cryptographically secure\n * random number generator.\n */\nexport function generateSecureRandomString(minBitsOfEntropy: number = 224) {\n const base32CharactersCount = Math.ceil(minBitsOfEntropy / 5);\n const bytesCount = Math.ceil(base32CharactersCount * 5 / 8);\n const randomBytes = generateRandomValues(new Uint8Array(bytesCount));\n const str = encodeBase32(randomBytes);\n return str.slice(str.length - base32CharactersCount).toLowerCase();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA6B;AAC7B,oBAAoC;AACpC,qBAA0B;AAEnB,SAAS,qBAAqB,OAAiC;AACpE,MAAI,CAAC,yBAAU,QAAQ;AACrB,UAAM,IAAI,kCAAoB,gFAAgF;AAAA,EAChH;AACA,MAAI,CAAC,yBAAU,OAAO,iBAAiB;AACrC,UAAM,IAAI,kCAAoB,4FAA4F;AAAA,EAC5H;AACA,SAAO,yBAAU,OAAO,gBAAgB,KAAK;AAC/C;AAMO,SAAS,2BAA2B,mBAA2B,KAAK;AACzE,QAAM,wBAAwB,KAAK,KAAK,mBAAmB,CAAC;AAC5D,QAAM,aAAa,KAAK,KAAK,wBAAwB,IAAI,CAAC;AAC1D,QAAM,cAAc,qBAAqB,IAAI,WAAW,UAAU,CAAC;AACnE,QAAM,UAAM,2BAAa,WAAW;AACpC,SAAO,IAAI,MAAM,IAAI,SAAS,qBAAqB,EAAE,YAAY;AACnE;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/utils/crypto.tsx"],"sourcesContent":["import { encodeBase32, encodeBase64 } from \"./bytes\";\nimport { StackAssertionError } from \"./errors\";\nimport { globalVar } from \"./globals\";\nimport { Result } from \"./results\";\n\nexport function generateRandomValues(array: Uint8Array): typeof array {\n if (!globalVar.crypto) {\n throw new StackAssertionError(\"Crypto API is not available in this environment. Are you using an old browser?\");\n }\n if (!globalVar.crypto.getRandomValues) {\n throw new StackAssertionError(\"crypto.getRandomValues is not available in this environment. Are you using an old browser?\");\n }\n return globalVar.crypto.getRandomValues(array);\n}\n\n/**\n * Generates a secure alphanumeric string using the system's cryptographically secure\n * random number generator.\n */\nexport function generateSecureRandomString(minBitsOfEntropy: number = 224) {\n const base32CharactersCount = Math.ceil(minBitsOfEntropy / 5);\n const bytesCount = Math.ceil(base32CharactersCount * 5 / 8);\n const randomBytes = generateRandomValues(new Uint8Array(bytesCount));\n const str = encodeBase32(randomBytes);\n return str.slice(str.length - base32CharactersCount).toLowerCase();\n}\n\nasync function getDerivedSymmetricKey(purpose: string, secret: string | Uint8Array, salt: Uint8Array) {\n const originalSecretKey = await crypto.subtle.importKey(\"raw\", typeof secret === \"string\" ? new TextEncoder().encode(secret) : secret, \"HKDF\", false, [\"deriveKey\"]);\n return await crypto.subtle.deriveKey(\n {\n name: \"HKDF\",\n salt,\n hash: \"SHA-256\",\n info: new TextEncoder().encode(JSON.stringify([\n \"stack-crypto-helper-derived-symmetric-key\",\n purpose,\n typeof secret === \"string\" ? \"string-key\" : \"binary-key\",\n encodeBase64(salt),\n ])),\n },\n originalSecretKey,\n { name: \"AES-GCM\", length: 256 },\n false,\n [\"encrypt\", \"decrypt\"]\n );\n}\n\nexport async function encrypt({ purpose, secret, value }: { purpose: string, secret: string | Uint8Array, value: Uint8Array }) {\n const iv = crypto.getRandomValues(new Uint8Array(12));\n const salt = crypto.getRandomValues(new Uint8Array(16));\n const derivedSecretKey = await getDerivedSymmetricKey(purpose, secret, salt);\n\n const cipher = await crypto.subtle.encrypt({\n name: \"AES-GCM\",\n iv,\n }, derivedSecretKey, value);\n\n const version = [0x01, 0x00];\n return new Uint8Array([...version, ...salt, ...iv, ...new Uint8Array(cipher)]);\n}\n\nexport async function decrypt({ purpose, secret, cipher }: { purpose: string, secret: string | Uint8Array, cipher: Uint8Array }) {\n const version = cipher.slice(0, 2);\n if (version[0] !== 0x01 || version[1] !== 0x00) throw new StackAssertionError(\"Invalid ciphertext version in decrypt(...); expected 0x0100\", { purpose });\n const salt = cipher.slice(2, 18);\n const iv = cipher.slice(18, 30);\n const cipherBytes = cipher.slice(30);\n const derivedSecretKey = await getDerivedSymmetricKey(purpose, secret, salt);\n\n try {\n const plaintext = await crypto.subtle.decrypt({\n name: \"AES-GCM\",\n iv,\n }, derivedSecretKey, cipherBytes);\n return Result.ok(new Uint8Array(plaintext));\n } catch (e) {\n if (e instanceof DOMException && e.name === \"OperationError\") {\n return Result.error(new Error(\"Invalid ciphertext or secret when decrypting encrypted value\", { cause: e }));\n }\n throw e;\n }\n}\n\nundefined?.test(\"encrypt & decrypt\", async ({ expect }) => {\n const encryptAndDecrypt = async (encryptPurpose: string, decryptPurpose: string, encryptSecret: string | Uint8Array, decryptSecret: string | Uint8Array, value: Uint8Array) => {\n const encrypted = await encrypt({ purpose: encryptPurpose, secret: encryptSecret, value });\n const decrypted = await decrypt({ purpose: decryptPurpose, secret: decryptSecret, cipher: encrypted });\n return decrypted;\n };\n\n const exampleBytes = new TextEncoder().encode(\"hello\");\n\n const exampleKey1 = crypto.getRandomValues(new Uint8Array(32));\n const exampleKey2 = crypto.getRandomValues(new Uint8Array(32));\n\n expect(await encryptAndDecrypt(\"p\", \"p\", \"secret\", \"secret\", exampleBytes)).toEqual(Result.ok(exampleBytes));\n expect(await encryptAndDecrypt(\"p\", \"p\", exampleKey1, exampleKey1, exampleBytes)).toEqual(Result.ok(exampleBytes));\n expect(await encryptAndDecrypt(\"p\", \"p\", exampleKey1, \"secret\", exampleBytes)).toEqual(Result.error(expect.objectContaining({ message: \"Invalid ciphertext or secret when decrypting encrypted value\" })));\n expect(await encryptAndDecrypt(\"p\", \"p\", exampleKey1, exampleKey2, exampleBytes)).toEqual(Result.error(expect.objectContaining({ message: \"Invalid ciphertext or secret when decrypting encrypted value\" })));\n expect(await encryptAndDecrypt(\"p\", \"not-p\", exampleKey1, exampleKey1, exampleBytes)).toEqual(Result.error(expect.objectContaining({ message: \"Invalid ciphertext or secret when decrypting encrypted value\" })));\n});\n\nexport type HashOptions = {\n purpose: string,\n salt?: string | Uint8Array,\n extra?: string | Uint8Array,\n value: string | Uint8Array,\n};\n\nexport async function hash(options: HashOptions) {\n return await iteratedHash({ ...options, iterations: 1 });\n}\n\nexport async function iteratedHash(options: HashOptions & { iterations: number }) {\n const stringOrUint8ArrayToUint8Array = (value: string | Uint8Array) => typeof value === \"string\" ? new TextEncoder().encode(value) : value;\n const stringOrUint8ArrayToBase64 = (value: string | Uint8Array) => encodeBase64(stringOrUint8ArrayToUint8Array(value));\n const input = await crypto.subtle.importKey(\n \"raw\",\n stringOrUint8ArrayToUint8Array(options.value),\n \"PBKDF2\",\n false,\n [\"deriveBits\"]\n );\n return new Uint8Array(await crypto.subtle.deriveBits({\n name: \"PBKDF2\",\n salt: new TextEncoder().encode(JSON.stringify([\n \"stack-crypto-helper-iterated-hash\",\n options.purpose,\n stringOrUint8ArrayToBase64(options.salt ?? \"\"),\n stringOrUint8ArrayToBase64(options.extra ?? \"\"),\n ])),\n iterations: options.iterations,\n hash: \"SHA-256\",\n }, input, 256));\n}\n\nundefined?.test(\"iteratedHash\", async ({ expect }) => {\n const valueBytes = new TextEncoder().encode(\"hello\");\n const incrementBytes = new Uint8Array([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10]);\n\n const hash = await iteratedHash({ purpose: \"purpose\", value: valueBytes, iterations: 100_000 });\n const hash2 = await iteratedHash({ purpose: \"purpose\", value: valueBytes, iterations: 100_000 });\n const hashWithDifferentPurpose = await iteratedHash({ purpose: \"different-purpose\", value: valueBytes, iterations: 100_000 });\n const hashWithEmptySalt = await iteratedHash({ purpose: \"purpose\", value: valueBytes, salt: new Uint8Array(0), iterations: 100_000 });\n const hashWithDifferentSalt = await iteratedHash({ purpose: \"purpose\", value: valueBytes, salt: incrementBytes, iterations: 100_000 });\n const hashWithEmptyExtra = await iteratedHash({ purpose: \"purpose\", value: valueBytes, extra: new Uint8Array(0), iterations: 100_000 });\n const hashWithDifferentExtra = await iteratedHash({ purpose: \"purpose\", value: valueBytes, extra: incrementBytes, iterations: 100_000 });\n const hashWithDifferentValue = await iteratedHash({ purpose: \"purpose\", value: new TextEncoder().encode(\"hello2\"), iterations: 100_000 });\n const hashWithDifferentSaltAndExtra = await iteratedHash({ purpose: \"purpose\", value: valueBytes, salt: incrementBytes, extra: incrementBytes, iterations: 100_000 });\n const hashWithDifferentIterations = await iteratedHash({ purpose: \"purpose\", value: valueBytes, iterations: 100_001 });\n\n\n expect(hash).toEqual(hash2);\n expect(hash).not.toEqual(hashWithDifferentPurpose);\n expect(hash).toEqual(hashWithEmptySalt);\n expect(hash).not.toEqual(hashWithDifferentSalt);\n expect(hash).toEqual(hashWithEmptyExtra);\n expect(hash).not.toEqual(hashWithDifferentExtra);\n expect(hash).not.toEqual(hashWithDifferentValue);\n expect(hash).not.toEqual(hashWithDifferentIterations);\n\n expect(hashWithDifferentSalt).not.toEqual(hashWithDifferentExtra);\n expect(hashWithDifferentSalt).not.toEqual(hashWithDifferentSaltAndExtra);\n expect(hashWithDifferentExtra).not.toEqual(hashWithDifferentSaltAndExtra);\n});\n"],"mappings":"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","names":[]}
|
package/dist/utils/esbuild.d.mts
CHANGED
|
@@ -8,6 +8,7 @@ declare function bundleJavaScript(sourceFiles: Record<string, string> & {
|
|
|
8
8
|
externalPackages?: Record<string, string>;
|
|
9
9
|
keepAsImports?: string[];
|
|
10
10
|
sourcemap?: false | 'inline';
|
|
11
|
+
allowHttpImports?: boolean;
|
|
11
12
|
}): Promise<Result<string, string>>;
|
|
12
13
|
|
|
13
14
|
export { bundleJavaScript, initializeEsbuild };
|
package/dist/utils/esbuild.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ declare function bundleJavaScript(sourceFiles: Record<string, string> & {
|
|
|
8
8
|
externalPackages?: Record<string, string>;
|
|
9
9
|
keepAsImports?: string[];
|
|
10
10
|
sourcemap?: false | 'inline';
|
|
11
|
+
allowHttpImports?: boolean;
|
|
11
12
|
}): Promise<Result<string, string>>;
|
|
12
13
|
|
|
13
14
|
export { bundleJavaScript, initializeEsbuild };
|
package/dist/utils/esbuild.js
CHANGED
|
@@ -46,12 +46,26 @@ globalThis.self ??= globalThis;
|
|
|
46
46
|
function initializeEsbuild() {
|
|
47
47
|
if (!esbuildInitializePromise) {
|
|
48
48
|
esbuildInitializePromise = (0, import_telemetry.withTraceSpan)("initializeEsbuild", async () => {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
49
|
+
if ((0, import_env.isBrowserLike)()) {
|
|
50
|
+
await esbuild.initialize({
|
|
51
|
+
wasmURL: esbuildWasmUrl
|
|
52
|
+
});
|
|
53
|
+
} else {
|
|
54
|
+
const esbuildWasmResponse = await fetch(esbuildWasmUrl);
|
|
55
|
+
if (!esbuildWasmResponse.ok) {
|
|
56
|
+
throw new import_errors.StackAssertionError(`Failed to fetch esbuild.wasm: ${esbuildWasmResponse.status} ${esbuildWasmResponse.statusText}: ${await esbuildWasmResponse.text()}`);
|
|
57
|
+
}
|
|
58
|
+
const esbuildWasm = await esbuildWasmResponse.arrayBuffer();
|
|
59
|
+
const esbuildWasmArray = new Uint8Array(esbuildWasm);
|
|
60
|
+
if (esbuildWasmArray[0] !== 0 || esbuildWasmArray[1] !== 97 || esbuildWasmArray[2] !== 115 || esbuildWasmArray[3] !== 109) {
|
|
61
|
+
throw new import_errors.StackAssertionError(`Invalid esbuild.wasm file: ${new TextDecoder().decode(esbuildWasmArray)}`);
|
|
62
|
+
}
|
|
63
|
+
const esbuildWasmModule = new WebAssembly.Module(esbuildWasm);
|
|
64
|
+
await esbuild.initialize({
|
|
65
|
+
wasmModule: esbuildWasmModule,
|
|
66
|
+
worker: false
|
|
67
|
+
});
|
|
68
|
+
}
|
|
55
69
|
})();
|
|
56
70
|
}
|
|
57
71
|
return esbuildInitializePromise;
|
|
@@ -61,6 +75,7 @@ async function bundleJavaScript(sourceFiles, options = {}) {
|
|
|
61
75
|
const sourceFilesMap = new Map(Object.entries(sourceFiles));
|
|
62
76
|
const externalPackagesMap = new Map(Object.entries(options.externalPackages ?? {}));
|
|
63
77
|
const keepAsImports = options.keepAsImports ?? [];
|
|
78
|
+
const httpImportCache = /* @__PURE__ */ new Map();
|
|
64
79
|
const extToLoader = /* @__PURE__ */ new Map([
|
|
65
80
|
["tsx", "tsx"],
|
|
66
81
|
["ts", "ts"],
|
|
@@ -82,6 +97,48 @@ async function bundleJavaScript(sourceFiles, options = {}) {
|
|
|
82
97
|
sourcemap: options.sourcemap ?? "inline",
|
|
83
98
|
external: keepAsImports,
|
|
84
99
|
plugins: [
|
|
100
|
+
...options.allowHttpImports ? [{
|
|
101
|
+
name: "esm-sh-only",
|
|
102
|
+
setup(build2) {
|
|
103
|
+
build2.onResolve({ filter: /.*/ }, (args) => {
|
|
104
|
+
const isHttp = args.path.startsWith("http://") || args.path.startsWith("https://");
|
|
105
|
+
const fromEsmNs = args.namespace === "esm-sh";
|
|
106
|
+
if (!isHttp && !fromEsmNs) return null;
|
|
107
|
+
const url = new URL(args.path, fromEsmNs ? args.importer : void 0);
|
|
108
|
+
if (url.protocol !== "https:" || url.host !== "esm.sh") {
|
|
109
|
+
throw new Error(`Blocked non-esm.sh URL import: ${url.href}`);
|
|
110
|
+
}
|
|
111
|
+
return { path: url.href, namespace: "esm-sh" };
|
|
112
|
+
});
|
|
113
|
+
build2.onLoad({ filter: /.*/, namespace: "esm-sh" }, async (args) => {
|
|
114
|
+
if (httpImportCache.has(args.path)) return httpImportCache.get(args.path);
|
|
115
|
+
const res = await fetch(args.path, { redirect: "follow" });
|
|
116
|
+
if (!res.ok) throw new Error(`Fetch ${res.status} ${res.statusText} for ${args.path}`);
|
|
117
|
+
const finalUrl = new URL(res.url);
|
|
118
|
+
if (finalUrl.host !== "esm.sh") {
|
|
119
|
+
throw new Error(`Redirect escaped esm.sh: ${finalUrl.href}`);
|
|
120
|
+
}
|
|
121
|
+
const ct = (res.headers.get("content-type") || "").toLowerCase();
|
|
122
|
+
let loader = ct.includes("css") ? "css" : ct.includes("json") ? "json" : ct.includes("typescript") ? "ts" : ct.includes("jsx") ? "jsx" : ct.includes("tsx") ? "tsx" : "js";
|
|
123
|
+
const p = finalUrl.pathname;
|
|
124
|
+
if (p.endsWith(".css")) loader = "css";
|
|
125
|
+
else if (p.endsWith(".json")) loader = "json";
|
|
126
|
+
else if (p.endsWith(".ts")) loader = "ts";
|
|
127
|
+
else if (p.endsWith(".tsx")) loader = "tsx";
|
|
128
|
+
else if (p.endsWith(".jsx")) loader = "jsx";
|
|
129
|
+
const contents = await res.text();
|
|
130
|
+
const result2 = {
|
|
131
|
+
contents,
|
|
132
|
+
loader,
|
|
133
|
+
// Ensures relative imports inside that module resolve against the file’s URL
|
|
134
|
+
resolveDir: new URL(".", finalUrl.href).toString(),
|
|
135
|
+
watchFiles: [finalUrl.href]
|
|
136
|
+
};
|
|
137
|
+
httpImportCache.set(args.path, result2);
|
|
138
|
+
return result2;
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}] : [],
|
|
85
142
|
{
|
|
86
143
|
name: "replace-packages-with-globals",
|
|
87
144
|
setup(build2) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/esbuild.tsx"],"sourcesContent":["import * as esbuild from 'esbuild-wasm/lib/browser.js';\nimport { join } from 'path';\nimport { isBrowserLike } from './env';\nimport { StackAssertionError, throwErr } from \"./errors\";\nimport { Result } from \"./results\";\nimport { traceSpan, withTraceSpan } from './telemetry';\n\nconst esbuildWasmUrl = `https://unpkg.com/esbuild-wasm@${esbuild.version}/esbuild.wasm`;\n\nlet esbuildInitializePromise: Promise<void> | null = null;\n// esbuild requires self property to be set, and it is not set by default in nodejs\n(globalThis.self as any) ??= globalThis as any;\n\nexport function initializeEsbuild(): Promise<void> {\n if (!esbuildInitializePromise) {\n esbuildInitializePromise = withTraceSpan('initializeEsbuild', async () => {\n await esbuild.initialize(isBrowserLike() ? {\n wasmURL: esbuildWasmUrl,\n } : {\n wasmModule: (\n await fetch(esbuildWasmUrl)\n .then(wasm => wasm.arrayBuffer())\n .then(wasm => new WebAssembly.Module(wasm))\n ),\n worker: false,\n });\n })();\n }\n\n return esbuildInitializePromise;\n}\n\nexport async function bundleJavaScript(sourceFiles: Record<string, string> & { '/entry.js': string }, options: {\n format?: 'iife' | 'esm' | 'cjs',\n externalPackages?: Record<string, string>,\n keepAsImports?: string[],\n sourcemap?: false | 'inline',\n} = {}): Promise<Result<string, string>> {\n await initializeEsbuild();\n\n const sourceFilesMap = new Map(Object.entries(sourceFiles));\n const externalPackagesMap = new Map(Object.entries(options.externalPackages ?? {}));\n const keepAsImports = options.keepAsImports ?? [];\n\n const extToLoader: Map<string, esbuild.Loader> = new Map([\n ['tsx', 'tsx'],\n ['ts', 'ts'],\n ['js', 'js'],\n ['jsx', 'jsx'],\n ['json', 'json'],\n ['css', 'css'],\n ]);\n let result;\n try {\n result = await traceSpan('bundleJavaScript', async () => await esbuild.build({\n entryPoints: ['/entry.js'],\n bundle: true,\n write: false,\n format: options.format ?? 'iife',\n platform: 'browser',\n target: 'es2015',\n jsx: 'automatic',\n sourcemap: options.sourcemap ?? 'inline',\n external: keepAsImports,\n plugins: [\n {\n name: 'replace-packages-with-globals',\n setup(build) {\n build.onResolve({ filter: /.*/ }, args => {\n // Skip packages that should remain external (not be shimmed)\n if (keepAsImports.includes(args.path)) {\n return undefined;\n }\n if (externalPackagesMap.has(args.path)) {\n return { path: args.path, namespace: 'package-shim' };\n }\n return undefined;\n });\n\n build.onLoad({ filter: /.*/, namespace: 'package-shim' }, (args) => {\n const contents = externalPackagesMap.get(args.path);\n if (contents == null) throw new StackAssertionError(`esbuild requested file ${args.path} that is not in the virtual file system`);\n\n return { contents, loader: 'ts' };\n });\n },\n },\n {\n name: 'virtual-fs',\n setup(build) {\n build.onResolve({ filter: /.*/ }, args => {\n const absolutePath = join(\"/\", args.path);\n if (sourceFilesMap.has(absolutePath)) {\n return { path: absolutePath, namespace: 'virtual' };\n }\n return undefined;\n });\n\n /* 2️⃣ Load the module from the map */\n build.onLoad({ filter: /.*/, namespace: 'virtual' }, args => {\n const contents = sourceFilesMap.get(args.path);\n if (contents == null) throw new StackAssertionError(`esbuild requested file ${args.path} that is not in the virtual file system`);\n\n const ext = args.path.split('.').pop() ?? '';\n const loader = extToLoader.get(ext) ?? throwErr(`esbuild requested file ${args.path} with unknown extension ${ext}`);\n\n return { contents, loader };\n });\n },\n },\n ],\n }));\n } catch (e) {\n if (e instanceof Error && e.message.startsWith(\"Build failed with \")) {\n return Result.error(e.message);\n }\n throw e;\n }\n\n if (result.errors.length > 0) {\n return Result.error(result.errors.map(e => e.text).join('\\n'));\n }\n\n if (result.outputFiles.length > 0) {\n return Result.ok(result.outputFiles[0].text);\n }\n return throwErr(\"No output generated??\");\n}\n"],"mappings":"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","names":["build"]}
|
|
1
|
+
{"version":3,"sources":["../../src/utils/esbuild.tsx"],"sourcesContent":["import * as esbuild from 'esbuild-wasm/lib/browser.js';\nimport { join } from 'path';\nimport { isBrowserLike } from './env';\nimport { StackAssertionError, throwErr } from \"./errors\";\nimport { Result } from \"./results\";\nimport { traceSpan, withTraceSpan } from './telemetry';\n\nconst esbuildWasmUrl = `https://unpkg.com/esbuild-wasm@${esbuild.version}/esbuild.wasm`;\n\nlet esbuildInitializePromise: Promise<void> | null = null;\n// esbuild requires self property to be set, and it is not set by default in nodejs\n(globalThis.self as any) ??= globalThis as any;\n\nexport function initializeEsbuild(): Promise<void> {\n if (!esbuildInitializePromise) {\n esbuildInitializePromise = withTraceSpan('initializeEsbuild', async () => {\n if (isBrowserLike()) {\n await esbuild.initialize({\n wasmURL: esbuildWasmUrl,\n });\n } else {\n const esbuildWasmResponse = await fetch(esbuildWasmUrl);\n if (!esbuildWasmResponse.ok) {\n throw new StackAssertionError(`Failed to fetch esbuild.wasm: ${esbuildWasmResponse.status} ${esbuildWasmResponse.statusText}: ${await esbuildWasmResponse.text()}`);\n }\n const esbuildWasm = await esbuildWasmResponse.arrayBuffer();\n const esbuildWasmArray = new Uint8Array(esbuildWasm);\n if (esbuildWasmArray[0] !== 0x00 || esbuildWasmArray[1] !== 0x61 || esbuildWasmArray[2] !== 0x73 || esbuildWasmArray[3] !== 0x6d) {\n throw new StackAssertionError(`Invalid esbuild.wasm file: ${new TextDecoder().decode(esbuildWasmArray)}`);\n }\n const esbuildWasmModule = new WebAssembly.Module(esbuildWasm);\n await esbuild.initialize({\n wasmModule: esbuildWasmModule,\n worker: false,\n });\n }\n })();\n }\n\n return esbuildInitializePromise;\n}\n\nexport async function bundleJavaScript(sourceFiles: Record<string, string> & { '/entry.js': string }, options: {\n format?: 'iife' | 'esm' | 'cjs',\n externalPackages?: Record<string, string>,\n keepAsImports?: string[],\n sourcemap?: false | 'inline',\n allowHttpImports?: boolean,\n} = {}): Promise<Result<string, string>> {\n await initializeEsbuild();\n\n const sourceFilesMap = new Map(Object.entries(sourceFiles));\n const externalPackagesMap = new Map(Object.entries(options.externalPackages ?? {}));\n const keepAsImports = options.keepAsImports ?? [];\n\n const httpImportCache = new Map<string, { contents: string, loader: esbuild.Loader, resolveDir: string }>();\n\n const extToLoader: Map<string, esbuild.Loader> = new Map([\n ['tsx', 'tsx'],\n ['ts', 'ts'],\n ['js', 'js'],\n ['jsx', 'jsx'],\n ['json', 'json'],\n ['css', 'css'],\n ]);\n let result;\n try {\n result = await traceSpan('bundleJavaScript', async () => await esbuild.build({\n entryPoints: ['/entry.js'],\n bundle: true,\n write: false,\n format: options.format ?? 'iife',\n platform: 'browser',\n target: 'es2015',\n jsx: 'automatic',\n sourcemap: options.sourcemap ?? 'inline',\n external: keepAsImports,\n plugins: [\n ...options.allowHttpImports ? [{\n name: \"esm-sh-only\",\n setup(build: esbuild.PluginBuild) {\n // Handle absolute URLs and relative imports from esm.sh modules.\n build.onResolve({ filter: /.*/ }, (args) => {\n // Only touch absolute http(s) specifiers or children of our own namespace\n const isHttp = args.path.startsWith(\"http://\") || args.path.startsWith(\"https://\");\n const fromEsmNs = args.namespace === \"esm-sh\";\n\n if (!isHttp && !fromEsmNs) return null; // Let other plugins handle bare/relative/local\n\n // Resolve relative URLs inside esm.sh-fetched modules\n const url = new URL(args.path, fromEsmNs ? args.importer : undefined);\n\n if (url.protocol !== \"https:\" || url.host !== \"esm.sh\") {\n throw new Error(`Blocked non-esm.sh URL import: ${url.href}`);\n }\n\n return { path: url.href, namespace: \"esm-sh\" };\n });\n\n build.onLoad({ filter: /.*/, namespace: \"esm-sh\" }, async (args) => {\n if (httpImportCache.has(args.path)) return httpImportCache.get(args.path)!;\n\n const res = await fetch(args.path, { redirect: \"follow\" });\n if (!res.ok) throw new Error(`Fetch ${res.status} ${res.statusText} for ${args.path}`);\n const finalUrl = new URL(res.url);\n // Defensive: follow shouldn’t leave esm.sh, but re-check.\n if (finalUrl.host !== \"esm.sh\") {\n throw new Error(`Redirect escaped esm.sh: ${finalUrl.href}`);\n }\n\n const ct = (res.headers.get(\"content-type\") || \"\").toLowerCase();\n let loader: esbuild.Loader =\n ct.includes(\"css\") ? \"css\" :\n ct.includes(\"json\") ? \"json\" :\n ct.includes(\"typescript\") ? \"ts\" :\n ct.includes(\"jsx\") ? \"jsx\" :\n ct.includes(\"tsx\") ? \"tsx\" :\n \"js\";\n\n // Fallback by extension (esm.sh sometimes omits CT)\n const p = finalUrl.pathname;\n if (p.endsWith(\".css\")) loader = \"css\";\n else if (p.endsWith(\".json\")) loader = \"json\";\n else if (p.endsWith(\".ts\")) loader = \"ts\";\n else if (p.endsWith(\".tsx\")) loader = \"tsx\";\n else if (p.endsWith(\".jsx\")) loader = \"jsx\";\n\n const contents = await res.text();\n const result = {\n contents,\n loader,\n // Ensures relative imports inside that module resolve against the file’s URL\n resolveDir: new URL(\".\", finalUrl.href).toString(),\n watchFiles: [finalUrl.href],\n };\n httpImportCache.set(args.path, result);\n return result;\n });\n },\n } as esbuild.Plugin] : [],\n {\n name: 'replace-packages-with-globals',\n setup(build) {\n build.onResolve({ filter: /.*/ }, args => {\n // Skip packages that should remain external (not be shimmed)\n if (keepAsImports.includes(args.path)) {\n return undefined;\n }\n if (externalPackagesMap.has(args.path)) {\n return { path: args.path, namespace: 'package-shim' };\n }\n return undefined;\n });\n\n build.onLoad({ filter: /.*/, namespace: 'package-shim' }, (args) => {\n const contents = externalPackagesMap.get(args.path);\n if (contents == null) throw new StackAssertionError(`esbuild requested file ${args.path} that is not in the virtual file system`);\n\n return { contents, loader: 'ts' };\n });\n },\n },\n {\n name: 'virtual-fs',\n setup(build) {\n build.onResolve({ filter: /.*/ }, args => {\n const absolutePath = join(\"/\", args.path);\n if (sourceFilesMap.has(absolutePath)) {\n return { path: absolutePath, namespace: 'virtual' };\n }\n return undefined;\n });\n\n /* 2️⃣ Load the module from the map */\n build.onLoad({ filter: /.*/, namespace: 'virtual' }, args => {\n const contents = sourceFilesMap.get(args.path);\n if (contents == null) throw new StackAssertionError(`esbuild requested file ${args.path} that is not in the virtual file system`);\n\n const ext = args.path.split('.').pop() ?? '';\n const loader = extToLoader.get(ext) ?? throwErr(`esbuild requested file ${args.path} with unknown extension ${ext}`);\n\n return { contents, loader };\n });\n },\n },\n ],\n }));\n } catch (e) {\n if (e instanceof Error && e.message.startsWith(\"Build failed with \")) {\n return Result.error(e.message);\n }\n throw e;\n }\n\n if (result.errors.length > 0) {\n return Result.error(result.errors.map(e => e.text).join('\\n'));\n }\n\n if (result.outputFiles.length > 0) {\n return Result.ok(result.outputFiles[0].text);\n }\n return throwErr(\"No output generated??\");\n}\n"],"mappings":"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","names":["build","result"]}
|
package/dist/utils/jwt.d.mts
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
import * as jose from 'jose';
|
|
2
2
|
|
|
3
|
+
declare function getJwtInfo(options: {
|
|
4
|
+
jwt: string;
|
|
5
|
+
}): Promise<({
|
|
6
|
+
status: "error";
|
|
7
|
+
error: {
|
|
8
|
+
error: string;
|
|
9
|
+
stringifiedInput: string;
|
|
10
|
+
};
|
|
11
|
+
} & {
|
|
12
|
+
status: "error";
|
|
13
|
+
}) | ({
|
|
14
|
+
status: "error";
|
|
15
|
+
error: {
|
|
16
|
+
error: string;
|
|
17
|
+
input: string;
|
|
18
|
+
};
|
|
19
|
+
} & {
|
|
20
|
+
status: "error";
|
|
21
|
+
}) | ({
|
|
22
|
+
status: "ok";
|
|
23
|
+
data: {
|
|
24
|
+
payload: jose.JWTPayload;
|
|
25
|
+
};
|
|
26
|
+
} & {
|
|
27
|
+
status: "ok";
|
|
28
|
+
}) | ({
|
|
29
|
+
status: "error";
|
|
30
|
+
error: {
|
|
31
|
+
exception: string;
|
|
32
|
+
};
|
|
33
|
+
} & {
|
|
34
|
+
status: "error";
|
|
35
|
+
})>;
|
|
3
36
|
declare function signJWT(options: {
|
|
4
37
|
issuer: string;
|
|
5
38
|
audience: string;
|
|
@@ -41,4 +74,4 @@ declare function oldGetKid(options: {
|
|
|
41
74
|
secret: string;
|
|
42
75
|
}): string;
|
|
43
76
|
|
|
44
|
-
export { type PrivateJwk, type PublicJwk, getPrivateJwks, getPublicJwkSet, oldGetKid, signJWT, verifyJWT };
|
|
77
|
+
export { type PrivateJwk, type PublicJwk, getJwtInfo, getPrivateJwks, getPublicJwkSet, oldGetKid, signJWT, verifyJWT };
|
package/dist/utils/jwt.d.ts
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
import * as jose from 'jose';
|
|
2
2
|
|
|
3
|
+
declare function getJwtInfo(options: {
|
|
4
|
+
jwt: string;
|
|
5
|
+
}): Promise<({
|
|
6
|
+
status: "error";
|
|
7
|
+
error: {
|
|
8
|
+
error: string;
|
|
9
|
+
stringifiedInput: string;
|
|
10
|
+
};
|
|
11
|
+
} & {
|
|
12
|
+
status: "error";
|
|
13
|
+
}) | ({
|
|
14
|
+
status: "error";
|
|
15
|
+
error: {
|
|
16
|
+
error: string;
|
|
17
|
+
input: string;
|
|
18
|
+
};
|
|
19
|
+
} & {
|
|
20
|
+
status: "error";
|
|
21
|
+
}) | ({
|
|
22
|
+
status: "ok";
|
|
23
|
+
data: {
|
|
24
|
+
payload: jose.JWTPayload;
|
|
25
|
+
};
|
|
26
|
+
} & {
|
|
27
|
+
status: "ok";
|
|
28
|
+
}) | ({
|
|
29
|
+
status: "error";
|
|
30
|
+
error: {
|
|
31
|
+
exception: string;
|
|
32
|
+
};
|
|
33
|
+
} & {
|
|
34
|
+
status: "error";
|
|
35
|
+
})>;
|
|
3
36
|
declare function signJWT(options: {
|
|
4
37
|
issuer: string;
|
|
5
38
|
audience: string;
|
|
@@ -41,4 +74,4 @@ declare function oldGetKid(options: {
|
|
|
41
74
|
secret: string;
|
|
42
75
|
}): string;
|
|
43
76
|
|
|
44
|
-
export { type PrivateJwk, type PublicJwk, getPrivateJwks, getPublicJwkSet, oldGetKid, signJWT, verifyJWT };
|
|
77
|
+
export { type PrivateJwk, type PublicJwk, getJwtInfo, getPrivateJwks, getPublicJwkSet, oldGetKid, signJWT, verifyJWT };
|
package/dist/utils/jwt.js
CHANGED
|
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/utils/jwt.tsx
|
|
31
31
|
var jwt_exports = {};
|
|
32
32
|
__export(jwt_exports, {
|
|
33
|
+
getJwtInfo: () => getJwtInfo,
|
|
33
34
|
getPrivateJwks: () => getPrivateJwks,
|
|
34
35
|
getPublicJwkSet: () => getPublicJwkSet,
|
|
35
36
|
oldGetKid: () => oldGetKid,
|
|
@@ -46,6 +47,8 @@ var import_env = require("./env.js");
|
|
|
46
47
|
var import_errors2 = require("./errors.js");
|
|
47
48
|
var import_globals = require("./globals.js");
|
|
48
49
|
var import_objects = require("./objects.js");
|
|
50
|
+
var import_results = require("./results.js");
|
|
51
|
+
var import_strings = require("./strings.js");
|
|
49
52
|
function getStackServerSecret() {
|
|
50
53
|
const STACK_SERVER_SECRET = (0, import_env.getEnvVariable)("STACK_SERVER_SECRET");
|
|
51
54
|
try {
|
|
@@ -55,6 +58,18 @@ function getStackServerSecret() {
|
|
|
55
58
|
}
|
|
56
59
|
return STACK_SERVER_SECRET;
|
|
57
60
|
}
|
|
61
|
+
async function getJwtInfo(options) {
|
|
62
|
+
try {
|
|
63
|
+
if (typeof options.jwt !== "string") return import_results.Result.error({ error: "JWT input is not a string!", stringifiedInput: (0, import_strings.nicify)(options.jwt) });
|
|
64
|
+
if (!options.jwt.startsWith("ey")) return import_results.Result.error({ error: "Input is a string, but not a JWT!", input: options.jwt });
|
|
65
|
+
const decodedJwt = jose.decodeJwt(options.jwt);
|
|
66
|
+
return import_results.Result.ok({ payload: decodedJwt });
|
|
67
|
+
} catch (e) {
|
|
68
|
+
return import_results.Result.error({
|
|
69
|
+
exception: (0, import_errors2.errorToNiceString)(e)
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
58
73
|
async function signJWT(options) {
|
|
59
74
|
const privateJwks = await getPrivateJwks({ audience: options.audience });
|
|
60
75
|
const privateKey = await jose.importJWK(privateJwks[0]);
|
|
@@ -122,6 +137,7 @@ function oldGetKid(options) {
|
|
|
122
137
|
}
|
|
123
138
|
// Annotate the CommonJS export names for ESM import in node:
|
|
124
139
|
0 && (module.exports = {
|
|
140
|
+
getJwtInfo,
|
|
125
141
|
getPrivateJwks,
|
|
126
142
|
getPublicJwkSet,
|
|
127
143
|
oldGetKid,
|
package/dist/utils/jwt.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/jwt.tsx"],"sourcesContent":["import crypto from \"crypto\";\nimport elliptic from \"elliptic\";\nimport * as jose from \"jose\";\nimport { JOSEError } from \"jose/errors\";\nimport { encodeBase64Url } from \"./bytes\";\nimport { getEnvVariable } from \"./env\";\nimport { StackAssertionError } from \"./errors\";\nimport { globalVar } from \"./globals\";\nimport { pick } from \"./objects\";\n\nfunction getStackServerSecret() {\n const STACK_SERVER_SECRET = getEnvVariable(\"STACK_SERVER_SECRET\");\n try {\n jose.base64url.decode(STACK_SERVER_SECRET);\n } catch (e) {\n throw new StackAssertionError(\"STACK_SERVER_SECRET is not valid. Please use the generateKeys script to generate a new secret.\", { cause: e });\n }\n return STACK_SERVER_SECRET;\n}\n\nexport async function signJWT(options: {\n issuer: string,\n audience: string,\n payload: any,\n expirationTime?: string,\n}) {\n const privateJwks = await getPrivateJwks({ audience: options.audience });\n const privateKey = await jose.importJWK(privateJwks[0]);\n\n return await new jose.SignJWT(options.payload)\n .setProtectedHeader({ alg: \"ES256\", kid: privateJwks[0].kid })\n .setIssuer(options.issuer)\n .setIssuedAt()\n .setAudience(options.audience)\n .setExpirationTime(options.expirationTime || \"5m\")\n .sign(privateKey);\n}\n\nexport async function verifyJWT(options: {\n allowedIssuers: string[],\n jwt: string,\n}) {\n const decodedJwt = jose.decodeJwt(options.jwt);\n const audience = decodedJwt.aud;\n if (!audience || typeof audience !== \"string\") {\n throw new JOSEError(\"Invalid JWT audience\");\n }\n\n const jwkSet = jose.createLocalJWKSet(await getPublicJwkSet(await getPrivateJwks({ audience })));\n const verified = await jose.jwtVerify(options.jwt, jwkSet, { issuer: options.allowedIssuers });\n return verified.payload;\n}\n\nexport type PrivateJwk = {\n kty: \"EC\",\n alg: \"ES256\",\n crv: \"P-256\",\n kid: string,\n d: string,\n x: string,\n y: string,\n};\nasync function getPrivateJwkFromDerivedSecret(derivedSecret: string, kid: string): Promise<PrivateJwk> {\n const secretHash = await globalVar.crypto.subtle.digest(\"SHA-256\", jose.base64url.decode(derivedSecret));\n const priv = new Uint8Array(secretHash);\n\n const ec = new elliptic.ec('p256');\n const key = ec.keyFromPrivate(priv);\n const publicKey = key.getPublic();\n\n return {\n kty: 'EC',\n crv: 'P-256',\n alg: 'ES256',\n kid: kid,\n d: encodeBase64Url(priv),\n x: encodeBase64Url(publicKey.getX().toBuffer()),\n y: encodeBase64Url(publicKey.getY().toBuffer()),\n };\n}\n\n/**\n * Returns a list of valid private JWKs for the given audience, with the first one taking precedence when signing new\n * JWTs.\n */\nexport async function getPrivateJwks(options: {\n audience: string,\n}): Promise<PrivateJwk[]> {\n const getHashOfJwkInfo = (type: string) => jose.base64url.encode(\n crypto\n .createHash('sha256')\n .update(JSON.stringify([type, getStackServerSecret(), {\n audience: options.audience,\n }]))\n .digest()\n );\n const perAudienceSecret = getHashOfJwkInfo(\"stack-jwk-audience-secret\");\n const perAudienceKid = getHashOfJwkInfo(\"stack-jwk-kid\").slice(0, 12);\n\n const oldPerAudienceSecret = oldGetPerAudienceSecret({ audience: options.audience });\n const oldPerAudienceKid = oldGetKid({ secret: oldPerAudienceSecret });\n\n return [\n // TODO next-release: make this not take precedence; then, in the release after that, remove it entirely\n await getPrivateJwkFromDerivedSecret(oldPerAudienceSecret, oldPerAudienceKid),\n\n await getPrivateJwkFromDerivedSecret(perAudienceSecret, perAudienceKid),\n ];\n}\n\nexport type PublicJwk = {\n kty: \"EC\",\n alg: \"ES256\",\n crv: \"P-256\",\n kid: string,\n x: string,\n y: string,\n};\nexport async function getPublicJwkSet(privateJwks: PrivateJwk[]): Promise<{ keys: PublicJwk[] }> {\n return {\n keys: privateJwks.map(jwk => pick(jwk, [\"kty\", \"alg\", \"crv\", \"x\", \"y\", \"kid\"])),\n };\n}\n\nfunction oldGetPerAudienceSecret(options: {\n audience: string,\n}) {\n if (options.audience === \"kid\") {\n throw new StackAssertionError(\"You cannot use the 'kid' audience for a per-audience secret, see comment below in jwt.tsx\");\n }\n return jose.base64url.encode(\n crypto\n .createHash('sha256')\n // TODO we should prefix a string like \"stack-audience-secret\" before we hash so you can't use `getKid(...)` to get the secret for eg. the \"kid\" audience if the same secret value is used\n // Sadly doing this modification is a bit annoying as we need to leave the old keys to be valid for a little longer\n .update(JSON.stringify([getStackServerSecret(), options.audience]))\n .digest()\n );\n};\n\nexport function oldGetKid(options: {\n secret: string,\n}) {\n return jose.base64url.encode(\n crypto\n .createHash('sha256')\n .update(JSON.stringify([options.secret, \"kid\"])) // TODO see above in getPerAudienceSecret\n .digest()\n ).slice(0, 12);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAAmB;AACnB,sBAAqB;AACrB,WAAsB;AACtB,oBAA0B;AAC1B,mBAAgC;AAChC,iBAA+B;AAC/B,IAAAA,
|
|
1
|
+
{"version":3,"sources":["../../src/utils/jwt.tsx"],"sourcesContent":["import crypto from \"crypto\";\nimport elliptic from \"elliptic\";\nimport * as jose from \"jose\";\nimport { JOSEError } from \"jose/errors\";\nimport { encodeBase64Url } from \"./bytes\";\nimport { getEnvVariable } from \"./env\";\nimport { StackAssertionError, errorToNiceString } from \"./errors\";\nimport { globalVar } from \"./globals\";\nimport { pick } from \"./objects\";\nimport { Result } from \"./results\";\nimport { nicify } from \"./strings\";\n\nfunction getStackServerSecret() {\n const STACK_SERVER_SECRET = getEnvVariable(\"STACK_SERVER_SECRET\");\n try {\n jose.base64url.decode(STACK_SERVER_SECRET);\n } catch (e) {\n throw new StackAssertionError(\"STACK_SERVER_SECRET is not valid. Please use the generateKeys script to generate a new secret.\", { cause: e });\n }\n return STACK_SERVER_SECRET;\n}\n\nexport async function getJwtInfo(options: {\n jwt: string,\n}) {\n try {\n if (typeof options.jwt !== \"string\") return Result.error({ error: \"JWT input is not a string!\", stringifiedInput: nicify(options.jwt) });\n if (!options.jwt.startsWith(\"ey\")) return Result.error({ error: \"Input is a string, but not a JWT!\", input: options.jwt });\n const decodedJwt = jose.decodeJwt(options.jwt);\n return Result.ok({ payload: decodedJwt });\n } catch (e) {\n return Result.error({\n exception: errorToNiceString(e),\n });\n }\n}\n\nexport async function signJWT(options: {\n issuer: string,\n audience: string,\n payload: any,\n expirationTime?: string,\n}) {\n const privateJwks = await getPrivateJwks({ audience: options.audience });\n const privateKey = await jose.importJWK(privateJwks[0]);\n\n return await new jose.SignJWT(options.payload)\n .setProtectedHeader({ alg: \"ES256\", kid: privateJwks[0].kid })\n .setIssuer(options.issuer)\n .setIssuedAt()\n .setAudience(options.audience)\n .setExpirationTime(options.expirationTime || \"5m\")\n .sign(privateKey);\n}\n\nexport async function verifyJWT(options: {\n allowedIssuers: string[],\n jwt: string,\n}) {\n const decodedJwt = jose.decodeJwt(options.jwt);\n const audience = decodedJwt.aud;\n if (!audience || typeof audience !== \"string\") {\n throw new JOSEError(\"Invalid JWT audience\");\n }\n\n const jwkSet = jose.createLocalJWKSet(await getPublicJwkSet(await getPrivateJwks({ audience })));\n const verified = await jose.jwtVerify(options.jwt, jwkSet, { issuer: options.allowedIssuers });\n return verified.payload;\n}\n\nexport type PrivateJwk = {\n kty: \"EC\",\n alg: \"ES256\",\n crv: \"P-256\",\n kid: string,\n d: string,\n x: string,\n y: string,\n};\nasync function getPrivateJwkFromDerivedSecret(derivedSecret: string, kid: string): Promise<PrivateJwk> {\n const secretHash = await globalVar.crypto.subtle.digest(\"SHA-256\", jose.base64url.decode(derivedSecret));\n const priv = new Uint8Array(secretHash);\n\n const ec = new elliptic.ec('p256');\n const key = ec.keyFromPrivate(priv);\n const publicKey = key.getPublic();\n\n return {\n kty: 'EC',\n crv: 'P-256',\n alg: 'ES256',\n kid: kid,\n d: encodeBase64Url(priv),\n x: encodeBase64Url(publicKey.getX().toBuffer()),\n y: encodeBase64Url(publicKey.getY().toBuffer()),\n };\n}\n\n/**\n * Returns a list of valid private JWKs for the given audience, with the first one taking precedence when signing new\n * JWTs.\n */\nexport async function getPrivateJwks(options: {\n audience: string,\n}): Promise<PrivateJwk[]> {\n const getHashOfJwkInfo = (type: string) => jose.base64url.encode(\n crypto\n .createHash('sha256')\n .update(JSON.stringify([type, getStackServerSecret(), {\n audience: options.audience,\n }]))\n .digest()\n );\n const perAudienceSecret = getHashOfJwkInfo(\"stack-jwk-audience-secret\");\n const perAudienceKid = getHashOfJwkInfo(\"stack-jwk-kid\").slice(0, 12);\n\n const oldPerAudienceSecret = oldGetPerAudienceSecret({ audience: options.audience });\n const oldPerAudienceKid = oldGetKid({ secret: oldPerAudienceSecret });\n\n return [\n // TODO next-release: make this not take precedence; then, in the release after that, remove it entirely\n await getPrivateJwkFromDerivedSecret(oldPerAudienceSecret, oldPerAudienceKid),\n\n await getPrivateJwkFromDerivedSecret(perAudienceSecret, perAudienceKid),\n ];\n}\n\nexport type PublicJwk = {\n kty: \"EC\",\n alg: \"ES256\",\n crv: \"P-256\",\n kid: string,\n x: string,\n y: string,\n};\nexport async function getPublicJwkSet(privateJwks: PrivateJwk[]): Promise<{ keys: PublicJwk[] }> {\n return {\n keys: privateJwks.map(jwk => pick(jwk, [\"kty\", \"alg\", \"crv\", \"x\", \"y\", \"kid\"])),\n };\n}\n\nfunction oldGetPerAudienceSecret(options: {\n audience: string,\n}) {\n if (options.audience === \"kid\") {\n throw new StackAssertionError(\"You cannot use the 'kid' audience for a per-audience secret, see comment below in jwt.tsx\");\n }\n return jose.base64url.encode(\n crypto\n .createHash('sha256')\n // TODO we should prefix a string like \"stack-audience-secret\" before we hash so you can't use `getKid(...)` to get the secret for eg. the \"kid\" audience if the same secret value is used\n // Sadly doing this modification is a bit annoying as we need to leave the old keys to be valid for a little longer\n .update(JSON.stringify([getStackServerSecret(), options.audience]))\n .digest()\n );\n};\n\nexport function oldGetKid(options: {\n secret: string,\n}) {\n return jose.base64url.encode(\n crypto\n .createHash('sha256')\n .update(JSON.stringify([options.secret, \"kid\"])) // TODO see above in getPerAudienceSecret\n .digest()\n ).slice(0, 12);\n}\n"],"mappings":"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","names":["import_errors","elliptic","crypto","crypto"]}
|
package/dist/utils/numbers.js
CHANGED
|
@@ -26,23 +26,28 @@ __export(numbers_exports, {
|
|
|
26
26
|
});
|
|
27
27
|
module.exports = __toCommonJS(numbers_exports);
|
|
28
28
|
var magnitudes = [
|
|
29
|
-
[
|
|
30
|
-
[
|
|
31
|
-
[
|
|
32
|
-
[
|
|
33
|
-
[1e3, "
|
|
29
|
+
[1e3, "k"],
|
|
30
|
+
[1e3, "M"],
|
|
31
|
+
[1e3, "bn"],
|
|
32
|
+
[1e3, "bln"],
|
|
33
|
+
[1e3, "trln"]
|
|
34
34
|
];
|
|
35
35
|
function prettyPrintWithMagnitudes(num) {
|
|
36
36
|
if (typeof num !== "number") throw new Error("Expected a number");
|
|
37
37
|
if (Number.isNaN(num)) return "NaN";
|
|
38
38
|
if (num < 0) return "-" + prettyPrintWithMagnitudes(-num);
|
|
39
39
|
if (!Number.isFinite(num)) return "\u221E";
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
let current = toFixedMax(num, 1);
|
|
41
|
+
let lastSuffix = "";
|
|
42
|
+
for (const [difference, suffix] of magnitudes) {
|
|
43
|
+
if (+current >= difference) {
|
|
44
|
+
current = toFixedMax(+current / difference, 1);
|
|
45
|
+
lastSuffix = suffix;
|
|
46
|
+
} else {
|
|
47
|
+
break;
|
|
43
48
|
}
|
|
44
49
|
}
|
|
45
|
-
return
|
|
50
|
+
return current + lastSuffix;
|
|
46
51
|
}
|
|
47
52
|
function toFixedMax(num, maxDecimals) {
|
|
48
53
|
return num.toFixed(maxDecimals).replace(/\.?0+$/, "");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/numbers.tsx"],"sourcesContent":["const magnitudes = [\n [
|
|
1
|
+
{"version":3,"sources":["../../src/utils/numbers.tsx"],"sourcesContent":["const magnitudes = [\n [1_000, \"k\"],\n [1_000, \"M\"],\n [1_000, \"bn\"],\n [1_000, \"bln\"],\n [1_000, \"trln\"],\n] as const;\n\nexport function prettyPrintWithMagnitudes(num: number): string {\n if (typeof num !== \"number\") throw new Error(\"Expected a number\");\n if (Number.isNaN(num)) return \"NaN\";\n if (num < 0) return \"-\" + prettyPrintWithMagnitudes(-num);\n if (!Number.isFinite(num)) return \"∞\";\n\n let current = toFixedMax(num, 1);\n let lastSuffix = \"\";\n for (const [difference, suffix] of magnitudes) {\n if (+current >= difference) {\n current = toFixedMax(+current / difference, 1);\n lastSuffix = suffix;\n } else {\n break;\n }\n }\n return current + lastSuffix;\n}\nundefined?.test(\"prettyPrintWithMagnitudes\", ({ expect }) => {\n // Test different magnitudes\n expect(prettyPrintWithMagnitudes(999)).toBe(\"999\");\n expect(prettyPrintWithMagnitudes(1000)).toBe(\"1k\");\n expect(prettyPrintWithMagnitudes(1500)).toBe(\"1.5k\");\n expect(prettyPrintWithMagnitudes(999499)).toBe(\"999.5k\");\n expect(prettyPrintWithMagnitudes(999500)).toBe(\"999.5k\");\n expect(prettyPrintWithMagnitudes(999949)).toBe(\"999.9k\");\n expect(prettyPrintWithMagnitudes(999950)).toBe(\"1M\");\n expect(prettyPrintWithMagnitudes(1000000)).toBe(\"1M\");\n expect(prettyPrintWithMagnitudes(1500000)).toBe(\"1.5M\");\n expect(prettyPrintWithMagnitudes(1000000000)).toBe(\"1bn\");\n expect(prettyPrintWithMagnitudes(1500000000)).toBe(\"1.5bn\");\n expect(prettyPrintWithMagnitudes(1000000000000)).toBe(\"1bln\");\n expect(prettyPrintWithMagnitudes(1500000000000)).toBe(\"1.5bln\");\n expect(prettyPrintWithMagnitudes(1000000000000000)).toBe(\"1trln\");\n expect(prettyPrintWithMagnitudes(1500000000000000)).toBe(\"1.5trln\");\n // Test small numbers\n expect(prettyPrintWithMagnitudes(100)).toBe(\"100\");\n expect(prettyPrintWithMagnitudes(0)).toBe(\"0\");\n expect(prettyPrintWithMagnitudes(0.5)).toBe(\"0.5\");\n // Test negative numbers\n expect(prettyPrintWithMagnitudes(-1000)).toBe(\"-1k\");\n expect(prettyPrintWithMagnitudes(-1500000)).toBe(\"-1.5M\");\n // Test special cases\n expect(prettyPrintWithMagnitudes(NaN)).toBe(\"NaN\");\n expect(prettyPrintWithMagnitudes(Infinity)).toBe(\"∞\");\n expect(prettyPrintWithMagnitudes(-Infinity)).toBe(\"-∞\");\n});\n\nexport function toFixedMax(num: number, maxDecimals: number): string {\n return num.toFixed(maxDecimals).replace(/\\.?0+$/, \"\");\n}\nundefined?.test(\"toFixedMax\", ({ expect }) => {\n expect(toFixedMax(1, 2)).toBe(\"1\");\n expect(toFixedMax(1.2, 2)).toBe(\"1.2\");\n expect(toFixedMax(1.23, 2)).toBe(\"1.23\");\n expect(toFixedMax(1.234, 2)).toBe(\"1.23\");\n expect(toFixedMax(1.0, 2)).toBe(\"1\");\n expect(toFixedMax(1.20, 2)).toBe(\"1.2\");\n expect(toFixedMax(0, 2)).toBe(\"0\");\n});\n\nexport function numberCompare(a: number, b: number): number {\n return Math.sign(a - b);\n}\nundefined?.test(\"numberCompare\", ({ expect }) => {\n expect(numberCompare(1, 2)).toBe(-1);\n expect(numberCompare(2, 1)).toBe(1);\n expect(numberCompare(1, 1)).toBe(0);\n expect(numberCompare(0, 0)).toBe(0);\n expect(numberCompare(-1, -2)).toBe(1);\n expect(numberCompare(-2, -1)).toBe(-1);\n expect(numberCompare(-1, 1)).toBe(-1);\n expect(numberCompare(1, -1)).toBe(1);\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAM,aAAa;AAAA,EACjB,CAAC,KAAO,GAAG;AAAA,EACX,CAAC,KAAO,GAAG;AAAA,EACX,CAAC,KAAO,IAAI;AAAA,EACZ,CAAC,KAAO,KAAK;AAAA,EACb,CAAC,KAAO,MAAM;AAChB;AAEO,SAAS,0BAA0B,KAAqB;AAC7D,MAAI,OAAO,QAAQ,SAAU,OAAM,IAAI,MAAM,mBAAmB;AAChE,MAAI,OAAO,MAAM,GAAG,EAAG,QAAO;AAC9B,MAAI,MAAM,EAAG,QAAO,MAAM,0BAA0B,CAAC,GAAG;AACxD,MAAI,CAAC,OAAO,SAAS,GAAG,EAAG,QAAO;AAElC,MAAI,UAAU,WAAW,KAAK,CAAC;AAC/B,MAAI,aAAa;AACjB,aAAW,CAAC,YAAY,MAAM,KAAK,YAAY;AAC7C,QAAI,CAAC,WAAW,YAAY;AAC1B,gBAAU,WAAW,CAAC,UAAU,YAAY,CAAC;AAC7C,mBAAa;AAAA,IACf,OAAO;AACL;AAAA,IACF;AAAA,EACF;AACA,SAAO,UAAU;AACnB;AA+BO,SAAS,WAAW,KAAa,aAA6B;AACnE,SAAO,IAAI,QAAQ,WAAW,EAAE,QAAQ,UAAU,EAAE;AACtD;AAWO,SAAS,cAAc,GAAW,GAAmB;AAC1D,SAAO,KAAK,KAAK,IAAI,CAAC;AACxB;","names":[]}
|
|
@@ -48,7 +48,7 @@ declare class TimeoutError extends Error {
|
|
|
48
48
|
readonly ms: number;
|
|
49
49
|
constructor(ms: number);
|
|
50
50
|
}
|
|
51
|
-
declare function timeout<T>(
|
|
51
|
+
declare function timeout<T>(promiseOrFunc: Promise<T> | (() => Promise<T>), ms: number): Promise<Result<T, TimeoutError>>;
|
|
52
52
|
declare function timeoutThrow<T>(promise: Promise<T>, ms: number): Promise<T>;
|
|
53
53
|
type RateLimitOptions = {
|
|
54
54
|
/**
|
package/dist/utils/promises.d.ts
CHANGED
|
@@ -48,7 +48,7 @@ declare class TimeoutError extends Error {
|
|
|
48
48
|
readonly ms: number;
|
|
49
49
|
constructor(ms: number);
|
|
50
50
|
}
|
|
51
|
-
declare function timeout<T>(
|
|
51
|
+
declare function timeout<T>(promiseOrFunc: Promise<T> | (() => Promise<T>), ms: number): Promise<Result<T, TimeoutError>>;
|
|
52
52
|
declare function timeoutThrow<T>(promise: Promise<T>, ms: number): Promise<T>;
|
|
53
53
|
type RateLimitOptions = {
|
|
54
54
|
/**
|