@stackframe/stack-shared 2.6.31 → 2.6.32

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,12 @@
1
1
  # @stackframe/stack-shared
2
2
 
3
+ ## 2.6.32
4
+
5
+ ### Patch Changes
6
+
7
+ - Bugfixes
8
+ - @stackframe/stack-sc@2.6.32
9
+
3
10
  ## 2.6.31
4
11
 
5
12
  ### Patch Changes
@@ -33,6 +33,7 @@ export declare class StackClientInterface {
33
33
  prodDashboard: string;
34
34
  prodBackend: string;
35
35
  }>;
36
+ protected _createNetworkError(cause: Error, session?: InternalSession | null, requestType?: "client" | "server" | "admin"): Promise<Error>;
36
37
  protected _networkRetry<T>(cb: () => Promise<Result<T, any>>, session?: InternalSession | null, requestType?: "client" | "server" | "admin"): Promise<T>;
37
38
  protected _networkRetryException<T>(cb: () => Promise<T>, session?: InternalSession | null, requestType?: "client" | "server" | "admin"): Promise<T>;
38
39
  fetchNewAccessToken(refreshToken: RefreshToken): Promise<AccessToken | null>;
@@ -5,7 +5,9 @@ import { AccessToken, InternalSession } from '../sessions';
5
5
  import { generateSecureRandomString } from '../utils/crypto';
6
6
  import { StackAssertionError, throwErr } from '../utils/errors';
7
7
  import { globalVar } from '../utils/globals';
8
+ import { HTTP_METHODS } from '../utils/http';
8
9
  import { filterUndefined } from '../utils/objects';
10
+ import { wait } from '../utils/promises';
9
11
  import { Result } from "../utils/results";
10
12
  import { deindent } from '../utils/strings';
11
13
  export class StackClientInterface {
@@ -68,6 +70,17 @@ export class StackClientInterface {
68
70
  prodBackend,
69
71
  };
70
72
  }
73
+ async _createNetworkError(cause, session, requestType) {
74
+ return new Error(deindent `
75
+ Stack Auth is unable to connect to the server. Please check your internet connection and try again.
76
+
77
+ If the problem persists, please contact support and provide a screenshot of your entire browser console.
78
+
79
+ ${cause}
80
+
81
+ ${JSON.stringify(await this.runNetworkDiagnostics(session, requestType), null, 2)}
82
+ `, { cause: cause });
83
+ }
71
84
  async _networkRetry(cb, session, requestType) {
72
85
  const retriedResult = await Result.retry(cb, 5, { exponentialDelayBase: 1000 });
73
86
  // try to diagnose the error for the user
@@ -75,15 +88,7 @@ export class StackClientInterface {
75
88
  if (globalVar.navigator && !globalVar.navigator.onLine) {
76
89
  throw new Error("Failed to send Stack network request. It seems like you are offline. (window.navigator.onLine is falsy)", { cause: retriedResult.error });
77
90
  }
78
- throw new Error(deindent `
79
- Stack Auth is unable to connect to the server. Please check your internet connection and try again.
80
-
81
- If the problem persists, please contact support and provide a screenshot of your entire browser console.
82
-
83
- ${retriedResult.error}
84
-
85
- ${JSON.stringify(await this.runNetworkDiagnostics(session, requestType), null, 2)}
86
- `, { cause: retriedResult.error });
91
+ throw this._createNetworkError(retriedResult.error, session, requestType);
87
92
  }
88
93
  return retriedResult.data;
89
94
  }
@@ -220,8 +225,13 @@ export class StackClientInterface {
220
225
  }
221
226
  catch (e) {
222
227
  if (e instanceof TypeError) {
223
- // Network error, retry
224
- return Result.error(e);
228
+ // Likely to be a network error. Retry if the request is idempotent, throw network error otherwise.
229
+ if (HTTP_METHODS[(params.method ?? "GET")].idempotent) {
230
+ return Result.error(e);
231
+ }
232
+ else {
233
+ throw this._createNetworkError(e, session, requestType);
234
+ }
225
235
  }
226
236
  throw e;
227
237
  }
@@ -254,10 +264,25 @@ export class StackClientInterface {
254
264
  if (res.ok) {
255
265
  return Result.ok(res);
256
266
  }
267
+ else if (res.status === 429) {
268
+ // Rate limited, so retry if we can
269
+ const retryAfter = res.headers.get("Retry-After");
270
+ if (retryAfter !== null) {
271
+ await wait(Number(retryAfter) * 1000);
272
+ return Result.error(new Error(`Rate limited, retrying after ${retryAfter} seconds`));
273
+ }
274
+ return Result.error(new Error("Rate limited, no retry-after header received"));
275
+ }
257
276
  else {
258
277
  const error = await res.text();
278
+ const errorObj = new StackAssertionError(`Failed to send request to ${url}: ${res.status} ${error}`, { request: params, res });
279
+ if (res.status === 508 && error.includes("INFINITE_LOOP_DETECTED")) {
280
+ // Some Vercel deployments seem to have an odd infinite loop bug. In that case, retry.
281
+ // See: https://github.com/stack-auth/stack/issues/319
282
+ return Result.error(errorObj);
283
+ }
259
284
  // Do not retry, throw error instead of returning one
260
- throw new StackAssertionError(`Failed to send request to ${url}: ${res.status} ${error}`, { request: params, res });
285
+ throw errorObj;
261
286
  }
262
287
  }
263
288
  async _processResponse(rawRes) {
@@ -4,6 +4,7 @@ export declare function encodeBase64(input: Uint8Array): string;
4
4
  export declare function decodeBase64(input: string): Uint8Array;
5
5
  export declare function encodeBase64Url(input: Uint8Array): string;
6
6
  export declare function decodeBase64Url(input: string): Uint8Array;
7
+ export declare function decodeBase64OrBase64Url(input: string): Uint8Array;
7
8
  export declare function isBase32(input: string): boolean;
8
9
  export declare function isBase64(input: string): boolean;
9
10
  export declare function isBase64Url(input: string): boolean;
@@ -82,6 +82,17 @@ export function decodeBase64Url(input) {
82
82
  }
83
83
  return decodeBase64(input.replace(/-/g, "+").replace(/_/g, "/") + "====".slice((input.length - 1) % 4 + 1));
84
84
  }
85
+ export function decodeBase64OrBase64Url(input) {
86
+ if (isBase64Url(input)) {
87
+ return decodeBase64Url(input);
88
+ }
89
+ else if (isBase64(input)) {
90
+ return decodeBase64(input);
91
+ }
92
+ else {
93
+ throw new StackAssertionError("Invalid base64 or base64url string");
94
+ }
95
+ }
85
96
  export function isBase32(input) {
86
97
  for (const char of input) {
87
98
  if (char === " ")
@@ -1,2 +1,39 @@
1
- export declare const HTTP_METHODS: readonly ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD", "TRACE", "CONNECT"];
2
- export type HttpMethod = typeof HTTP_METHODS[number];
1
+ export declare const HTTP_METHODS: {
2
+ readonly GET: {
3
+ readonly safe: true;
4
+ readonly idempotent: true;
5
+ };
6
+ readonly POST: {
7
+ readonly safe: false;
8
+ readonly idempotent: false;
9
+ };
10
+ readonly PUT: {
11
+ readonly safe: false;
12
+ readonly idempotent: true;
13
+ };
14
+ readonly DELETE: {
15
+ readonly safe: false;
16
+ readonly idempotent: true;
17
+ };
18
+ readonly PATCH: {
19
+ readonly safe: false;
20
+ readonly idempotent: false;
21
+ };
22
+ readonly OPTIONS: {
23
+ readonly safe: true;
24
+ readonly idempotent: true;
25
+ };
26
+ readonly HEAD: {
27
+ readonly safe: true;
28
+ readonly idempotent: true;
29
+ };
30
+ readonly TRACE: {
31
+ readonly safe: true;
32
+ readonly idempotent: true;
33
+ };
34
+ readonly CONNECT: {
35
+ readonly safe: false;
36
+ readonly idempotent: false;
37
+ };
38
+ };
39
+ export type HttpMethod = keyof typeof HTTP_METHODS;
@@ -1 +1,38 @@
1
- export const HTTP_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD", "TRACE", "CONNECT"];
1
+ export const HTTP_METHODS = {
2
+ "GET": {
3
+ safe: true,
4
+ idempotent: true,
5
+ },
6
+ "POST": {
7
+ safe: false,
8
+ idempotent: false,
9
+ },
10
+ "PUT": {
11
+ safe: false,
12
+ idempotent: true,
13
+ },
14
+ "DELETE": {
15
+ safe: false,
16
+ idempotent: true,
17
+ },
18
+ "PATCH": {
19
+ safe: false,
20
+ idempotent: false,
21
+ },
22
+ "OPTIONS": {
23
+ safe: true,
24
+ idempotent: true,
25
+ },
26
+ "HEAD": {
27
+ safe: true,
28
+ idempotent: true,
29
+ },
30
+ "TRACE": {
31
+ safe: true,
32
+ idempotent: true,
33
+ },
34
+ "CONNECT": {
35
+ safe: false,
36
+ idempotent: false,
37
+ },
38
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackframe/stack-shared",
3
- "version": "2.6.31",
3
+ "version": "2.6.32",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "files": [
@@ -50,7 +50,7 @@
50
50
  "oauth4webapi": "^2.10.3",
51
51
  "semver": "^7.6.3",
52
52
  "uuid": "^9.0.1",
53
- "@stackframe/stack-sc": "2.6.31"
53
+ "@stackframe/stack-sc": "2.6.32"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@simplewebauthn/types": "^11.0.0",