mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-04 21:04:37 +08:00
Report all errors to Sentry
This commit is contained in:
parent
9d733177d3
commit
6fe5ca45eb
@ -9,7 +9,7 @@
|
||||
"build:docs": "turbo run build --no-cache --filter=stack-docs...",
|
||||
"build:server": "turbo run build --no-cache --filter=@stackframe/stack-server...",
|
||||
"build:demo": "turbo run build --no-cache --filter=demo-app...",
|
||||
"clean": "turbo run clean --no-cache && rimraf node_modules",
|
||||
"clean": "turbo run clean --no-cache && rimraf .turbo && rimraf node_modules",
|
||||
"codegen": "turbo run codegen --no-cache",
|
||||
"psql:server": "pnpm run --filter=@stackframe/stack-server psql",
|
||||
"prisma:server": "pnpm run --filter=@stackframe/stack-server prisma",
|
||||
|
||||
@ -1,9 +1,18 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { smartRouteHandler } from "@/lib/route-handlers";
|
||||
import { StackAssertionError } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import * as yup from "yup";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
// A faulty API route to test Sentry's error monitoring
|
||||
export function GET() {
|
||||
throw new Error("Sentry Example API Route Error");
|
||||
return NextResponse.json({ data: "Testing Sentry Error..." });
|
||||
}
|
||||
export const GET = smartRouteHandler({
|
||||
request: yup.object({
|
||||
method: yup.string().oneOf(["GET"]).required(),
|
||||
}),
|
||||
response: yup.object({
|
||||
statusCode: yup.number().oneOf([200]).required(),
|
||||
bodyType: yup.string().oneOf(["text"]).required(),
|
||||
body: yup.string().required(),
|
||||
}),
|
||||
handler: async (req) => {
|
||||
console.error("hiya");
|
||||
throw new StackAssertionError("This is a test error", {abc: "smth", def: new Error("This is an error"), map: new Map([["key", "value"]])});
|
||||
},
|
||||
});
|
||||
|
||||
@ -3,7 +3,7 @@ import * as yup from "yup";
|
||||
import { generators } from "openid-client";
|
||||
import { cookies } from "next/headers";
|
||||
import { encryptJWT } from "@stackframe/stack-shared/dist/utils/jwt";
|
||||
import { StatusError } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import { StackAssertionError, StatusError } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import { deprecatedSmartRouteHandler, deprecatedParseRequest } from "@/lib/route-handlers";
|
||||
import { getAuthorizationUrl } from "@/oauth";
|
||||
import { getProject } from "@/lib/projects";
|
||||
@ -50,7 +50,7 @@ export const GET = deprecatedSmartRouteHandler(async (req: NextRequest, options:
|
||||
|
||||
if (!project) {
|
||||
// This should never happen, make typescript happy
|
||||
throw new Error("Project not found");
|
||||
throw new StackAssertionError("Project not found");
|
||||
}
|
||||
|
||||
const provider = project.evaluatedConfig.oauthProviders.find((p) => p.id === providerId);
|
||||
|
||||
@ -2,7 +2,7 @@ import * as yup from "yup";
|
||||
import { cookies } from "next/headers";
|
||||
import { Request as OAuthRequest, Response as OAuthResponse } from "@node-oauth/oauth2-server";
|
||||
import { NextRequest } from "next/server";
|
||||
import { StatusError } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import { StackAssertionError, StatusError } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import { decryptJWT } from "@stackframe/stack-shared/dist/utils/jwt";
|
||||
import { deprecatedSmartRouteHandler, deprecatedParseRequest as deprecatedParseRequest } from "@/lib/route-handlers";
|
||||
import { getAuthorizationCallback, oauthServer } from "@/oauth";
|
||||
@ -69,7 +69,7 @@ export const GET = deprecatedSmartRouteHandler(async (req: NextRequest, options:
|
||||
|
||||
if (!project) {
|
||||
// This should never happen, make typescript happy
|
||||
throw new Error("Project not found");
|
||||
throw new StackAssertionError("Project not found");
|
||||
}
|
||||
|
||||
const provider = project.evaluatedConfig.oauthProviders.find((p) => p.id === providerId);
|
||||
|
||||
@ -6,7 +6,7 @@ import { sendPasswordResetEmail } from "@/email";
|
||||
import { getApiKeySet, publishableClientKeyHeaderSchema } from "@/lib/api-keys";
|
||||
import { getProject } from "@/lib/projects";
|
||||
import { validateUrl } from "@/utils/url";
|
||||
import { StatusError } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import { StackAssertionError, StatusError } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import { KnownErrors } from "@stackframe/stack-shared";
|
||||
|
||||
const postSchema = yup.object({
|
||||
@ -38,7 +38,7 @@ export const POST = deprecatedSmartRouteHandler(async (req: NextRequest) => {
|
||||
|
||||
const project = await getProject(projectId);
|
||||
if (!project) {
|
||||
throw new Error("Project not found"); // This should never happen, make typescript happy
|
||||
throw new StackAssertionError("Project not found"); // This should never happen, make typescript happy
|
||||
}
|
||||
|
||||
if (!project.evaluatedConfig.credentialEnabled) {
|
||||
|
||||
@ -54,12 +54,9 @@ const handler = deprecatedSmartRouteHandler(async (req: NextRequest) => {
|
||||
if (user.primaryEmailVerified) {
|
||||
throw new KnownErrors.EmailAlreadyVerified();
|
||||
}
|
||||
try {
|
||||
await sendVerificationEmail(projectId, userId, emailVerificationRedirectUrl);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
await sendVerificationEmail(projectId, userId, emailVerificationRedirectUrl);
|
||||
|
||||
return NextResponse.json({});
|
||||
});
|
||||
export const POST = handler;
|
||||
|
||||
@ -7,7 +7,7 @@ import { deprecatedParseRequest, deprecatedSmartRouteHandler } from "@/lib/route
|
||||
import { encodeAccessToken } from "@/lib/access-token";
|
||||
import { getApiKeySet, publishableClientKeyHeaderSchema } from "@/lib/api-keys";
|
||||
import { getProject } from "@/lib/projects";
|
||||
import { StatusError } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import { StackAssertionError, StatusError } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import { KnownErrors } from "@stackframe/stack-shared";
|
||||
|
||||
const postSchema = yup.object({
|
||||
@ -39,7 +39,7 @@ export const POST = deprecatedSmartRouteHandler(async (req: NextRequest) => {
|
||||
|
||||
const project = await getProject(projectId);
|
||||
if (!project) {
|
||||
throw new Error("Project not found"); // This should never happen, make typescript happy
|
||||
throw new StackAssertionError("Project not found"); // This should never happen, make typescript happy
|
||||
}
|
||||
|
||||
if (!project.evaluatedConfig.credentialEnabled) {
|
||||
@ -58,7 +58,7 @@ export const POST = deprecatedSmartRouteHandler(async (req: NextRequest) => {
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
throw new Error("This should never happen (the comparePassword call should've already caused this to fail)");
|
||||
throw new StackAssertionError("This should never happen (the comparePassword call should've already caused this to fail)");
|
||||
}
|
||||
|
||||
const refreshToken = generateSecureRandomString();
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import '../polyfills';
|
||||
|
||||
import type { Metadata } from 'next';
|
||||
import {GeistSans} from 'geist/font/sans';
|
||||
import {GeistMono} from "geist/font/mono";
|
||||
|
||||
@ -55,7 +55,7 @@ export default function Page() {
|
||||
}, async () => {
|
||||
const res = await fetch("/api/sentry-example-api");
|
||||
if (!res.ok) {
|
||||
throw new Error("Sentry Example Frontend Error");
|
||||
throw new Error("Sentry Example Frontend Error!!!!");
|
||||
}
|
||||
});
|
||||
}}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { Box } from "@mui/joy";
|
||||
import { useCallback, useRef, useState } from "react";
|
||||
import { throwErr } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import { deepPlainEquals } from "@stackframe/stack-shared/dist/utils/objects";
|
||||
import { useMutationObserver } from "@/hooks/use-mutation-observer";
|
||||
import { throwStackErr } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
|
||||
type Level = 1 | 2 | 3 | 4 | 5 | 6;
|
||||
function isLevel(level: number): level is Level {
|
||||
@ -26,14 +26,14 @@ export function PageOverview(props: { children: React.ReactNode, onOverviewChang
|
||||
const [lastOverview, setLastOverview] = useState<Overview | null>(null);
|
||||
|
||||
const mutationCallback = useCallback((mutations: MutationRecord[] | "init") => {
|
||||
const node = ref?.current ?? throwErr("mutation callback should never be called when ref is null!");
|
||||
const node = ref?.current ?? throwStackErr("mutation callback should never be called when ref is null!");
|
||||
const headings = [...node.querySelectorAll("h1, h2, h3, h4, h5, h6")] as HTMLHeadingElement[];
|
||||
const headingsWithId = headings.filter((heading) => !!heading.id);
|
||||
|
||||
const sections: Section[] = [];
|
||||
for (const heading of headingsWithId) {
|
||||
const headingLevel = parseInt(heading.tagName[1]);
|
||||
if (!isLevel(headingLevel)) throwErr("Invalid heading tag name " + heading.tagName);
|
||||
if (!isLevel(headingLevel)) throwStackErr("Invalid heading tag name " + heading.tagName);
|
||||
|
||||
let curSections = sections;
|
||||
while (curSections.length > 0) {
|
||||
|
||||
@ -7,7 +7,7 @@ import { generateUuid } from "@stackframe/stack-shared/dist/utils/uuids";
|
||||
import { EmailConfigJson, SharedProvider, StandardProvider, sharedProviders, standardProviders } from "@stackframe/stack-shared/dist/interface/clientInterface";
|
||||
import { typedToUppercase } from "@stackframe/stack-shared/dist/utils/strings";
|
||||
import { OAuthProviderUpdateOptions, ProjectUpdateOptions } from "@stackframe/stack-shared/dist/interface/adminInterface";
|
||||
import { throwErr } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import { StackAssertionError, captureError, throwStackErr } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
|
||||
|
||||
function toDBSharedProvider(type: SharedProvider): ProxiedOAuthProviderType {
|
||||
@ -111,11 +111,11 @@ export async function isProjectAdmin(projectId: string, adminAccessToken: string
|
||||
function listProjectIds(projectUser: ServerUserJson) {
|
||||
const serverMetadata = projectUser.serverMetadata;
|
||||
if (typeof serverMetadata !== "object" || !(!serverMetadata || "managedProjectIds" in serverMetadata)) {
|
||||
throw new Error("Invalid server metadata, did something go wrong?");
|
||||
throw new StackAssertionError("Invalid server metadata, did something go wrong?", { serverMetadata });
|
||||
}
|
||||
const managedProjectIds = serverMetadata?.managedProjectIds ?? [];
|
||||
if (!isStringArray(managedProjectIds)) {
|
||||
throw new Error("Invalid server metadata, did something go wrong? Expected string array");
|
||||
throw new StackAssertionError("Invalid server metadata, did something go wrong? Expected string array", { managedProjectIds });
|
||||
}
|
||||
|
||||
return managedProjectIds;
|
||||
@ -263,7 +263,7 @@ export async function updateProject(
|
||||
const providerMap = new Map(oldProviders.map((provider) => [
|
||||
provider.id,
|
||||
{
|
||||
providerUpdate: oauthProvidersUpdate.find((p) => p.id === provider.id) ?? throwErr(`Missing provider update for provider '${provider.id}'`),
|
||||
providerUpdate: oauthProvidersUpdate.find((p) => p.id === provider.id) ?? throwStackErr(`Missing provider update for provider '${provider.id}'`),
|
||||
oldProvider: provider,
|
||||
}
|
||||
]));
|
||||
@ -314,7 +314,7 @@ export async function updateProject(
|
||||
},
|
||||
};
|
||||
} else {
|
||||
console.error(`Invalid provider type '${providerUpdate.type}'`);
|
||||
throw new StackAssertionError(`Invalid provider type '${providerUpdate.type}'`, { providerUpdate });
|
||||
}
|
||||
|
||||
transaction.push(prismaClient.oAuthProviderConfig.update({
|
||||
@ -351,7 +351,7 @@ export async function updateProject(
|
||||
},
|
||||
};
|
||||
} else {
|
||||
console.error(`Invalid provider type '${provider.update.type}'`);
|
||||
throw new StackAssertionError(`Invalid provider type '${provider.update.type}'`, { provider });
|
||||
}
|
||||
|
||||
transaction.push(prismaClient.oAuthProviderConfig.create({
|
||||
@ -459,7 +459,7 @@ function projectJsonFromDbType(project: ProjectDB): ProjectJson {
|
||||
tenantId: provider.standardOAuthConfig.tenantId || undefined,
|
||||
}];
|
||||
}
|
||||
console.error(`Exactly one of the provider configs should be set on provider config '${provider.id}' of project '${project.id}'. Ignoring it`, { project });
|
||||
captureError("projectJsonFromDbType", new StackAssertionError(`Exactly one of the provider configs should be set on provider config '${provider.id}' of project '${project.id}'. Ignoring it`, { project }));
|
||||
return [];
|
||||
}),
|
||||
emailConfig,
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import "../polyfills";
|
||||
|
||||
import { NextRequest } from "next/server";
|
||||
import { StatusError, throwErr } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import { StatusError, captureError, throwErr } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import * as yup from "yup";
|
||||
import { DeepPartial } from "@stackframe/stack-shared/dist/utils/objects";
|
||||
import { Json } from "@stackframe/stack-shared/dist/utils/json";
|
||||
@ -204,7 +206,7 @@ function catchError(error: unknown): StatusError {
|
||||
}
|
||||
|
||||
if (error instanceof StatusError) return error;
|
||||
console.error(`Unhandled error in route handler:`, error);
|
||||
captureError(`route-handler`, error);
|
||||
return new StatusError(StatusError.InternalServerError);
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import './polyfills';
|
||||
|
||||
import { NextResponse } from 'next/server';
|
||||
import type { NextRequest } from 'next/server';
|
||||
import { deindent } from '@stackframe/stack-shared/dist/utils/strings';
|
||||
|
||||
@ -6,6 +6,7 @@ import { decodeAccessToken, encodeAccessToken } from "@/lib/access-token";
|
||||
import { validateUrl } from "@/utils/url";
|
||||
import { checkApiKeySet } from "@/lib/api-keys";
|
||||
import { getProject } from "@/lib/projects";
|
||||
import { captureError } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
|
||||
const enabledScopes = ["openid"];
|
||||
|
||||
@ -102,7 +103,7 @@ export class OAuthModel implements AuthorizationCodeModel {
|
||||
try {
|
||||
decoded = await decodeAccessToken(accessToken);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
captureError("getAccessToken", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -222,8 +223,8 @@ export class OAuthModel implements AuthorizationCodeModel {
|
||||
|
||||
return !!deletedCode;
|
||||
} catch (e) {
|
||||
if (! (e instanceof PrismaClientKnownRequestError)) {
|
||||
console.error(e);
|
||||
if (!(e instanceof PrismaClientKnownRequestError)) {
|
||||
throw e;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -86,8 +86,7 @@ export abstract class OAuthBaseProvider {
|
||||
tokenSet = await this.oauthClient.oauthCallback(this.redirectUri, callbackParams, params);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("OAuth callback failed", error);
|
||||
throw new Error("OAuth callback failed");
|
||||
throw new Error("OAuth callback failed", { cause: error });
|
||||
}
|
||||
if (!tokenSet.access_token) {
|
||||
throw new Error("No access token received");
|
||||
|
||||
13
packages/stack-server/src/polyfills.tsx
Normal file
13
packages/stack-server/src/polyfills.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import { registerErrorSink } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
|
||||
const sentryErrorSink = (location: string, error: unknown) => {
|
||||
console.log("YAAAA");
|
||||
Sentry.captureException(error, { extra: { location } });
|
||||
};
|
||||
|
||||
export function ensurePolyfilled() {
|
||||
registerErrorSink(sentryErrorSink);
|
||||
}
|
||||
|
||||
ensurePolyfilled();
|
||||
@ -1,3 +1,5 @@
|
||||
import './polyfills';
|
||||
|
||||
import { StackServerApp } from '@stackframe/stack';
|
||||
|
||||
export const stackServerApp = new StackServerApp({
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import React from "react";
|
||||
import { captureError } from "../utils/errors";
|
||||
|
||||
export function useAsyncCallback<A extends any[], R>(
|
||||
callback: (...args: A) => Promise<R>,
|
||||
@ -33,7 +34,7 @@ export function useAsyncCallbackWithLoggedError<A extends any[], R>(
|
||||
try {
|
||||
return await callback(...args);
|
||||
} catch (e) {
|
||||
console.error("Uncaught error in async callback", e);
|
||||
captureError("async-callback", e);
|
||||
throw e;
|
||||
}
|
||||
}, deps);
|
||||
|
||||
@ -5,6 +5,7 @@ import { Result } from "../utils/results";
|
||||
import { ReadonlyJson } from '../utils/json';
|
||||
import { AsyncStore, ReadonlyAsyncStore } from '../utils/stores';
|
||||
import { KnownError, KnownErrors } from '../known-errors';
|
||||
import { StackAssertionError } from '../utils/errors';
|
||||
|
||||
export type UserCustomizableJson = {
|
||||
readonly projectId: string,
|
||||
@ -202,16 +203,14 @@ export class StackClientInterface {
|
||||
|
||||
let challenges: oauth.WWWAuthenticateChallenge[] | undefined;
|
||||
if ((challenges = oauth.parseWwwAuthenticateChallenges(response.data))) {
|
||||
for (const challenge of challenges) {
|
||||
console.error('WWW-Authenticate Challenge', challenge);
|
||||
}
|
||||
throw new Error(); // Handle WWW-Authenticate Challenges as needed
|
||||
// TODO Handle WWW-Authenticate Challenges as needed
|
||||
throw new StackAssertionError("OAuth WWW-Authenticate challenge not implemented", { challenges });
|
||||
}
|
||||
|
||||
const result = await oauth.processRefreshTokenResponse(as, client, response.data);
|
||||
if (oauth.isOAuth2Error(result)) {
|
||||
console.error('Error Response', result);
|
||||
throw new Error(); // Handle OAuth 2.0 response body error
|
||||
// TODO Handle OAuth 2.0 response body error
|
||||
throw new StackAssertionError("OAuth error", { result });
|
||||
}
|
||||
|
||||
tokenStore.update(old => ({
|
||||
@ -602,8 +601,7 @@ export class StackClientInterface {
|
||||
};
|
||||
const params = oauth.validateAuthResponse(as, client, oauthParams, state);
|
||||
if (oauth.isOAuth2Error(params)) {
|
||||
console.error('Error validating OAuth response', params);
|
||||
throw new Error("Error validating OAuth response"); // Handle OAuth 2.0 redirect error
|
||||
throw new StackAssertionError("Error validating OAuth response", { params }); // Handle OAuth 2.0 redirect error
|
||||
}
|
||||
const response = await oauth.authorizationCodeGrantRequest(
|
||||
as,
|
||||
@ -615,16 +613,14 @@ export class StackClientInterface {
|
||||
|
||||
let challenges: oauth.WWWAuthenticateChallenge[] | undefined;
|
||||
if ((challenges = oauth.parseWwwAuthenticateChallenges(response))) {
|
||||
for (const challenge of challenges) {
|
||||
console.error('WWW-Authenticate Challenge', challenge);
|
||||
}
|
||||
throw new Error("WWW-Authenticate challenge not implemented"); // Handle WWW-Authenticate Challenges as needed
|
||||
// TODO Handle WWW-Authenticate Challenges as needed
|
||||
throw new StackAssertionError("OAuth WWW-Authenticate challenge not implemented", { challenges });
|
||||
}
|
||||
|
||||
const result = await oauth.processAuthorizationCodeOAuth2Response(as, client, response);
|
||||
if (oauth.isOAuth2Error(result)) {
|
||||
console.error('Error Response', result);
|
||||
throw new Error(); // Handle OAuth 2.0 response body error
|
||||
// TODO Handle OAuth 2.0 response body error
|
||||
throw new StackAssertionError("OAuth error", { result });
|
||||
}
|
||||
tokenStore.update(old => ({
|
||||
accessToken: result.access_token ?? null,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { StatusError, throwErr } from "./utils/errors";
|
||||
import { identity, identityArgs } from "./utils/functions";
|
||||
import { StatusError, throwErr, throwStackErr } from "./utils/errors";
|
||||
import { identityArgs } from "./utils/functions";
|
||||
import { Json } from "./utils/json";
|
||||
|
||||
export type KnownErrorJson = {
|
||||
@ -48,7 +48,7 @@ export abstract class KnownError extends StatusError {
|
||||
}
|
||||
|
||||
get errorCode(): string {
|
||||
return (this.constructor as any).errorCode ?? throwErr(`Can't find error code for this KnownError. Is its constructor a KnownErrorConstructor? ${this}`);
|
||||
return (this.constructor as any).errorCode ?? throwStackErr(`Can't find error code for this KnownError. Is its constructor a KnownErrorConstructor? ${this}`);
|
||||
}
|
||||
|
||||
public static constructorArgsFromJson(json: KnownErrorJson): ConstructorParameters<typeof KnownError> {
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { Json } from "./json";
|
||||
|
||||
|
||||
export function throwErr(errorMessage: string): never;
|
||||
export function throwErr(error: Error): never;
|
||||
export function throwErr(...args: StatusErrorConstructorParameters): never;
|
||||
@ -14,6 +15,36 @@ export function throwErr(...args: any[]): never {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class StackAssertionError extends Error {
|
||||
constructor(message: string, public readonly extraData?: Record<string, any>, options?: ErrorOptions) {
|
||||
super(`${message}\n\nThis is likely an error in Stack. Please report it.`, options);
|
||||
}
|
||||
}
|
||||
|
||||
export function throwStackErr(message: string, extraData?: any): never {
|
||||
throw new StackAssertionError(message, extraData);
|
||||
}
|
||||
|
||||
|
||||
const errorSinks = new Set<(location: string, error: unknown) => void>();
|
||||
export function registerErrorSink(sink: (location: string, error: unknown) => void): void {
|
||||
if (errorSinks.has(sink)) {
|
||||
console.log("Error sink already registered", sink);
|
||||
return;
|
||||
}
|
||||
console.log("Registering error sink", sink);
|
||||
errorSinks.add(sink);
|
||||
}
|
||||
registerErrorSink((location, ...args) => console.error(`Error in ${location}:`, ...args));
|
||||
|
||||
export function captureError(location: string, error: unknown): void {
|
||||
for (const sink of errorSinks) {
|
||||
sink(location, error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
type Status = {
|
||||
statusCode: number,
|
||||
message: string,
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { StackAssertionError, captureError } from "./errors";
|
||||
import { Result } from "./results";
|
||||
import { generateUuid } from "./uuids";
|
||||
import type { RejectedThenable, FulfilledThenable, PendingThenable } from "react";
|
||||
@ -105,15 +106,17 @@ export function runAsynchronously(
|
||||
}
|
||||
const duringError = new ErrorDuringRunAsynchronously();
|
||||
promiseOrFunc?.catch(error => {
|
||||
const newError = new Error(
|
||||
const newError = new StackAssertionError(
|
||||
"Uncaught error in asynchronous function: " + error.toString(),
|
||||
{
|
||||
duringError,
|
||||
},
|
||||
{
|
||||
cause: error,
|
||||
}
|
||||
);
|
||||
if (!options.ignoreErrors) {
|
||||
console.error(newError);
|
||||
console.error(duringError);
|
||||
captureError("runAsynchronously", newError);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ import { saveVerifierAndState, getVerifierAndState } from "./cookie";
|
||||
import { constructRedirectUrl } from "../utils/url";
|
||||
import { TokenStore } from "@stackframe/stack-shared/dist/interface/clientInterface";
|
||||
import { neverResolve } from "@stackframe/stack-shared/dist/utils/promises";
|
||||
import { StackAssertionError } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
|
||||
export async function signInWithOAuth(
|
||||
iface: StackClientInterface,
|
||||
@ -73,7 +74,7 @@ export async function callOAuthCallback(
|
||||
// callOAuthCallback is called multiple times in parallel
|
||||
const { codeVerifier, state } = getVerifierAndState();
|
||||
if (!codeVerifier || !state) {
|
||||
throw new Error("Invalid OAuth callback URL");
|
||||
throw new Error("Invalid OAuth callback URL parameters. It seems like the OAuth flow was interrupted, so please try again.");
|
||||
}
|
||||
const originalUrl = consumeOAuthCallbackQueryParams(state);
|
||||
if (!originalUrl) return null;
|
||||
@ -89,7 +90,6 @@ export async function callOAuthCallback(
|
||||
tokenStore,
|
||||
);
|
||||
} catch (e) {
|
||||
console.error("Error signing in during OAuth callback", e);
|
||||
throw new Error("Error signing in. Please try again.");
|
||||
throw new StackAssertionError("Error signing in during OAuth callback. Please try again.", { cause: e });
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React, { use, useCallback, useMemo } from "react";
|
||||
import { KnownErrors, OAuthProviderConfigJson, ServerUserCustomizableJson, ServerUserJson, StackAdminInterface, StackClientInterface, StackServerInterface } from "@stackframe/stack-shared";
|
||||
import { getCookie, setOrDeleteCookie } from "./cookie";
|
||||
import { throwErr } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import { StackAssertionError, throwErr } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import { generateUuid } from "@stackframe/stack-shared/dist/utils/uuids";
|
||||
import { AsyncResult, Result } from "@stackframe/stack-shared/dist/utils/results";
|
||||
import { suspendIfSsr } from "@stackframe/stack-shared/dist/utils/react";
|
||||
@ -297,7 +297,7 @@ class _StackClientAppImpl<HasTokenStore extends boolean, ProjectId extends strin
|
||||
|
||||
this._uniqueIdentifier = options.uniqueIdentifier ?? generateUuid();
|
||||
if (allClientApps.has(this._uniqueIdentifier)) {
|
||||
throw new Error("A Stack client app with the same unique identifier already exists");
|
||||
throw new StackAssertionError("A Stack client app with the same unique identifier already exists");
|
||||
}
|
||||
allClientApps.set(this._uniqueIdentifier, [options.checkString ?? "default check string", this]);
|
||||
|
||||
@ -714,8 +714,7 @@ class _StackClientAppImpl<HasTokenStore extends boolean, ProjectId extends strin
|
||||
if (existing) {
|
||||
const [existingCheckString, clientApp] = existing;
|
||||
if (existingCheckString !== providedCheckString) {
|
||||
console.error("The provided app JSON does not match the configuration of the existing client app with the same unique identifier", { providedObj: json, existingString: existingCheckString });
|
||||
throw new Error("The provided app JSON does not match the configuration of the existing client app with the same unique identifier");
|
||||
throw new StackAssertionError("The provided app JSON does not match the configuration of the existing client app with the same unique identifier", { providedObj: json, existingString: existingCheckString });
|
||||
}
|
||||
return clientApp as any;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user