@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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # @stackframe/stack-shared
2
2
 
3
+ ## 2.7.23
4
+
5
+ ### Patch Changes
6
+
7
+ - Various changes
8
+
9
+ ## 2.7.22
10
+
11
+ ### Patch Changes
12
+
13
+ - Various changes
14
+
3
15
  ## 2.7.21
4
16
 
5
17
  ### Patch Changes
@@ -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
- deleteServerServerUser(userId: string): Promise<void>;
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 deleteServerServerUser(userId) {
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
- });
@@ -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
- const resolvedCache = new DependenciesMap();
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
- const rejectedCache = new DependenciesMap();
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
  }
@@ -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 retries(): number;
70
+ get attempts(): number;
71
71
  }
72
- declare function retry<T>(fn: (attempt: number) => Result<T> | Promise<Result<T>>, totalAttempts: number, { exponentialDelayBase }?: {
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 {};
@@ -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 retries() {
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.retries).toBe(1);
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.retries).toBe(2);
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.retries).toBe(2);
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
- // Just use a minimal delay for testing
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
- // We don't need to mock the wait function anymore
327
- // Instead, we've modified the retry function to use a minimal delay
328
- try {
329
- // Test successful on first attempt
330
- const successFn = async () => Result.ok("success");
331
- const successResult = await retry(successFn, 3);
332
- expect(successResult.status).toBe("ok");
333
- if (successResult.status === "ok") {
334
- expect(successResult.data).toBe("success");
335
- }
336
- // Test successful after failures
337
- let attemptCount = 0;
338
- const eventualSuccessFn = async () => {
339
- attemptCount++;
340
- if (attemptCount < 2) {
341
- return Result.error(new Error(`Attempt ${attemptCount} failed`));
342
- }
343
- return Result.ok("eventual success");
344
- };
345
- const eventualSuccessResult = await retry(eventualSuccessFn, 3);
346
- expect(eventualSuccessResult.status).toBe("ok");
347
- if (eventualSuccessResult.status === "ok") {
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
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackframe/stack-shared",
3
- "version": "2.7.21",
3
+ "version": "2.7.23",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "type": "module",