Improved error handling

This commit is contained in:
Stan Wohlwend 2024-04-23 12:02:18 +02:00
parent 13cb201369
commit 5e3004c18b
3 changed files with 34 additions and 15 deletions

View File

@ -10,7 +10,7 @@ import { getProject } from "@/lib/projects";
import { validateUrl } from "@/utils/url";
import { getPasswordError } from "@stackframe/stack-shared/dist/helpers/password";
import { getApiKeySet, publishableClientKeyHeaderSchema } from "@/lib/api-keys";
import { StatusError } from "@stackframe/stack-shared/dist/utils/errors";
import { StatusError, captureError } from "@stackframe/stack-shared/dist/utils/errors";
import { KnownErrors } from "@stackframe/stack-shared";
const postSchema = yup.object({
@ -97,7 +97,7 @@ export const POST = deprecatedSmartRouteHandler(async (req: NextRequest) => {
try {
await sendVerificationEmail(projectId, newUser.projectUserId, emailVerificationRedirectUrl);
} catch (error) {
console.error(error);
captureError("send-verification-email-on-sign-up", error);
}
return NextResponse.json({

View File

@ -52,10 +52,11 @@ export function resolved<T>(value: T): ReactPromise<T> {
/**
* Like Promise.resolve(...), but also adds the status and value properties for use with React's `use` hook.
*/
export function rejected<T>(reason: unknown): ReactPromise<T> {
return Object.assign(Promise.reject(reason), {
export function rejected<T>(reason: unknown, options: { disableErrorWrapping?: boolean } = {}): ReactPromise<T> {
const actualReason = options.disableErrorWrapping ? reason : createReactPromiseErrorWrapper(reason);
return Object.assign(Promise.reject(actualReason), {
status: "rejected",
reason,
reason: actualReason,
} as const);
}
@ -63,23 +64,41 @@ export function neverResolve(): ReactPromise<never> {
return pending(new Promise<never>(() => {}));
}
export function pending<T>(promise: Promise<T>): ReactPromise<T> {
const res = Object.assign(promise, {
status: "pending",
} as Pick<ReactPromise<T>, "status"> & { value: T, reason: unknown });
res.then(
export function pending<T>(promise: Promise<T>, options: { disableErrorWrapping?: boolean } = {}): ReactPromise<T> {
const res = promise.then(
value => {
res.status = "fulfilled";
res.value = value;
(res as any).value = value;
return value;
},
reason => {
actualReason => {
res.status = "rejected";
res.reason = reason;
const reason = options.disableErrorWrapping ? actualReason : createReactPromiseErrorWrapper(actualReason);
(res as any).reason = reason;
throw reason;
},
);
) as ReactPromise<T>;
res.status = "pending";
return res;
}
function createReactPromiseErrorWrapper(error: unknown): Error {
return new ReactPromiseErrorWrapper(error);
}
class ReactPromiseErrorWrapper extends StackAssertionError {
public readonly wrappedErrorMessage: string;
constructor(public readonly error: unknown) {
const wrappedErrorMessage = error instanceof ReactPromiseErrorWrapper ? error.wrappedErrorMessage : `${error}`;
super(
`Error occured while creating a ReactPromise: ${wrappedErrorMessage}\n\nSee the \`cause\` property for the original error. This error is a wrapper around the original error to preserve the stack trace (which would go lost when using Stack's pending(...) or rejected(...) functions).`,
{ cause: error }
);
this.wrappedErrorMessage = wrappedErrorMessage;
this.name = "ReactPromiseErrorWrapper";
}
}
export async function wait(ms: number) {
return await new Promise<void>(resolve => setTimeout(resolve, ms));
}

View File

@ -310,7 +310,7 @@ class _StackClientAppImpl<HasTokenStore extends boolean, ProjectId extends strin
if (options.currentClientUserJson !== undefined) {
this._currentUserCache.forceSetCachedValue([getTokenStore(this._tokenStoreOptions)], options.currentClientUserJson);
} else if (this.hasPersistentTokenStore()) {
runAsynchronously(() => this.getUser(), { ignoreErrors: true });
runAsynchronously(this.getUser(), { ignoreErrors: true });
}
if (options.currentProjectJson !== undefined) {
this._currentProjectCache.forceSetCachedValue([], options.currentProjectJson);