X Tutup
Skip to content

Hosted components#1229

Open
BilalG1 wants to merge 6 commits intodevfrom
hosted-components-page
Open

Hosted components#1229
BilalG1 wants to merge 6 commits intodevfrom
hosted-components-page

Conversation

@BilalG1
Copy link
Collaborator

@BilalG1 BilalG1 commented Mar 2, 2026

Summary by CodeRabbit

  • New Features
    • Added a new "Hosted Components" app with routing, root app shell, auth-aware UI, a handler route, and a welcome page showing the signed-in user.
  • Chores
    • Dev environment API URL and project tooling added (build, lint, typecheck, Vite/TS config, package manifest).
  • Tests
    • Excluded the new app from the Vitest workspace.

@vercel
Copy link

vercel bot commented Mar 2, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
stack-backend Ready Ready Preview, Comment Mar 10, 2026 3:25am
stack-dashboard Ready Ready Preview, Comment Mar 10, 2026 3:25am
stack-demo Ready Ready Preview, Comment Mar 10, 2026 3:25am
stack-docs Ready Ready Preview, Comment Mar 10, 2026 3:25am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 2, 2026

📝 Walkthrough

Walkthrough

Adds a new "Hosted Components" frontend app plus Vite/TypeScript/ESLint config, TanStack Router routes (root, index, handler), React client entry, Stack client integration, vitest exclusion, and a dev-launchpad registration entry.

Changes

Cohort / File(s) Summary
Dev Launchpad
apps/dev-launchpad/public/index.html
Inserts "Hosted Components" app entry (portSuffix "09") into the apps list between Docs and Inbucket.
App manifest & env
apps/hosted-components/package.json, apps/hosted-components/.env.development
Adds new frontend package manifest and dev env var VITE_STACK_API_URL=http://localhost:9102.
Tooling & config
apps/hosted-components/tsconfig.json, apps/hosted-components/.eslintrc.cjs, apps/hosted-components/vite.config.ts, vitest.workspace.ts
Adds TS, ESLint, and Vite configs for the new app; excludes the app from workspace vitest runs.
Client bootstrap & router
apps/hosted-components/src/client.tsx, apps/hosted-components/src/router.tsx, apps/hosted-components/src/routeTree.gen.ts
Adds React hydration entry, router factory (getRouter), and generated type-safe route tree (root, index, handler).
Routes & UI
apps/hosted-components/src/routes/__root.tsx, apps/hosted-components/src/routes/index.tsx, apps/hosted-components/src/routes/handler/$.tsx
Implements root route with projectId parsing/validation, ErrorBoundary, Stack client initialization/providers, plus index and handler page routes.
Shared utils
packages/stack-shared/src/utils/monkey-patch.tsx, packages/stack-shared/src/utils/react.tsx
Adds console monkey-patch utility and integrates a sentinel + ensureMonkeyPatch call into NoSuspenseBoundaryError to suppress specific console.error noise.

Sequence Diagram(s)

sequenceDiagram
  participant Browser as Browser/Client
  participant Client as client.tsx
  participant Router as TanStack Router
  participant Root as __root Route
  participant Stack as StackClientApp

  Browser->>Client: load & hydrate
  Client->>Router: initialize/getRouter
  Router->>Root: render RootComponent (route match)
  Root->>Stack: compute projectId & instantiate StackClientApp
  Root->>Stack: wrap app with StackProvider/StackTheme -> Outlet
  Router->>Client: render matched page (index / handler)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • N2D4

Poem

🐰 A tiny hop, a bundle spun anew,
Routes stitched gently where the handlers grew,
A sentinel hush for noisy logs at night,
Hydrate and render in morning light,
Tiny paws applaud this hosted view.

🚥 Pre-merge checks | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is essentially empty, containing only the repository contribution template header with no actual description, context, or explanation of changes. Add a detailed description explaining the purpose of the hosted components app, key features implemented, and how SSR and Stack client integration work within this new application.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title "Hosted components" is vague and generic, using a non-descriptive term that doesn't convey meaningful information about the primary change or scope. Clarify the title to reflect the main objective, such as "Add hosted components app with SSR and Stack client integration" or "Create hosted components application with server-side rendering."

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch hosted-components-page

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@BilalG1 BilalG1 requested a review from N2D4 March 2, 2026 17:52
@BilalG1 BilalG1 assigned N2D4 and unassigned BilalG1 Mar 2, 2026
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 2, 2026

Greptile Summary

Adds a new hosted-components app using TanStack Start framework to provide hosted authentication UI accessible via project-specific subdomains (e.g., projectId.built-with-stack-auth.com). The app extracts the project ID from the subdomain, validates it as a UUID, and initializes Stack authentication with appropriate routing.

Key changes:

  • New React app with TanStack Start and Router for server-side rendering
  • Root component handles project ID extraction and validation from subdomain
  • Authentication routes at /handler/* for sign-in/sign-up flows
  • Protected index route displaying authenticated user information
  • Integrated with dev launchpad for local testing

Minor issue:

  • Missing clean script in package.json per project conventions

Confidence Score: 5/5

  • Safe to merge - well-structured new app with only minor style convention issue
  • Clean implementation of a new hosted components app with proper project structure, TypeScript configuration, and security practices. Redirect URLs are hardcoded (no injection risks), project ID validation is in place, and the code follows React best practices. The only issue is a missing clean script in package.json which is a non-blocking style convention
  • No files require special attention

Important Files Changed

Filename Overview
apps/hosted-components/package.json Added package configuration for new hosted-components app with TanStack Start dependencies, but missing clean script required by project conventions
apps/hosted-components/src/routes/__root.tsx Implemented root component with project ID extraction from subdomain, Stack authentication setup, and error handling
apps/hosted-components/src/routes/index.tsx Landing page showing authenticated user information with protected route
apps/hosted-components/vite.config.ts Standard Vite configuration with TanStack Start plugin and SPA mode enabled

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[User visits subdomain] --> B{Extract project ID from subdomain}
    B --> C{Project ID exists?}
    C -->|No| D[Show 'Invalid URL' error]
    C -->|Yes| E{Valid UUID format?}
    E -->|No| F[Show 'Invalid project ID' error]
    E -->|Yes| G[Initialize StackClientApp]
    G --> H{User route}
    H -->|/handler/*| I[StackHandler component<br/>Sign-in/Sign-up flows]
    H -->|/| J{User authenticated?}
    J -->|No| K[Redirect to sign-in]
    J -->|Yes| L[Show welcome page<br/>with UserButton]
Loading

Last reviewed commit: 07b9be1

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

13 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
apps/hosted-components/src/routes/__root.tsx (1)

115-117: Render an explicit loading state instead of a blank screen.

Line [116] returns an empty fragment while initialization is in progress; use a visible loading UI so state transitions are explicit.

Based on learnings: "When building frontend code, always carefully deal with loading and error states. Be very explicit with these."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/hosted-components/src/routes/__root.tsx` around lines 115 - 117, The
early return for projectId (if (projectId === undefined) return <></>;) shows a
blank screen; replace it with an explicit loading UI so users see progress. In
the if (projectId === undefined) branch in __root.tsx, return a visible loading
state (for example a <Loading /> component or a simple accessible element like a
div with role="status" and a spinner/text) instead of an empty fragment,
ensuring any existing Loading component or CSS spinner is used for consistency
with the app.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/hosted-components/package.json`:
- Line 7: The dev script in package.json sets Vite to port 8109 ("dev": "vite
dev --port ${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}09") which conflicts with the
app’s expected 8105; update the dev script to default to 8105 instead (adjust
the NEXT_PUBLIC_STACK_PORT_PREFIX fallback or the appended digits) so the "dev"
npm script, the Vite server, and the local URL guidance in src/routes/__root.tsx
all use port 8105 consistently.

In `@apps/hosted-components/src/routes/__root.tsx`:
- Around line 97-113: Replace the type bypass and non-null assertion: create a
properly typed adapter for redirectMethod that calls the React Router
useNavigate hook (e.g., a small function that captures const navigate =
useNavigate(); and returns a function (to: string) => void) and pass that
adapter to new StackClientApp instead of `useNavigate as any`; and replace the
`stackApp!` usage with a defensive null-coalescing pattern (e.g., `stackApp ??
throwErr('stackApp was unexpectedly null despite validation checks')` or an
explicit guard that throws) so the code is type-safe and fails with an explicit
error if stackApp is null.

---

Nitpick comments:
In `@apps/hosted-components/src/routes/__root.tsx`:
- Around line 115-117: The early return for projectId (if (projectId ===
undefined) return <></>;) shows a blank screen; replace it with an explicit
loading UI so users see progress. In the if (projectId === undefined) branch in
__root.tsx, return a visible loading state (for example a <Loading /> component
or a simple accessible element like a div with role="status" and a spinner/text)
instead of an empty fragment, ensuring any existing Loading component or CSS
spinner is used for consistency with the app.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1232132 and 07b9be1.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (12)
  • apps/dev-launchpad/public/index.html
  • apps/hosted-components/.env.development
  • apps/hosted-components/.eslintrc.cjs
  • apps/hosted-components/package.json
  • apps/hosted-components/src/client.tsx
  • apps/hosted-components/src/routeTree.gen.ts
  • apps/hosted-components/src/router.tsx
  • apps/hosted-components/src/routes/__root.tsx
  • apps/hosted-components/src/routes/handler/$.tsx
  • apps/hosted-components/src/routes/index.tsx
  • apps/hosted-components/tsconfig.json
  • apps/hosted-components/vite.config.ts

@@ -0,0 +1 @@
VITE_STACK_API_URL=http://localhost:8102
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs to respect ${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}

}

if (!projectId) {
return <FullPageError title="Invalid URL" message={`Could not determine project ID from subdomain. Visit <projectId>.localhost:8105.`} />;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

port should be ${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}09

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or honestly the entire message should adapt based on the current URL (if it's not hosted locally it should show that URL)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get this error about half the time when running pnpm run dev

[2] @stackframe/hosted-components:dev: Error: Failed to resolve entry for package "@stackframe/react". The package may have incorrect main/module/exports specified in its package.json.
[2] @stackframe/hosted-components:dev:     at packageEntryFailure (file:///Users/konstantinwohlwend/Documents/stack/node_modules/.pnpm/vite@7.3.1_@types+node@22.19.0_jiti@2.6.1_lightningcss@1.30.1_terser@5.44.0_tsx@4.21.0_yaml@2.8.0/node_modules/vite/dist/node/chunks/config.js:32816:32)
[2] @stackframe/hosted-components:dev:     ... 8 lines matching cause stack trace ...
[2] @stackframe/hosted-components:dev:     at async TransformPluginContext.resolve (file:///Users/konstantinwohlwend/Documents/stack/node_modules/.pnpm/vite@7.3.1_@types+node@22.19.0_jiti@2.6.1_lightningcss@1.30.1_terser@5.44.0_tsx@4.21.0_yaml@2.8.0/node_modules/vite/dist/node/chunks/config.js:28929:13) {
[2] @stackframe/hosted-components:dev:   cause: Error: Failed to resolve entry for package "@stackframe/react". The package may have incorrect main/module/exports specified in its package.json.
[2] @stackframe/hosted-components:dev:       at packageEntryFailure (file:///Users/konstantinwohlwend/Documents/stack/node_modules/.pnpm/vite@7.3.1_@types+node@22.19.0_jiti@2.6.1_lightningcss@1.30.1_terser@5.44.0_tsx@4.21.0_yaml@2.8.0/node_modules/vite/dist/node/chunks/config.js:32816:32)
[2] @stackframe/hosted-components:dev:       at resolvePackageEntry (file:///Users/konstantinwohlwend/Documents/stack/node_modules/.pnpm/vite@7.3.1_@types+node@22.19.0_jiti@2.6.1_lightningcss@1.30.1_terser@5.44.0_tsx@4.21.0_yaml@2.8.0/node_modules/vite/dist/node/chunks/config.js:32813:2)
[2] @stackframe/hosted-components:dev:       at tryNodeResolve (file:///Users/konstantinwohlwend/Documents/stack/node_modules/.pnpm/vite@7.3.1_@types+node@22.19.0_jiti@2.6.1_lightningcss@1.30.1_terser@5.44.0_tsx@4.21.0_yaml@2.8.0/node_modules/vite/dist/node/chunks/config.js:32716:70)
[2] @stackframe/hosted-components:dev:       at ResolveIdContext.handler (file:///Users/konstantinwohlwend/Documents/stack/node_modules/.pnpm/vite@7.3.1_@types+node@22.19.0_jiti@2.6.1_lightningcss@1.30.1_terser@5.44.0_tsx@4.21.0_yaml@2.8.0/node_modules/vite/dist/node/chunks/config.js:32555:16)
[2] @stackframe/hosted-components:dev:       at EnvironmentPluginContainer.resolveId (file:///Users/konstantinwohlwend/Documents/stack/node_modules/.pnpm/vite@7.3.1_@types+node@22.19.0_jiti@2.6.1_lightningcss@1.30.1_terser@5.44.0_tsx@4.21.0_yaml@2.8.0/node_modules/vite/dist/node/chunks/config.js:28717:56)
[2] @stackframe/hosted-components:dev:       at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
[2] @stackframe/hosted-components:dev:       at async ResolveIdContext.resolve (file:///Users/konstantinwohlwend/Documents/stack/node_modules/.pnpm/vite@7.3.1_@types+node@22.19.0_jiti@2.6.1_lightningcss@1.30.1_terser@5.44.0_tsx@4.21.0_yaml@2.8.0/node_modules/vite/dist/node/chunks/config.js:28929:13)
[2] @stackframe/hosted-components:dev:       at async ResolveIdContext.resolveId (file:///Users/konstantinwohlwend/Documents/stack/node_modules/.pnpm/@tanstack+start-plugin-core@1.163.2_@tanstack+react-router@1.163.2_react-dom@19.2.3_rea_c17f8a37508f9eb0a0349e723ec00ade/node_modules/@tanstack/start-plugin-core/dist/esm/import-protection-plugin/plugin.js:719:26)
[2] @stackframe/hosted-components:dev:       at async EnvironmentPluginContainer.resolveId (file:///Users/konstantinwohlwend/Documents/stack/node_modules/.pnpm/vite@7.3.1_@types+node@22.19.0_jiti@2.6.1_lightningcss@1.30.1_terser@5.44.0_tsx@4.21.0_yaml@2.8.0/node_modules/vite/dist/node/chunks/config.js:28717:19)
[2] @stackframe/hosted-components:dev:       at async TransformPluginContext.resolve (file:///Users/konstantinwohlwend/Documents/stack/node_modules/.pnpm/vite@7.3.1_@types+node@22.19.0_jiti@2.6.1_lightningcss@1.30.1_terser@5.44.0_tsx@4.21.0_yaml@2.8.0/node_modules/vite/dist/node/chunks/config.js:28929:13) {
[2] @stackframe/hosted-components:dev:     code: 'ERR_RESOLVE_PACKAGE_ENTRY_FAIL',
[2] @stackframe/hosted-components:dev:     pos: 117,
[2] @stackframe/hosted-components:dev:     plugin: 'vite:import-analysis',
[2] @stackframe/hosted-components:dev:     id: '/Users/konstantinwohlwend/Documents/stack/apps/hosted-components/src/routes/__root.tsx',
[2] @stackframe/hosted-components:dev:     pluginCode: 'import { Fragment, jsxDEV } from "react/jsx-dev-runtime";\n' +
[2] @stackframe/hosted-components:dev:       'import { StackClientApp, StackProvider, StackTheme } from "@stackframe/react";\n' +
[2] @stackframe/hosted-components:dev:       'import {\n' +
[2] @stackframe/hosted-components:dev:       '  HeadContent,\n' +
[2] @stackframe/hosted-components:dev:       '  Outlet,\n' +
[2] @stackframe/hosted-components:dev:       '  Scripts,\n' +
[2] @stackframe/hosted-components:dev:       '  createRootRoute,\n' +
[2] @stackframe/hosted-components:dev:       '  useNavigate\n' +
[2] @stackframe/hosted-components:dev:       '} from "@tanstack/react-router";\n' +
[2] @stackframe/hosted-components:dev:       'import { Component, useEffect, useMemo, useState } from "react";\n' +
[2] @stackframe/hosted-components:dev:       'export function getProjectId() {\n' +
[2] @stackframe/hosted-components:dev:       '  const hostname = window.location.hostname;\n' +
[2] @stackframe/hosted-components:dev:       '  const parts = hostname.split(".");\n' +
[2] @stackframe/hosted-components:dev:       '  if (parts.length >= 2) {\n' +
[2] @stackframe/hosted-components:dev:       '    return parts[0];\n' +
[2] @stackframe/hosted-components:dev:       '  }\n' +
[2] @stackframe/hosted-components:dev:       '  return null;\n' +
[2] @stackframe/hosted-components:dev:       '}\n' +
[2] @stackframe/hosted-components:dev:       'function FullPageError({ title, message }) {\n' +
[2] @stackframe/hosted-components:dev:       '  return /* @__PURE__ */ jsxDEV("div", { style: { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "100vh" }, children: /* @__PURE__ */ jsxDEV("div", { style: { textAlign: "center", maxWidth: 480, padding: 24 }, children: [\n' +
[2] @stackframe/hosted-components:dev:       '    /* @__PURE__ */ jsxDEV("h1", { style: { fontSize: 24, marginBottom: 8 }, children: title }, void 0, false, {\n' +
[2] @stackframe/hosted-components:dev:       '      fileName: "/Users/konstantinwohlwend/Documents/stack/apps/hosted-components/src/routes/__root.tsx",\n' +
[2] @stackframe/hosted-components:dev:       '      lineNumber: 30,\n' +
[2] @stackframe/hosted-components:dev:       '      columnNumber: 9\n' +
[2] @stackframe/hosted-components:dev:       '    }, this),\n' +
[2] @stackframe/hosted-components:dev:       '    /* @__PURE__ */ jsxDEV("p", { style: { color: "#666" }, children: message }, void 0, false, {\n' +
[2] @stackframe/hosted-components:dev:       '      fileName: "/Users/konstantinwohlwend/Documents/stack/apps/hosted-components/src/routes/__root.tsx",\n' +
[2] @stackframe/hosted-components:dev:       '      lineNumber: 31,\n' +
[2] @stackframe/hosted-components:dev:       '      columnNumber: 9\n' +
[2] @stackframe/hosted-components:dev:       '    }, this)\n' +
[2] @stackframe/hosted-components:dev:       '  ] }, void 0, true, {\n' +
[2] @stackframe/hosted-components:dev:       '    fileName: "/Users/konstantinwohlwend/Documents/stack/apps/hosted-components/src/routes/__root.tsx",\n' +
[2] @stackframe/hosted-components:dev:       '    lineNumber: 29,\n' +
[2] @stackframe/hosted-components:dev:       '    columnNumber: 7\n' +
[2] @stackframe/hosted-components:dev:       '  }, this) }, void 0, false, {\n' +
[2] @stackframe/hosted-components:dev:       '    fileName: "/Users/konstantinwohlwend/Documents/stack/apps/hosted-components/src/routes/__root.tsx",\n' +
[2] @stackframe/hosted-components:dev:       '    lineNumber: 28,\n' +
[2] @stackframe/hosted-components:dev:       '    columnNumber: 5\n' +
[2] @stackframe/hosted-components:dev:       '  }, this);\n' +
[2] @stackframe/hosted-components:dev:       '}\n' +
[2] @stackframe/hosted-components:dev:       'class ErrorBoundary extends Component {\n' +
[2] @stackframe/hosted-components:dev:       '  constructor(props) {\n' +
[2] @stackframe/hosted-components:dev:       '    super(props);\n' +
[2] @stackframe/hosted-components:dev:       '    this.state = { error: null };\n' +
[2] @stackframe/hosted-components:dev:       '  }\n' +
[2] @stackframe/hosted-components:dev:       '  static getDerivedStateFromError(error) {\n' +
[2] @stackframe/hosted-components:dev:       '    return { error };\n' +
[2] @stackframe/hosted-components:dev:       '  }\n' +
[2] @stackframe/hosted-components:dev:       '  componentDidCatch(error, errorInfo) {\n' +
[2] @stackframe/hosted-components:dev:       '    console.error("Hosted components error:", error, errorInfo);\n' +
[2] @stackframe/hosted-components:dev:       '  }\n' +
[2] @stackframe/hosted-components:dev:       '  render() {\n' +
[2] @stackframe/hosted-components:dev:       '    if (this.state.error) {\n' +
[2] @stackframe/hosted-components:dev:       '      return /* @__PURE__ */ jsxDEV(FullPageError, { title: "Something went wrong", message: this.state.error.message }, void 0, false, {\n' +
[2] @stackframe/hosted-components:dev:       '        fileName: "/Users/konstantinwohlwend/Documents/stack/apps/hosted-components/src/routes/__root.tsx",\n' +
[2] @stackframe/hosted-components:dev:       '        lineNumber: 53,\n' +
[2] @stackframe/hosted-components:dev:       '        columnNumber: 14\n' +
[2] @stackframe/hosted-components:dev:       '      }, this);\n' +
[2] @stackframe/hosted-components:dev:       '    }\n' +
[2] @stackframe/hosted-components:dev:       '    return this.props.children;\n' +
[2] @stackframe/hosted-components:dev:       '  }\n' +
[2] @stackframe/hosted-components:dev:       '}\n' +
[2] @stackframe/hosted-components:dev:       'export const Route = createRootRoute({\n' +
[2] @stackframe/hosted-components:dev:       '  head: () => ({\n' +
[2] @stackframe/hosted-components:dev:       '    meta: [\n' +
[2] @stackframe/hosted-components:dev:       '      { charSet: "utf-8" },\n' +
[2] @stackframe/hosted-components:dev:       '      { name: "viewport", content: "width=device-width, initial-scale=1" }\n' +
[2] @stackframe/hosted-components:dev:       '    ]\n' +
[2] @stackframe/hosted-components:dev:       '  }),\n' +
[2] @stackframe/hosted-components:dev:       '  shellComponent: RootDocument,\n' +
[2] @stackframe/hosted-components:dev:       '  component: RootComponent\n' +
[2] @stackframe/hosted-components:dev:       '});\n' +
[2] @stackframe/hosted-components:dev:       'function RootDocument({ children }) {\n' +
[2] @stackframe/hosted-components:dev:       '  return /* @__PURE__ */ jsxDEV("html", { children: [\n' +
[2] @stackframe/hosted-components:dev:       '    /* @__PURE__ */ jsxDEV("head", { children: [\n' +
[2] @stackframe/hosted-components:dev:       '      /* @__PURE__ */ jsxDEV(HeadContent, {}, void 0, false, {\n' +
[2] @stackframe/hosted-components:dev:       '        fileName: "/Users/konstantinwohlwend/Documents/stack/apps/hosted-components/src/routes/__root.tsx",\n' +
[2] @stackframe/hosted-components:dev:       '        lineNumber: 75,\n' +
[2] @stackframe/hosted-components:dev:       '        columnNumber: 9\n' +
[2] @stackframe/hosted-components:dev:       '      }, this),\n' +
[2] @stackframe/hosted-components:dev:       '      /* @__PURE__ */ jsxDEV("link", { rel: "preconnect", href: "https://fonts.googleapis.com" }, void 0, false, {\n' +
[2] @stackframe/hosted-components:dev:       '        fileName: "/Users/konstantinwohlwend/Documents/stack/apps/hosted-components/src/routes/__root.tsx",\n' +
[2] @stackframe/hosted-components:dev:       '        lineNumber: 76,\n' +
[2] @stackframe/hosted-components:dev:       '        columnNumber: 9\n' +
[2] @stackframe/hosted-components:dev:       '      }, this),\n' +
[2] @stackframe/hosted-components:dev:       '      /* @__PURE__ */ jsxDEV("link", { rel: "preconnect", href: "https://fonts.gstatic.com", crossOrigin: "anonymous" }, void 0, false, {\n' +
[2] @stackframe/hosted-components:dev:       '        fileName: "/Users/konstantinwohlwend/Documents/stack/apps/hosted-components/src/routes/__root.tsx",\n' +
[2] @stackframe/hosted-components:dev:       '        lineNumber: 77,\n' +
[2] @stackframe/hosted-components:dev:       '        columnNumber: 9\n' +
[2] @stackframe/hosted-components:dev:       '      }, this),\n' +
[2] @stackframe/hosted-components:dev:       '      /* @__PURE__ */ jsxDEV("link", { href: "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap", rel: "stylesheet" }, void 0, false, {\n' +
[2] @stackframe/hosted-components:dev:       '        fileName: "/Users/konstantinwohlwend/Documents/stack/apps/hosted-components/src/routes/__root.tsx",\n' +
[2] @stackframe/hosted-components:dev:       '        lineNumber: 78,\n' +
[2] @stackframe/hosted-components:dev:       '        columnNumber: 9\n' +
[2] @stackframe/hosted-components:dev:       '      }, this)\n' +
[2] @stackframe/hosted-components:dev:       '    ] }, void 0, true, {\n' +
[2] @stackframe/hosted-components:dev:       '      fileName: "/Users/konstantinwohlwend/Documents/stack/apps/hosted-components/src/routes/__root.tsx",\n' +
[2] @stackframe/hosted-components:dev:       '      lineNumber: 74,\n' +
[2] @stackframe/hosted-components:dev:       '      columnNumber: 7\n' +
[2] @stackframe/hosted-components:dev:       '    }, this),\n' +
[2] @stackframe/hosted-components:dev:       `    /* @__PURE__ */ jsxDEV("body", { style: { fontFamily: "'Inter', sans-serif", margin: 0 }, children: [\n` +
[2] @stackframe/hosted-components:dev:       '      children,\n' +
[2] @stackframe/hosted-components:dev:       '      /* @__PURE__ */ jsxDEV(Scripts, {}, void 0, false, {\n' +
[2] @stackframe/hosted-components:dev:       '        fileName: "/Users/konstantinwohlwend/Documents/stack/apps/hosted-components/src/routes/__root.tsx",\n' +
[2] @stackframe/hosted-components:dev:       '        lineNumber: 82,\n' +
[2] @stackframe/hosted-components:dev:       '        columnNumber: 9\n' +
[2] @stackframe/hosted-components:dev:       '      }, this)\n' +
[2] @stackframe/hosted-components:dev:       '    ] }, void 0, true, {\n' +
[2] @stackframe/hosted-components:dev:       '      fileName: "/Users/konstantinwohlwend/Documents/stack/apps/hosted-components/src/routes/__root.tsx",\n' +
[2] @stackframe/hosted-components:dev:       '      lineNumber: 80,\n' +
[2] @stackframe/hosted-components:dev:       '      columnNumber: 7\n' +
[2] @stackframe/hosted-components:dev:       '    }, this)\n' +
[2] @stackframe/hosted-components:dev:       '  ] }, void 0, true, {\n' +
[2] @stackframe/hosted-components:dev:       '    fileName: "/Users/konstantinwohlwend/Documents/stack/apps/hosted-components/src/routes/__root.tsx",\n' +
[2] @stackframe/hosted-components:dev:       '    lineNumber: 73,\n' +
[2] @stackframe/hosted-components:dev:       '    columnNumber: 5\n' +
[2] @stackframe/hosted-components:dev:       '  }, this);\n' +
[2] @stackframe/hosted-components:dev:       '}\n' +
[2] @stackframe/hosted-components:dev:       'function RootComponent() {\n' +
[2] @stackframe/hosted-components:dev:       '  const [projectId, setProjectId] = useState(void 0);\n' +
[2] @stackframe/hosted-components:dev:       '  useEffect(() => {\n' +
[2] @stackframe/hosted-components:dev:       '    setProjectId(getProjectId());\n' +
[2] @stackframe/hosted-components:dev:       '  }, []);\n' +
[2] @stackframe/hosted-components:dev:       '  const isValidProjectId = projectId ? projectId === "internal" || /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(projectId) : false;\n' +
[2] @stackframe/hosted-components:dev:       '  const stackApp = useMemo(() => {\n' +
[2] @stackframe/hosted-components:dev:       '    if (!projectId || !isValidProjectId) return null;\n' +
[2] @stackframe/hosted-components:dev:       '    return new StackClientApp({\n' +
[2] @stackframe/hosted-components:dev:       '      projectId,\n' +
[2] @stackframe/hosted-components:dev:       '      tokenStore: "cookie",\n' +
[2] @stackframe/hosted-components:dev:       '      baseUrl: import.meta.env.VITE_STACK_API_URL || void 0,\n' +
[2] @stackframe/hosted-components:dev:       '      urls: {\n' +
[2] @stackframe/hosted-components:dev:       '        handler: "/handler",\n' +
[2] @stackframe/hosted-components:dev:       '        signIn: "/handler/sign-in",\n' +
[2] @stackframe/hosted-components:dev:       '        signUp: "/handler/sign-up",\n' +
[2] @stackframe/hosted-components:dev:       '        afterSignIn: "/",\n' +
[2] @stackframe/hosted-components:dev:       '        afterSignUp: "/",\n' +
[2] @stackframe/hosted-components:dev:       '        afterSignOut: "/handler/sign-in"\n' +
[2] @stackframe/hosted-components:dev:       '      },\n' +
[2] @stackframe/hosted-components:dev:       '      redirectMethod: { useNavigate }\n' +
[2] @stackframe/hosted-components:dev:       '    });\n' +
[2] @stackframe/hosted-components:dev:       '  }, [projectId]);\n' +
[2] @stackframe/hosted-components:dev:       '  if (projectId === void 0) {\n' +
[2] @stackframe/hosted-components:dev:       '    return /* @__PURE__ */ jsxDEV(Fragment, {}, void 0, false, {\n' +
[2] @stackframe/hosted-components:dev:       '      fileName: "/Users/konstantinwohlwend/Documents/stack/apps/hosted-components/src/routes/__root.tsx",\n' +
[2] @stackframe/hosted-components:dev:       '      lineNumber: 116,\n' +
[2] @stackframe/hosted-components:dev:       '      columnNumber: 12\n' +
[2] @stackframe/hosted-components:dev:       '    }, this);\n' +
[2] @stackframe/hosted-components:dev:       '  }\n' +
[2] @stackframe/hosted-components:dev:       '  if (!projectId) {\n' +
[2] @stackframe/hosted-components:dev:       '    return /* @__PURE__ */ jsxDEV(FullPageError, { title: "Invalid URL", message: `Could not determine project ID from subdomain. Visit <projectId>.localhost:8105.` }, void 0, false, {\n' +
[2] @stackframe/hosted-components:dev:       '      fileName: "/Users/konstantinwohlwend/Documents/stack/apps/hosted-components/src/routes/__root.tsx",\n' +
[2] @stackframe/hosted-components:dev:       '      lineNumber: 120,\n' +
[2] @stackframe/hosted-components:dev:       '      columnNumber: 12\n' +
[2] @stackframe/hosted-components:dev:       '    }, this);\n' +
[2] @stackframe/hosted-components:dev:       '  }\n' +
[2] @stackframe/hosted-components:dev:       '  if (!isValidProjectId) {\n' +
[2] @stackframe/hosted-components:dev:       '    return /* @__PURE__ */ jsxDEV(FullPageError, { title: "Something went wrong", message: `Invalid project ID: ${projectId}. Project IDs must be UUIDs.` }, void 0, false, {\n' +
[2] @stackframe/hosted-components:dev:       '      fileName: "/Users/konstantinwohlwend/Documents/stack/apps/hosted-components/src/routes/__root.tsx",\n' +
[2] @stackframe/hosted-components:dev:       '      lineNumber: 124,\n' +
[2] @stackframe/hosted-components:dev:       '      columnNumber: 12\n' +
[2] @stackframe/hosted-components:dev:       '    }, this);\n' +
[2] @stackframe/hosted-components:dev:       '  }\n' +
[2] @stackframe/hosted-components:dev:       '  return /* @__PURE__ */ jsxDEV(ErrorBoundary, { children: /* @__PURE__ */ jsxDEV(StackProvider, { app: stackApp, children: /* @__PURE__ */ jsxDEV(StackTheme, { children: /* @__PURE__ */ jsxDEV(Outlet, {}, void 0, false, {\n' +
[2] @stackframe/hosted-components:dev:       '    fileName: "/Users/konstantinwohlwend/Documents/stack/apps/hosted-components/src/routes/__root.tsx",\n' +
[2] @stackframe/hosted-components:dev:       '    lineNumber: 131,\n' +
[2] @stackframe/hosted-components:dev:       '    columnNumber: 11\n' +
[2] @stackframe/hosted-components:dev:       '  }, this) }, void 0, false, {\n' +
[2] @stackframe/hosted-components:dev:       '    fileName: "/Users/konstantinwohlwend/Documents/stack/apps/hosted-components/src/routes/__root.tsx",\n' +
[2] @stackframe/hosted-components:dev:       '    lineNumber: 130,\n' +
[2] @stackframe/hosted-components:dev:       '    columnNumber: 9\n' +
[2] @stackframe/hosted-components:dev:       '  }, this) }, void 0, false, {\n' +
[2] @stackframe/hosted-components:dev:       '    fileName: "/Users/konstantinwohlwend/Documents/stack/apps/hosted-components/src/routes/__root.tsx",\n' +
[2] @stackframe/hosted-components:dev:       '    lineNumber: 129,\n' +
[2] @stackframe/hosted-components:dev:       '    columnNumber: 7\n' +
[2] @stackframe/hosted-components:dev:       '  }, this) }, void 0, false, {\n' +
[2] @stackframe/hosted-components:dev:       '    fileName: "/Users/konstantinwohlwend/Documents/stack/apps/hosted-components/src/routes/__root.tsx",\n' +
[2] @stackframe/hosted-components:dev:       '    lineNumber: 128,\n' +
[2] @stackframe/hosted-components:dev:       '    columnNumber: 5\n' +
[2] @stackframe/hosted-components:dev:       '  }, this);\n' +
[2] @stackframe/hosted-components:dev:       '}\n',
[2] @stackframe/hosted-components:dev:     loc: {
[2] @stackframe/hosted-components:dev:       file: '/Users/konstantinwohlwend/Documents/stack/apps/hosted-components/src/routes/__root.tsx',
[2] @stackframe/hosted-components:dev:       line: 2,
[2] @stackframe/hosted-components:dev:       column: 58
[2] @stackframe/hosted-components:dev:     },
[2] @stackframe/hosted-components:dev:     frame: '1  |  import { Fragment, jsxDEV } from "react/jsx-dev-runtime";\n' +
[2] @stackframe/hosted-components:dev:       '2  |  import { StackClientApp, StackProvider, StackTheme } from "@stackframe/react";\n' +
[2] @stackframe/hosted-components:dev:       '   |                                                             ^\n' +
[2] @stackframe/hosted-components:dev:       '3  |  import {\n' +
[2] @stackframe/hosted-components:dev:       '4  |    HeadContent,',

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assumably because the package is still building when the hosted components dev starts, and the hosted components dev hot reload does not look at node_modules

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you also create a vercel project for this? domain built-with-stack-auth.com is on cloudflare

@github-actions github-actions bot assigned BilalG1 and unassigned N2D4 Mar 10, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
apps/hosted-components/package.json (1)

7-7: ⚠️ Potential issue | 🟠 Major

Use the same default port as the rest of this app.

This still defaults Vite to 8109, while apps/hosted-components/src/routes/__root.tsx points users to <projectId>.localhost:8105. The default dev flow and the in-app guidance will disagree until these match.

Proposed fix
-    "dev": "vite dev --port ${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}09",
+    "dev": "vite dev --port ${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}05",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/hosted-components/package.json` at line 7, The dev script in
package.json currently defaults Vite to port 8109 via "dev": "vite dev --port
${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}09"; change the default to match the rest of
the app (8105) by updating the dev script to use the same fallback prefix so
Vite will start on 8105 when NEXT_PUBLIC_STACK_PORT_PREFIX is not set; edit the
"dev" script entry (symbol: "dev") and adjust the fallback port expression
(symbol: NEXT_PUBLIC_STACK_PORT_PREFIX) so the resolved default is 8105 instead
of 8109.
🧹 Nitpick comments (1)
apps/hosted-components/src/routes/__root.tsx (1)

14-24: Guard getProjectId() against SSR callers.

This route module is loaded on the server too, and getProjectId() is exported. Any future SSR/test caller will crash on window. Make the browser-only assumption explicit inside the helper.

Suggested change
 export function getProjectId(): string | null {
+  if (typeof window === "undefined") {
+    return null;
+  }
+
   // Extract from subdomain: <projectId>.built-with-stack-auth.com
   // Also works with <projectId>.localhost for local dev
   const hostname = window.location.hostname;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/hosted-components/src/routes/__root.tsx` around lines 14 - 24,
getProjectId() currently assumes a browser environment and directly accesses
window, which will crash during SSR/testing; update the function to first guard
for non-browser callers by checking typeof window === "undefined" (and/or typeof
window.location === "undefined") and returning null in that case, then continue
with the existing hostname parsing logic (hostname.split and parts[0]) when
running in the browser so server-side imports won't throw.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/hosted-components/package.json`:
- Around line 7-15: The dev script in hosted-components fails because
`@stackframe/react`'s dist files aren't guaranteed to exist; update the task
wiring so hosted-components' dev depends on the package build: add a dependsOn
entry (e.g., dependsOn: ["^build"]) to the hosted-components "dev" task in
turbo.json so the workspace runs the dependent package build first, or
alternatively add a Vite alias in hosted-components' Vite config to resolve
"@stackframe/react" directly to its source (src) to avoid needing dist files
during dev.

In `@apps/hosted-components/src/routes/__root.tsx`:
- Around line 89-93: The initial state for projectId is incorrectly bootstrapped
to the string "internal", causing the app to create a StackClientApp for the
internal project before the real hostname is known; change the initialization so
projectId starts as undefined (or null) instead of "internal", keep the existing
useEffect that calls getProjectId() and setProjectId(), and ensure any code that
constructs/bootstraps StackClientApp (or reads projectId) guards against
undefined and only creates the client after projectId is set to the real value.

In `@apps/hosted-components/src/routes/index.tsx`:
- Around line 6-10: The pendingComponent render lacks accessible status
announcement for screen readers; update the pendingComponent function to include
an accessible status (e.g., role="status" or aria-live="polite") and provide
visible or visually-hidden text such as "Loading, please wait" so assistive tech
is informed during auth loading/redirect; keep the existing spinner for visual
users and add the announcement text inside the same wrapper (or as a
screen-reader-only element) to ensure both visual and non-visual users receive
feedback.

---

Duplicate comments:
In `@apps/hosted-components/package.json`:
- Line 7: The dev script in package.json currently defaults Vite to port 8109
via "dev": "vite dev --port ${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}09"; change the
default to match the rest of the app (8105) by updating the dev script to use
the same fallback prefix so Vite will start on 8105 when
NEXT_PUBLIC_STACK_PORT_PREFIX is not set; edit the "dev" script entry (symbol:
"dev") and adjust the fallback port expression (symbol:
NEXT_PUBLIC_STACK_PORT_PREFIX) so the resolved default is 8105 instead of 8109.

---

Nitpick comments:
In `@apps/hosted-components/src/routes/__root.tsx`:
- Around line 14-24: getProjectId() currently assumes a browser environment and
directly accesses window, which will crash during SSR/testing; update the
function to first guard for non-browser callers by checking typeof window ===
"undefined" (and/or typeof window.location === "undefined") and returning null
in that case, then continue with the existing hostname parsing logic
(hostname.split and parts[0]) when running in the browser so server-side imports
won't throw.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 02535b65-2167-4f30-a751-ce9a02098417

📥 Commits

Reviewing files that changed from the base of the PR and between 201bb24 and a813ca7.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (7)
  • apps/hosted-components/.env.development
  • apps/hosted-components/package.json
  • apps/hosted-components/src/routes/__root.tsx
  • apps/hosted-components/src/routes/index.tsx
  • apps/hosted-components/vite.config.ts
  • packages/stack-shared/src/utils/monkey-patch.tsx
  • packages/stack-shared/src/utils/react.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/hosted-components/.env.development
  • apps/hosted-components/vite.config.ts

Comment on lines +7 to +15
"dev": "vite dev --port ${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}09",
"build": "vite build",
"start": "node .output/server/index.mjs",
"lint": "eslint --ext .ts,.tsx .",
"typecheck": "tsc --noEmit",
"clean": "rimraf .output && rimraf node_modules"
},
"dependencies": {
"@stackframe/react": "workspace:*",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

react_pkg=""
while IFS= read -r pkg; do
  if jq -e '.name == "@stackframe/react"' "$pkg" >/dev/null; then
    react_pkg="$pkg"
    break
  fi
done < <(fd '^package.json$' packages)

if [ -z "$react_pkg" ]; then
  echo "Could not find package.json for `@stackframe/react`"
  exit 1
fi

pkg_dir="$(dirname "$react_pkg")"

echo "== `@stackframe/react` package metadata =="
jq '{name, main, module, types, exports, scripts}' "$react_pkg"

echo
echo "== Export targets and whether they exist in the checkout =="
jq -r '
  [.main, .module, .types] | map(select(type=="string"))[] ,
  (.exports? // {} | .. | strings)
' "$react_pkg" | sort -u | while IFS= read -r rel; do
  [ -z "$rel" ] && continue
  case "$rel" in
    .*|dist/*|lib/*|src/*)
      if [ -e "$pkg_dir/$rel" ]; then
        echo "OK    $rel"
      else
        echo "MISS  $rel"
      fi
      ;;
  esac
done

echo
echo "== Hosted-components / Turbo wiring =="
rg -n -C2 '"dev"|"build"|"predev"|"\^build"|@stackframe/react|@stackframe/hosted-components' \
  turbo.json package.json pnpm-workspace.yaml "$react_pkg"

Repository: stack-auth/stack-auth

Length of output: 122


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find `@stackframe/react` package.json
react_pkg=$(find packages -name 'package.json' -exec grep -l '"name": "@stackframe/react"' {} \;)

if [ -z "$react_pkg" ]; then
  echo "Could not find package.json for `@stackframe/react`"
  exit 1
fi

pkg_dir=$(dirname "$react_pkg")

echo "== `@stackframe/react` package metadata =="
jq '{name, main, module, types, exports, scripts}' "$react_pkg"

echo
echo "== Export targets and whether they exist in the checkout =="
jq -r '.main // empty, .module // empty, .types // empty, (.exports? // {} | to_entries[] | .value | select(type=="string"))' "$react_pkg" | sort -u | while read -r rel; do
  [ -z "$rel" ] && continue
  case "$rel" in
    .*|dist/*|lib/*|src/*)
      if [ -e "$pkg_dir/$rel" ]; then
        echo "OK    $rel"
      else
        echo "MISS  $rel"
      fi
      ;;
  esac
done

echo
echo "== Check turbo.json for task wiring =="
if [ -f turbo.json ]; then
  jq '.tasks[] | select(.cache==false or .cache==true) | {task: .task, dependsOn: .dependsOn, outputs: .outputs}' turbo.json 2>/dev/null | head -100
fi

echo
echo "== hosted-components package.json =="
if [ -f apps/hosted-components/package.json ]; then
  jq '.scripts' apps/hosted-components/package.json
fi

echo
echo "== Search for build dependencies related to react/hosted-components =="
grep -r "dependsOn\|@stackframe/react" turbo.json 2>/dev/null || echo "No matches found"

Repository: stack-auth/stack-auth

Length of output: 315


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find `@stackframe/react` package.json
react_pkg=$(find packages -name 'package.json' -exec grep -l '"name": "@stackframe/react"' {} \;)

if [ -z "$react_pkg" ]; then
  echo "Could not find package.json for `@stackframe/react`"
  exit 1
fi

pkg_dir=$(dirname "$react_pkg")

echo "== `@stackframe/react` package metadata =="
jq '.name, .main, .module, .types, .exports' "$react_pkg"

echo
echo "== Export targets and whether they exist in the checkout =="
jq -r '.main // empty, .module // empty, .types // empty, (.exports | to_entries[]? | .value | strings)' "$react_pkg" 2>/dev/null | sort -u | while read -r rel; do
  [ -z "$rel" ] && continue
  if [ -e "$pkg_dir/$rel" ]; then
    echo "✓ EXISTS: $rel"
  else
    echo "✗ MISSING: $rel"
  fi
done

echo
echo "== hosted-components package.json location and scripts =="
cat apps/hosted-components/package.json | jq '.scripts'

echo
echo "== turbo.json tasks =="
jq '.tasks | keys[]' turbo.json | head -20

echo
echo "== Searching turbo.json for hosted-components task wiring =="
jq '.tasks | to_entries[] | select(.value | tostring | contains("hosted-components"))' turbo.json

Repository: stack-auth/stack-auth

Length of output: 1566


🏁 Script executed:

#!/bin/bash

echo "== Full turbo.json content =="
cat turbo.json | jq '.' | head -200

echo
echo "== `@stackframe/react` build task in turbo.json =="
jq '.tasks."@stackframe/react#build"' turbo.json

echo
echo "== hosted-components build and dev tasks in turbo.json =="
jq '.tasks."hosted-components#build", .tasks."hosted-components#dev"' turbo.json

echo
echo "== All `@stackframe` tasks =="
jq '.tasks | keys[] | select(startswith("@stackframe"))' turbo.json

echo
echo "== Check react package.json for build script =="
jq '.scripts' packages/react/package.json

Repository: stack-auth/stack-auth

Length of output: 3303


🏁 Script executed:

#!/bin/bash

echo "== Check if `@stackframe/react` is listed in hosted-components dependencies =="
jq '.dependencies, .devDependencies' apps/hosted-components/package.json

echo
echo "== Check if there's any pnpm filter or Turbo configuration for hosted-components =="
cat pnpm-workspace.yaml | grep -A5 hosted-components || echo "No workspace config found"

echo
echo "== Does hosted-components reference `@stackframe/react` in source =="
grep -r "@stackframe/react" apps/hosted-components/src --include="*.ts" --include="*.tsx" 2>/dev/null | head -5 || echo "No direct references found"

Repository: stack-auth/stack-auth

Length of output: 1289


Ensure @stackframe/react builds before hosted-components dev starts.

pnpm run dev fails intermittently with ERR_RESOLVE_PACKAGE_ENTRY_FAIL for @stackframe/react because Vite attempts to resolve the package's exports during import analysis. The package declares main: "./dist/index.js" and types: "./dist/index.d.ts", but these files do not exist in the checkout—they only exist after running pnpm run build in the @stackframe/react package.

No task wiring currently guarantees the react package builds before hosted-components dev starts. The dev task in turbo.json has no dependsOn entries, so running pnpm run dev in hosted-components will fail if react's dist files haven't been built in a prior step.

Add explicit build dependency wiring (e.g., add dependsOn: ["^build"] to the dev task for hosted-components, or configure a Vite alias to resolve @stackframe/react directly to source files) to eliminate the flakiness.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/hosted-components/package.json` around lines 7 - 15, The dev script in
hosted-components fails because `@stackframe/react`'s dist files aren't guaranteed
to exist; update the task wiring so hosted-components' dev depends on the
package build: add a dependsOn entry (e.g., dependsOn: ["^build"]) to the
hosted-components "dev" task in turbo.json so the workspace runs the dependent
package build first, or alternatively add a Vite alias in hosted-components'
Vite config to resolve "@stackframe/react" directly to its source (src) to avoid
needing dist files during dev.

Comment on lines +89 to +93
const [projectId, setProjectId] = useState<string | null | undefined>("internal");

useEffect(() => {
setProjectId(getProjectId());
}, []);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don't bootstrap every request as internal.

Starting projectId as "internal" means the server render and first client render both create a StackClientApp for the internal project before the hostname is read. That can trigger auth/navigation against the wrong project and then flip after mount.

Suggested change
-function RootComponent() {
-  const [projectId, setProjectId] = useState<string | null | undefined>("internal");
+function RootComponent() {
+  const [projectId, setProjectId] = useState<string | null | undefined>(undefined);
 
   useEffect(() => {
     setProjectId(getProjectId());
   }, []);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/hosted-components/src/routes/__root.tsx` around lines 89 - 93, The
initial state for projectId is incorrectly bootstrapped to the string
"internal", causing the app to create a StackClientApp for the internal project
before the real hostname is known; change the initialization so projectId starts
as undefined (or null) instead of "internal", keep the existing useEffect that
calls getProjectId() and setProjectId(), and ensure any code that
constructs/bootstraps StackClientApp (or reads projectId) guards against
undefined and only creates the client after projectId is set to the real value.

Comment on lines +6 to +10
pendingComponent: () => (
<div style={{ display: "flex", alignItems: "center", justifyContent: "center", minHeight: "100vh" }}>
<div style={{ width: 24, height: 24, border: "2px solid #e5e5e5", borderTop: "2px solid #333", borderRadius: "50%", animation: "spin 0.6s linear infinite" }} />
<style>{`@keyframes spin { to { transform: rotate(360deg) } }`}</style>
</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Announce the pending state to assistive tech.

This is the only UI users get while auth is loading/redirecting, but it has no status role or text. Screen readers will just get silence here.

Suggested change
   pendingComponent: () => (
-    <div style={{ display: "flex", alignItems: "center", justifyContent: "center", minHeight: "100vh" }}>
-      <div style={{ width: 24, height: 24, border: "2px solid `#e5e5e5`", borderTop: "2px solid `#333`", borderRadius: "50%", animation: "spin 0.6s linear infinite" }} />
+    <div
+      role="status"
+      aria-live="polite"
+      style={{ display: "flex", alignItems: "center", justifyContent: "center", minHeight: "100vh" }}
+    >
+      <div
+        aria-hidden="true"
+        style={{ width: 24, height: 24, border: "2px solid `#e5e5e5`", borderTop: "2px solid `#333`", borderRadius: "50%", animation: "spin 0.6s linear infinite" }}
+      />
+      <span style={{ position: "absolute", width: 1, height: 1, padding: 0, margin: -1, overflow: "hidden", clip: "rect(0, 0, 0, 0)", whiteSpace: "nowrap", border: 0 }}>
+        Loading…
+      </span>
       <style>{`@keyframes spin { to { transform: rotate(360deg) } }`}</style>
     </div>
   ),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pendingComponent: () => (
<div style={{ display: "flex", alignItems: "center", justifyContent: "center", minHeight: "100vh" }}>
<div style={{ width: 24, height: 24, border: "2px solid #e5e5e5", borderTop: "2px solid #333", borderRadius: "50%", animation: "spin 0.6s linear infinite" }} />
<style>{`@keyframes spin { to { transform: rotate(360deg) } }`}</style>
</div>
pendingComponent: () => (
<div
role="status"
aria-live="polite"
style={{ display: "flex", alignItems: "center", justifyContent: "center", minHeight: "100vh" }}
>
<div
aria-hidden="true"
style={{ width: 24, height: 24, border: "2px solid `#e5e5e5`", borderTop: "2px solid `#333`", borderRadius: "50%", animation: "spin 0.6s linear infinite" }}
/>
<span style={{ position: "absolute", width: 1, height: 1, padding: 0, margin: -1, overflow: "hidden", clip: "rect(0, 0, 0, 0)", whiteSpace: "nowrap", border: 0 }}>
Loading…
</span>
<style>{`@keyframes spin { to { transform: rotate(360deg) } }`}</style>
</div>
),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/hosted-components/src/routes/index.tsx` around lines 6 - 10, The
pendingComponent render lacks accessible status announcement for screen readers;
update the pendingComponent function to include an accessible status (e.g.,
role="status" or aria-live="polite") and provide visible or visually-hidden text
such as "Loading, please wait" so assistive tech is informed during auth
loading/redirect; keep the existing spinner for visual users and add the
announcement text inside the same wrapper (or as a screen-reader-only element)
to ensure both visual and non-visual users receive feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

X Tutup