Hosted components (#1229)

<!--

Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md

-->


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added a new "Hosted Components" app with its own app shell, routing,
auth-aware UI, a handler route, and a welcome page showing the signed-in
user.
* **Chores**
* Added dev tooling and configuration for the new app (build, lint,
typecheck, Vite/TS, package manifest) and updated dev env API URL.
* **Tests**
  * Excluded the new app from the test workspace.
* **Bug Fixes**
* Suppressed noisy console errors for a specific internal sentinel and
clarified related error messaging.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Konstantin Wohlwend <[email protected]>
This commit is contained in:
BilalG1
2026-03-10 11:29:05 -07:00
committed by GitHub
co-authored by Konstantin Wohlwend
parent 85ea5d25c8
commit a64055cfca
16 changed files with 1287 additions and 44 deletions
@@ -0,0 +1,29 @@
import { createGlobal } from "./globals";
export const NO_SUSPENSE_BOUNDARY_ERROR_SENTINEL = "__stack-no-suspense-boundary-error__";
export function isNoSuspenseBoundaryError(value: unknown): boolean {
return (
typeof value === "object"
&& value !== null
&& (value as Record<string, unknown>).__noSuspenseBoundarySentinel === NO_SUSPENSE_BOUNDARY_ERROR_SENTINEL
);
}
export function ensureMonkeyPatch() {
createGlobal("__console-error-monkey-patch__", () => {
const originalConsoleError = console.error;
console.error = function (...args: unknown[]) {
// React's default error handlers will log all errors to the console, even those that we intentionally use to suppress SSR.
// Next.js among others override the default error handler and will not log SSR errors to the console.
// However, vanilla React and other frameworks like TanStack Start use the default error handler.
// Hence, we suppress the error here if it is a NoSuspenseBoundaryError.
// It's very cursed, but it's really our best option. Talk to @konsti if you want to know more.
if (args.length === 1 && isNoSuspenseBoundaryError(args[0])) {
return;
}
return originalConsoleError.apply(this, args);
};
return true;
});
}
+8 -2
View File
@@ -2,6 +2,7 @@ import React, { SetStateAction } from "react";
import { isBrowserLike } from "./env";
import { neverResolve, runAsynchronously } from "./promises";
import { AsyncResult } from "./results";
import { ensureMonkeyPatch, NO_SUSPENSE_BOUNDARY_ERROR_SENTINEL } from "./monkey-patch";
import { deindent } from "./strings";
export function componentWrapper<
@@ -233,14 +234,17 @@ export function shouldRethrowRenderingError(error: unknown): boolean {
export class NoSuspenseBoundaryError extends Error {
digest: string;
reason: string;
__noSuspenseBoundarySentinel = NO_SUSPENSE_BOUNDARY_ERROR_SENTINEL;
constructor(options: { caller?: string }) {
ensureMonkeyPatch();
super(deindent`
Suspense boundary not found! Read the error message below carefully on how to fix it.
Suspense boundary not found! Read the error message below carefully (or paste it into your AI agent).
${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.
The fix depends on which of the 4 scenarios caused it:
There are several potential causes:
1. [Next.js] You are missing a loading.tsx file in your app directory. Fix it by adding a loading.tsx file in your app directory.
@@ -260,6 +264,8 @@ export class NoSuspenseBoundaryError extends Error {
4. 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.
5. Your version of Stack Auth is too old. Upgrade to the latest version to see if that fixes the issue.
See: https://nextjs.org/docs/messages/missing-suspense-with-csr-bailout
More information on SSR and Suspense boundaries: https://react.dev/reference/react/Suspense#providing-a-fallback-for-server-errors-and-client-only-content