@stackframe/stack-shared 2.7.21 → 2.7.23
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 +12 -0
- package/dist/interface/serverInterface.d.ts +1 -1
- package/dist/interface/serverInterface.js +1 -1
- package/dist/utils/compile-time.js +3 -17
- package/dist/utils/promises.js +4 -2
- package/dist/utils/results.d.ts +5 -3
- package/dist/utils/results.js +30 -52
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -89,7 +89,7 @@ export declare class StackServerInterface extends StackClientInterface {
|
|
|
89
89
|
}): Promise<void>;
|
|
90
90
|
grantServerTeamUserPermission(teamId: string, userId: string, permissionId: string): Promise<void>;
|
|
91
91
|
revokeServerTeamUserPermission(teamId: string, userId: string, permissionId: string): Promise<void>;
|
|
92
|
-
|
|
92
|
+
deleteServerUser(userId: string): Promise<void>;
|
|
93
93
|
createServerContactChannel(data: ContactChannelsCrud['Server']['Create']): Promise<ContactChannelsCrud['Server']['Read']>;
|
|
94
94
|
updateServerContactChannel(userId: string, contactChannelId: string, data: ContactChannelsCrud['Server']['Update']): Promise<ContactChannelsCrud['Server']['Read']>;
|
|
95
95
|
deleteServerContactChannel(userId: string, contactChannelId: string): Promise<void>;
|
|
@@ -235,7 +235,7 @@ export class StackServerInterface extends StackClientInterface {
|
|
|
235
235
|
body: JSON.stringify({}),
|
|
236
236
|
}, null);
|
|
237
237
|
}
|
|
238
|
-
async
|
|
238
|
+
async deleteServerUser(userId) {
|
|
239
239
|
await this.sendServerRequest(urlString `/users/${userId}`, {
|
|
240
240
|
method: "DELETE",
|
|
241
241
|
headers: {
|
|
@@ -4,22 +4,8 @@
|
|
|
4
4
|
* to exist at runtime).
|
|
5
5
|
*/
|
|
6
6
|
export function scrambleDuringCompileTime(t) {
|
|
7
|
+
if (Math.random() < 0.00001 && Math.random() > 0.99999 && Math.random() < 0.00001 && Math.random() > 0.99999) {
|
|
8
|
+
return "this will never happen";
|
|
9
|
+
}
|
|
7
10
|
return t;
|
|
8
11
|
}
|
|
9
|
-
import.meta.vitest?.test("scrambleDuringCompileTime", ({ expect }) => {
|
|
10
|
-
// Test with primitive values
|
|
11
|
-
expect(scrambleDuringCompileTime(42)).toBe(42);
|
|
12
|
-
expect(scrambleDuringCompileTime("hello")).toBe("hello");
|
|
13
|
-
expect(scrambleDuringCompileTime(true)).toBe(true);
|
|
14
|
-
expect(scrambleDuringCompileTime(null)).toBe(null);
|
|
15
|
-
expect(scrambleDuringCompileTime(undefined)).toBe(undefined);
|
|
16
|
-
// Test with objects (reference equality)
|
|
17
|
-
const obj = { a: 1 };
|
|
18
|
-
expect(scrambleDuringCompileTime(obj)).toBe(obj);
|
|
19
|
-
// Test with arrays (reference equality)
|
|
20
|
-
const arr = [1, 2, 3];
|
|
21
|
-
expect(scrambleDuringCompileTime(arr)).toBe(arr);
|
|
22
|
-
// Test with functions (reference equality)
|
|
23
|
-
const fn = () => "test";
|
|
24
|
-
expect(scrambleDuringCompileTime(fn)).toBe(fn);
|
|
25
|
-
});
|
package/dist/utils/promises.js
CHANGED
|
@@ -67,12 +67,13 @@ import.meta.vitest?.test("createPromise", async ({ expect }) => {
|
|
|
67
67
|
expect(multiResolvePromise.value).toBe(1); // Only first resolve took effect
|
|
68
68
|
expect(await multiResolvePromise).toBe(1);
|
|
69
69
|
});
|
|
70
|
-
|
|
70
|
+
let resolvedCache = null;
|
|
71
71
|
/**
|
|
72
72
|
* Like Promise.resolve(...), but also adds the status and value properties for use with React's `use` hook, and caches
|
|
73
73
|
* the value so that invoking `resolved` twice returns the same promise.
|
|
74
74
|
*/
|
|
75
75
|
export function resolved(value) {
|
|
76
|
+
resolvedCache ??= new DependenciesMap();
|
|
76
77
|
if (resolvedCache.has([value])) {
|
|
77
78
|
return resolvedCache.get([value]);
|
|
78
79
|
}
|
|
@@ -103,12 +104,13 @@ import.meta.vitest?.test("resolved", async ({ expect }) => {
|
|
|
103
104
|
const promise4 = resolved(43);
|
|
104
105
|
expect(promise4).not.toBe(promise1);
|
|
105
106
|
});
|
|
106
|
-
|
|
107
|
+
let rejectedCache = null;
|
|
107
108
|
/**
|
|
108
109
|
* Like Promise.reject(...), but also adds the status and value properties for use with React's `use` hook, and caches
|
|
109
110
|
* the value so that invoking `rejected` twice returns the same promise.
|
|
110
111
|
*/
|
|
111
112
|
export function rejected(reason) {
|
|
113
|
+
rejectedCache ??= new DependenciesMap();
|
|
112
114
|
if (rejectedCache.has([reason])) {
|
|
113
115
|
return rejectedCache.get([reason]);
|
|
114
116
|
}
|
package/dist/utils/results.d.ts
CHANGED
|
@@ -67,9 +67,11 @@ declare function mapResult<T, U, E = unknown, P = unknown>(result: AsyncResult<T
|
|
|
67
67
|
declare class RetryError extends AggregateError {
|
|
68
68
|
readonly errors: unknown[];
|
|
69
69
|
constructor(errors: unknown[]);
|
|
70
|
-
get
|
|
70
|
+
get attempts(): number;
|
|
71
71
|
}
|
|
72
|
-
declare function retry<T>(fn: (
|
|
72
|
+
declare function retry<T>(fn: (attemptIndex: number) => Result<T> | Promise<Result<T>>, totalAttempts: number, { exponentialDelayBase }?: {
|
|
73
73
|
exponentialDelayBase?: number | undefined;
|
|
74
|
-
}): Promise<Result<T, RetryError
|
|
74
|
+
}): Promise<Result<T, RetryError> & {
|
|
75
|
+
attempts: number;
|
|
76
|
+
}>;
|
|
75
77
|
export {};
|
package/dist/utils/results.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { wait } from "./promises";
|
|
1
2
|
import { deindent } from "./strings";
|
|
2
3
|
export const Result = {
|
|
3
4
|
fromThrowing,
|
|
@@ -270,7 +271,7 @@ class RetryError extends AggregateError {
|
|
|
270
271
|
this.errors = errors;
|
|
271
272
|
this.name = "RetryError";
|
|
272
273
|
}
|
|
273
|
-
get
|
|
274
|
+
get attempts() {
|
|
274
275
|
return this.errors.length;
|
|
275
276
|
}
|
|
276
277
|
}
|
|
@@ -281,7 +282,7 @@ import.meta.vitest?.test("RetryError", ({ expect }) => {
|
|
|
281
282
|
const retryErrorSingle = new RetryError([singleError]);
|
|
282
283
|
expect(retryErrorSingle.name).toBe("RetryError");
|
|
283
284
|
expect(retryErrorSingle.errors).toEqual([singleError]);
|
|
284
|
-
expect(retryErrorSingle.
|
|
285
|
+
expect(retryErrorSingle.attempts).toBe(1);
|
|
285
286
|
expect(retryErrorSingle.cause).toBe(singleError);
|
|
286
287
|
expect(retryErrorSingle.message).toContain("Error after 1 attempts");
|
|
287
288
|
// Test with multiple different errors
|
|
@@ -290,7 +291,7 @@ import.meta.vitest?.test("RetryError", ({ expect }) => {
|
|
|
290
291
|
const retryErrorMultiple = new RetryError([error1, error2]);
|
|
291
292
|
expect(retryErrorMultiple.name).toBe("RetryError");
|
|
292
293
|
expect(retryErrorMultiple.errors).toEqual([error1, error2]);
|
|
293
|
-
expect(retryErrorMultiple.
|
|
294
|
+
expect(retryErrorMultiple.attempts).toBe(2);
|
|
294
295
|
expect(retryErrorMultiple.cause).toBe(error2);
|
|
295
296
|
expect(retryErrorMultiple.message).toContain("Error after 2 attempts");
|
|
296
297
|
expect(retryErrorMultiple.message).toContain("Attempt 1");
|
|
@@ -300,7 +301,7 @@ import.meta.vitest?.test("RetryError", ({ expect }) => {
|
|
|
300
301
|
const retryErrorSame = new RetryError([sameError, sameError]);
|
|
301
302
|
expect(retryErrorSame.name).toBe("RetryError");
|
|
302
303
|
expect(retryErrorSame.errors).toEqual([sameError, sameError]);
|
|
303
|
-
expect(retryErrorSame.
|
|
304
|
+
expect(retryErrorSame.attempts).toBe(2);
|
|
304
305
|
expect(retryErrorSame.cause).toBe(sameError);
|
|
305
306
|
expect(retryErrorSame.message).toContain("Error after 2 attempts");
|
|
306
307
|
expect(retryErrorSame.message).toContain("Attempts 1-2");
|
|
@@ -310,61 +311,38 @@ async function retry(fn, totalAttempts, { exponentialDelayBase = 1000 } = {}) {
|
|
|
310
311
|
for (let i = 0; i < totalAttempts; i++) {
|
|
311
312
|
const res = await fn(i);
|
|
312
313
|
if (res.status === "ok") {
|
|
313
|
-
return Result.ok(res.data);
|
|
314
|
+
return Object.assign(Result.ok(res.data), { attempts: i + 1 });
|
|
314
315
|
}
|
|
315
316
|
else {
|
|
316
317
|
errors.push(res.error);
|
|
317
318
|
if (i < totalAttempts - 1) {
|
|
318
|
-
|
|
319
|
-
await new Promise(resolve => setTimeout(resolve, 1));
|
|
319
|
+
await wait((Math.random() + 0.5) * exponentialDelayBase * (2 ** i));
|
|
320
320
|
}
|
|
321
321
|
}
|
|
322
322
|
}
|
|
323
|
-
return Result.error(new RetryError(errors));
|
|
323
|
+
return Object.assign(Result.error(new RetryError(errors)), { attempts: totalAttempts });
|
|
324
324
|
}
|
|
325
325
|
import.meta.vitest?.test("retry", async ({ expect }) => {
|
|
326
|
-
//
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
expect(eventualSuccessResult.data).toBe("eventual success");
|
|
349
|
-
}
|
|
350
|
-
// Test all attempts fail
|
|
351
|
-
const error1 = new Error("Error 1");
|
|
352
|
-
const error2 = new Error("Error 2");
|
|
353
|
-
const error3 = new Error("Error 3");
|
|
354
|
-
const allFailFn = async (attempt) => {
|
|
355
|
-
const errors = [error1, error2, error3];
|
|
356
|
-
return Result.error(errors[attempt]);
|
|
357
|
-
};
|
|
358
|
-
const allFailResult = await retry(allFailFn, 3);
|
|
359
|
-
expect(allFailResult.status).toBe("error");
|
|
360
|
-
if (allFailResult.status === "error") {
|
|
361
|
-
expect(allFailResult.error).toBeInstanceOf(RetryError);
|
|
362
|
-
const retryError = allFailResult.error;
|
|
363
|
-
expect(retryError.errors).toEqual([error1, error2, error3]);
|
|
364
|
-
expect(retryError.retries).toBe(3);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
finally {
|
|
368
|
-
// No cleanup needed
|
|
369
|
-
}
|
|
326
|
+
// Test successful on first attempt
|
|
327
|
+
const successFn = async () => Result.ok("success");
|
|
328
|
+
const successResult = await retry(successFn, 3, { exponentialDelayBase: 0 });
|
|
329
|
+
expect(successResult).toEqual({ status: "ok", data: "success", attempts: 1 });
|
|
330
|
+
// Test successful after failures
|
|
331
|
+
let attemptCount = 0;
|
|
332
|
+
const eventualSuccessFn = async () => {
|
|
333
|
+
return ++attemptCount < 2 ? Result.error(new Error(`Attempt ${attemptCount} failed`))
|
|
334
|
+
: Result.ok("eventual success");
|
|
335
|
+
};
|
|
336
|
+
const eventualSuccessResult = await retry(eventualSuccessFn, 3, { exponentialDelayBase: 0 });
|
|
337
|
+
expect(eventualSuccessResult).toEqual({ status: "ok", data: "eventual success", attempts: 2 });
|
|
338
|
+
// Test all attempts fail
|
|
339
|
+
const errors = [new Error("Error 1"), new Error("Error 2"), new Error("Error 3")];
|
|
340
|
+
const allFailFn = async (attempt) => {
|
|
341
|
+
return Result.error(errors[attempt]);
|
|
342
|
+
};
|
|
343
|
+
const allFailResult = await retry(allFailFn, 3, { exponentialDelayBase: 0 });
|
|
344
|
+
expect(allFailResult).toEqual({ status: "error", error: expect.any(RetryError), attempts: 3 });
|
|
345
|
+
const retryError = allFailResult.error;
|
|
346
|
+
expect(retryError.errors).toEqual(errors);
|
|
347
|
+
expect(retryError.attempts).toBe(3);
|
|
370
348
|
});
|