@stackframe/stack-shared 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/LICENSE +7 -0
  2. package/dist/helpers/fetch-token.d.ts +1 -0
  3. package/dist/helpers/fetch-token.d.ts.map +1 -0
  4. package/dist/helpers/fetch-token.js +1 -0
  5. package/dist/helpers/password.d.ts +1 -0
  6. package/dist/helpers/password.d.ts.map +1 -0
  7. package/dist/helpers/password.js +33 -0
  8. package/dist/hooks/use-async-external-store.d.ts +4 -0
  9. package/dist/hooks/use-async-external-store.d.ts.map +1 -0
  10. package/dist/hooks/use-async-external-store.js +20 -0
  11. package/dist/hooks/use-strict-memo.d.ts +6 -0
  12. package/dist/hooks/use-strict-memo.d.ts.map +1 -0
  13. package/dist/hooks/use-strict-memo.js +64 -0
  14. package/dist/index.d.ts +4 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +4 -0
  17. package/dist/interface/adminInterface.d.ts +88 -0
  18. package/dist/interface/adminInterface.d.ts.map +1 -0
  19. package/dist/interface/adminInterface.js +109 -0
  20. package/dist/interface/clientInterface.d.ts +134 -0
  21. package/dist/interface/clientInterface.d.ts.map +1 -0
  22. package/dist/interface/clientInterface.js +444 -0
  23. package/dist/interface/serverInterface.d.ts +33 -0
  24. package/dist/interface/serverInterface.d.ts.map +1 -0
  25. package/dist/interface/serverInterface.js +70 -0
  26. package/dist/utils/arrays.d.ts +10 -0
  27. package/dist/utils/arrays.d.ts.map +1 -0
  28. package/dist/utils/arrays.js +54 -0
  29. package/dist/utils/caches.d.ts +76 -0
  30. package/dist/utils/caches.d.ts.map +1 -0
  31. package/dist/utils/caches.js +100 -0
  32. package/dist/utils/crypto.d.ts +1 -0
  33. package/dist/utils/crypto.d.ts.map +1 -0
  34. package/dist/utils/crypto.js +5 -0
  35. package/dist/utils/dates.d.ts +12 -0
  36. package/dist/utils/dates.d.ts.map +1 -0
  37. package/dist/utils/dates.js +57 -0
  38. package/dist/utils/dom.d.ts +4 -0
  39. package/dist/utils/dom.d.ts.map +1 -0
  40. package/dist/utils/dom.js +11 -0
  41. package/dist/utils/env.d.ts +4 -0
  42. package/dist/utils/env.d.ts.map +1 -0
  43. package/dist/utils/env.js +7 -0
  44. package/dist/utils/errors.d.ts +184 -0
  45. package/dist/utils/errors.d.ts.map +1 -0
  46. package/dist/utils/errors.js +72 -0
  47. package/dist/utils/html.d.ts +2 -0
  48. package/dist/utils/html.d.ts.map +1 -0
  49. package/dist/utils/html.js +12 -0
  50. package/dist/utils/json.d.ts +10 -0
  51. package/dist/utils/json.d.ts.map +1 -0
  52. package/dist/utils/json.js +26 -0
  53. package/dist/utils/jwt.d.ts +3 -0
  54. package/dist/utils/jwt.d.ts.map +1 -0
  55. package/dist/utils/jwt.js +16 -0
  56. package/dist/utils/math.d.ts +4 -0
  57. package/dist/utils/math.d.ts.map +1 -0
  58. package/dist/utils/math.js +6 -0
  59. package/dist/utils/numbers.d.ts +2 -0
  60. package/dist/utils/numbers.d.ts.map +1 -0
  61. package/dist/utils/numbers.js +26 -0
  62. package/dist/utils/objects.d.ts +18 -0
  63. package/dist/utils/objects.d.ts.map +1 -0
  64. package/dist/utils/objects.js +63 -0
  65. package/dist/utils/password.d.ts +2 -0
  66. package/dist/utils/password.d.ts.map +1 -0
  67. package/dist/utils/password.js +8 -0
  68. package/dist/utils/promises.d.ts +49 -0
  69. package/dist/utils/promises.d.ts.map +1 -0
  70. package/dist/utils/promises.js +145 -0
  71. package/dist/utils/react.d.ts +12 -0
  72. package/dist/utils/react.d.ts.map +1 -0
  73. package/dist/utils/react.js +44 -0
  74. package/dist/utils/results.d.ts +73 -0
  75. package/dist/utils/results.d.ts.map +1 -0
  76. package/dist/utils/results.js +112 -0
  77. package/dist/utils/stores.d.ts +57 -0
  78. package/dist/utils/stores.d.ts.map +1 -0
  79. package/dist/utils/stores.js +122 -0
  80. package/dist/utils/strings.d.ts +40 -0
  81. package/dist/utils/strings.d.ts.map +1 -0
  82. package/dist/utils/strings.js +91 -0
  83. package/dist/utils/types.d.ts +33 -0
  84. package/dist/utils/types.d.ts.map +1 -0
  85. package/dist/utils/types.js +61 -0
  86. package/dist/utils/uuids.d.ts +1 -0
  87. package/dist/utils/uuids.d.ts.map +1 -0
  88. package/dist/utils/uuids.js +4 -0
  89. package/package.json +40 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"math.d.ts","sourceRoot":"","sources":["../../src/utils/math.tsx"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAEtD"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Similar to the modulo operator, but always returns a positive number (even when the input is negative).
3
+ */
4
+ export function remainder(n, d) {
5
+ return ((n % d) + Math.abs(d)) % d;
6
+ }
@@ -0,0 +1,2 @@
1
+ export declare function prettyPrintWithMagnitudes(num: number): string;
2
+ export declare function toFixedMax(num: number, maxDecimals: number): string;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"numbers.d.ts","sourceRoot":"","sources":["../../src/utils/numbers.tsx"],"names":[],"mappings":"AAQA,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAY7D;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAEnE"}
@@ -0,0 +1,26 @@
1
+ const magnitudes = [
2
+ [1_000_000_000_000_000, "trln"],
3
+ [1_000_000_000_000, "bln"],
4
+ [1_000_000_000, "bn"],
5
+ [1_000_000, "M"],
6
+ [1_000, "k"],
7
+ ];
8
+ export function prettyPrintWithMagnitudes(num) {
9
+ if (typeof num !== "number")
10
+ throw new Error("Expected a number");
11
+ if (Number.isNaN(num))
12
+ return "NaN";
13
+ if (num < 0)
14
+ return "-" + prettyPrintWithMagnitudes(-num);
15
+ if (!Number.isFinite(num))
16
+ return "∞";
17
+ for (const [magnitude, suffix] of magnitudes) {
18
+ if (num >= magnitude) {
19
+ return toFixedMax(num / magnitude, 1) + suffix;
20
+ }
21
+ }
22
+ return toFixedMax(num, 1); // Handle numbers less than 1,000 without suffix.
23
+ }
24
+ export function toFixedMax(num, maxDecimals) {
25
+ return num.toFixed(maxDecimals).replace(/\.?0+$/, "");
26
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Assumes both objects are primitives, arrays, or non-function plain objects, and compares them deeply.
3
+ *
4
+ * Note that since they are assumed to be plain objects, this function does not compare prototypes.
5
+ */
6
+ export declare function deepPlainEquals<T>(obj1: T, obj2: unknown): obj2 is T;
7
+ export declare function typedEntries<T extends {}>(obj: T): [keyof T, T[keyof T]][];
8
+ export declare function typedFromEntries<T extends {}>(entries: [keyof T, T[keyof T]][]): Partial<T>;
9
+ export declare function typedKeys<T extends {}>(obj: T): (keyof T)[];
10
+ export declare function typedValues<T extends {}>(obj: T): T[keyof T][];
11
+ export declare function typedAssign<T extends {}, U extends {}>(target: T, source: U): asserts target is T & U;
12
+ /**
13
+ * Returns a new object with all undefined values removed. Useful when spreading optional parameters on an object, as
14
+ * TypeScript's `Partial<XYZ>` type allows `undefined` values.
15
+ */
16
+ export declare function filterUndefined<T extends {}>(obj: T): {
17
+ [k in keyof T]+?: T[k] & ({} | null);
18
+ };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"objects.d.ts","sourceRoot":"","sources":["../../src/utils/objects.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,CAAC,CAgCpE;AAED,wBAAgB,YAAY,CAAC,CAAC,SAAS,EAAE,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAE1E;AAED,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAE3F;AAED,wBAAgB,SAAS,CAAC,CAAC,SAAS,EAAE,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,CAE3D;AAED,wBAAgB,WAAW,CAAC,CAAC,SAAS,EAAE,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,CAE9D;AAED,wBAAgB,WAAW,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,CAErG;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,EAAE,EAAE,GAAG,EAAE,CAAC,GAAG;KAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC;CAAE,CAE9F"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Assumes both objects are primitives, arrays, or non-function plain objects, and compares them deeply.
3
+ *
4
+ * Note that since they are assumed to be plain objects, this function does not compare prototypes.
5
+ */
6
+ export function deepPlainEquals(obj1, obj2) {
7
+ if (typeof obj1 !== typeof obj2)
8
+ return false;
9
+ if (obj1 === obj2)
10
+ return true;
11
+ switch (typeof obj1) {
12
+ case 'object': {
13
+ if (!obj1 || !obj2)
14
+ return false;
15
+ if (Array.isArray(obj1) || Array.isArray(obj2)) {
16
+ if (!Array.isArray(obj1) || !Array.isArray(obj2))
17
+ return false;
18
+ if (obj1.length !== obj2.length)
19
+ return false;
20
+ return obj1.every((v, i) => deepPlainEquals(v, obj2[i]));
21
+ }
22
+ const keys1 = Object.keys(obj1);
23
+ const keys2 = Object.keys(obj2);
24
+ if (keys1.length !== keys2.length)
25
+ return false;
26
+ return keys1.every((k) => k in obj2 && deepPlainEquals(obj1[k], obj2[k]));
27
+ }
28
+ case 'undefined':
29
+ case 'string':
30
+ case 'number':
31
+ case 'boolean':
32
+ case 'bigint':
33
+ case 'symbol':
34
+ case 'function': {
35
+ return false;
36
+ }
37
+ default: {
38
+ throw new Error("Unexpected typeof " + typeof obj1);
39
+ }
40
+ }
41
+ }
42
+ export function typedEntries(obj) {
43
+ return Object.entries(obj);
44
+ }
45
+ export function typedFromEntries(entries) {
46
+ return Object.fromEntries(entries);
47
+ }
48
+ export function typedKeys(obj) {
49
+ return Object.keys(obj);
50
+ }
51
+ export function typedValues(obj) {
52
+ return Object.values(obj);
53
+ }
54
+ export function typedAssign(target, source) {
55
+ Object.assign(target, source);
56
+ }
57
+ /**
58
+ * Returns a new object with all undefined values removed. Useful when spreading optional parameters on an object, as
59
+ * TypeScript's `Partial<XYZ>` type allows `undefined` values.
60
+ */
61
+ export function filterUndefined(obj) {
62
+ return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined));
63
+ }
@@ -0,0 +1,2 @@
1
+ export declare function hashPassword(password: string): Promise<string>;
2
+ export declare function comparePassword(password: string, hash: string): Promise<boolean>;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"password.d.ts","sourceRoot":"","sources":["../../src/utils/password.tsx"],"names":[],"mappings":"AAEA,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,mBAGlD;AAED,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,oBAEnE"}
@@ -0,0 +1,8 @@
1
+ import bcrypt from 'bcrypt';
2
+ export async function hashPassword(password) {
3
+ const salt = await bcrypt.genSalt(10);
4
+ return await bcrypt.hash(password, salt);
5
+ }
6
+ export async function comparePassword(password, hash) {
7
+ return await bcrypt.compare(password, hash);
8
+ }
@@ -0,0 +1,49 @@
1
+ import { Result } from "./results";
2
+ import type { RejectedThenable, FulfilledThenable, PendingThenable } from "react";
3
+ export type ReactPromise<T> = Promise<T> & (RejectedThenable<T> | FulfilledThenable<T> | PendingThenable<T>);
4
+ type Resolve<T> = (value: T) => void;
5
+ type Reject = (reason: unknown) => void;
6
+ export declare function createPromise<T>(callback: (resolve: Resolve<T>, reject: Reject) => void): ReactPromise<T>;
7
+ /**
8
+ * Like Promise.resolve(...), but also adds the status and value properties for use with React's `use` hook.
9
+ */
10
+ export declare function resolved<T>(value: T): ReactPromise<T>;
11
+ /**
12
+ * Like Promise.resolve(...), but also adds the status and value properties for use with React's `use` hook.
13
+ */
14
+ export declare function rejected<T>(reason: unknown): ReactPromise<T>;
15
+ export declare function neverResolve(): ReactPromise<never>;
16
+ export declare function wait(ms: number): Promise<void>;
17
+ export declare function waitUntil(date: Date): Promise<void>;
18
+ export declare function runAsynchronously(promiseOrFunc: Promise<unknown> | (() => Promise<unknown>) | undefined): void;
19
+ declare class TimeoutError extends Error {
20
+ readonly ms: number;
21
+ constructor(ms: number);
22
+ }
23
+ export declare function timeout<T>(promise: Promise<T>, ms: number): Promise<Result<T, TimeoutError>>;
24
+ export declare function timeoutThrow<T>(promise: Promise<T>, ms: number): Promise<T>;
25
+ export type RateLimitOptions = {
26
+ /**
27
+ * The number of requests to process in parallel. Currently only 1 is supported.
28
+ */
29
+ concurrency: 1;
30
+ /**
31
+ * If true, multiple requests waiting at the same time will be reduced to just one. Default is false.
32
+ */
33
+ batchCalls?: boolean;
34
+ /**
35
+ * Waits for throttleMs since the start of last request before starting the next request. Default is 0.
36
+ */
37
+ throttleMs?: number;
38
+ /**
39
+ * Waits for gapMs since the end of last request before starting the next request. Default is 0.
40
+ */
41
+ gapMs?: number;
42
+ /**
43
+ * Waits until there have been no new requests for debounceMs before starting a new request. Default is 0.
44
+ */
45
+ debounceMs?: number;
46
+ };
47
+ export declare function rateLimited<T>(func: () => Promise<T>, options: RateLimitOptions): () => Promise<T>;
48
+ export declare function throttled<T, A extends any[]>(func: (...args: A) => Promise<T>, delayMs: number): (...args: A) => Promise<T>;
49
+ export {};
@@ -0,0 +1 @@
1
+ {"version":3,"file":"promises.d.ts","sourceRoot":"","sources":["../../src/utils/promises.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAEnC,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAElF,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,GAAG,CACvC,gBAAgB,CAAC,CAAC,CAAC,GACnB,iBAAiB,CAAC,CAAC,CAAC,GACpB,eAAe,CAAC,CAAC,CAAC,CACrB,CAAC;AAEF,KAAK,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;AACrC,KAAK,MAAM,GAAG,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;AACxC,wBAAgB,aAAa,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,YAAY,CAAC,CAAC,CAAC,CA0BzG;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAKrD;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,GAAG,YAAY,CAAC,CAAC,CAAC,CAK5D;AAED,wBAAgB,YAAY,IAAI,YAAY,CAAC,KAAK,CAAC,CAIlD;AAED,wBAAsB,IAAI,CAAC,EAAE,EAAE,MAAM,iBAEpC;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,IAAI,iBAEzC;AAED,wBAAgB,iBAAiB,CAAC,aAAa,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,SAAS,GAAG,IAAI,CAa9G;AAGD,cAAM,YAAa,SAAQ,KAAK;aACF,EAAE,EAAE,MAAM;gBAAV,EAAE,EAAE,MAAM;CAIvC;AAED,wBAAsB,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,CAKlG;AAED,wBAAsB,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAEjF;AAGD,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;OAEG;IACH,WAAW,EAAE,CAAC,CAAC;IAEf;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,wBAAgB,WAAW,CAAC,CAAC,EAC3B,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACtB,OAAO,EAAE,gBAAgB,GACxB,MAAM,OAAO,CAAC,CAAC,CAAC,CAoDlB;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,CAAC,SAAS,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAe3H"}
@@ -0,0 +1,145 @@
1
+ import { Result } from "./results";
2
+ import { generateUuid } from "./uuids";
3
+ export function createPromise(callback) {
4
+ let status = "pending";
5
+ let valueOrReason = undefined;
6
+ let resolve = null;
7
+ let reject = null;
8
+ const promise = new Promise((res, rej) => {
9
+ resolve = (value) => {
10
+ if (status !== "pending")
11
+ return;
12
+ status = "fulfilled";
13
+ valueOrReason = value;
14
+ res(value);
15
+ };
16
+ reject = (reason) => {
17
+ if (status !== "pending")
18
+ return;
19
+ status = "rejected";
20
+ valueOrReason = reason;
21
+ rej(reason);
22
+ };
23
+ });
24
+ callback(resolve, reject);
25
+ return Object.assign(promise, {
26
+ status: status,
27
+ ...status === "fulfilled" ? { value: valueOrReason } : {},
28
+ ...status === "rejected" ? { reason: valueOrReason } : {},
29
+ });
30
+ }
31
+ /**
32
+ * Like Promise.resolve(...), but also adds the status and value properties for use with React's `use` hook.
33
+ */
34
+ export function resolved(value) {
35
+ return Object.assign(Promise.resolve(value), {
36
+ status: "fulfilled",
37
+ value,
38
+ });
39
+ }
40
+ /**
41
+ * Like Promise.resolve(...), but also adds the status and value properties for use with React's `use` hook.
42
+ */
43
+ export function rejected(reason) {
44
+ return Object.assign(Promise.reject(reason), {
45
+ status: "rejected",
46
+ reason,
47
+ });
48
+ }
49
+ export function neverResolve() {
50
+ return Object.assign(new Promise(() => { }), {
51
+ status: "pending",
52
+ });
53
+ }
54
+ export async function wait(ms) {
55
+ return await new Promise(resolve => setTimeout(resolve, ms));
56
+ }
57
+ export async function waitUntil(date) {
58
+ return await wait(date.getTime() - Date.now());
59
+ }
60
+ export function runAsynchronously(promiseOrFunc) {
61
+ if (typeof promiseOrFunc === "function") {
62
+ promiseOrFunc = promiseOrFunc();
63
+ }
64
+ promiseOrFunc?.catch(error => {
65
+ const newError = new Error("Uncaught error in asynchronous function: " + error.toString(), {
66
+ cause: error,
67
+ });
68
+ console.error(newError);
69
+ });
70
+ }
71
+ class TimeoutError extends Error {
72
+ ms;
73
+ constructor(ms) {
74
+ super(`Timeout after ${ms}ms`);
75
+ this.ms = ms;
76
+ this.name = "TimeoutError";
77
+ }
78
+ }
79
+ export async function timeout(promise, ms) {
80
+ return await Promise.race([
81
+ promise.then(value => Result.ok(value)),
82
+ wait(ms).then(() => Result.error(new TimeoutError(ms))),
83
+ ]);
84
+ }
85
+ export async function timeoutThrow(promise, ms) {
86
+ return Result.orThrow(await timeout(promise, ms));
87
+ }
88
+ export function rateLimited(func, options) {
89
+ let waitUntil = performance.now();
90
+ let queue = [];
91
+ let addedToQueueCallbacks = new Map;
92
+ const next = async () => {
93
+ while (true) {
94
+ if (waitUntil > performance.now()) {
95
+ await wait(Math.max(1, waitUntil - performance.now() + 1));
96
+ }
97
+ else if (queue.length === 0) {
98
+ const uuid = generateUuid();
99
+ await new Promise(resolve => {
100
+ addedToQueueCallbacks.set(uuid, resolve);
101
+ });
102
+ addedToQueueCallbacks.delete(uuid);
103
+ }
104
+ else {
105
+ break;
106
+ }
107
+ }
108
+ const nextFuncs = options.batchCalls ? queue.splice(0, queue.length) : [queue.shift()];
109
+ const start = performance.now();
110
+ const value = await Result.fromPromise(func());
111
+ const end = performance.now();
112
+ waitUntil = Math.max(waitUntil, start + (options.throttleMs ?? 0), end + (options.gapMs ?? 0));
113
+ for (const nextFunc of nextFuncs) {
114
+ value.status === "ok" ? nextFunc[0](value.data) : nextFunc[1](value.error);
115
+ }
116
+ };
117
+ runAsynchronously(async () => {
118
+ while (true) {
119
+ await next();
120
+ }
121
+ });
122
+ return () => {
123
+ return new Promise((resolve, reject) => {
124
+ waitUntil = Math.max(waitUntil, performance.now() + (options.debounceMs ?? 0));
125
+ queue.push([resolve, reject]);
126
+ addedToQueueCallbacks.forEach(cb => cb());
127
+ });
128
+ };
129
+ }
130
+ export function throttled(func, delayMs) {
131
+ let timeout = null;
132
+ let nextAvailable = null;
133
+ return async (...args) => {
134
+ while (nextAvailable !== null) {
135
+ await nextAvailable;
136
+ }
137
+ nextAvailable = new Promise(resolve => {
138
+ timeout = setTimeout(() => {
139
+ nextAvailable = null;
140
+ resolve(func(...args));
141
+ }, delayMs);
142
+ });
143
+ return await nextAvailable;
144
+ };
145
+ }
@@ -0,0 +1,12 @@
1
+ /// <reference types="react" />
2
+ export declare function getNodeText(node: React.ReactNode): string;
3
+ /**
4
+ * Suspends the currently rendered component indefinitely. Will not unsuspend unless the component rerenders.
5
+ *
6
+ * You can use this to translate older query- or AsyncResult-based code to new the Suspense system, for example: `if (query.isLoading) suspend();`
7
+ */
8
+ export declare function suspend(): never;
9
+ /**
10
+ * Use this in a component or a hook to disable SSR.
11
+ */
12
+ export declare function suspendIfSsr(): void;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../../src/utils/react.tsx"],"names":[],"mappings":";AAIA,wBAAgB,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,SAAS,GAAG,MAAM,CAczD;AAED;;;;GAIG;AACH,wBAAgB,OAAO,IAAI,KAAK,CAG/B;AAGD;;GAEG;AACH,wBAAgB,YAAY,SAiB3B"}
@@ -0,0 +1,44 @@
1
+ import { use } from "react";
2
+ import { neverResolve } from "./promises";
3
+ import { deindent } from "./strings";
4
+ export function getNodeText(node) {
5
+ if (["number", "string"].includes(typeof node)) {
6
+ return `${node}`;
7
+ }
8
+ if (!node) {
9
+ return "";
10
+ }
11
+ if (Array.isArray(node)) {
12
+ return node.map(getNodeText).join("");
13
+ }
14
+ if (typeof node === "object" && "props" in node) {
15
+ return getNodeText(node.props.children);
16
+ }
17
+ throw new Error(`Unknown node type: ${typeof node}`);
18
+ }
19
+ /**
20
+ * Suspends the currently rendered component indefinitely. Will not unsuspend unless the component rerenders.
21
+ *
22
+ * You can use this to translate older query- or AsyncResult-based code to new the Suspense system, for example: `if (query.isLoading) suspend();`
23
+ */
24
+ export function suspend() {
25
+ use(neverResolve());
26
+ throw new Error("Somehow a Promise that never resolves was resolved?");
27
+ }
28
+ /**
29
+ * Use this in a component or a hook to disable SSR.
30
+ */
31
+ export function suspendIfSsr() {
32
+ if (typeof window === "undefined") {
33
+ const error = Object.assign(new Error(deindent `
34
+ This code path is not supported in SSR. This error should be caught by the closest Suspense boundary, and hence never be thrown on the client. If you still see the error, make sure the component is rendered inside a Suspense boundary.
35
+
36
+ See: https://react.dev/reference/react/Suspense#providing-a-fallback-for-server-errors-and-client-only-content
37
+ `), {
38
+ // set the digest so nextjs doesn't log the error
39
+ // https://github.com/vercel/next.js/blob/d01d6d9c35a8c2725b3d74c1402ab76d4779a6cf/packages/next/src/shared/lib/lazy-dynamic/bailout-to-csr.ts#L14
40
+ digest: "BAILOUT_TO_CLIENT_SIDE_RENDERING",
41
+ });
42
+ throw error;
43
+ }
44
+ }
@@ -0,0 +1,73 @@
1
+ export type Result<T, E = unknown> = {
2
+ status: "ok";
3
+ data: T;
4
+ } | {
5
+ status: "error";
6
+ error: E;
7
+ };
8
+ export type AsyncResult<T, E = unknown, P = void> = Result<T, E> | {
9
+ status: "pending";
10
+ } & {
11
+ progress: P;
12
+ };
13
+ export declare const Result: {
14
+ fromThrowing: typeof fromThrowing;
15
+ fromPromise: typeof promiseToResult;
16
+ ok<T>(data: T): {
17
+ status: "ok";
18
+ data: T;
19
+ } & {
20
+ status: "ok";
21
+ };
22
+ error<E>(error: E): {
23
+ status: "error";
24
+ error: E;
25
+ } & {
26
+ status: "error";
27
+ };
28
+ map: typeof mapResult;
29
+ or: <T_1, E_1, U>(result: Result<T_1, E_1>, fallback: U) => T_1 | U;
30
+ orThrow: <T_2, E_2>(result: Result<T_2, E_2>) => T_2;
31
+ orThrowAsync: <T_3, E_3>(result: Promise<Result<T_3, E_3>>) => Promise<T_3>;
32
+ retry: typeof retry;
33
+ };
34
+ export declare const AsyncResult: {
35
+ fromThrowing: typeof fromThrowing;
36
+ fromPromise: typeof promiseToResult;
37
+ ok: <T>(data: T) => {
38
+ status: "ok";
39
+ data: T;
40
+ } & {
41
+ status: "ok";
42
+ };
43
+ error: <E>(error: E) => {
44
+ status: "error";
45
+ error: E;
46
+ } & {
47
+ status: "error";
48
+ };
49
+ pending: typeof pending;
50
+ map: typeof mapResult;
51
+ or: <T_1, E_1, P, U>(result: AsyncResult<T_1, E_1, P>, fallback: U) => T_1 | U;
52
+ orThrow: <T_2, E_2, P_1>(result: AsyncResult<T_2, E_2, P_1>) => T_2;
53
+ retry: typeof retry;
54
+ };
55
+ declare function pending(): AsyncResult<never, never, void> & {
56
+ status: "pending";
57
+ };
58
+ declare function pending<P>(progress: P): AsyncResult<never, never, P> & {
59
+ status: "pending";
60
+ };
61
+ declare function promiseToResult<T, E = unknown>(promise: Promise<T>): Promise<Result<T, E>>;
62
+ declare function fromThrowing<T>(fn: () => T): Result<T, unknown>;
63
+ declare function mapResult<T, U, E = unknown, P = unknown>(result: Result<T, E>, fn: (data: T) => U): Result<U, E>;
64
+ declare function mapResult<T, U, E = unknown, P = unknown>(result: AsyncResult<T, E, P>, fn: (data: T) => U): AsyncResult<U, E, P>;
65
+ declare class RetryError extends Error {
66
+ readonly errors: unknown[];
67
+ constructor(errors: unknown[]);
68
+ get retries(): number;
69
+ }
70
+ declare function retry<T>(fn: () => Result<T> | Promise<Result<T>>, retries: number, { exponentialDelayBase }: {
71
+ exponentialDelayBase?: number | undefined;
72
+ }): Promise<Result<T, RetryError>>;
73
+ export {};
@@ -0,0 +1 @@
1
+ {"version":3,"file":"results.d.ts","sourceRoot":"","sources":["../../src/utils/results.tsx"],"names":[],"mappings":"AAEA,MAAM,MAAM,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,OAAO,IAC7B;IACE,MAAM,EAAE,IAAI,CAAC;IACb,IAAI,EAAE,CAAC,CAAC;CACT,GACD;IACE,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,CAAC,CAAC;CACV,CAAC;AAEN,MAAM,MAAM,WAAW,CAAC,CAAC,EAAE,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,IAAI,IAC5C,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GACV;IACE,MAAM,EAAE,SAAS,CAAC;CACnB,GACD;IACE,QAAQ,EAAE,CAAC,CAAC;CACb,CAAC;AAGR,eAAO,MAAM,MAAM;;;;gBAlBL,IAAI;;;gBAqB6B,IAAI;;;gBAjBrC,OAAO;;;gBAuB8B,OAAO;;;;;;;CAkBzD,CAAC;AAEF,eAAO,MAAM,WAAW;;;;gBA/CV,IAAI;;;gBAqB6B,IAAI;;;gBAjBrC,OAAO;;;gBAuB8B,OAAO;;;;;;;CAoCzD,CAAC;AAEF,iBAAS,OAAO,IAAI,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,CAAC;AAC5E,iBAAS,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,GAAG;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,CAAC;AAQvF,iBAAS,eAAe,CAAC,CAAC,EAAE,CAAC,GAAG,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAQnF;AAED,iBAAS,YAAY,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC,CAMxD;AAED,iBAAS,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC3G,iBAAS,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;AAe3H,cAAM,UAAW,SAAQ,KAAK;aACA,MAAM,EAAE,OAAO,EAAE;gBAAjB,MAAM,EAAE,OAAO,EAAE;IAK7C,IAAI,OAAO,WAEV;CACF;AAED,iBAAe,KAAK,CAAC,CAAC,EACpB,EAAE,EAAE,MAAM,MAAM,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EACxC,OAAO,EAAE,MAAM,EACf,EAAE,oBAA2B,EAAE;;CAAA,GAC9B,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAchC"}
@@ -0,0 +1,112 @@
1
+ import { wait } from "./promises";
2
+ export const Result = {
3
+ fromThrowing,
4
+ fromPromise: promiseToResult,
5
+ ok(data) {
6
+ return {
7
+ status: "ok",
8
+ data,
9
+ };
10
+ },
11
+ error(error) {
12
+ return {
13
+ status: "error",
14
+ error,
15
+ };
16
+ },
17
+ map: mapResult,
18
+ or: (result, fallback) => {
19
+ return result.status === "ok" ? result.data : fallback;
20
+ },
21
+ orThrow: (result) => {
22
+ if (result.status === "error")
23
+ throw result.error;
24
+ return result.data;
25
+ },
26
+ orThrowAsync: async (result) => {
27
+ return Result.orThrow(await result);
28
+ },
29
+ retry,
30
+ };
31
+ export const AsyncResult = {
32
+ fromThrowing,
33
+ fromPromise: promiseToResult,
34
+ ok: Result.ok,
35
+ error: Result.error,
36
+ pending,
37
+ map: mapResult,
38
+ or: (result, fallback) => {
39
+ if (result.status === "pending")
40
+ return fallback;
41
+ return Result.or(result, fallback);
42
+ },
43
+ orThrow: (result) => {
44
+ if (result.status === "pending")
45
+ throw new Error("Result still pending");
46
+ return Result.orThrow(result);
47
+ },
48
+ retry,
49
+ };
50
+ function pending(progress) {
51
+ return {
52
+ status: "pending",
53
+ progress: progress,
54
+ };
55
+ }
56
+ function promiseToResult(promise) {
57
+ return promise.then(data => ({
58
+ status: "ok",
59
+ data,
60
+ }), error => ({
61
+ status: "error",
62
+ error,
63
+ }));
64
+ }
65
+ function fromThrowing(fn) {
66
+ try {
67
+ return Result.ok(fn());
68
+ }
69
+ catch (error) {
70
+ return Result.error(error);
71
+ }
72
+ }
73
+ function mapResult(result, fn) {
74
+ if (result.status === "error")
75
+ return {
76
+ status: "error",
77
+ error: result.error,
78
+ };
79
+ if (result.status === "pending")
80
+ return {
81
+ status: "pending",
82
+ ..."progress" in result ? { progress: result.progress } : {},
83
+ };
84
+ return Result.ok(fn(result.data));
85
+ }
86
+ class RetryError extends Error {
87
+ errors;
88
+ constructor(errors) {
89
+ super(`Error after retrying ${errors.length} times.`, { cause: errors[errors.length - 1] });
90
+ this.errors = errors;
91
+ this.name = "RetryError";
92
+ }
93
+ get retries() {
94
+ return this.errors.length;
95
+ }
96
+ }
97
+ async function retry(fn, retries, { exponentialDelayBase = 2000 }) {
98
+ const errors = [];
99
+ for (let i = 0; i < retries; i++) {
100
+ const res = await fn();
101
+ if (res.status === "ok") {
102
+ return Result.ok(res.data);
103
+ }
104
+ else {
105
+ errors.push(res.error);
106
+ if (i < retries - 1) {
107
+ await wait(Math.random() * exponentialDelayBase * 2 ** i);
108
+ }
109
+ }
110
+ }
111
+ return Result.error(new RetryError(errors));
112
+ }
@@ -0,0 +1,57 @@
1
+ import { AsyncResult } from "./results";
2
+ import { ReactPromise } from "./promises";
3
+ export type ReadonlyAsyncStore<T> = {
4
+ isAvailable(): boolean;
5
+ get(): AsyncResult<T, unknown, void>;
6
+ getOrWait(): ReactPromise<T>;
7
+ onChange(callback: (value: T, oldValue: T | undefined) => void): {
8
+ unsubscribe: () => void;
9
+ };
10
+ onceChange(callback: (value: T, oldValue: T | undefined) => void): {
11
+ unsubscribe: () => void;
12
+ };
13
+ };
14
+ export declare class AsyncStore<T> implements ReadonlyAsyncStore<T> {
15
+ private _isAvailable;
16
+ private _value;
17
+ private _isRejected;
18
+ private _rejectionError;
19
+ private readonly _waitingRejectFunctions;
20
+ private readonly _callbacks;
21
+ private _updateCounter;
22
+ private _lastSuccessfulUpdate;
23
+ constructor(...args: [] | [T]);
24
+ isAvailable(): boolean;
25
+ isRejected(): boolean;
26
+ get(): ({
27
+ status: "pending";
28
+ } & {
29
+ progress: void;
30
+ } & {
31
+ status: "pending";
32
+ }) | ({
33
+ status: "error";
34
+ error: unknown;
35
+ } & {
36
+ status: "error";
37
+ }) | ({
38
+ status: "ok";
39
+ data: T;
40
+ } & {
41
+ status: "ok";
42
+ });
43
+ getOrWait(): ReactPromise<T>;
44
+ _setIfLatest(value: T, curCounter: number): boolean;
45
+ set(value: T): void;
46
+ update(updater: (value: T | undefined) => T): T;
47
+ setAsync(promise: Promise<T>): Promise<boolean>;
48
+ setUnavailable(): void;
49
+ setRejected(error: unknown): void;
50
+ map<U>(mapper: (value: T) => U): AsyncStore<U>;
51
+ onChange(callback: (value: T, oldValue: T | undefined) => void): {
52
+ unsubscribe: () => void;
53
+ };
54
+ onceChange(callback: (value: T, oldValue: T | undefined) => void): {
55
+ unsubscribe: () => void;
56
+ };
57
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stores.d.ts","sourceRoot":"","sources":["../../src/utils/stores.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC,OAAO,EAAE,YAAY,EAAsB,MAAM,YAAY,CAAC;AAE9D,MAAM,MAAM,kBAAkB,CAAC,CAAC,IAAI;IAClC,WAAW,IAAI,OAAO,CAAC;IACvB,GAAG,IAAI,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACrC,SAAS,IAAI,YAAY,CAAC,CAAC,CAAC,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,SAAS,KAAK,IAAI,GAAG;QAAE,WAAW,EAAE,MAAM,IAAI,CAAA;KAAE,CAAC;IAC7F,UAAU,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,SAAS,KAAK,IAAI,GAAG;QAAE,WAAW,EAAE,MAAM,IAAI,CAAA;KAAE,CAAC;CAChG,CAAC;AAEF,qBAAa,UAAU,CAAC,CAAC,CAAE,YAAW,kBAAkB,CAAC,CAAC,CAAC;IACzD,OAAO,CAAC,YAAY,CAAU;IAC9B,OAAO,CAAC,MAAM,CAA4B;IAE1C,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,eAAe,CAAU;IACjC,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAiD;IAEzF,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAyE;IAEpG,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,qBAAqB,CAAM;gBAEvB,GAAG,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC;IAS7B,WAAW,IAAI,OAAO;IAItB,UAAU,IAAI,OAAO;IAIrB,GAAG;;;;;;;;;;;;;;;;;IAUH,SAAS,IAAI,YAAY,CAAC,CAAC,CAAC;IAqB5B,YAAY,CAAC,KAAK,EAAE,CAAC,EAAE,UAAU,EAAE,MAAM;IAgBzC,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI;IAInB,MAAM,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,SAAS,KAAK,CAAC,GAAG,CAAC;IAMzC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAMrD,cAAc,IAAI,IAAI;IAMtB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAOjC,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC;IAQ9C,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,SAAS,KAAK,IAAI,GAAG;QAAE,WAAW,EAAE,MAAM,IAAI,CAAA;KAAE;IAU5F,UAAU,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,SAAS,KAAK,IAAI,GAAG;QAAE,WAAW,EAAE,MAAM,IAAI,CAAA;KAAE;CAO/F"}