@stackframe/stack-shared 2.8.36 → 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 +18 -0
- package/dist/config/schema.d.mts +91 -15
- package/dist/config/schema.d.ts +91 -15
- package/dist/config/schema.js +24 -4
- package/dist/config/schema.js.map +1 -1
- package/dist/esm/config/schema.js +24 -4
- package/dist/esm/config/schema.js.map +1 -1
- package/dist/esm/helpers/vault/server-side.js +5 -1
- package/dist/esm/helpers/vault/server-side.js.map +1 -1
- package/dist/esm/interface/admin-interface.js +53 -2
- 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 +5 -7
- package/dist/esm/interface/server-interface.js.map +1 -1
- package/dist/esm/known-errors.js +21 -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/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/types.js.map +1 -1
- package/dist/helpers/vault/server-side.js +5 -1
- package/dist/helpers/vault/server-side.js.map +1 -1
- package/dist/index.d.mts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/interface/admin-interface.d.mts +34 -3
- package/dist/interface/admin-interface.d.ts +34 -3
- package/dist/interface/admin-interface.js +53 -2
- 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/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/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/server-interface.d.mts +8 -43
- package/dist/interface/server-interface.d.ts +8 -43
- package/dist/interface/server-interface.js +5 -7
- package/dist/interface/server-interface.js.map +1 -1
- package/dist/known-errors.d.mts +6 -0
- package/dist/known-errors.d.ts +6 -0
- package/dist/known-errors.js +21 -1
- package/dist/known-errors.js.map +1 -1
- package/dist/schema-fields.d.mts +3 -2
- package/dist/schema-fields.d.ts +3 -2
- 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/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/types.d.mts +10 -1
- package/dist/utils/types.d.ts +10 -1
- package/dist/utils/types.js.map +1 -1
- package/package.json +3 -2
package/dist/esm/utils/jwt.js
CHANGED
|
@@ -5,9 +5,11 @@ import * as jose from "jose";
|
|
|
5
5
|
import { JOSEError } from "jose/errors";
|
|
6
6
|
import { encodeBase64Url } from "./bytes.js";
|
|
7
7
|
import { getEnvVariable } from "./env.js";
|
|
8
|
-
import { StackAssertionError } from "./errors.js";
|
|
8
|
+
import { StackAssertionError, errorToNiceString } from "./errors.js";
|
|
9
9
|
import { globalVar } from "./globals.js";
|
|
10
10
|
import { pick } from "./objects.js";
|
|
11
|
+
import { Result } from "./results.js";
|
|
12
|
+
import { nicify } from "./strings.js";
|
|
11
13
|
function getStackServerSecret() {
|
|
12
14
|
const STACK_SERVER_SECRET = getEnvVariable("STACK_SERVER_SECRET");
|
|
13
15
|
try {
|
|
@@ -17,6 +19,18 @@ function getStackServerSecret() {
|
|
|
17
19
|
}
|
|
18
20
|
return STACK_SERVER_SECRET;
|
|
19
21
|
}
|
|
22
|
+
async function getJwtInfo(options) {
|
|
23
|
+
try {
|
|
24
|
+
if (typeof options.jwt !== "string") return Result.error({ error: "JWT input is not a string!", stringifiedInput: nicify(options.jwt) });
|
|
25
|
+
if (!options.jwt.startsWith("ey")) return Result.error({ error: "Input is a string, but not a JWT!", input: options.jwt });
|
|
26
|
+
const decodedJwt = jose.decodeJwt(options.jwt);
|
|
27
|
+
return Result.ok({ payload: decodedJwt });
|
|
28
|
+
} catch (e) {
|
|
29
|
+
return Result.error({
|
|
30
|
+
exception: errorToNiceString(e)
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
20
34
|
async function signJWT(options) {
|
|
21
35
|
const privateJwks = await getPrivateJwks({ audience: options.audience });
|
|
22
36
|
const privateKey = await jose.importJWK(privateJwks[0]);
|
|
@@ -83,6 +97,7 @@ function oldGetKid(options) {
|
|
|
83
97
|
).slice(0, 12);
|
|
84
98
|
}
|
|
85
99
|
export {
|
|
100
|
+
getJwtInfo,
|
|
86
101
|
getPrivateJwks,
|
|
87
102
|
getPublicJwkSet,
|
|
88
103
|
oldGetKid,
|
|
@@ -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,OAAO,YAAY;AACnB,OAAO,cAAc;AACrB,YAAY,UAAU;AACtB,SAAS,iBAAiB;AAC1B,SAAS,uBAAuB;AAChC,SAAS,sBAAsB;AAC/B,SAAS,
|
|
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":[]}
|
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
// src/utils/numbers.tsx
|
|
2
2
|
var magnitudes = [
|
|
3
|
-
[
|
|
4
|
-
[
|
|
5
|
-
[
|
|
6
|
-
[
|
|
7
|
-
[1e3, "
|
|
3
|
+
[1e3, "k"],
|
|
4
|
+
[1e3, "M"],
|
|
5
|
+
[1e3, "bn"],
|
|
6
|
+
[1e3, "bln"],
|
|
7
|
+
[1e3, "trln"]
|
|
8
8
|
];
|
|
9
9
|
function prettyPrintWithMagnitudes(num) {
|
|
10
10
|
if (typeof num !== "number") throw new Error("Expected a number");
|
|
11
11
|
if (Number.isNaN(num)) return "NaN";
|
|
12
12
|
if (num < 0) return "-" + prettyPrintWithMagnitudes(-num);
|
|
13
13
|
if (!Number.isFinite(num)) return "\u221E";
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
let current = toFixedMax(num, 1);
|
|
15
|
+
let lastSuffix = "";
|
|
16
|
+
for (const [difference, suffix] of magnitudes) {
|
|
17
|
+
if (+current >= difference) {
|
|
18
|
+
current = toFixedMax(+current / difference, 1);
|
|
19
|
+
lastSuffix = suffix;
|
|
20
|
+
} else {
|
|
21
|
+
break;
|
|
17
22
|
}
|
|
18
23
|
}
|
|
19
|
-
return
|
|
24
|
+
return current + lastSuffix;
|
|
20
25
|
}
|
|
21
26
|
function toFixedMax(num, maxDecimals) {
|
|
22
27
|
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,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":[]}
|
|
@@ -148,7 +148,8 @@ var TimeoutError = class extends Error {
|
|
|
148
148
|
this.name = "TimeoutError";
|
|
149
149
|
}
|
|
150
150
|
};
|
|
151
|
-
async function timeout(
|
|
151
|
+
async function timeout(promiseOrFunc, ms) {
|
|
152
|
+
const promise = typeof promiseOrFunc === "function" ? promiseOrFunc() : promiseOrFunc;
|
|
152
153
|
return await Promise.race([
|
|
153
154
|
promise.then((value) => Result.ok(value)),
|
|
154
155
|
wait(ms).then(() => Result.error(new TimeoutError(ms)))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/utils/promises.tsx"],"sourcesContent":["import { KnownError } from \"..\";\nimport { StackAssertionError, captureError, concatStacktraces, errorToNiceString } from \"./errors\";\nimport { DependenciesMap } from \"./maps\";\nimport { Result } from \"./results\";\nimport { generateUuid } from \"./uuids\";\n\nexport type ReactPromise<T> = Promise<T> & (\n | { status: \"rejected\", reason: unknown }\n | { status: \"fulfilled\", value: T }\n | { status: \"pending\" }\n);\n\ntype Resolve<T> = (value: T) => void;\ntype Reject = (reason: unknown) => void;\nexport function createPromise<T>(callback: (resolve: Resolve<T>, reject: Reject) => void): ReactPromise<T> {\n let status = \"pending\" as \"fulfilled\" | \"rejected\" | \"pending\";\n let valueOrReason: T | unknown | undefined = undefined;\n let resolve: Resolve<T> | null = null;\n let reject: Reject | null = null;\n const promise = new Promise<T>((res, rej) => {\n resolve = (value) => {\n if (status !== \"pending\") return;\n status = \"fulfilled\";\n valueOrReason = value;\n res(value);\n };\n reject = (reason) => {\n if (status !== \"pending\") return;\n status = \"rejected\";\n valueOrReason = reason;\n rej(reason);\n };\n });\n\n callback(resolve!, reject!);\n return Object.assign(promise, {\n status: status,\n ...status === \"fulfilled\" ? { value: valueOrReason as T } : {},\n ...status === \"rejected\" ? { reason: valueOrReason } : {},\n } as any);\n}\nundefined?.test(\"createPromise\", async ({ expect }) => {\n // Test resolved promise\n const resolvedPromise = createPromise<number>((resolve) => {\n resolve(42);\n });\n expect(resolvedPromise.status).toBe(\"fulfilled\");\n expect((resolvedPromise as any).value).toBe(42);\n expect(await resolvedPromise).toBe(42);\n\n // Test rejected promise\n const error = new Error(\"Test error\");\n const rejectedPromise = createPromise<number>((_, reject) => {\n reject(error);\n });\n expect(rejectedPromise.status).toBe(\"rejected\");\n expect((rejectedPromise as any).reason).toBe(error);\n await expect(rejectedPromise).rejects.toBe(error);\n\n // Test pending promise\n const pendingPromise = createPromise<number>(() => {\n // Do nothing, leave it pending\n });\n expect(pendingPromise.status).toBe(\"pending\");\n expect((pendingPromise as any).value).toBeUndefined();\n expect((pendingPromise as any).reason).toBeUndefined();\n\n // Test that resolving after already resolved does nothing\n let resolveCount = 0;\n const multiResolvePromise = createPromise<number>((resolve) => {\n resolve(1);\n resolveCount++;\n resolve(2);\n resolveCount++;\n });\n expect(resolveCount).toBe(2); // Both resolve calls executed\n expect(multiResolvePromise.status).toBe(\"fulfilled\");\n expect((multiResolvePromise as any).value).toBe(1); // Only first resolve took effect\n expect(await multiResolvePromise).toBe(1);\n});\n\nlet resolvedCache: DependenciesMap<[unknown], ReactPromise<unknown>> | null = null;\n/**\n * Like Promise.resolve(...), but also adds the status and value properties for use with React's `use` hook, and caches\n * the value so that invoking `resolved` twice returns the same promise.\n */\nexport function resolved<T>(value: T): ReactPromise<T> {\n resolvedCache ??= new DependenciesMap<[unknown], ReactPromise<unknown>>();\n if (resolvedCache.has([value])) {\n return resolvedCache.get([value]) as ReactPromise<T>;\n }\n\n const res = Object.assign(Promise.resolve(value), {\n status: \"fulfilled\",\n value,\n } as const);\n resolvedCache.set([value], res);\n return res;\n}\nundefined?.test(\"resolved\", async ({ expect }) => {\n // Test with primitive value\n const promise1 = resolved(42);\n expect(promise1.status).toBe(\"fulfilled\");\n // Need to use type assertion since value is only available when status is \"fulfilled\"\n expect((promise1 as { value: number }).value).toBe(42);\n expect(await promise1).toBe(42);\n\n // Test with object value\n const obj = { test: true };\n const promise2 = resolved(obj);\n expect(promise2.status).toBe(\"fulfilled\");\n expect((promise2 as { value: typeof obj }).value).toBe(obj);\n expect(await promise2).toBe(obj);\n\n // Test caching (same reference for same value)\n const promise3 = resolved(42);\n expect(promise3).toBe(promise1); // Same reference due to caching\n\n // Test with different value (different reference)\n const promise4 = resolved(43);\n expect(promise4).not.toBe(promise1);\n});\n\nlet rejectedCache: DependenciesMap<[unknown], ReactPromise<unknown>> | null = null;\n/**\n * Like Promise.reject(...), but also adds the status and value properties for use with React's `use` hook, and caches\n * the value so that invoking `rejected` twice returns the same promise.\n */\nexport function rejected<T>(reason: unknown): ReactPromise<T> {\n rejectedCache ??= new DependenciesMap<[unknown], ReactPromise<unknown>>();\n if (rejectedCache.has([reason])) {\n return rejectedCache.get([reason]) as ReactPromise<T>;\n }\n\n const promise = Promise.reject(reason);\n ignoreUnhandledRejection(promise);\n const res = Object.assign(promise, {\n status: \"rejected\",\n reason: reason,\n } as const);\n rejectedCache.set([reason], res);\n return res;\n}\nundefined?.test(\"rejected\", ({ expect }) => {\n // Test with error object\n const error = new Error(\"Test error\");\n const promise1 = rejected<number>(error);\n expect(promise1.status).toBe(\"rejected\");\n // Need to use type assertion since reason is only available when status is \"rejected\"\n expect((promise1 as { reason: Error }).reason).toBe(error);\n\n // Test with string reason\n const promise2 = rejected<string>(\"error message\");\n expect(promise2.status).toBe(\"rejected\");\n expect((promise2 as { reason: string }).reason).toBe(\"error message\");\n\n // Test caching (same reference for same reason)\n const promise3 = rejected<number>(error);\n expect(promise3).toBe(promise1); // Same reference due to caching\n\n // Test with different reason (different reference)\n const differentError = new Error(\"Different error\");\n const promise4 = rejected<number>(differentError);\n expect(promise4).not.toBe(promise1);\n\n // Note: We're not using await expect(promise).rejects to avoid unhandled rejections\n});\n\n// We'll skip the rejection test for pending() since it's causing unhandled rejections\n// The function is already well tested through other tests like rejected() and createPromise()\n\n\nconst neverResolvePromise = pending(new Promise<never>(() => {}));\nexport function neverResolve(): ReactPromise<never> {\n return neverResolvePromise;\n}\nundefined?.test(\"neverResolve\", ({ expect }) => {\n const promise = neverResolve();\n expect(promise.status).toBe(\"pending\");\n expect((promise as any).value).toBeUndefined();\n expect((promise as any).reason).toBeUndefined();\n\n // Test that multiple calls return the same promise\n const promise2 = neverResolve();\n expect(promise2).toBe(promise);\n});\n\nexport function pending<T>(promise: Promise<T>, options: { disableErrorWrapping?: boolean } = {}): ReactPromise<T> {\n const res = promise.then(\n value => {\n res.status = \"fulfilled\";\n (res as any).value = value;\n return value;\n },\n actualReason => {\n res.status = \"rejected\";\n (res as any).reason = actualReason;\n throw actualReason;\n },\n ) as ReactPromise<T>;\n res.status = \"pending\";\n return res;\n}\nundefined?.test(\"pending\", async ({ expect }) => {\n // Test with a promise that resolves\n const resolvePromise = Promise.resolve(42);\n const pendingPromise = pending(resolvePromise);\n\n // Initially it should be pending\n expect(pendingPromise.status).toBe(\"pending\");\n\n // After resolution, it should be fulfilled\n await resolvePromise;\n // Need to wait a tick for the then handler to execute\n await new Promise(resolve => setTimeout(resolve, 0));\n expect(pendingPromise.status).toBe(\"fulfilled\");\n expect((pendingPromise as { value: number }).value).toBe(42);\n\n // For the rejection test, we'll use a separate test to avoid unhandled rejections\n});\n\n/**\n * Should be used to wrap Promises that are not immediately awaited, so they don't throw an unhandled promise rejection\n * error.\n *\n * Vercel kills serverless functions on unhandled promise rejection errors, so this is important.\n */\nexport function ignoreUnhandledRejection<T extends Promise<any>>(promise: T): void {\n promise.catch(() => {});\n}\nundefined?.test(\"ignoreUnhandledRejection\", async ({ expect }) => {\n // Test with a promise that resolves\n const resolvePromise = Promise.resolve(42);\n ignoreUnhandledRejection(resolvePromise);\n expect(await resolvePromise).toBe(42); // Should still resolve to the same value\n\n // Test with a promise that rejects\n // The promise should still reject, but the rejection is caught internally\n // so it doesn't cause an unhandled rejection error\n const error = new Error(\"Test error\");\n const rejectPromise = Promise.reject(error);\n ignoreUnhandledRejection(rejectPromise);\n await expect(rejectPromise).rejects.toBe(error);\n});\n\n/**\n * See concatStacktraces for more information.\n */\nexport function concatStacktracesIfRejected<T>(promise: Promise<T>): void {\n const currentError = new Error();\n promise.catch(error => {\n if (error instanceof Error) {\n concatStacktraces(error, currentError);\n } else {\n // we can only concatenate errors, so we'll just ignore the non-error\n }\n });\n}\n\nexport async function wait(ms: number) {\n if (!Number.isFinite(ms) || ms < 0) {\n throw new StackAssertionError(`wait() requires a non-negative integer number of milliseconds to wait. (found: ${ms}ms)`);\n }\n if (ms >= 2**31) {\n throw new StackAssertionError(\"The maximum timeout for wait() is 2147483647ms (2**31 - 1). (found: ${ms}ms)\");\n }\n return await new Promise<void>(resolve => setTimeout(resolve, ms));\n}\nundefined?.test(\"wait\", async ({ expect }) => {\n // Test with valid input\n const start = Date.now();\n await wait(10);\n const elapsed = Date.now() - start;\n expect(elapsed).toBeGreaterThanOrEqual(5); // Allow some flexibility in timing\n\n // Test with zero\n await expect(wait(0)).resolves.toBeUndefined();\n\n // Test with negative number\n await expect(wait(-10)).rejects.toThrow(\"wait() requires a non-negative integer\");\n\n // Test with non-finite number\n await expect(wait(NaN)).rejects.toThrow(\"wait() requires a non-negative integer\");\n await expect(wait(Infinity)).rejects.toThrow(\"wait() requires a non-negative integer\");\n\n // Test with too large number\n await expect(wait(2**31)).rejects.toThrow(\"The maximum timeout for wait()\");\n});\n\nexport async function waitUntil(date: Date) {\n return await wait(date.getTime() - Date.now());\n}\nundefined?.test(\"waitUntil\", async ({ expect }) => {\n // Test with future date\n const futureDate = new Date(Date.now() + 10);\n const start = Date.now();\n await waitUntil(futureDate);\n const elapsed = Date.now() - start;\n expect(elapsed).toBeGreaterThanOrEqual(5); // Allow some flexibility in timing\n\n // Test with past date - this will throw because wait() requires non-negative time\n // We need to verify it throws the correct error\n try {\n await waitUntil(new Date(Date.now() - 1000));\n expect.fail(\"Should have thrown an error\");\n } catch (error) {\n expect(error).toBeInstanceOf(StackAssertionError);\n expect((error as Error).message).toContain(\"wait() requires a non-negative integer\");\n }\n});\n\nexport function runAsynchronouslyWithAlert(...args: Parameters<typeof runAsynchronously>) {\n return runAsynchronously(\n args[0],\n {\n ...args[1],\n onError: error => {\n if (KnownError.isKnownError(error) && typeof process !== \"undefined\" && (process.env.NODE_ENV as any)?.includes(\"production\")) {\n alert(error.message);\n } else {\n alert(`An unhandled error occurred. Please ${process.env.NODE_ENV === \"development\" ? `check the browser console for the full error.` : \"report this to the developer.\"}\\n\\n${error}`);\n }\n args[1]?.onError?.(error);\n },\n },\n ...args.slice(2) as [],\n );\n}\nundefined?.test(\"runAsynchronouslyWithAlert\", ({ expect }) => {\n // Simple test to verify the function calls runAsynchronously\n // We can't easily test the alert functionality without mocking\n const testFn = () => Promise.resolve(\"test\");\n const testOptions = { noErrorLogging: true };\n\n // Just verify it doesn't throw\n expect(() => runAsynchronouslyWithAlert(testFn, testOptions)).not.toThrow();\n\n // We can't easily test the error handling without mocking, so we'll\n // just verify the function exists and can be called\n expect(typeof runAsynchronouslyWithAlert).toBe(\"function\");\n});\n\nexport function runAsynchronously(\n promiseOrFunc: void | Promise<unknown> | (() => void | Promise<unknown>) | undefined,\n options: {\n noErrorLogging?: boolean,\n onError?: (error: Error) => void,\n } = {},\n): void {\n if (typeof promiseOrFunc === \"function\") {\n promiseOrFunc = promiseOrFunc();\n }\n if (promiseOrFunc) {\n concatStacktracesIfRejected(promiseOrFunc);\n promiseOrFunc.catch(error => {\n options.onError?.(error);\n const newError = new StackAssertionError(\n \"Uncaught error in asynchronous function: \" + errorToNiceString(error),\n { cause: error },\n );\n if (!options.noErrorLogging) {\n captureError(\"runAsynchronously\", newError);\n }\n });\n }\n}\nundefined?.test(\"runAsynchronously\", ({ expect }) => {\n // Simple test to verify the function exists and can be called\n const testFn = () => Promise.resolve(\"test\");\n\n // Just verify it doesn't throw\n expect(() => runAsynchronously(testFn)).not.toThrow();\n expect(() => runAsynchronously(Promise.resolve(\"test\"))).not.toThrow();\n expect(() => runAsynchronously(undefined)).not.toThrow();\n\n // We can't easily test the error handling without mocking, so we'll\n // just verify the function exists and can be called with options\n expect(() => runAsynchronously(testFn, { noErrorLogging: true })).not.toThrow();\n expect(() => runAsynchronously(testFn, { onError: () => {} })).not.toThrow();\n});\n\n\nclass TimeoutError extends Error {\n constructor(public readonly ms: number) {\n super(`Timeout after ${ms}ms`);\n this.name = \"TimeoutError\";\n }\n}\n\nexport async function timeout<T>(promise: Promise<T>, ms: number): Promise<Result<T, TimeoutError>> {\n return await Promise.race([\n promise.then(value => Result.ok(value)),\n wait(ms).then(() => Result.error(new TimeoutError(ms))),\n ]);\n}\nundefined?.test(\"timeout\", async ({ expect }) => {\n // Test with a promise that resolves quickly\n const fastPromise = Promise.resolve(42);\n const fastResult = await timeout(fastPromise, 100);\n expect(fastResult.status).toBe(\"ok\");\n if (fastResult.status === \"ok\") {\n expect(fastResult.data).toBe(42);\n }\n\n // Test with a promise that takes longer than the timeout\n const slowPromise = new Promise(resolve => setTimeout(() => resolve(\"too late\"), 50));\n const slowResult = await timeout(slowPromise, 10);\n expect(slowResult.status).toBe(\"error\");\n if (slowResult.status === \"error\") {\n expect(slowResult.error).toBeInstanceOf(TimeoutError);\n expect((slowResult.error as TimeoutError).ms).toBe(10);\n }\n});\n\nexport async function timeoutThrow<T>(promise: Promise<T>, ms: number): Promise<T> {\n return Result.orThrow(await timeout(promise, ms));\n}\nundefined?.test(\"timeoutThrow\", async ({ expect }) => {\n // Test with a promise that resolves quickly\n const fastPromise = Promise.resolve(42);\n const fastResult = await timeoutThrow(fastPromise, 100);\n expect(fastResult).toBe(42);\n\n // Test with a promise that takes longer than the timeout\n const slowPromise = new Promise(resolve => setTimeout(() => resolve(\"too late\"), 50));\n await expect(timeoutThrow(slowPromise, 10)).rejects.toThrow(\"Timeout after 10ms\");\n await expect(timeoutThrow(slowPromise, 10)).rejects.toBeInstanceOf(TimeoutError);\n});\n\n\nexport type RateLimitOptions = {\n /**\n * The number of requests to process in parallel. Currently only 1 is supported.\n */\n concurrency: 1,\n\n /**\n * If true, multiple requests waiting at the same time will be reduced to just one. Default is false.\n */\n batchCalls?: boolean,\n\n /**\n * Waits for throttleMs since the start of last request before starting the next request. Default is 0.\n */\n throttleMs?: number,\n\n /**\n * Waits for gapMs since the end of last request before starting the next request. Default is 0.\n */\n gapMs?: number,\n\n /**\n * Waits until there have been no new requests for debounceMs before starting a new request. Default is 0.\n */\n debounceMs?: number,\n};\n\nexport function rateLimited<T>(\n func: () => Promise<T>,\n options: RateLimitOptions,\n): () => Promise<T> {\n let waitUntil = performance.now();\n let queue: [(t: T) => void, (e: unknown) => void][] = [];\n let addedToQueueCallbacks = new Map<string, () => void>;\n\n const next = async () => {\n while (true) {\n if (waitUntil > performance.now()) {\n await wait(Math.max(1, waitUntil - performance.now() + 1));\n } else if (queue.length === 0) {\n const uuid = generateUuid();\n await new Promise<void>(resolve => {\n addedToQueueCallbacks.set(uuid, resolve);\n });\n addedToQueueCallbacks.delete(uuid);\n } else {\n break;\n }\n }\n const nextFuncs = options.batchCalls ? queue.splice(0, queue.length) : [queue.shift()!];\n\n const start = performance.now();\n const value = await Result.fromPromise(func());\n const end = performance.now();\n\n waitUntil = Math.max(\n waitUntil,\n start + (options.throttleMs ?? 0),\n end + (options.gapMs ?? 0),\n );\n\n for (const nextFunc of nextFuncs) {\n if (value.status === \"ok\") {\n nextFunc[0](value.data);\n } else {\n nextFunc[1](value.error);\n }\n }\n };\n\n runAsynchronously(async () => {\n while (true) {\n await next();\n }\n });\n\n return () => {\n return new Promise<T>((resolve, reject) => {\n waitUntil = Math.max(\n waitUntil,\n performance.now() + (options.debounceMs ?? 0),\n );\n queue.push([resolve, reject]);\n addedToQueueCallbacks.forEach(cb => cb());\n });\n };\n}\n\nexport function throttled<T, A extends any[]>(func: (...args: A) => Promise<T>, delayMs: number): (...args: A) => Promise<T> {\n let timeout: ReturnType<typeof setTimeout> | null = null;\n let nextAvailable: Promise<T> | null = null;\n return async (...args) => {\n while (nextAvailable !== null) {\n await nextAvailable;\n }\n nextAvailable = new Promise<T>(resolve => {\n timeout = setTimeout(() => {\n nextAvailable = null;\n resolve(func(...args));\n }, delayMs);\n });\n return await nextAvailable;\n };\n}\n"],"mappings":";AAAA,SAAS,kBAAkB;AAC3B,SAAS,qBAAqB,cAAc,mBAAmB,yBAAyB;AACxF,SAAS,uBAAuB;AAChC,SAAS,cAAc;AACvB,SAAS,oBAAoB;AAUtB,SAAS,cAAiB,UAA0E;AACzG,MAAI,SAAS;AACb,MAAI,gBAAyC;AAC7C,MAAI,UAA6B;AACjC,MAAI,SAAwB;AAC5B,QAAM,UAAU,IAAI,QAAW,CAAC,KAAK,QAAQ;AAC3C,cAAU,CAAC,UAAU;AACnB,UAAI,WAAW,UAAW;AAC1B,eAAS;AACT,sBAAgB;AAChB,UAAI,KAAK;AAAA,IACX;AACA,aAAS,CAAC,WAAW;AACnB,UAAI,WAAW,UAAW;AAC1B,eAAS;AACT,sBAAgB;AAChB,UAAI,MAAM;AAAA,IACZ;AAAA,EACF,CAAC;AAED,WAAS,SAAU,MAAO;AAC1B,SAAO,OAAO,OAAO,SAAS;AAAA,IAC5B;AAAA,IACA,GAAG,WAAW,cAAc,EAAE,OAAO,cAAmB,IAAI,CAAC;AAAA,IAC7D,GAAG,WAAW,aAAa,EAAE,QAAQ,cAAc,IAAI,CAAC;AAAA,EAC1D,CAAQ;AACV;AAyCA,IAAI,gBAA0E;AAKvE,SAAS,SAAY,OAA2B;AACrD,oBAAkB,IAAI,gBAAkD;AACxE,MAAI,cAAc,IAAI,CAAC,KAAK,CAAC,GAAG;AAC9B,WAAO,cAAc,IAAI,CAAC,KAAK,CAAC;AAAA,EAClC;AAEA,QAAM,MAAM,OAAO,OAAO,QAAQ,QAAQ,KAAK,GAAG;AAAA,IAChD,QAAQ;AAAA,IACR;AAAA,EACF,CAAU;AACV,gBAAc,IAAI,CAAC,KAAK,GAAG,GAAG;AAC9B,SAAO;AACT;AAyBA,IAAI,gBAA0E;AAKvE,SAAS,SAAY,QAAkC;AAC5D,oBAAkB,IAAI,gBAAkD;AACxE,MAAI,cAAc,IAAI,CAAC,MAAM,CAAC,GAAG;AAC/B,WAAO,cAAc,IAAI,CAAC,MAAM,CAAC;AAAA,EACnC;AAEA,QAAM,UAAU,QAAQ,OAAO,MAAM;AACrC,2BAAyB,OAAO;AAChC,QAAM,MAAM,OAAO,OAAO,SAAS;AAAA,IACjC,QAAQ;AAAA,IACR;AAAA,EACF,CAAU;AACV,gBAAc,IAAI,CAAC,MAAM,GAAG,GAAG;AAC/B,SAAO;AACT;AA8BA,IAAM,sBAAsB,QAAQ,IAAI,QAAe,MAAM;AAAC,CAAC,CAAC;AACzD,SAAS,eAAoC;AAClD,SAAO;AACT;AAYO,SAAS,QAAW,SAAqB,UAA8C,CAAC,GAAoB;AACjH,QAAM,MAAM,QAAQ;AAAA,IAClB,WAAS;AACP,UAAI,SAAS;AACb,MAAC,IAAY,QAAQ;AACrB,aAAO;AAAA,IACT;AAAA,IACA,kBAAgB;AACd,UAAI,SAAS;AACb,MAAC,IAAY,SAAS;AACtB,YAAM;AAAA,IACR;AAAA,EACF;AACA,MAAI,SAAS;AACb,SAAO;AACT;AAyBO,SAAS,yBAAiD,SAAkB;AACjF,UAAQ,MAAM,MAAM;AAAA,EAAC,CAAC;AACxB;AAmBO,SAAS,4BAA+B,SAA2B;AACxE,QAAM,eAAe,IAAI,MAAM;AAC/B,UAAQ,MAAM,WAAS;AACrB,QAAI,iBAAiB,OAAO;AAC1B,wBAAkB,OAAO,YAAY;AAAA,IACvC,OAAO;AAAA,IAEP;AAAA,EACF,CAAC;AACH;AAEA,eAAsB,KAAK,IAAY;AACrC,MAAI,CAAC,OAAO,SAAS,EAAE,KAAK,KAAK,GAAG;AAClC,UAAM,IAAI,oBAAoB,kFAAkF,EAAE,KAAK;AAAA,EACzH;AACA,MAAI,MAAM,KAAG,IAAI;AACf,UAAM,IAAI,oBAAoB,8EAA8E;AAAA,EAC9G;AACA,SAAO,MAAM,IAAI,QAAc,aAAW,WAAW,SAAS,EAAE,CAAC;AACnE;AAsBA,eAAsB,UAAU,MAAY;AAC1C,SAAO,MAAM,KAAK,KAAK,QAAQ,IAAI,KAAK,IAAI,CAAC;AAC/C;AAoBO,SAAS,8BAA8B,MAA4C;AACxF,SAAO;AAAA,IACL,KAAK,CAAC;AAAA,IACN;AAAA,MACE,GAAG,KAAK,CAAC;AAAA,MACT,SAAS,WAAS;AAChB,YAAI,WAAW,aAAa,KAAK,KAAK,OAAO,YAAY,eAAgB,QAAQ,IAAI,UAAkB,SAAS,YAAY,GAAG;AAC7H,gBAAM,MAAM,OAAO;AAAA,QACrB,OAAO;AACL,gBAAM,uCAAuC,QAAQ,IAAI,aAAa,gBAAgB,kDAAkD,+BAA+B;AAAA;AAAA,EAAO,KAAK,EAAE;AAAA,QACvL;AACA,aAAK,CAAC,GAAG,UAAU,KAAK;AAAA,MAC1B;AAAA,IACF;AAAA,IACA,GAAG,KAAK,MAAM,CAAC;AAAA,EACjB;AACF;AAeO,SAAS,kBACd,eACA,UAGI,CAAC,GACC;AACN,MAAI,OAAO,kBAAkB,YAAY;AACvC,oBAAgB,cAAc;AAAA,EAChC;AACA,MAAI,eAAe;AACjB,gCAA4B,aAAa;AACzC,kBAAc,MAAM,WAAS;AAC3B,cAAQ,UAAU,KAAK;AACvB,YAAM,WAAW,IAAI;AAAA,QACnB,8CAA8C,kBAAkB,KAAK;AAAA,QACrE,EAAE,OAAO,MAAM;AAAA,MACjB;AACA,UAAI,CAAC,QAAQ,gBAAgB;AAC3B,qBAAa,qBAAqB,QAAQ;AAAA,MAC5C;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAiBA,IAAM,eAAN,cAA2B,MAAM;AAAA,EAC/B,YAA4B,IAAY;AACtC,UAAM,iBAAiB,EAAE,IAAI;AADH;AAE1B,SAAK,OAAO;AAAA,EACd;AACF;AAEA,eAAsB,QAAW,SAAqB,IAA8C;AAClG,SAAO,MAAM,QAAQ,KAAK;AAAA,IACxB,QAAQ,KAAK,WAAS,OAAO,GAAG,KAAK,CAAC;AAAA,IACtC,KAAK,EAAE,EAAE,KAAK,MAAM,OAAO,MAAM,IAAI,aAAa,EAAE,CAAC,CAAC;AAAA,EACxD,CAAC;AACH;AAoBA,eAAsB,aAAgB,SAAqB,IAAwB;AACjF,SAAO,OAAO,QAAQ,MAAM,QAAQ,SAAS,EAAE,CAAC;AAClD;AAyCO,SAAS,YACd,MACA,SACkB;AAClB,MAAIA,aAAY,YAAY,IAAI;AAChC,MAAI,QAAkD,CAAC;AACvD,MAAI,wBAAwB,oBAAI;AAEhC,QAAM,OAAO,YAAY;AACvB,WAAO,MAAM;AACX,UAAIA,aAAY,YAAY,IAAI,GAAG;AACjC,cAAM,KAAK,KAAK,IAAI,GAAGA,aAAY,YAAY,IAAI,IAAI,CAAC,CAAC;AAAA,MAC3D,WAAW,MAAM,WAAW,GAAG;AAC7B,cAAM,OAAO,aAAa;AAC1B,cAAM,IAAI,QAAc,aAAW;AACjC,gCAAsB,IAAI,MAAM,OAAO;AAAA,QACzC,CAAC;AACD,8BAAsB,OAAO,IAAI;AAAA,MACnC,OAAO;AACL;AAAA,MACF;AAAA,IACF;AACA,UAAM,YAAY,QAAQ,aAAa,MAAM,OAAO,GAAG,MAAM,MAAM,IAAI,CAAC,MAAM,MAAM,CAAE;AAEtF,UAAM,QAAQ,YAAY,IAAI;AAC9B,UAAM,QAAQ,MAAM,OAAO,YAAY,KAAK,CAAC;AAC7C,UAAM,MAAM,YAAY,IAAI;AAE5B,IAAAA,aAAY,KAAK;AAAA,MACfA;AAAA,MACA,SAAS,QAAQ,cAAc;AAAA,MAC/B,OAAO,QAAQ,SAAS;AAAA,IAC1B;AAEA,eAAW,YAAY,WAAW;AAChC,UAAI,MAAM,WAAW,MAAM;AACzB,iBAAS,CAAC,EAAE,MAAM,IAAI;AAAA,MACxB,OAAO;AACL,iBAAS,CAAC,EAAE,MAAM,KAAK;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAEA,oBAAkB,YAAY;AAC5B,WAAO,MAAM;AACX,YAAM,KAAK;AAAA,IACb;AAAA,EACF,CAAC;AAED,SAAO,MAAM;AACX,WAAO,IAAI,QAAW,CAAC,SAAS,WAAW;AACzC,MAAAA,aAAY,KAAK;AAAA,QACfA;AAAA,QACA,YAAY,IAAI,KAAK,QAAQ,cAAc;AAAA,MAC7C;AACA,YAAM,KAAK,CAAC,SAAS,MAAM,CAAC;AAC5B,4BAAsB,QAAQ,QAAM,GAAG,CAAC;AAAA,IAC1C,CAAC;AAAA,EACH;AACF;AAEO,SAAS,UAA8B,MAAkC,SAA6C;AAC3H,MAAIC,WAAgD;AACpD,MAAI,gBAAmC;AACvC,SAAO,UAAU,SAAS;AACxB,WAAO,kBAAkB,MAAM;AAC7B,YAAM;AAAA,IACR;AACA,oBAAgB,IAAI,QAAW,aAAW;AACxC,MAAAA,WAAU,WAAW,MAAM;AACzB,wBAAgB;AAChB,gBAAQ,KAAK,GAAG,IAAI,CAAC;AAAA,MACvB,GAAG,OAAO;AAAA,IACZ,CAAC;AACD,WAAO,MAAM;AAAA,EACf;AACF;","names":["waitUntil","timeout"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/utils/promises.tsx"],"sourcesContent":["import { KnownError } from \"..\";\nimport { StackAssertionError, captureError, concatStacktraces, errorToNiceString } from \"./errors\";\nimport { DependenciesMap } from \"./maps\";\nimport { Result } from \"./results\";\nimport { generateUuid } from \"./uuids\";\n\nexport type ReactPromise<T> = Promise<T> & (\n | { status: \"rejected\", reason: unknown }\n | { status: \"fulfilled\", value: T }\n | { status: \"pending\" }\n);\n\ntype Resolve<T> = (value: T) => void;\ntype Reject = (reason: unknown) => void;\nexport function createPromise<T>(callback: (resolve: Resolve<T>, reject: Reject) => void): ReactPromise<T> {\n let status = \"pending\" as \"fulfilled\" | \"rejected\" | \"pending\";\n let valueOrReason: T | unknown | undefined = undefined;\n let resolve: Resolve<T> | null = null;\n let reject: Reject | null = null;\n const promise = new Promise<T>((res, rej) => {\n resolve = (value) => {\n if (status !== \"pending\") return;\n status = \"fulfilled\";\n valueOrReason = value;\n res(value);\n };\n reject = (reason) => {\n if (status !== \"pending\") return;\n status = \"rejected\";\n valueOrReason = reason;\n rej(reason);\n };\n });\n\n callback(resolve!, reject!);\n return Object.assign(promise, {\n status: status,\n ...status === \"fulfilled\" ? { value: valueOrReason as T } : {},\n ...status === \"rejected\" ? { reason: valueOrReason } : {},\n } as any);\n}\nundefined?.test(\"createPromise\", async ({ expect }) => {\n // Test resolved promise\n const resolvedPromise = createPromise<number>((resolve) => {\n resolve(42);\n });\n expect(resolvedPromise.status).toBe(\"fulfilled\");\n expect((resolvedPromise as any).value).toBe(42);\n expect(await resolvedPromise).toBe(42);\n\n // Test rejected promise\n const error = new Error(\"Test error\");\n const rejectedPromise = createPromise<number>((_, reject) => {\n reject(error);\n });\n expect(rejectedPromise.status).toBe(\"rejected\");\n expect((rejectedPromise as any).reason).toBe(error);\n await expect(rejectedPromise).rejects.toBe(error);\n\n // Test pending promise\n const pendingPromise = createPromise<number>(() => {\n // Do nothing, leave it pending\n });\n expect(pendingPromise.status).toBe(\"pending\");\n expect((pendingPromise as any).value).toBeUndefined();\n expect((pendingPromise as any).reason).toBeUndefined();\n\n // Test that resolving after already resolved does nothing\n let resolveCount = 0;\n const multiResolvePromise = createPromise<number>((resolve) => {\n resolve(1);\n resolveCount++;\n resolve(2);\n resolveCount++;\n });\n expect(resolveCount).toBe(2); // Both resolve calls executed\n expect(multiResolvePromise.status).toBe(\"fulfilled\");\n expect((multiResolvePromise as any).value).toBe(1); // Only first resolve took effect\n expect(await multiResolvePromise).toBe(1);\n});\n\nlet resolvedCache: DependenciesMap<[unknown], ReactPromise<unknown>> | null = null;\n/**\n * Like Promise.resolve(...), but also adds the status and value properties for use with React's `use` hook, and caches\n * the value so that invoking `resolved` twice returns the same promise.\n */\nexport function resolved<T>(value: T): ReactPromise<T> {\n resolvedCache ??= new DependenciesMap<[unknown], ReactPromise<unknown>>();\n if (resolvedCache.has([value])) {\n return resolvedCache.get([value]) as ReactPromise<T>;\n }\n\n const res = Object.assign(Promise.resolve(value), {\n status: \"fulfilled\",\n value,\n } as const);\n resolvedCache.set([value], res);\n return res;\n}\nundefined?.test(\"resolved\", async ({ expect }) => {\n // Test with primitive value\n const promise1 = resolved(42);\n expect(promise1.status).toBe(\"fulfilled\");\n // Need to use type assertion since value is only available when status is \"fulfilled\"\n expect((promise1 as { value: number }).value).toBe(42);\n expect(await promise1).toBe(42);\n\n // Test with object value\n const obj = { test: true };\n const promise2 = resolved(obj);\n expect(promise2.status).toBe(\"fulfilled\");\n expect((promise2 as { value: typeof obj }).value).toBe(obj);\n expect(await promise2).toBe(obj);\n\n // Test caching (same reference for same value)\n const promise3 = resolved(42);\n expect(promise3).toBe(promise1); // Same reference due to caching\n\n // Test with different value (different reference)\n const promise4 = resolved(43);\n expect(promise4).not.toBe(promise1);\n});\n\nlet rejectedCache: DependenciesMap<[unknown], ReactPromise<unknown>> | null = null;\n/**\n * Like Promise.reject(...), but also adds the status and value properties for use with React's `use` hook, and caches\n * the value so that invoking `rejected` twice returns the same promise.\n */\nexport function rejected<T>(reason: unknown): ReactPromise<T> {\n rejectedCache ??= new DependenciesMap<[unknown], ReactPromise<unknown>>();\n if (rejectedCache.has([reason])) {\n return rejectedCache.get([reason]) as ReactPromise<T>;\n }\n\n const promise = Promise.reject(reason);\n ignoreUnhandledRejection(promise);\n const res = Object.assign(promise, {\n status: \"rejected\",\n reason: reason,\n } as const);\n rejectedCache.set([reason], res);\n return res;\n}\nundefined?.test(\"rejected\", ({ expect }) => {\n // Test with error object\n const error = new Error(\"Test error\");\n const promise1 = rejected<number>(error);\n expect(promise1.status).toBe(\"rejected\");\n // Need to use type assertion since reason is only available when status is \"rejected\"\n expect((promise1 as { reason: Error }).reason).toBe(error);\n\n // Test with string reason\n const promise2 = rejected<string>(\"error message\");\n expect(promise2.status).toBe(\"rejected\");\n expect((promise2 as { reason: string }).reason).toBe(\"error message\");\n\n // Test caching (same reference for same reason)\n const promise3 = rejected<number>(error);\n expect(promise3).toBe(promise1); // Same reference due to caching\n\n // Test with different reason (different reference)\n const differentError = new Error(\"Different error\");\n const promise4 = rejected<number>(differentError);\n expect(promise4).not.toBe(promise1);\n\n // Note: We're not using await expect(promise).rejects to avoid unhandled rejections\n});\n\n// We'll skip the rejection test for pending() since it's causing unhandled rejections\n// The function is already well tested through other tests like rejected() and createPromise()\n\n\nconst neverResolvePromise = pending(new Promise<never>(() => {}));\nexport function neverResolve(): ReactPromise<never> {\n return neverResolvePromise;\n}\nundefined?.test(\"neverResolve\", ({ expect }) => {\n const promise = neverResolve();\n expect(promise.status).toBe(\"pending\");\n expect((promise as any).value).toBeUndefined();\n expect((promise as any).reason).toBeUndefined();\n\n // Test that multiple calls return the same promise\n const promise2 = neverResolve();\n expect(promise2).toBe(promise);\n});\n\nexport function pending<T>(promise: Promise<T>, options: { disableErrorWrapping?: boolean } = {}): ReactPromise<T> {\n const res = promise.then(\n value => {\n res.status = \"fulfilled\";\n (res as any).value = value;\n return value;\n },\n actualReason => {\n res.status = \"rejected\";\n (res as any).reason = actualReason;\n throw actualReason;\n },\n ) as ReactPromise<T>;\n res.status = \"pending\";\n return res;\n}\nundefined?.test(\"pending\", async ({ expect }) => {\n // Test with a promise that resolves\n const resolvePromise = Promise.resolve(42);\n const pendingPromise = pending(resolvePromise);\n\n // Initially it should be pending\n expect(pendingPromise.status).toBe(\"pending\");\n\n // After resolution, it should be fulfilled\n await resolvePromise;\n // Need to wait a tick for the then handler to execute\n await new Promise(resolve => setTimeout(resolve, 0));\n expect(pendingPromise.status).toBe(\"fulfilled\");\n expect((pendingPromise as { value: number }).value).toBe(42);\n\n // For the rejection test, we'll use a separate test to avoid unhandled rejections\n});\n\n/**\n * Should be used to wrap Promises that are not immediately awaited, so they don't throw an unhandled promise rejection\n * error.\n *\n * Vercel kills serverless functions on unhandled promise rejection errors, so this is important.\n */\nexport function ignoreUnhandledRejection<T extends Promise<any>>(promise: T): void {\n promise.catch(() => {});\n}\nundefined?.test(\"ignoreUnhandledRejection\", async ({ expect }) => {\n // Test with a promise that resolves\n const resolvePromise = Promise.resolve(42);\n ignoreUnhandledRejection(resolvePromise);\n expect(await resolvePromise).toBe(42); // Should still resolve to the same value\n\n // Test with a promise that rejects\n // The promise should still reject, but the rejection is caught internally\n // so it doesn't cause an unhandled rejection error\n const error = new Error(\"Test error\");\n const rejectPromise = Promise.reject(error);\n ignoreUnhandledRejection(rejectPromise);\n await expect(rejectPromise).rejects.toBe(error);\n});\n\n/**\n * See concatStacktraces for more information.\n */\nexport function concatStacktracesIfRejected<T>(promise: Promise<T>): void {\n const currentError = new Error();\n promise.catch(error => {\n if (error instanceof Error) {\n concatStacktraces(error, currentError);\n } else {\n // we can only concatenate errors, so we'll just ignore the non-error\n }\n });\n}\n\nexport async function wait(ms: number) {\n if (!Number.isFinite(ms) || ms < 0) {\n throw new StackAssertionError(`wait() requires a non-negative integer number of milliseconds to wait. (found: ${ms}ms)`);\n }\n if (ms >= 2**31) {\n throw new StackAssertionError(\"The maximum timeout for wait() is 2147483647ms (2**31 - 1). (found: ${ms}ms)\");\n }\n return await new Promise<void>(resolve => setTimeout(resolve, ms));\n}\nundefined?.test(\"wait\", async ({ expect }) => {\n // Test with valid input\n const start = Date.now();\n await wait(10);\n const elapsed = Date.now() - start;\n expect(elapsed).toBeGreaterThanOrEqual(5); // Allow some flexibility in timing\n\n // Test with zero\n await expect(wait(0)).resolves.toBeUndefined();\n\n // Test with negative number\n await expect(wait(-10)).rejects.toThrow(\"wait() requires a non-negative integer\");\n\n // Test with non-finite number\n await expect(wait(NaN)).rejects.toThrow(\"wait() requires a non-negative integer\");\n await expect(wait(Infinity)).rejects.toThrow(\"wait() requires a non-negative integer\");\n\n // Test with too large number\n await expect(wait(2**31)).rejects.toThrow(\"The maximum timeout for wait()\");\n});\n\nexport async function waitUntil(date: Date) {\n return await wait(date.getTime() - Date.now());\n}\nundefined?.test(\"waitUntil\", async ({ expect }) => {\n // Test with future date\n const futureDate = new Date(Date.now() + 10);\n const start = Date.now();\n await waitUntil(futureDate);\n const elapsed = Date.now() - start;\n expect(elapsed).toBeGreaterThanOrEqual(5); // Allow some flexibility in timing\n\n // Test with past date - this will throw because wait() requires non-negative time\n // We need to verify it throws the correct error\n try {\n await waitUntil(new Date(Date.now() - 1000));\n expect.fail(\"Should have thrown an error\");\n } catch (error) {\n expect(error).toBeInstanceOf(StackAssertionError);\n expect((error as Error).message).toContain(\"wait() requires a non-negative integer\");\n }\n});\n\nexport function runAsynchronouslyWithAlert(...args: Parameters<typeof runAsynchronously>) {\n return runAsynchronously(\n args[0],\n {\n ...args[1],\n onError: error => {\n if (KnownError.isKnownError(error) && typeof process !== \"undefined\" && (process.env.NODE_ENV as any)?.includes(\"production\")) {\n alert(error.message);\n } else {\n alert(`An unhandled error occurred. Please ${process.env.NODE_ENV === \"development\" ? `check the browser console for the full error.` : \"report this to the developer.\"}\\n\\n${error}`);\n }\n args[1]?.onError?.(error);\n },\n },\n ...args.slice(2) as [],\n );\n}\nundefined?.test(\"runAsynchronouslyWithAlert\", ({ expect }) => {\n // Simple test to verify the function calls runAsynchronously\n // We can't easily test the alert functionality without mocking\n const testFn = () => Promise.resolve(\"test\");\n const testOptions = { noErrorLogging: true };\n\n // Just verify it doesn't throw\n expect(() => runAsynchronouslyWithAlert(testFn, testOptions)).not.toThrow();\n\n // We can't easily test the error handling without mocking, so we'll\n // just verify the function exists and can be called\n expect(typeof runAsynchronouslyWithAlert).toBe(\"function\");\n});\n\nexport function runAsynchronously(\n promiseOrFunc: void | Promise<unknown> | (() => void | Promise<unknown>) | undefined,\n options: {\n noErrorLogging?: boolean,\n onError?: (error: Error) => void,\n } = {},\n): void {\n if (typeof promiseOrFunc === \"function\") {\n promiseOrFunc = promiseOrFunc();\n }\n if (promiseOrFunc) {\n concatStacktracesIfRejected(promiseOrFunc);\n promiseOrFunc.catch(error => {\n options.onError?.(error);\n const newError = new StackAssertionError(\n \"Uncaught error in asynchronous function: \" + errorToNiceString(error),\n { cause: error },\n );\n if (!options.noErrorLogging) {\n captureError(\"runAsynchronously\", newError);\n }\n });\n }\n}\nundefined?.test(\"runAsynchronously\", ({ expect }) => {\n // Simple test to verify the function exists and can be called\n const testFn = () => Promise.resolve(\"test\");\n\n // Just verify it doesn't throw\n expect(() => runAsynchronously(testFn)).not.toThrow();\n expect(() => runAsynchronously(Promise.resolve(\"test\"))).not.toThrow();\n expect(() => runAsynchronously(undefined)).not.toThrow();\n\n // We can't easily test the error handling without mocking, so we'll\n // just verify the function exists and can be called with options\n expect(() => runAsynchronously(testFn, { noErrorLogging: true })).not.toThrow();\n expect(() => runAsynchronously(testFn, { onError: () => {} })).not.toThrow();\n});\n\n\nclass TimeoutError extends Error {\n constructor(public readonly ms: number) {\n super(`Timeout after ${ms}ms`);\n this.name = \"TimeoutError\";\n }\n}\n\nexport async function timeout<T>(promiseOrFunc: Promise<T> | (() => Promise<T>), ms: number): Promise<Result<T, TimeoutError>> {\n const promise = typeof promiseOrFunc === \"function\" ? promiseOrFunc() : promiseOrFunc;\n return await Promise.race([\n promise.then(value => Result.ok(value)),\n wait(ms).then(() => Result.error(new TimeoutError(ms))),\n ]);\n}\nundefined?.test(\"timeout\", async ({ expect }) => {\n // Test with a promise that resolves quickly\n const fastPromise = Promise.resolve(42);\n const fastResult = await timeout(fastPromise, 100);\n expect(fastResult.status).toBe(\"ok\");\n if (fastResult.status === \"ok\") {\n expect(fastResult.data).toBe(42);\n }\n\n // Test with a promise that takes longer than the timeout\n const slowPromise = new Promise(resolve => setTimeout(() => resolve(\"too late\"), 50));\n const slowResult = await timeout(slowPromise, 10);\n expect(slowResult.status).toBe(\"error\");\n if (slowResult.status === \"error\") {\n expect(slowResult.error).toBeInstanceOf(TimeoutError);\n expect((slowResult.error as TimeoutError).ms).toBe(10);\n }\n});\n\nexport async function timeoutThrow<T>(promise: Promise<T>, ms: number): Promise<T> {\n return Result.orThrow(await timeout(promise, ms));\n}\nundefined?.test(\"timeoutThrow\", async ({ expect }) => {\n // Test with a promise that resolves quickly\n const fastPromise = Promise.resolve(42);\n const fastResult = await timeoutThrow(fastPromise, 100);\n expect(fastResult).toBe(42);\n\n // Test with a promise that takes longer than the timeout\n const slowPromise = new Promise(resolve => setTimeout(() => resolve(\"too late\"), 50));\n await expect(timeoutThrow(slowPromise, 10)).rejects.toThrow(\"Timeout after 10ms\");\n await expect(timeoutThrow(slowPromise, 10)).rejects.toBeInstanceOf(TimeoutError);\n});\n\n\nexport type RateLimitOptions = {\n /**\n * The number of requests to process in parallel. Currently only 1 is supported.\n */\n concurrency: 1,\n\n /**\n * If true, multiple requests waiting at the same time will be reduced to just one. Default is false.\n */\n batchCalls?: boolean,\n\n /**\n * Waits for throttleMs since the start of last request before starting the next request. Default is 0.\n */\n throttleMs?: number,\n\n /**\n * Waits for gapMs since the end of last request before starting the next request. Default is 0.\n */\n gapMs?: number,\n\n /**\n * Waits until there have been no new requests for debounceMs before starting a new request. Default is 0.\n */\n debounceMs?: number,\n};\n\nexport function rateLimited<T>(\n func: () => Promise<T>,\n options: RateLimitOptions,\n): () => Promise<T> {\n let waitUntil = performance.now();\n let queue: [(t: T) => void, (e: unknown) => void][] = [];\n let addedToQueueCallbacks = new Map<string, () => void>;\n\n const next = async () => {\n while (true) {\n if (waitUntil > performance.now()) {\n await wait(Math.max(1, waitUntil - performance.now() + 1));\n } else if (queue.length === 0) {\n const uuid = generateUuid();\n await new Promise<void>(resolve => {\n addedToQueueCallbacks.set(uuid, resolve);\n });\n addedToQueueCallbacks.delete(uuid);\n } else {\n break;\n }\n }\n const nextFuncs = options.batchCalls ? queue.splice(0, queue.length) : [queue.shift()!];\n\n const start = performance.now();\n const value = await Result.fromPromise(func());\n const end = performance.now();\n\n waitUntil = Math.max(\n waitUntil,\n start + (options.throttleMs ?? 0),\n end + (options.gapMs ?? 0),\n );\n\n for (const nextFunc of nextFuncs) {\n if (value.status === \"ok\") {\n nextFunc[0](value.data);\n } else {\n nextFunc[1](value.error);\n }\n }\n };\n\n runAsynchronously(async () => {\n while (true) {\n await next();\n }\n });\n\n return () => {\n return new Promise<T>((resolve, reject) => {\n waitUntil = Math.max(\n waitUntil,\n performance.now() + (options.debounceMs ?? 0),\n );\n queue.push([resolve, reject]);\n addedToQueueCallbacks.forEach(cb => cb());\n });\n };\n}\n\nexport function throttled<T, A extends any[]>(func: (...args: A) => Promise<T>, delayMs: number): (...args: A) => Promise<T> {\n let timeout: ReturnType<typeof setTimeout> | null = null;\n let nextAvailable: Promise<T> | null = null;\n return async (...args) => {\n while (nextAvailable !== null) {\n await nextAvailable;\n }\n nextAvailable = new Promise<T>(resolve => {\n timeout = setTimeout(() => {\n nextAvailable = null;\n resolve(func(...args));\n }, delayMs);\n });\n return await nextAvailable;\n };\n}\n"],"mappings":"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","names":["waitUntil","timeout"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/utils/types.tsx"],"sourcesContent":["import { DeepPartial } from \"./objects\";\nimport { Join } from \"./strings\";\n\nexport type IsAny<T> = 0 extends (1 & T) ? true : false;\nexport type IsNever<T> = [T] extends [never] ? true : false;\nexport type IsNullish<T> = [T] extends [null | undefined] ? true : false;\nexport type IsUnion<T, U = T> =\n IsNever<T> extends true ? false\n : IsAny<T> extends true ? false\n : T extends U // distributive conditional https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#distributive-conditional-types\n ? /* if the *whole* original type (`U`) still fits inside the current variant, then `T` wasn’t a union */ ([U] extends [T] ? false : true)\n : never;\n\nexport type NullishCoalesce<T, U> = T extends null | undefined ? U : T;\n\nexport type LastUnionElement<U> = UnionToIntersection<U extends any ? (x: U) => 0 : never> extends (x: infer L) => 0 ? L & U : never;\n\ntype primitive = string | number | boolean | bigint | symbol | null | undefined;\n\n/**\n * Makes a type prettier by recursively expanding all object types. For example, `Omit<{ a: 1 }, \"a\">` becomes just `{}`.\n */\nexport type Expand<T> = T extends (...args: infer A) => infer R\n ? ((...args: A) => R) extends T\n ? (...args: Expand<A>) => Expand<R>\n : ((...args: Expand<A>) => Expand<R>) & { [K in keyof T]: Expand<T[K]> }\n : T extends object\n ? T extends primitive\n ? T\n : T extends infer O\n ? { [K in keyof O]: Expand<O[K]> }\n : never\n : T;\n\n\n/**\n * Removes all optional undefined/never keys from an object.\n */\nexport type DeepRemoveOptionalUndefined<T> = T extends object ? { [K in keyof T]: DeepRemoveOptionalUndefined<T[K]> } : T;\n\n// why this works: https://stackoverflow.com/a/50375286\nexport type UnionToIntersection<U> =\n (U extends any ? (x: U) => void : never) extends ((x: infer I) => void) ? I : never\n\ntype _UnionToTupleInner<U, R extends any[], Last> = UnionToTuple<Exclude<U, Last>, [...R, Last]>\nexport type UnionToTuple<U, R extends any[] = []> = [U] extends [never] ? R : _UnionToTupleInner<U, R, LastUnionElement<U>>;\n\nexport type CollapseObjectUnion<T extends object> = {\n [K in AllUnionKeys<T>]?: T extends Record<K, infer V> ? V : never;\n};\ntypeAssertIs<CollapseObjectUnion<{ a: string } | { b: number }>, { a?: string, b?: number }>()();\ntypeAssertIs<CollapseObjectUnion<{ a: string } | { a: number }>, { a?: string | number }>()();\n\nexport type IntersectAll<T extends any[]> = UnionToIntersection<T[number]>;\n\nexport type OptionalKeys<T> = {\n [K in keyof T]: {} extends Pick<T, K> ? K : never;\n}[keyof T];\nexport type RequiredKeys<T> = {\n [K in keyof T]: {} extends Pick<T, K> ? never : K;\n}[keyof T];\n\n/**\n * Returns a type whose keys are the intersection of the keys of T and U, deeply.\n */\nexport type KeyIntersect<T, U> =\n | { [K in keyof T & keyof U]?: T[K] & U[K] }\n | { [K in RequiredKeys<T> & keyof U]: T[K] & U[K] }\n | { [K in RequiredKeys<U> & keyof T]: U[K] & T[K] }\n\n/**\n * Returns ALL keys of all union elements.\n */\nexport type AllUnionKeys<T extends object> = T extends T ? keyof T : never;\ntypeAssertIs<AllUnionKeys<{ a: string } | { b: number }>, \"a\" | \"b\">()();\n\nexport type SubtractType<T, U> = T extends object ? { [K in keyof T]: K extends keyof U ? SubtractType<T[K], U[K]> : T[K] } : (T extends U ? never : T); // note: this only works due to the distributive property of conditional types https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#distributive-conditional-types\n\n\ntype _AntiIntersectInner<T, U> = T extends object ? (\n & Omit<U, keyof T>\n & { [K in keyof Pick<U, { [K in keyof T & keyof U]: U[K] extends T[K] ? (T[K] extends U[K] ? never : K) : never }[keyof T & keyof U]>]: PseudoAntiIntersect<T[K], U[K]> }\n & { [K in keyof Pick<U, keyof T & keyof U>]?: PseudoAntiIntersect<T[K], U[K]> }\n) : U;\n/**\n * Returns a type R such that T & R = U.\n */\nexport type AntiIntersect<T, U> = U extends T ? _AntiIntersectInner<T, U> : \"Cannot anti-intersect a type with a type that is not a subtype of it\"; // NOTE: This type is mostly untested — not sure how well it works on the edge cases\nexport type PseudoAntiIntersect<T, U> = _AntiIntersectInner<T, T & U>;\n\n/**\n * A variation of TypeScript's conditionals with slightly different semantics. It is the perfect type for cases where:\n *\n * - If all possible values are contained in `Extends`, then it will be mapped to `Then`.\n * - If all possible values are not contained in `Extends`, then it will be mapped to `Otherwise`.\n * - If some possible values are contained in `Extends` and some are not, then it will be mapped to `Then | Otherwise`.\n *\n * This is different from TypeScript's built-in conditional types (`Value extends Extends ? Then : Otherwise`), which\n * returns `Otherwise` for the third case (causing unsoundness in many real-world cases).\n */\nexport type IfAndOnlyIf<Value, Extends, Then, Otherwise> =\n | (Value extends Extends ? never : Otherwise)\n | (Value & Extends extends never ? never : Then);\n\n\n/**\n * Can be used to prettify a type in the IDE; for example, some complicated intersected types can be flattened into a single type.\n */\nexport type PrettifyType<T> = T extends object ? { [K in keyof T]: T[K] } & {} : T;\n\ntype _ToStringAndJoin<T extends any[], Separator extends string> =\n T extends [infer U, ...infer Rest extends any[]]\n ? `${TypeToString<U>}${Rest extends [any, ...any[]] ? `${Separator}${_ToStringAndJoin<Rest, Separator>}` : \"\"}`\n : \"<error-joining-tuple-elements>\";\ntype _TypeToStringInner<T> =\n IsAny<T> extends true ? \"any\"\n : IsNever<T> extends true ? \"never\"\n : IsUnion<T> extends true ? _ToStringAndJoin<UnionToTuple<T>, \" | \">\n : [T] extends [number] ? (number extends T ? \"number\" : `${T}`)\n : [T] extends [boolean] ? `${T}`\n : [T] extends [undefined] ? \"undefined\"\n : [T] extends [null] ? \"null\"\n : [T] extends [string] ? (string extends T ? \"string\" : `'${T}'`)\n : [T] extends [[]] ? \"[]\"\n : [T] extends [[any, ...any[]]] ? `[${_ToStringAndJoin<T, \", \">}]`\n : [T] extends [(infer E)[]] ? `${TypeToString<E>}[]`\n : [T] extends [Function] ? \"function\"\n : [T] extends [symbol] ? `symbol(${T['description']})`\n : [T] extends [object] ? `{ ${Join<UnionToTuple<{ [K in keyof T]: `${TypeToString<K>}: ${TypeToString<T[K]>}` }[keyof T]>, \", \">} }`\n : \"<unknown-type>\"\nexport type TypeToString<T> = _TypeToStringInner<T> extends `${infer S}` ? S : never;\n\n/**\n * Can be used to create assertions on types. For example, if passed any T other than `true`, the following will\n * show a type error:\n *\n * ```ts\n * typeAssert<T>()(); // the second pair of braces is important!\n * ```\n */\nexport function typeAssert<T>(): (\n IsAny<T> extends true ? TypeAssertionError<`Type assertion failed. Expected true, but got any.`>\n : IsNever<T> extends true ? TypeAssertionError<`Type assertion failed. Expected true, but got never.`>\n : T extends true ? (() => undefined)\n : TypeAssertionError<`Type assertion failed. Expected true, but got: ${TypeToString<T>}`>\n) {\n return (() => undefined) as any;\n}\ntype TypeAssertionError<T> =\n & [T]\n & /* this promise makes sure that if we accidentally forget the second pair of braces, eslint will complain (if we have no-floating-promises enabled) */ Promise<any>;\n\n\ntypeAssertExtends<ReturnType<typeof typeAssert<true>>, () => undefined>()();\ntypeAssertExtends<ReturnType<typeof typeAssert<false>>, TypeAssertionError<`Type assertion failed. Expected true, but got: false`>>()();\ntypeAssertExtends<ReturnType<typeof typeAssert<never>>, TypeAssertionError<`Type assertion failed. Expected true, but got never.`>>()();\ntypeAssertExtends<ReturnType<typeof typeAssert<any>>, TypeAssertionError<`Type assertion failed. Expected true, but got any.`>>()();\n\n/**\n * Functionally equivalent to `typeAssert<T extends S ? true : false>()()`, but with better error messages.\n */\nexport function typeAssertExtends<T, S>(): (\n [T] extends [S] ? (() => undefined) : TypeAssertionError<`Type assertion failed. Expected ${TypeToString<T>} to extend ${TypeToString<S>}`>\n) {\n return (() => undefined) as any;\n}\n\ntypeAssertExtends<ReturnType<typeof typeAssertExtends<never, true>>, () => undefined>()();\ntypeAssertExtends<ReturnType<typeof typeAssertExtends<any, true>>, () => undefined>()();\ntypeAssertExtends<ReturnType<typeof typeAssertExtends<false, false>>, () => undefined>()();\ntypeAssertExtends<ReturnType<typeof typeAssertExtends<\"abc\", string>>, () => undefined>()();\ntypeAssertExtends<ReturnType<typeof typeAssertExtends<{a: 1, b: 123}, {a: number}>>, () => undefined>()();\ntypeAssertExtends<ReturnType<typeof typeAssertExtends<never, never>>, () => undefined>()();\ntypeAssertExtends<ReturnType<typeof typeAssertExtends<true, any>>, () => undefined>()();\n\ntypeAssertExtends<ReturnType<typeof typeAssertExtends<{a: number}, {a: 1}>>, [\"Type assertion failed. Expected { 'a': number } to extend { 'a': 1 }\"]>()();\ntypeAssertExtends<ReturnType<typeof typeAssertExtends<any, never>>, [\"Type assertion failed. Expected any to extend never\"]>()();\ntypeAssertExtends<ReturnType<typeof typeAssertExtends<false, true>>, [\"Type assertion failed. Expected false to extend true\"]>()();\ntypeAssertExtends<ReturnType<typeof typeAssertExtends<false, never>>, [\"Type assertion failed. Expected false to extend never\"]>()();\n\n\nexport function typeAssertIs<T, U>(): (\n IsAny<T> extends true ? (IsAny<U> extends true ? (() => undefined) : TypeAssertionError<`Type assertion failed. Expected ${TypeToString<T>} to be ${TypeToString<U>}`>)\n : IsAny<U> extends true ? TypeAssertionError<`Type assertion failed. Expected ${TypeToString<T>} to be ${TypeToString<U>}`>\n : [T] extends [U] ? ([U] extends [T] ? (() => undefined) : TypeAssertionError<`Type assertion failed. Expected ${TypeToString<T>} to be ${TypeToString<U>}`>)\n : TypeAssertionError<`Type assertion failed. Expected ${TypeToString<T>} to be ${TypeToString<U>}`>\n) {\n return (() => undefined) as any;\n}\n\ntypeAssertExtends<ReturnType<typeof typeAssertIs<\"123\", \"123\">>, () => undefined>()();\ntypeAssertExtends<ReturnType<typeof typeAssertIs<{a: 1}, {a: 1}>>, () => undefined>()();\ntypeAssertExtends<ReturnType<typeof typeAssertIs<DeepPartial<{a: 1}>, {a?: 1}>>, () => undefined>()();\ntypeAssertExtends<ReturnType<typeof typeAssertIs<any, any>>, () => undefined>()();\ntypeAssertExtends<ReturnType<typeof typeAssertIs<never, never>>, () => undefined>()();\ntypeAssertExtends<ReturnType<typeof typeAssertIs<1, any>>, [\"Type assertion failed. Expected 1 to be any\"]>()();\ntypeAssertExtends<ReturnType<typeof typeAssertIs<any, 1>>, [\"Type assertion failed. Expected any to be 1\"]>()();\ntypeAssertExtends<ReturnType<typeof typeAssertIs<false, true>>, [\"Type assertion failed. Expected false to be true\"]>()();\ntypeAssertExtends<ReturnType<typeof typeAssertIs<{a: number}, {a: 1}>>, [\"Type assertion failed. Expected { 'a': number } to be { 'a': 1 }\"]>()();\ntypeAssertExtends<ReturnType<typeof typeAssertIs<any, never>>, [\"Type assertion failed. Expected any to be never\"]>()();\ntypeAssertExtends<ReturnType<typeof typeAssertIs<false, true>>, [\"Type assertion failed. Expected false to be true\"]>()();\ntypeAssertExtends<ReturnType<typeof typeAssertIs<false, never>>, [\"Type assertion failed. Expected false to be never\"]>()();\n"],"mappings":";AAkDA,aAA6F,EAAE;AAC/F,aAA0F,EAAE;AAuB5F,aAAqE,EAAE;AAkEhE,SAAS,aAKd;AACA,SAAQ,MAAM;AAChB;AAMA,kBAAwE,EAAE;AAC1E,kBAAoI,EAAE;AACtI,kBAAoI,EAAE;AACtI,kBAAgI,EAAE;AAK3H,SAAS,oBAEd;AACA,SAAQ,MAAM;AAChB;AAEA,kBAAsF,EAAE;AACxF,kBAAoF,EAAE;AACtF,kBAAuF,EAAE;AACzF,kBAAwF,EAAE;AAC1F,kBAAsG,EAAE;AACxG,kBAAuF,EAAE;AACzF,kBAAoF,EAAE;AAEtF,kBAAuJ,EAAE;AACzJ,kBAA6H,EAAE;AAC/H,kBAA+H,EAAE;AACjI,kBAAiI,EAAE;AAG5H,SAAS,eAKd;AACA,SAAQ,MAAM;AAChB;AAEA,kBAAkF,EAAE;AACpF,kBAAoF,EAAE;AACtF,kBAAkG,EAAE;AACpG,kBAA8E,EAAE;AAChF,kBAAkF,EAAE;AACpF,kBAA4G,EAAE;AAC9G,kBAA4G,EAAE;AAC9G,kBAAsH,EAAE;AACxH,kBAA8I,EAAE;AAChJ,kBAAoH,EAAE;AACtH,kBAAsH,EAAE;AACxH,kBAAwH,EAAE;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/utils/types.tsx"],"sourcesContent":["import { DeepPartial } from \"./objects\";\nimport { Join } from \"./strings\";\n\nexport type IsAny<T> = 0 extends (1 & T) ? true : false;\nexport type IsNever<T> = [T] extends [never] ? true : false;\nexport type IsNullish<T> = [T] extends [null | undefined] ? true : false;\nexport type IsUnion<T, U = T> =\n IsNever<T> extends true ? false\n : IsAny<T> extends true ? false\n : T extends U // distributive conditional https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#distributive-conditional-types\n ? /* if the *whole* original type (`U`) still fits inside the current variant, then `T` wasn’t a union */ ([U] extends [T] ? false : true)\n : never;\n\nexport type NullishCoalesce<T, U> = T extends null | undefined ? U : T;\n\nexport type LastUnionElement<U> = UnionToIntersection<U extends any ? (x: U) => 0 : never> extends (x: infer L) => 0 ? L & U : never;\n\ntype primitive = string | number | boolean | bigint | symbol | null | undefined;\n\n/**\n * Makes a type prettier by recursively expanding all object types. For example, `Omit<{ a: 1 }, \"a\">` becomes just `{}`.\n */\nexport type Expand<T> = T extends (...args: infer A) => infer R\n ? ((...args: A) => R) extends T\n ? (...args: Expand<A>) => Expand<R>\n : ((...args: Expand<A>) => Expand<R>) & { [K in keyof T]: Expand<T[K]> }\n : T extends object\n ? T extends primitive\n ? T\n : T extends infer O\n ? { [K in keyof O]: Expand<O[K]> }\n : never\n : T;\n\n\n/**\n * Removes all optional undefined/never keys from an object.\n */\nexport type DeepRemoveOptionalUndefined<T> = T extends object ? { [K in keyof T]: DeepRemoveOptionalUndefined<T[K]> } : T;\n\n// why this works: https://stackoverflow.com/a/50375286\nexport type UnionToIntersection<U> =\n (U extends any ? (x: U) => void : never) extends ((x: infer I) => void) ? I : never\n\ntype _UnionToTupleInner<U, R extends any[], Last> = UnionToTuple<Exclude<U, Last>, [...R, Last]>\nexport type UnionToTuple<U, R extends any[] = []> = [U] extends [never] ? R : _UnionToTupleInner<U, R, LastUnionElement<U>>;\n\nexport type CollapseObjectUnion<T extends object> = {\n [K in AllUnionKeys<T>]?: T extends Record<K, infer V> ? V : never;\n};\ntypeAssertIs<CollapseObjectUnion<{ a: string } | { b: number }>, { a?: string, b?: number }>()();\ntypeAssertIs<CollapseObjectUnion<{ a: string } | { a: number }>, { a?: string | number }>()();\n\nexport type IntersectAll<T extends any[]> = UnionToIntersection<T[number]>;\n\nexport type OptionalKeys<T> = {\n [K in keyof T]: {} extends Pick<T, K> ? K : never;\n}[keyof T];\nexport type RequiredKeys<T> = {\n [K in keyof T]: {} extends Pick<T, K> ? never : K;\n}[keyof T];\n\n/**\n * Returns a type whose keys are the intersection of the keys of T and U, deeply.\n */\nexport type KeyIntersect<T, U> =\n | { [K in keyof T & keyof U]?: T[K] & U[K] }\n | { [K in RequiredKeys<T> & keyof U]: T[K] & U[K] }\n | { [K in RequiredKeys<U> & keyof T]: U[K] & T[K] }\n\n/**\n * Returns ALL keys of all union elements.\n */\nexport type AllUnionKeys<T extends object> = T extends T ? keyof T : never;\ntypeAssertIs<AllUnionKeys<{ a: string } | { b: number }>, \"a\" | \"b\">()();\n\nexport type SubtractType<T, U> = T extends object ? { [K in keyof T]: K extends keyof U ? SubtractType<T[K], U[K]> : T[K] } : (T extends U ? never : T); // note: this only works due to the distributive property of conditional types https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#distributive-conditional-types\n\nexport type XOR<T extends readonly any[]> = T extends readonly [infer A, infer B, ...infer Rest]\n ? Rest extends []\n ? (A & { [K in keyof B]?: never }) | (B & { [K in keyof A]?: never })\n : XOR<[(A & { [K in keyof B]?: never }) | (B & { [K in keyof A]?: never }), ...Rest]>\n : T[0];\n\n\ntype _AntiIntersectInner<T, U> = T extends object ? (\n & Omit<U, keyof T>\n & { [K in keyof Pick<U, { [K in keyof T & keyof U]: U[K] extends T[K] ? (T[K] extends U[K] ? never : K) : never }[keyof T & keyof U]>]: PseudoAntiIntersect<T[K], U[K]> }\n & { [K in keyof Pick<U, keyof T & keyof U>]?: PseudoAntiIntersect<T[K], U[K]> }\n) : U;\n/**\n * Returns a type R such that T & R = U.\n */\nexport type AntiIntersect<T, U> = U extends T ? _AntiIntersectInner<T, U> : \"Cannot anti-intersect a type with a type that is not a subtype of it\"; // NOTE: This type is mostly untested — not sure how well it works on the edge cases\nexport type PseudoAntiIntersect<T, U> = _AntiIntersectInner<T, T & U>;\n\n/**\n * A variation of TypeScript's conditionals with slightly different semantics. It is the perfect type for cases where:\n *\n * - If all possible values are contained in `Extends`, then it will be mapped to `Then`.\n * - If all possible values are not contained in `Extends`, then it will be mapped to `Otherwise`.\n * - If some possible values are contained in `Extends` and some are not, then it will be mapped to `Then | Otherwise`.\n *\n * This is different from TypeScript's built-in conditional types (`Value extends Extends ? Then : Otherwise`), which\n * returns `Otherwise` for the third case (causing unsoundness in many real-world cases).\n */\nexport type IfAndOnlyIf<Value, Extends, Then, Otherwise> =\n | (Value extends Extends ? never : Otherwise)\n | (Value & Extends extends never ? never : Then);\n\n\n/**\n * Can be used to prettify a type in the IDE; for example, some complicated intersected types can be flattened into a single type.\n */\nexport type PrettifyType<T> = T extends object ? { [K in keyof T]: T[K] } & {} : T;\n\ntype _ToStringAndJoin<T extends any[], Separator extends string> =\n T extends [infer U, ...infer Rest extends any[]]\n ? `${TypeToString<U>}${Rest extends [any, ...any[]] ? `${Separator}${_ToStringAndJoin<Rest, Separator>}` : \"\"}`\n : \"<error-joining-tuple-elements>\";\ntype _TypeToStringInner<T> =\n IsAny<T> extends true ? \"any\"\n : IsNever<T> extends true ? \"never\"\n : IsUnion<T> extends true ? _ToStringAndJoin<UnionToTuple<T>, \" | \">\n : [T] extends [number] ? (number extends T ? \"number\" : `${T}`)\n : [T] extends [boolean] ? `${T}`\n : [T] extends [undefined] ? \"undefined\"\n : [T] extends [null] ? \"null\"\n : [T] extends [string] ? (string extends T ? \"string\" : `'${T}'`)\n : [T] extends [[]] ? \"[]\"\n : [T] extends [[any, ...any[]]] ? `[${_ToStringAndJoin<T, \", \">}]`\n : [T] extends [(infer E)[]] ? `${TypeToString<E>}[]`\n : [T] extends [Function] ? \"function\"\n : [T] extends [symbol] ? `symbol(${T['description']})`\n : [T] extends [object] ? `{ ${Join<UnionToTuple<{ [K in keyof T]: `${TypeToString<K>}: ${TypeToString<T[K]>}` }[keyof T]>, \", \">} }`\n : \"<unknown-type>\"\nexport type TypeToString<T> = _TypeToStringInner<T> extends `${infer S}` ? S : never;\n\n/**\n * Can be used to create assertions on types. For example, if passed any T other than `true`, the following will\n * show a type error:\n *\n * ```ts\n * typeAssert<T>()(); // the second pair of braces is important!\n * ```\n */\nexport function typeAssert<T>(): (\n IsAny<T> extends true ? TypeAssertionError<`Type assertion failed. Expected true, but got any.`>\n : IsNever<T> extends true ? TypeAssertionError<`Type assertion failed. Expected true, but got never.`>\n : T extends true ? (() => undefined)\n : TypeAssertionError<`Type assertion failed. Expected true, but got: ${TypeToString<T>}`>\n) {\n return (() => undefined) as any;\n}\ntype TypeAssertionError<T> =\n & [T]\n & /* this promise makes sure that if we accidentally forget the second pair of braces, eslint will complain (if we have no-floating-promises enabled) */ Promise<any>;\n\n\ntypeAssertExtends<ReturnType<typeof typeAssert<true>>, () => undefined>()();\ntypeAssertExtends<ReturnType<typeof typeAssert<false>>, TypeAssertionError<`Type assertion failed. Expected true, but got: false`>>()();\ntypeAssertExtends<ReturnType<typeof typeAssert<never>>, TypeAssertionError<`Type assertion failed. Expected true, but got never.`>>()();\ntypeAssertExtends<ReturnType<typeof typeAssert<any>>, TypeAssertionError<`Type assertion failed. Expected true, but got any.`>>()();\n\n/**\n * Functionally equivalent to `typeAssert<T extends S ? true : false>()()`, but with better error messages.\n */\nexport function typeAssertExtends<T, S>(): (\n [T] extends [S] ? (() => undefined) : TypeAssertionError<`Type assertion failed. Expected ${TypeToString<T>} to extend ${TypeToString<S>}`>\n) {\n return (() => undefined) as any;\n}\n\ntypeAssertExtends<ReturnType<typeof typeAssertExtends<never, true>>, () => undefined>()();\ntypeAssertExtends<ReturnType<typeof typeAssertExtends<any, true>>, () => undefined>()();\ntypeAssertExtends<ReturnType<typeof typeAssertExtends<false, false>>, () => undefined>()();\ntypeAssertExtends<ReturnType<typeof typeAssertExtends<\"abc\", string>>, () => undefined>()();\ntypeAssertExtends<ReturnType<typeof typeAssertExtends<{a: 1, b: 123}, {a: number}>>, () => undefined>()();\ntypeAssertExtends<ReturnType<typeof typeAssertExtends<never, never>>, () => undefined>()();\ntypeAssertExtends<ReturnType<typeof typeAssertExtends<true, any>>, () => undefined>()();\n\ntypeAssertExtends<ReturnType<typeof typeAssertExtends<{a: number}, {a: 1}>>, [\"Type assertion failed. Expected { 'a': number } to extend { 'a': 1 }\"]>()();\ntypeAssertExtends<ReturnType<typeof typeAssertExtends<any, never>>, [\"Type assertion failed. Expected any to extend never\"]>()();\ntypeAssertExtends<ReturnType<typeof typeAssertExtends<false, true>>, [\"Type assertion failed. Expected false to extend true\"]>()();\ntypeAssertExtends<ReturnType<typeof typeAssertExtends<false, never>>, [\"Type assertion failed. Expected false to extend never\"]>()();\n\n\nexport function typeAssertIs<T, U>(): (\n IsAny<T> extends true ? (IsAny<U> extends true ? (() => undefined) : TypeAssertionError<`Type assertion failed. Expected ${TypeToString<T>} to be ${TypeToString<U>}`>)\n : IsAny<U> extends true ? TypeAssertionError<`Type assertion failed. Expected ${TypeToString<T>} to be ${TypeToString<U>}`>\n : [T] extends [U] ? ([U] extends [T] ? (() => undefined) : TypeAssertionError<`Type assertion failed. Expected ${TypeToString<T>} to be ${TypeToString<U>}`>)\n : TypeAssertionError<`Type assertion failed. Expected ${TypeToString<T>} to be ${TypeToString<U>}`>\n) {\n return (() => undefined) as any;\n}\n\ntypeAssertExtends<ReturnType<typeof typeAssertIs<\"123\", \"123\">>, () => undefined>()();\ntypeAssertExtends<ReturnType<typeof typeAssertIs<{a: 1}, {a: 1}>>, () => undefined>()();\ntypeAssertExtends<ReturnType<typeof typeAssertIs<DeepPartial<{a: 1}>, {a?: 1}>>, () => undefined>()();\ntypeAssertExtends<ReturnType<typeof typeAssertIs<any, any>>, () => undefined>()();\ntypeAssertExtends<ReturnType<typeof typeAssertIs<never, never>>, () => undefined>()();\ntypeAssertExtends<ReturnType<typeof typeAssertIs<1, any>>, [\"Type assertion failed. Expected 1 to be any\"]>()();\ntypeAssertExtends<ReturnType<typeof typeAssertIs<any, 1>>, [\"Type assertion failed. Expected any to be 1\"]>()();\ntypeAssertExtends<ReturnType<typeof typeAssertIs<false, true>>, [\"Type assertion failed. Expected false to be true\"]>()();\ntypeAssertExtends<ReturnType<typeof typeAssertIs<{a: number}, {a: 1}>>, [\"Type assertion failed. Expected { 'a': number } to be { 'a': 1 }\"]>()();\ntypeAssertExtends<ReturnType<typeof typeAssertIs<any, never>>, [\"Type assertion failed. Expected any to be never\"]>()();\ntypeAssertExtends<ReturnType<typeof typeAssertIs<false, true>>, [\"Type assertion failed. Expected false to be true\"]>()();\ntypeAssertExtends<ReturnType<typeof typeAssertIs<false, never>>, [\"Type assertion failed. Expected false to be never\"]>()();\n"],"mappings":";AAkDA,aAA6F,EAAE;AAC/F,aAA0F,EAAE;AAuB5F,aAAqE,EAAE;AAwEhE,SAAS,aAKd;AACA,SAAQ,MAAM;AAChB;AAMA,kBAAwE,EAAE;AAC1E,kBAAoI,EAAE;AACtI,kBAAoI,EAAE;AACtI,kBAAgI,EAAE;AAK3H,SAAS,oBAEd;AACA,SAAQ,MAAM;AAChB;AAEA,kBAAsF,EAAE;AACxF,kBAAoF,EAAE;AACtF,kBAAuF,EAAE;AACzF,kBAAwF,EAAE;AAC1F,kBAAsG,EAAE;AACxG,kBAAuF,EAAE;AACzF,kBAAoF,EAAE;AAEtF,kBAAuJ,EAAE;AACzJ,kBAA6H,EAAE;AAC/H,kBAA+H,EAAE;AACjI,kBAAiI,EAAE;AAG5H,SAAS,eAKd;AACA,SAAQ,MAAM;AAChB;AAEA,kBAAkF,EAAE;AACpF,kBAAoF,EAAE;AACtF,kBAAkG,EAAE;AACpG,kBAA8E,EAAE;AAChF,kBAAkF,EAAE;AACpF,kBAA4G,EAAE;AAC9G,kBAA4G,EAAE;AAC9G,kBAAsH,EAAE;AACxH,kBAA8I,EAAE;AAChJ,kBAAoH,EAAE;AACtH,kBAAsH,EAAE;AACxH,kBAAwH,EAAE;","names":[]}
|
|
@@ -25,15 +25,19 @@ __export(server_side_exports, {
|
|
|
25
25
|
});
|
|
26
26
|
module.exports = __toCommonJS(server_side_exports);
|
|
27
27
|
var import_client_kms = require("@aws-sdk/client-kms");
|
|
28
|
+
var import_oidc = require("@vercel/functions/oidc");
|
|
28
29
|
var import_bytes = require("../../utils/bytes.js");
|
|
29
30
|
var import_crypto = require("../../utils/crypto.js");
|
|
30
31
|
var import_env = require("../../utils/env.js");
|
|
31
32
|
var import_results = require("../../utils/results.js");
|
|
32
33
|
function getKmsClient() {
|
|
34
|
+
const roleArn = (0, import_env.getEnvVariable)("STACK_AWS_VERCEL_OIDC_ROLE_ARN", "");
|
|
33
35
|
return new import_client_kms.KMSClient({
|
|
34
36
|
region: (0, import_env.getEnvVariable)("STACK_AWS_REGION"),
|
|
35
37
|
endpoint: (0, import_env.getEnvVariable)("STACK_AWS_KMS_ENDPOINT"),
|
|
36
|
-
credentials: {
|
|
38
|
+
credentials: roleArn ? (0, import_oidc.awsCredentialsProvider)({
|
|
39
|
+
roleArn
|
|
40
|
+
}) : {
|
|
37
41
|
accessKeyId: (0, import_env.getEnvVariable)("STACK_AWS_ACCESS_KEY_ID"),
|
|
38
42
|
secretAccessKey: (0, import_env.getEnvVariable)("STACK_AWS_SECRET_ACCESS_KEY")
|
|
39
43
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/helpers/vault/server-side.ts"],"sourcesContent":["import {\n CreateAliasCommand,\n CreateKeyCommand,\n DecryptCommand,\n DescribeKeyCommand,\n GenerateDataKeyCommand,\n KMSClient\n} from \"@aws-sdk/client-kms\";\nimport { decodeBase64, encodeBase64 } from \"../../utils/bytes\";\nimport { decrypt, encrypt } from \"../../utils/crypto\";\nimport { getEnvVariable } from \"../../utils/env\";\nimport { Result } from \"../../utils/results\";\n\nfunction getKmsClient() {\n return new KMSClient({\n region: getEnvVariable(\"STACK_AWS_REGION\"),\n endpoint: getEnvVariable(\"STACK_AWS_KMS_ENDPOINT\"),\n credentials: {\n accessKeyId: getEnvVariable(\"STACK_AWS_ACCESS_KEY_ID\"),\n secretAccessKey: getEnvVariable(\"STACK_AWS_SECRET_ACCESS_KEY\")
|
|
1
|
+
{"version":3,"sources":["../../../src/helpers/vault/server-side.ts"],"sourcesContent":["import {\n CreateAliasCommand,\n CreateKeyCommand,\n DecryptCommand,\n DescribeKeyCommand,\n GenerateDataKeyCommand,\n KMSClient\n} from \"@aws-sdk/client-kms\";\nimport { awsCredentialsProvider } from '@vercel/functions/oidc';\nimport { decodeBase64, encodeBase64 } from \"../../utils/bytes\";\nimport { decrypt, encrypt } from \"../../utils/crypto\";\nimport { getEnvVariable } from \"../../utils/env\";\nimport { Result } from \"../../utils/results\";\n\n\nfunction getKmsClient() {\n const roleArn = getEnvVariable(\"STACK_AWS_VERCEL_OIDC_ROLE_ARN\", \"\");\n return new KMSClient({\n region: getEnvVariable(\"STACK_AWS_REGION\"),\n endpoint: getEnvVariable(\"STACK_AWS_KMS_ENDPOINT\"),\n credentials: roleArn ? awsCredentialsProvider({\n roleArn,\n }) : {\n accessKeyId: getEnvVariable(\"STACK_AWS_ACCESS_KEY_ID\"),\n secretAccessKey: getEnvVariable(\"STACK_AWS_SECRET_ACCESS_KEY\"),\n },\n });\n}\n\nasync function getOrCreateKekId(): Promise<string> {\n const id = \"alias/stack-data-vault-server-side-kek\";\n const kms = getKmsClient();\n try {\n const describeResult = await kms.send(new DescribeKeyCommand({ KeyId: id }));\n if (describeResult.KeyMetadata?.KeyId) return describeResult.KeyMetadata.KeyId;\n } catch (e) {\n if (e instanceof Error && e.name !== \"NotFoundException\") {\n throw e;\n }\n }\n const { KeyMetadata } = await kms.send(new CreateKeyCommand({\n KeyUsage: \"ENCRYPT_DECRYPT\",\n Description: \"DataVault KEK\"\n }));\n await kms.send(new CreateAliasCommand({ AliasName: id, TargetKeyId: KeyMetadata!.KeyId! }));\n return id;\n}\n\nasync function genDEK() {\n const kekId = await getOrCreateKekId();\n const kms = getKmsClient();\n const out = await kms.send(new GenerateDataKeyCommand({ KeyId: kekId, KeySpec: \"AES_256\" }));\n if (!out.Plaintext || !out.CiphertextBlob) throw new Error(\"GenerateDataKey failed\");\n return {\n dekBytes: out.Plaintext,\n edkBytes: out.CiphertextBlob,\n };\n}\n\nasync function unwrapDEK(edk_b64: string) {\n const edkBytes = decodeBase64(edk_b64);\n const kms = getKmsClient();\n const out = await kms.send(new DecryptCommand({ CiphertextBlob: edkBytes }));\n if (!out.Plaintext) throw new Error(\"KMS Decrypt failed\");\n return {\n dekBytes: out.Plaintext,\n edkBytes,\n };\n}\n\nexport async function encryptWithKms(value: string) {\n const { dekBytes, edkBytes } = await genDEK();\n try {\n const ciphertext = await encrypt({\n purpose: \"stack-data-vault-server-side-encryption\",\n secret: dekBytes,\n value: new TextEncoder().encode(value),\n });\n return { edkBase64: encodeBase64(edkBytes), ciphertextBase64: encodeBase64(ciphertext) };\n } finally {\n dekBytes.fill(0);\n }\n}\n\nexport async function decryptWithKms(encrypted: Awaited<ReturnType<typeof encryptWithKms>>) {\n const { dekBytes } = await unwrapDEK(encrypted.edkBase64);\n try {\n const value = Result.orThrow(await decrypt({\n purpose: \"stack-data-vault-server-side-encryption\",\n secret: dekBytes,\n cipher: decodeBase64(encrypted.ciphertextBase64),\n }));\n return new TextDecoder().decode(value);\n } finally {\n dekBytes.fill(0);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAOO;AACP,kBAAuC;AACvC,mBAA2C;AAC3C,oBAAiC;AACjC,iBAA+B;AAC/B,qBAAuB;AAGvB,SAAS,eAAe;AACtB,QAAM,cAAU,2BAAe,kCAAkC,EAAE;AACnE,SAAO,IAAI,4BAAU;AAAA,IACnB,YAAQ,2BAAe,kBAAkB;AAAA,IACzC,cAAU,2BAAe,wBAAwB;AAAA,IACjD,aAAa,cAAU,oCAAuB;AAAA,MAC5C;AAAA,IACF,CAAC,IAAI;AAAA,MACH,iBAAa,2BAAe,yBAAyB;AAAA,MACrD,qBAAiB,2BAAe,6BAA6B;AAAA,IAC/D;AAAA,EACF,CAAC;AACH;AAEA,eAAe,mBAAoC;AACjD,QAAM,KAAK;AACX,QAAM,MAAM,aAAa;AACzB,MAAI;AACF,UAAM,iBAAiB,MAAM,IAAI,KAAK,IAAI,qCAAmB,EAAE,OAAO,GAAG,CAAC,CAAC;AAC3E,QAAI,eAAe,aAAa,MAAO,QAAO,eAAe,YAAY;AAAA,EAC3E,SAAS,GAAG;AACV,QAAI,aAAa,SAAS,EAAE,SAAS,qBAAqB;AACxD,YAAM;AAAA,IACR;AAAA,EACF;AACA,QAAM,EAAE,YAAY,IAAI,MAAM,IAAI,KAAK,IAAI,mCAAiB;AAAA,IAC1D,UAAU;AAAA,IACV,aAAa;AAAA,EACf,CAAC,CAAC;AACF,QAAM,IAAI,KAAK,IAAI,qCAAmB,EAAE,WAAW,IAAI,aAAa,YAAa,MAAO,CAAC,CAAC;AAC1F,SAAO;AACT;AAEA,eAAe,SAAS;AACtB,QAAM,QAAQ,MAAM,iBAAiB;AACrC,QAAM,MAAM,aAAa;AACzB,QAAM,MAAM,MAAM,IAAI,KAAK,IAAI,yCAAuB,EAAE,OAAO,OAAO,SAAS,UAAU,CAAC,CAAC;AAC3F,MAAI,CAAC,IAAI,aAAa,CAAC,IAAI,eAAgB,OAAM,IAAI,MAAM,wBAAwB;AACnF,SAAO;AAAA,IACL,UAAU,IAAI;AAAA,IACd,UAAU,IAAI;AAAA,EAChB;AACF;AAEA,eAAe,UAAU,SAAiB;AACxC,QAAM,eAAW,2BAAa,OAAO;AACrC,QAAM,MAAM,aAAa;AACzB,QAAM,MAAM,MAAM,IAAI,KAAK,IAAI,iCAAe,EAAE,gBAAgB,SAAS,CAAC,CAAC;AAC3E,MAAI,CAAC,IAAI,UAAW,OAAM,IAAI,MAAM,oBAAoB;AACxD,SAAO;AAAA,IACL,UAAU,IAAI;AAAA,IACd;AAAA,EACF;AACF;AAEA,eAAsB,eAAe,OAAe;AAClD,QAAM,EAAE,UAAU,SAAS,IAAI,MAAM,OAAO;AAC5C,MAAI;AACF,UAAM,aAAa,UAAM,uBAAQ;AAAA,MAC/B,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,OAAO,IAAI,YAAY,EAAE,OAAO,KAAK;AAAA,IACvC,CAAC;AACD,WAAO,EAAE,eAAW,2BAAa,QAAQ,GAAG,sBAAkB,2BAAa,UAAU,EAAE;AAAA,EACzF,UAAE;AACA,aAAS,KAAK,CAAC;AAAA,EACjB;AACF;AAEA,eAAsB,eAAe,WAAuD;AAC1F,QAAM,EAAE,SAAS,IAAI,MAAM,UAAU,UAAU,SAAS;AACxD,MAAI;AACF,UAAM,QAAQ,sBAAO,QAAQ,UAAM,uBAAQ;AAAA,MACzC,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,YAAQ,2BAAa,UAAU,gBAAgB;AAAA,IACjD,CAAC,CAAC;AACF,WAAO,IAAI,YAAY,EAAE,OAAO,KAAK;AAAA,EACvC,UAAE;AACA,aAAS,KAAK,CAAC;AAAA,EACjB;AACF;","names":[]}
|
package/dist/index.d.mts
CHANGED
|
@@ -16,6 +16,8 @@ import './interface/crud/project-permissions.mjs';
|
|
|
16
16
|
import './interface/crud/projects.mjs';
|
|
17
17
|
import './interface/crud/svix-token.mjs';
|
|
18
18
|
import './interface/crud/team-permissions.mjs';
|
|
19
|
+
import './interface/crud/transactions.mjs';
|
|
20
|
+
import './utils/dates.mjs';
|
|
19
21
|
import './utils/errors.mjs';
|
|
20
22
|
import './utils/json.mjs';
|
|
21
23
|
import './interface/crud/connected-accounts.mjs';
|
|
@@ -23,6 +25,7 @@ import './interface/crud/contact-channels.mjs';
|
|
|
23
25
|
import './interface/crud/current-user.mjs';
|
|
24
26
|
import './interface/crud/items.mjs';
|
|
25
27
|
import './interface/crud/notification-preferences.mjs';
|
|
28
|
+
import './interface/crud/oauth-providers.mjs';
|
|
26
29
|
import './interface/crud/sessions.mjs';
|
|
27
30
|
import './interface/crud/team-invitation.mjs';
|
|
28
31
|
import './interface/crud/team-member-profiles.mjs';
|
|
@@ -31,6 +34,5 @@ import './interface/crud/teams.mjs';
|
|
|
31
34
|
import './interface/crud/users.mjs';
|
|
32
35
|
import './schema-fields.mjs';
|
|
33
36
|
import './utils/currency-constants.mjs';
|
|
34
|
-
import './utils/dates.mjs';
|
|
35
37
|
import '@simplewebauthn/types';
|
|
36
38
|
import './interface/crud/project-api-keys.mjs';
|
package/dist/index.d.ts
CHANGED
|
@@ -16,6 +16,8 @@ import './interface/crud/project-permissions.js';
|
|
|
16
16
|
import './interface/crud/projects.js';
|
|
17
17
|
import './interface/crud/svix-token.js';
|
|
18
18
|
import './interface/crud/team-permissions.js';
|
|
19
|
+
import './interface/crud/transactions.js';
|
|
20
|
+
import './utils/dates.js';
|
|
19
21
|
import './utils/errors.js';
|
|
20
22
|
import './utils/json.js';
|
|
21
23
|
import './interface/crud/connected-accounts.js';
|
|
@@ -23,6 +25,7 @@ import './interface/crud/contact-channels.js';
|
|
|
23
25
|
import './interface/crud/current-user.js';
|
|
24
26
|
import './interface/crud/items.js';
|
|
25
27
|
import './interface/crud/notification-preferences.js';
|
|
28
|
+
import './interface/crud/oauth-providers.js';
|
|
26
29
|
import './interface/crud/sessions.js';
|
|
27
30
|
import './interface/crud/team-invitation.js';
|
|
28
31
|
import './interface/crud/team-member-profiles.js';
|
|
@@ -31,6 +34,5 @@ import './interface/crud/teams.js';
|
|
|
31
34
|
import './interface/crud/users.js';
|
|
32
35
|
import './schema-fields.js';
|
|
33
36
|
import './utils/currency-constants.js';
|
|
34
|
-
import './utils/dates.js';
|
|
35
37
|
import '@simplewebauthn/types';
|
|
36
38
|
import './interface/crud/project-api-keys.js';
|
|
@@ -8,6 +8,7 @@ import { ProjectPermissionDefinitionsCrud } from './crud/project-permissions.mjs
|
|
|
8
8
|
import { ProjectsCrud } from './crud/projects.mjs';
|
|
9
9
|
import { SvixTokenCrud } from './crud/svix-token.mjs';
|
|
10
10
|
import { TeamPermissionDefinitionsCrud } from './crud/team-permissions.mjs';
|
|
11
|
+
import { AdminTransaction } from './crud/transactions.mjs';
|
|
11
12
|
import { ServerAuthApplicationOptions, StackServerInterface } from './server-interface.mjs';
|
|
12
13
|
import './client-interface.mjs';
|
|
13
14
|
import '../utils/errors.mjs';
|
|
@@ -17,11 +18,13 @@ import '../crud.mjs';
|
|
|
17
18
|
import 'yup';
|
|
18
19
|
import '../utils/types.mjs';
|
|
19
20
|
import '../utils/strings.mjs';
|
|
21
|
+
import '../utils/dates.mjs';
|
|
20
22
|
import './crud/connected-accounts.mjs';
|
|
21
23
|
import './crud/contact-channels.mjs';
|
|
22
24
|
import './crud/current-user.mjs';
|
|
23
25
|
import './crud/items.mjs';
|
|
24
26
|
import './crud/notification-preferences.mjs';
|
|
27
|
+
import './crud/oauth-providers.mjs';
|
|
25
28
|
import './crud/sessions.mjs';
|
|
26
29
|
import './crud/team-invitation.mjs';
|
|
27
30
|
import './crud/team-member-profiles.mjs';
|
|
@@ -30,7 +33,6 @@ import './crud/teams.mjs';
|
|
|
30
33
|
import './crud/users.mjs';
|
|
31
34
|
import '../schema-fields.mjs';
|
|
32
35
|
import '../utils/currency-constants.mjs';
|
|
33
|
-
import '../utils/dates.mjs';
|
|
34
36
|
import '@simplewebauthn/types';
|
|
35
37
|
import './crud/project-api-keys.mjs';
|
|
36
38
|
|
|
@@ -89,6 +91,26 @@ declare class StackAdminInterface extends StackServerInterface {
|
|
|
89
91
|
theme_id?: string;
|
|
90
92
|
tsx_source: string;
|
|
91
93
|
}[]>;
|
|
94
|
+
listInternalEmailDrafts(): Promise<{
|
|
95
|
+
id: string;
|
|
96
|
+
display_name: string;
|
|
97
|
+
theme_id?: string | undefined | false;
|
|
98
|
+
tsx_source: string;
|
|
99
|
+
sent_at_millis?: number | null;
|
|
100
|
+
}[]>;
|
|
101
|
+
createEmailDraft(options: {
|
|
102
|
+
display_name?: string;
|
|
103
|
+
theme_id?: string | false;
|
|
104
|
+
tsx_source?: string;
|
|
105
|
+
}): Promise<{
|
|
106
|
+
id: string;
|
|
107
|
+
}>;
|
|
108
|
+
updateEmailDraft(id: string, data: {
|
|
109
|
+
display_name?: string;
|
|
110
|
+
theme_id?: string | null | false;
|
|
111
|
+
tsx_source?: string;
|
|
112
|
+
sent_at_millis?: number | null;
|
|
113
|
+
}): Promise<void>;
|
|
92
114
|
listEmailThemes(): Promise<{
|
|
93
115
|
id: string;
|
|
94
116
|
display_name: string;
|
|
@@ -104,7 +126,7 @@ declare class StackAdminInterface extends StackServerInterface {
|
|
|
104
126
|
getSvixToken(): Promise<SvixTokenCrud["Admin"]["Read"]>;
|
|
105
127
|
deleteProject(): Promise<void>;
|
|
106
128
|
transferProject(session: InternalSession, newTeamId: string): Promise<void>;
|
|
107
|
-
getMetrics(): Promise<any>;
|
|
129
|
+
getMetrics(includeAnonymous?: boolean): Promise<any>;
|
|
108
130
|
sendTestEmail(data: {
|
|
109
131
|
recipient_email: string;
|
|
110
132
|
email_config: {
|
|
@@ -121,7 +143,7 @@ declare class StackAdminInterface extends StackServerInterface {
|
|
|
121
143
|
}>;
|
|
122
144
|
listSentEmails(): Promise<InternalEmailsCrud["Admin"]["List"]>;
|
|
123
145
|
sendSignInInvitationEmail(email: string, callbackUrl: string): Promise<void>;
|
|
124
|
-
sendChatMessage(threadId: string, contextType: "email-theme" | "email-template", messages: Array<{
|
|
146
|
+
sendChatMessage(threadId: string, contextType: "email-theme" | "email-template" | "email-draft", messages: Array<{
|
|
125
147
|
role: string;
|
|
126
148
|
content: any;
|
|
127
149
|
}>, abortSignal?: AbortSignal): Promise<{
|
|
@@ -169,6 +191,15 @@ declare class StackAdminInterface extends StackServerInterface {
|
|
|
169
191
|
createStripeWidgetAccountSession(): Promise<{
|
|
170
192
|
client_secret: string;
|
|
171
193
|
}>;
|
|
194
|
+
listTransactions(params?: {
|
|
195
|
+
cursor?: string;
|
|
196
|
+
limit?: number;
|
|
197
|
+
type?: 'subscription' | 'one_time' | 'item_quantity_change';
|
|
198
|
+
customerType?: 'user' | 'team' | 'custom';
|
|
199
|
+
}): Promise<{
|
|
200
|
+
transactions: AdminTransaction[];
|
|
201
|
+
nextCursor: string | null;
|
|
202
|
+
}>;
|
|
172
203
|
testModePurchase(options: {
|
|
173
204
|
price_id: string;
|
|
174
205
|
full_code: string;
|