@stackframe/stack-shared 2.6.25 → 2.6.26
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 +8 -0
- package/dist/utils/errors.d.ts +1 -0
- package/dist/utils/errors.js +18 -1
- package/dist/utils/promises.js +4 -13
- package/dist/utils/react.d.ts +7 -0
- package/dist/utils/react.js +36 -32
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
package/dist/utils/errors.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Json } from "./json";
|
|
|
2
2
|
export declare function throwErr(errorMessage: string, extraData?: any): never;
|
|
3
3
|
export declare function throwErr(error: Error): never;
|
|
4
4
|
export declare function throwErr(...args: StatusErrorConstructorParameters): never;
|
|
5
|
+
export declare function concatStacktraces(first: Error, ...errors: Error[]): void;
|
|
5
6
|
export declare class StackAssertionError extends Error implements ErrorWithCustomCapture {
|
|
6
7
|
readonly extraData?: Record<string, any> | undefined;
|
|
7
8
|
constructor(message: string, extraData?: Record<string, any> | undefined, options?: ErrorOptions);
|
package/dist/utils/errors.js
CHANGED
|
@@ -11,6 +11,23 @@ export function throwErr(...args) {
|
|
|
11
11
|
throw new StatusError(...args);
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
|
+
function removeStacktraceNameLine(stack) {
|
|
15
|
+
// some browsers (eg. Chrome) prepend the stack with an extra line with the error name
|
|
16
|
+
const addsNameLine = new Error().stack?.startsWith("Error\n");
|
|
17
|
+
return stack.split("\n").slice(addsNameLine ? 1 : 0).join("\n");
|
|
18
|
+
}
|
|
19
|
+
export function concatStacktraces(first, ...errors) {
|
|
20
|
+
// some browsers (eg. Firefox) add an extra empty line at the end
|
|
21
|
+
const addsEmptyLineAtEnd = first.stack?.endsWith("\n");
|
|
22
|
+
// Add a reference to this function itself so that we know that stacktraces were concatenated
|
|
23
|
+
// If you are coming here from a stacktrace, please know that the two parts before and after this line are different
|
|
24
|
+
// stacktraces that were concatenated with concatStacktraces
|
|
25
|
+
const separator = removeStacktraceNameLine(new Error().stack ?? "").split("\n")[0];
|
|
26
|
+
for (const error of errors) {
|
|
27
|
+
const toAppend = removeStacktraceNameLine(error.stack ?? "");
|
|
28
|
+
first.stack += (addsEmptyLineAtEnd ? "" : "\n") + separator + "\n" + toAppend;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
14
31
|
export class StackAssertionError extends Error {
|
|
15
32
|
constructor(message, extraData, options) {
|
|
16
33
|
const disclaimer = `\n\nThis is likely an error in Stack. Please make sure you are running the newest version and report it.`;
|
|
@@ -33,7 +50,7 @@ export function registerErrorSink(sink) {
|
|
|
33
50
|
errorSinks.add(sink);
|
|
34
51
|
}
|
|
35
52
|
registerErrorSink((location, ...args) => {
|
|
36
|
-
console.error(`\x1b[
|
|
53
|
+
console.error(`\x1b[41mCaptured error in ${location}:`, ...args, "\x1b[0m");
|
|
37
54
|
});
|
|
38
55
|
registerErrorSink((location, error, ...extraArgs) => {
|
|
39
56
|
globalVar.stackCapturedErrors = globalVar.stackCapturedErrors ?? [];
|
package/dist/utils/promises.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StackAssertionError, captureError } from "./errors";
|
|
1
|
+
import { StackAssertionError, captureError, concatStacktraces } from "./errors";
|
|
2
2
|
import { DependenciesMap } from "./maps";
|
|
3
3
|
import { Result } from "./results";
|
|
4
4
|
import { generateUuid } from "./uuids";
|
|
@@ -101,12 +101,6 @@ export async function wait(ms) {
|
|
|
101
101
|
export async function waitUntil(date) {
|
|
102
102
|
return await wait(date.getTime() - Date.now());
|
|
103
103
|
}
|
|
104
|
-
class ErrorDuringRunAsynchronously extends Error {
|
|
105
|
-
constructor() {
|
|
106
|
-
super("The error above originated in a runAsynchronously() call. Here is the stacktrace associated with it.");
|
|
107
|
-
this.name = "ErrorDuringRunAsynchronously";
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
104
|
export function runAsynchronouslyWithAlert(...args) {
|
|
111
105
|
return runAsynchronously(args[0], {
|
|
112
106
|
...args[1],
|
|
@@ -120,13 +114,10 @@ export function runAsynchronously(promiseOrFunc, options = {}) {
|
|
|
120
114
|
if (typeof promiseOrFunc === "function") {
|
|
121
115
|
promiseOrFunc = promiseOrFunc();
|
|
122
116
|
}
|
|
123
|
-
const duringError = new
|
|
117
|
+
const duringError = new Error();
|
|
124
118
|
promiseOrFunc?.catch(error => {
|
|
125
|
-
const newError = new StackAssertionError("Uncaught error in asynchronous function: " + error.toString(), {
|
|
126
|
-
|
|
127
|
-
}, {
|
|
128
|
-
cause: error,
|
|
129
|
-
});
|
|
119
|
+
const newError = new StackAssertionError("Uncaught error in asynchronous function: " + error.toString(), { cause: error });
|
|
120
|
+
concatStacktraces(newError, duringError);
|
|
130
121
|
options.onError?.(newError);
|
|
131
122
|
if (!options.noErrorLogging) {
|
|
132
123
|
captureError("runAsynchronously", newError);
|
package/dist/utils/react.d.ts
CHANGED
|
@@ -9,6 +9,13 @@ export declare function getNodeText(node: React.ReactNode): string;
|
|
|
9
9
|
* You can use this to translate older query- or AsyncResult-based code to new the Suspense system, for example: `if (query.isLoading) suspend();`
|
|
10
10
|
*/
|
|
11
11
|
export declare function suspend(): never;
|
|
12
|
+
export declare class NoSuspenseBoundaryError extends Error {
|
|
13
|
+
digest: string;
|
|
14
|
+
reason: string;
|
|
15
|
+
constructor(options: {
|
|
16
|
+
caller?: string;
|
|
17
|
+
});
|
|
18
|
+
}
|
|
12
19
|
/**
|
|
13
20
|
* Use this in a component or a hook to disable SSR. Should be wrapped in a Suspense boundary, or it will throw an error.
|
|
14
21
|
*/
|
package/dist/utils/react.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import { isBrowserLike } from "./env";
|
|
2
3
|
import { neverResolve } from "./promises";
|
|
3
4
|
import { deindent } from "./strings";
|
|
4
|
-
import { isBrowserLike } from "./env";
|
|
5
5
|
export function forwardRefIfNeeded(render) {
|
|
6
6
|
// TODO: when we drop support for react 18, remove this
|
|
7
7
|
const version = React.version;
|
|
@@ -37,41 +37,45 @@ export function suspend() {
|
|
|
37
37
|
React.use(neverResolve());
|
|
38
38
|
throw new Error("Somehow a Promise that never resolves was resolved?");
|
|
39
39
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
This usually has one of three causes:
|
|
49
|
-
|
|
50
|
-
1. You are missing a loading.tsx file in your app directory. Fix it by adding a loading.tsx file in your app directory.
|
|
40
|
+
export class NoSuspenseBoundaryError extends Error {
|
|
41
|
+
constructor(options) {
|
|
42
|
+
super(deindent `
|
|
43
|
+
${options.caller ?? "This code path"} attempted to display a loading indicator, but didn't find a Suspense boundary above it. Please read the error message below carefully.
|
|
44
|
+
|
|
45
|
+
The fix depends on which of the 3 scenarios caused it:
|
|
46
|
+
|
|
47
|
+
1. You are missing a loading.tsx file in your app directory. Fix it by adding a loading.tsx file in your app directory.
|
|
51
48
|
|
|
52
|
-
|
|
49
|
+
2. The component is rendered in the root (outermost) layout.tsx or template.tsx file. Next.js does not wrap those files in a Suspense boundary, even if there is a loading.tsx file in the same folder. To fix it, wrap your layout inside a route group like this:
|
|
53
50
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
51
|
+
- app
|
|
52
|
+
- - layout.tsx // contains <html> and <body>, alongside providers and other components that don't need ${options.caller ?? "this code path"}
|
|
53
|
+
- - loading.tsx // required for suspense
|
|
54
|
+
- - (main)
|
|
55
|
+
- - - layout.tsx // contains the main layout of your app, like a sidebar or a header, and can use ${options.caller ?? "this code path"}
|
|
56
|
+
- - - route.tsx // your actual main page
|
|
57
|
+
- - - the rest of your app
|
|
61
58
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
59
|
+
For more information on this approach, see Next's documentation on route groups: https://nextjs.org/docs/app/building-your-application/routing/route-groups
|
|
60
|
+
|
|
61
|
+
3. You caught this error with try-catch or a custom error boundary. Fix this by rethrowing the error or not catching it in the first place.
|
|
65
62
|
|
|
66
|
-
|
|
63
|
+
See: https://nextjs.org/docs/messages/missing-suspense-with-csr-bailout
|
|
67
64
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
65
|
+
More information on SSR and Suspense boundaries: https://react.dev/reference/react/Suspense#providing-a-fallback-for-server-errors-and-client-only-content
|
|
66
|
+
`);
|
|
67
|
+
this.name = "NoSuspenseBoundaryError";
|
|
68
|
+
this.reason = options.caller ?? "suspendIfSsr()";
|
|
69
|
+
// set the digest so nextjs doesn't log the error
|
|
70
|
+
// https://github.com/vercel/next.js/blob/d01d6d9c35a8c2725b3d74c1402ab76d4779a6cf/packages/next/src/shared/lib/lazy-dynamic/bailout-to-csr.ts#L14
|
|
71
|
+
this.digest = "BAILOUT_TO_CLIENT_SIDE_RENDERING";
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Use this in a component or a hook to disable SSR. Should be wrapped in a Suspense boundary, or it will throw an error.
|
|
76
|
+
*/
|
|
77
|
+
export function suspendIfSsr(caller) {
|
|
78
|
+
if (!isBrowserLike()) {
|
|
79
|
+
throw new NoSuspenseBoundaryError({ caller });
|
|
76
80
|
}
|
|
77
81
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stackframe/stack-shared",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.26",
|
|
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.
|
|
53
|
+
"@stackframe/stack-sc": "2.6.26"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@simplewebauthn/types": "^11.0.0",
|