mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-19 21:00:40 +08:00
ff0c5cf6f4
489 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
47b9a3a431 | chore: update package versions | ||
|
|
e07c509f81 | chore: update package versions | ||
|
|
e93b7520c4
|
feat(analytics): add route analytics heatmaps (#1520)
## Summary Adds route analytics heatmaps, stacked on top of `codex/analytics-overview-filters` (#1496). - Heatmap API routes (`/analytics/heatmap`, internal heatmap + heatmap-token endpoints) - Signed heatmap token signing/verification lib + tests - Dashboard heatmaps page (client + route) - Dev-tool + event-tracker support for heatmap capture - ClickHouse migration support ## Demo https://app.devin.ai/attachments/49cd6a96-8962-46d9-b8fb-145746cc6dee/rec-c80ec66f-21a3-49fb-bfae-19195ce7b930-edited.mp4 ## Notes Base branch is `codex/analytics-overview-filters` so the diff shows only the heatmap changes. Will retarget to `dev` once the base PR lands. Link to Devin session: https://app.devin.ai/sessions/16f8adac29b948b38280c85418617fea Requested by: @mantrakp04 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **New Features** * Added clickmap overlay to analytics dashboard, enabling visual click heatmap analysis on live websites. * Enhanced analytics metrics with hourly breakdowns, bounce rates, and top regions/browsers/devices filtering. * **Bug Fixes** * Improved click event tracking accuracy and dead-click detection. * Fixed overlay z-index stacking for better visibility. * **Style** * Updated dashboard card padding and navigation button styling for consistency. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: mantra <mantra@stack-auth.com> Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
3c89bb8c19
|
Hosted components redesigned (#1573)
## Summary Redesigns the hosted components app (`apps/hosted-components`) with a new Tailwind-based UI: rebuilt auth pages (sign-in, sign-up, magic link, forgot/reset password, MFA, email verification, team invitation, CLI auth confirm) and a full hosted Account Settings suite (profile, emails & auth, notifications, active sessions, API keys, payments, teams), with dark mode support. Also fixes found along the way: form error clearing in forgot-password/password-reset, `runAsynchronouslyWithAlert` for the notifications switch, a `CopyButton` DOM prop leak, a bogus mobile-session icon check, and imports the app stylesheet in the root route so the app's Tailwind styles actually apply. ## Before / after screenshots Captured on the local dev setup (`internal` project). Onboarding is not shown since it redirects without standalone UI in this setup. <details> <summary><b>Sign in</b></summary> | | Before | After | |---|---|---| | Light |  |  | | Dark |  |  | </details> <details> <summary><b>Sign up</b></summary> | | Before | After | |---|---|---| | Light |  |  | | Dark |  |  | </details> <details> <summary><b>Forgot password</b></summary> | | Before | After | |---|---|---| | Light |  |  | | Dark |  |  | </details> <details> <summary><b>Password reset</b></summary> | | Before | After | |---|---|---| | Light |  |  | | Dark |  |  | </details> <details> <summary><b>Email verification</b></summary> | | Before | After | |---|---|---| | Light |  |  | | Dark |  |  | </details> <details> <summary><b>MFA</b></summary> | | Before | After | |---|---|---| | Light |  |  | | Dark |  |  | </details> <details> <summary><b>Error</b></summary> | | Before | After | |---|---|---| | Light |  |  | | Dark |  |  | </details> <details> <summary><b>Team invitation</b></summary> | | Before | After | |---|---|---| | Light |  |  | | Dark |  |  | </details> <details> <summary><b>CLI auth confirm</b></summary> | | Before | After | |---|---|---| | Light |  |  | | Dark |  |  | </details> <details> <summary><b>Account settings — Profile</b></summary> | | Before | After | |---|---|---| | Light |  |  | | Dark |  |  | </details> <details> <summary><b>Account settings — Notifications</b></summary> | | Before | After | |---|---|---| | Light |  |  | | Dark |  |  | </details> <details> <summary><b>Account settings — Active sessions</b></summary> | | Before | After | |---|---|---| | Light |  |  | | Dark |  |  | </details> <details> <summary><b>Account settings — API keys</b></summary> | | Before | After | |---|---|---| | Light |  |  | | Dark |  |  | </details> <details> <summary><b>Account settings — Payments</b></summary> | | Before | After | |---|---|---| | Light |  |  | | Dark |  |  | </details> <details> <summary><b>Account settings — Emails & auth</b></summary> | | Before | After | |---|---|---| | Light |  |  | | Dark |  |  | </details> Link to Devin session: https://app.devin.ai/sessions/1d2380aa55694f2fb12ed96e200a32ad Requested by: @Developing-Gamer --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: armaan <armaan@stack-auth.com> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> |
||
|
|
7063aa2df7 | chore: update package versions | ||
|
|
6de253633f | chore: update package versions | ||
|
|
5eedb484e1 | chore: update package versions | ||
|
|
64eeedce9f
|
Fix dev CI: docker prune missing template + cross-domain test failures (#1582)
Unbreaks the test workflows that have been red on every dev push since June 4. All root causes trace to direct pushes whose own CI runs already failed (`8b78587da`, `c60016226`, `59daf1321`). > Note: this PR originally also fixed the "Docker Server Build and Push" workflow (missing `@hexclave/template` in the Dockerfiles' `turbo prune` scope), but dev picked up the identical fix via |
||
|
|
f4c13db079
|
fix(sdk): stop nested cross-domain auth from restarting the redirect chain on the hosted domain (#1581)
## What users see
Setting up a new project with hosted components, clicking **Sign in**
sometimes throws the browser into a redirect ping-pong between the app
and the hosted components site — anywhere from 5 to 9+ cross-domain
redirects — before the sign-in page finally renders. Reproduced live
against production:

Captured redirect chain from that recording (one line per navigation, ~1
per second):
```
localhost:3000/ ← click "Sign in"
HOSTED /handler/sign-in?...nested_refresh_token_id ← start session handoff
localhost:3000/?redirect_uri=...&state=S1 ← bounce: "prove the session"
HOSTED /handler/sign-in?...&code=... ← code delivered... then RESTART ↩
localhost:3000/?redirect_uri=...&state=S2 ← bounce again (fresh state!)
HOSTED /handler/sign-in?...&code=... ← code delivered... RESTART ↩
localhost:3000/?redirect_uri=...&state=S3 ← again
HOSTED /handler/sign-in?...&code=... ← again
localhost:3000/?redirect_uri=...&state=S4 ← again
HOSTED /handler/sign-in?...&code=... ← exchange finally wins the race
HOSTED /handler/sign-in (clean URL) ← sign-in form renders
```
The designed handshake is only 3 cross-domain redirects. Everything past
that is one bug restarting the chain over and over.
## The bug
When a page on the hosted domain loads with a one-time `code`, the
`StackClientApp` constructor schedules **two** async startup flows
back-to-back:
1. `callOAuthCallback` — which **synchronously strips `code` + `state`
from the URL** (`history.replaceState`) before starting its network
token exchange, and
2. `_maybeHandleNestedCrossDomainAuth` — which has a guard for exactly
this situation ("a real OAuth callback wins"), implemented as *"is
`code`+`state` in the URL?"*
Flow 1 runs first. By the time flow 2 reads `window.location`, the
params it's guarding on are already gone — so it concludes no OAuth
callback is happening, sees the (un-stripped) nested handoff marker, and
bounces back to the app domain to request a *new* code, cancelling the
in-flight exchange:
```mermaid
sequenceDiagram
participant A as Your app (a.com)
participant B as Hosted sign-in (b.com)
A->>B: 1. go to sign-in ("I have session X")
B->>A: 2. "prove it" (state, code_challenge)
A->>B: 3. one-time code for session X
Note over B: callOAuthCallback strips code+state from URL,<br/>starts token exchange (network)
Note over B: nested handler runs next, checks URL for code+state…<br/>already gone → guard defeated ❌
B->>A: 2'. "prove it" AGAIN (fresh state) — exchange cancelled
A->>B: 3'. another one-time code
Note over B: …loop repeats until the exchange happens to<br/>finish before the re-bounce navigation commits
```
Whether each cycle escapes is a coin flip between two competing
navigations (the exchange's success redirect vs. the handler's
re-bounce), which is why the loop count varies run to run and the issue
reproduces so inconsistently.
## The fix
Capture the URL once, at construction time — before anything can mutate
it — and let the nested handler consult that snapshot in addition to the
live URL:
- The constructor now captures `new URL(window.location.href)` when
scheduling the nested-auth resolution and passes it in.
- `_maybeHandleNestedCrossDomainAuth(urlAtConstructionTime?)` stands
down if **either** the live URL **or** the construction-time URL carries
`code` + `state`.
A stripped callback still counts as a callback, so the handler no longer
re-bounces while the exchange is in flight. Every other path is
unchanged: the handler still reads all of its working params from the
live URL (the strip never touches the nested params), hop-1/hop-2 pages
have no `code` in either snapshot, and ordinary social-login callbacks
never had this race (the component-driven flow strips long after the
handler has run).
Note this fix removes the *restarts*. The remaining 3-redirect baseline
for signed-out users is a separate design issue (the analytics-created
anonymous session triggering the handoff at all) and is intentionally
out of scope here.
## Tests
- New: `does not re-bounce nested cross-domain auth after the OAuth
callback consumed code+state from the URL` — pins both guards
(mutation-tested: reverting either fix line fails it).
- New: `passes the construction-time URL to the nested cross-domain auth
handler` — pins the eager capture; fails if the URL is read lazily at
handler run time.
- Full cross-domain suite passes (the `signOut` timeout in that file is
a pre-existing flake on `dev`, reproducible without this change).
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes a race in nested cross-domain auth that caused repeated redirects
between the app and the hosted sign-in. We now snapshot the URL at
construction so OAuth callbacks are respected even after `code` and
`state` are stripped.
- **Bug Fixes**
- Capture `window.location` at construction and pass it to
`_maybeHandleNestedCrossDomainAuth`.
- Handler stands down if `code` and `state` exist in the live or
captured URL.
- Stops the redirect ping‑pong; the 3‑redirect baseline remains
unchanged.
- Keeps reading nested params from the live URL; no other paths changed.
- Adds tests to pin the race and the construction‑time URL behavior.
<sup>Written for commit
|
||
|
|
74c888fed7
|
chore(mcp/docs): canonicalize HEXCLAVE_ env vars in docs + raise ask_hexclave step limit & timeout (#1571)
## Summary
Follow-up from analyzing the dogfooding report on the `ask_hexclave` MCP
tool. Two root causes were confirmed against source:
1. **The "`STACK_` vs `HEXCLAVE_` env var hallucination" wasn't a
hallucination** — it's an incomplete Stack Auth → Hexclave rebrand. The
SDK resolves both prefixes (`packages/js/src/generated/env.ts`), with
`HEXCLAVE_*` canonical and `STACK_*` a legacy fallback, but several
docs/examples still showed the old `STACK_*` names. That inconsistency
is what misled agents into thinking `HEXCLAVE_*` was made up.
2. **`ask_hexclave` timeouts** — the tool proxies to a `quality:
"smart"` agentic docs-search loop. The agent step budget (50) and the
120s timeouts were too tight; broad/multi-part questions blew the budget
(reproduced 3× while investigating).
## Changes
### Docs: canonicalize client SDK auth env vars to `HEXCLAVE_*`
Converted `PROJECT_ID`, `PUBLISHABLE_CLIENT_KEY`, `SECRET_SERVER_KEY`,
`API_URL` (+ `NEXT_PUBLIC_` / `VITE_` forms) from `STACK_*` →
`HEXCLAVE_*` in app-setup docs + the package template:
-
`docs-mintlify/guides/integrations/{convex,tanstack-start,vercel}/overview.mdx`
- `docs-mintlify/guides/going-further/local-vs-cloud-dashboard.mdx`
- `docs-mintlify/guides/apps/analytics/overview.mdx`
- `docs-mintlify/guides/other/tutorials/ship-production-ready-auth.mdx`
- `docs-mintlify/sdk/objects/hexclave-app.mdx`
- `packages/template/src/integrations/convex/component/README.md` (the
tracked source of the generated `@hexclave/js` + `@hexclave/next` copies
— the generated copies are git-ignored)
**Deliberately left untouched** — read literally by the backend/CLI (no
`HEXCLAVE_` alias) or user-defined: `STACK_CLICKHOUSE_*`,
`STACK_DATABASE_*`, `STACK_OPENROUTER_*`, `STACK_CLI_*`, `STACK_SEED_*`,
`STACK_WEBHOOK_SECRET`, `STACK_DATA_VAULT_SECRET`, and the `x-stack-*`
HTTP headers. So `self-host.mdx`, `cli.mdx`, `jwts.mdx`, `webhooks`, and
`data-vault` docs are intentionally unchanged.
### Reliability: raise `ask_hexclave` step limit + timeout
- `apps/backend/src/app/api/latest/ai/query/[mode]/route.ts`:
docs/search agent step limit **50 → 75** (+50%); AI generation abort
**120s → 180s**
- `apps/mcp/src/mcp-handler.ts`: MCP function `maxDuration` **120 →
180** (kept ≥ backend timeout so the proxy doesn't die before the
backend finishes)
## Notes
- Also includes a small pre-existing `run pnpm fml` commit (regenerated
docs snippets / `llms-full.txt`).
- The step/timeout bumps address the *symptom*. The durable reliability
fix is streaming/keepalive on the MCP proxy so the client never idles
out mid-query — proposed as a follow-up.
- **Not** included: the separate `sendEmail` doc-vs-SDK drift (docs
declare `Promise<Result<void, KnownErrors>>` in
`sdk/objects/hexclave-app.mdx`, but the SDK returns `Promise<void>` and
throws). That's a docs *correctness* bug deserving its own PR.
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Canonicalized auth env vars in docs/templates to `HEXCLAVE_*`, raised
docs/search step limits and timeouts, and clarified `HexclaveApp`
defaults. MCP tool and server instructions now require loading the
`skill` resource before queries.
- **Bug Fixes**
- Docs: Use `HEXCLAVE_PROJECT_ID`, `HEXCLAVE_PUBLISHABLE_CLIENT_KEY`,
`HEXCLAVE_SECRET_SERVER_KEY`, and optional `HEXCLAVE_API_URL` across
guides/templates (Vercel, Convex, TanStack Start, analytics). In SDK
docs, `secretServerKey` defaults to `HEXCLAVE_SECRET_SERVER_KEY`, and
client defaults use `NEXT_PUBLIC_HEXCLAVE_*`. Backend-only `STACK_*`
vars (`STACK_CLICKHOUSE_*`, `STACK_DATABASE_*`, `STACK_OPENROUTER_*`,
CLI/data-vault/webhook headers) unchanged.
- Reliability: Increase docs/search step limit 50→75 and timeouts
120s→180s; set MCP `maxDuration` to 180s; use `performance.now()` for
duration logging. MCP instructions updated to require loading the
`skill` resource before using tools.
<sup>Written for commit
|
||
|
|
1999ad8be3 | chore: update package versions | ||
|
|
59daf1321c
|
[codex] Add analytics overview filters (#1496)
## Summary
Adds richer analytics overview metrics and filterable dashboard
breakdowns.
- adds hourly overview series for the 1-day range
- adds country, referrer, browser, OS, and device filters to internal
metrics
- adds bounce rate, session duration, top countries, top browsers, top
operating systems, and device breakdowns
- updates the overview dashboard with filter chips, top-list cards,
animated metric states, and 1-day hourly chart support
- captures user agent on page-view analytics events, with a server-side
fallback for older clients
## Validation
Attempted targeted tests:
`pnpm test run
apps/backend/src/app/api/latest/internal/metrics/route.test.ts
'apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/analytics-chart-mode.test.ts'`
This did not reach Vitest in the temporary split worktree because
`node_modules` is not installed there and the repo pre-step failed at
`pnpm exec tsx ./scripts/generate-sdks.ts`.
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Adds analytics overview filters with optional date‑range bounds and
1‑day hourly charts, plus smoother, accessible animations across charts
and top lists. Improves correctness and stability with deterministic
caching, normalized inputs, client‑only user‑agent capture, and
globe/layout fixes.
- **New Features**
- Filterable analytics overview (country, referrer, browser, OS, device)
with normalized inputs and optional `since`/`until`; API/admin/dashboard
accept `AnalyticsOverviewFilters` with deterministic cache keys.
- 1‑day hourly charts (page views, visitors) and a metric mode toggle
(DAU, Visitors, Revenue); animated top‑lists and sparklines powered by
`motion` with reduced‑motion support.
- UI: filter chips/menu, clearer tooltips (incl. user metric cards),
optional interactive globe with dynamic camera distance; exported
`TooltipPortal` from `@hexclave/ui`.
- **Refactors & Bug Fixes**
- Event ingest: client sends `user_agent`; removed server‑side fallback;
added user‑agent filter‑fragment builder and tests.
- Metrics correctness: aligned hourly bounds to start of UTC hour;
derived 1‑day revenue total from daily series; resilient chart x‑axis
formatting; country filter options use analytics `top_regions`;
fixed‑'en' locale for top‑lists; added date‑range parsing/validation for
filters.
- UI/runtime: smoother pill/tab slider animations with guards for
missing Web APIs; added `containedHeight` to `PageLayout` and wired into
sidebar/session replays; globe disables zoom when non‑interactive.
- Misc: instrumentation runs only in Node (`process.env.NEXT_RUNTIME ===
"nodejs"`); analytics/overview page redirects with URL‑encoded
`projectId`; Docker: include `@hexclave/template` in `turbo prune` to
fix CI builds.
<sup>Written for commit
|
||
|
|
7f99f15b42
|
fix(rde): graceful config load errors + lightweight /config import path (#1557)
## Problem
A user hit `Failed to register development environment session (500)`
when running the RDE (`hexclave dev` / `stack dev`). Removing
`defineStackConfig` from their `stack.config.ts` made it go away.
**Root cause:** the local dashboard evaluates the project's config file
in a plain Node context via `jiti`
([config-file.ts](apps/dashboard/src/lib/remote-development-environment/config-file.ts)).
When the config imports a *value* (e.g. `defineStackConfig`) from a
framework package like `@stackframe/stack` / `@hexclave/next`, jiti
executes the entire SDK — React, `server-only`, Next internals — which
throws in that context. The exception propagated as a bare 500. Dropping
`defineStackConfig` removed the value import, so jiti no longer loaded
the framework.
## Changes
**1. Graceful error (Fix 3)**
`readConfigFile` now wraps the `jiti.import` in try/catch and rethrows a
message pointing at the lightweight import path, instead of a raw 500.
**2. Lightweight `/config` subpath (Fix 1)**
Added a side-effect-free `./config` entrypoint to the framework packages
— `@hexclave/{js,next,react,tanstack-start}/config` — that re-exports
`defineHexclaveConfig` / `defineStackConfig` + the `HexclaveConfig` type
from `@hexclave/shared/config`, with **no framework runtime**. Source of
truth:
[`packages/template/src/config.ts`](packages/template/src/config.ts) +
the export in
[`package-template.json`](packages/template/package-template.json),
propagated to the generated packages via `generate-sdks`.
> Why per-package and not `@hexclave/shared/config`: `@hexclave/shared`
is only a *transitive* dependency from a user's perspective, so
importing from it fails under pnpm strict mode. Users depend on the
framework package directly, so `@hexclave/next/config` always resolves.
This was confirmed empirically — the previous tests that imported
`@hexclave/shared/config` were red.
**3. Docs / prompts / renderer aligned to the new path**
-
[`ai-setup-prompt.ts`](packages/shared/src/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.ts)
+ regenerated `docs-mintlify` (setup.mdx, llms-full.txt, snippets).
- Hand-written
[`hexclave-config.mdx`](docs-mintlify/guides/going-further/hexclave-config.mdx)
and
[`local-vs-cloud-dashboard.mdx`](docs-mintlify/guides/going-further/local-vs-cloud-dashboard.mdx).
(`docs/**` left untouched — legacy.)
- `renderConfigFileContent` (the config file the dashboard/CLI
auto-writes) now emits `import type { HexclaveConfig } from
"<pkg>/config"`. Legacy `@stackframe/*` packages predate the subpath, so
they keep their root import (guarded).
## Behavioral note
Existing config files that import from a package root get their import
line upgraded to `/config` on their next dashboard/CLI sync — a
one-time, harmless rewrite that migrates them onto the safe path. The
github-config-push idempotence test was updated to use the current
`/config` format so it still genuinely verifies "no spurious commit."
## Testing
- 43 unit tests pass across `config-file`, `github-config-push`,
`config-rendering`, `config-authoring`, `local-emulator`. The two
previously-red RDE `define*` tests now pass through jiti via
`@hexclave/next/config` (the real code path), and were made
resolution-stable by rooting their temp dir at the test file instead of
`process.cwd()`.
- Typecheck green on all source-changed packages (shared, cli, js, next,
react, tanstack-start). Lint clean.
- ⚠️ The two e2e suites (`cli.test.ts`, `config-local-emulator.test.ts`)
need backend+DB infra; their snapshot updates are mechanical and
**confirmable only in CI**.
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Prevents 500s when loading `hexclave.config.ts` by adding a lightweight
`<pkg>/config` entrypoint and showing a clear, actionable error without
leaking framework stacks. Import detection, rendering, CLI, tests, and
docs now default to `/config` (including `@hexclave/tanstack-start`) so
configs load in plain Node contexts.
- **New Features**
- Added `/config` subpaths in `@hexclave/js`, `@hexclave/next`,
`@hexclave/react`, `@hexclave/tanstack-start` (and template)
re-exporting `defineHexclaveConfig`, `defineStackConfig`, and
`HexclaveConfig` with no framework runtime.
- Renderer, CLI, and docs import `HexclaveConfig` from `<pkg>/config`;
legacy `@stackframe/*` keep root imports. Existing config files
auto-upgrade on next dashboard/CLI sync.
- **Bug Fixes**
- Wrapped `jiti` config load with try/catch; capture raw error for
diagnostics and show a concise message pointing to `<pkg>/config` (no
nested framework stack traces).
- Import detection accepts optional `/config` suffix; renderer always
appends `/config` for Hexclave packages and recognizes
`@hexclave/tanstack-start`.
- Tests stabilized by scoping temp dirs to the test file; CLI error
example now references `HexclaveConfig` from `<pkg>/config` for Hexclave
packages.
<sup>Written for commit
|
||
|
|
76fc62e98b
|
fix(rde): stop the RDE dashboard blanking on every access-token refresh (#1566)
## Problem
In the Remote Development Environment (`hexclave dev`), the dashboard
suspends and goes **blank for a moment every ~30–60 seconds**, then
repopulates. It never happens on a plain `pnpm dev` dashboard.
## Root cause
The RDE auth gate
([`remote-development-environment-auth-gate.tsx`](../blob/dev/apps/dashboard/src/app/remote-development-environment-auth-gate.tsx))
keeps the browser signed in by re-installing a freshly minted
**access-token-only** session (`signInWithTokens({ accessToken,
refreshToken: "" })`) on a timer (capped by
`RDE_ACCESS_TOKEN_MAX_AGE_MS`).
`InternalSession.calculateSessionKey` keyed access-only sessions by the
**access-token string**:
```ts
} else if (ofTokens.accessToken) {
return `access-${ofTokens.accessToken}`; // 👈 changes on every refresh
}
```
So each refresh = a new key = a brand-new `InternalSession` object.
Every session-scoped cache (`useUser` / `useConfig` / `useTeams` /
`useOwnedProjects`, via `createCacheBySession`) is keyed by the
**session object**, so a new object means a cold cache → pending promise
→ `React.use()` suspends → the whole tree falls back to its (empty)
Suspense boundary.
It only *shows* in RDE because the backend is remote: the post-swap
refetch has real network latency, so the blank is visible for hundreds
of ms. On localhost the same swap is a sub-frame flicker. (A background
`refresh()`/write-only is stale-preserving and does **not** suspend —
only a new session dependency does.)
## Fix
Two changes in the SDK source (`packages/shared` + `packages/template`;
the `js`/`next`/`react`/`tanstack-start` copies are generated):
1. **Stable key for access-only sessions.** Key by the token's
`refresh_token_id` (decoded from the JWT) instead of the raw token
string. Every access token minted for the same session shares that id,
so the session identity — and therefore every cache — stays stable
across refreshes. Falls back to the raw token if the JWT can't be
decoded. Refresh-keyed and not-logged-in paths are untouched.
2. **In-place token update.** New
`InternalSession.updateAccessToken(token)`, called from
`_signInToAccountWithTokens`, pushes the fresh token into the reused
session object instead of constructing a new one (no-op when the session
is invalid / the token is unchanged / null).
Net effect: re-minting the RDE access token reuses the same
`InternalSession`, caches stay warm, nothing suspends, no blank.
## Why this is safe
- Session reuse is scoped **per token store** —
`_sessionsByTokenStoreAndSessionKey` is a `WeakMap` keyed by the
token-store object first, then the session key — so server-side
per-request sessions remain isolated regardless of how coarse the key
is. No cross-user/cross-session mixup.
- `refresh_token_id` is a per-session UUID; the coarser key only merges
*the same session's* successive access tokens, which is the intent.
- This keying convention matches the existing `refresh-${refreshToken}`
path (key by the unique token value).
## Testing
- **Reproduced live** via a temporary harness driven through a real
browser: forcing the session-identity swap triggered the exact
cold-cache refetch storm (`useUser`/`useTeams`/`useOwnedProjects`
refetching) that produces the blank.
- **Unit-verified** the new behavior against the built `shared` dist:
two access tokens sharing a `refresh_token_id` → same session key;
different ids → different keys; opaque token → fallback; refresh-keyed
unchanged; `updateAccessToken` swaps the token in place and is a correct
no-op when invalid/unchanged/null.
- **Typecheck + lint clean** across `shared`, `template`, `next`,
`react`, `tanstack-start`, and the dashboard.
- Reviewed by independent passes for correctness, security/blast-radius,
and simplification — no actionable findings.
## How to verify in the real RDE path
Set `RDE_ACCESS_TOKEN_MAX_AGE_MS` to e.g. `5000` in the auth gate and
run `hexclave dev`: before this change the dashboard blanks every few
seconds; after, it stays populated.
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Stops the RDE dashboard from going blank every 30–60s by keeping session
identity stable during access‑token refreshes. Session-scoped caches
stay warm, so the UI no longer suspends.
- **Bug Fixes**
- Key access-only sessions by the JWT `refresh_token_id` (stable across
re‑mints); fall back to the raw token if undecodable. Implemented in
`packages/shared`; tests in `packages/shared/src/sessions.test.ts`.
- Add `InternalSession.updateAccessToken()` and use it in
`_signInToAccountWithTokens` to update the token in place only when the
incoming pair resolves to the same `sessionKey` (rejects
foreign/null/unchanged/undecodable; covers access‑only and
refresh‑backed sessions). Prefetch the current user via
`runAsynchronously` in write‑only mode. Implemented in `packages/shared`
and `packages/template`.
<sup>Written for commit
|
||
|
|
3132de1cae | chore: update package versions | ||
|
|
dbb397dcbc | Home URL should be non-hosted | ||
|
|
96273a9d65 | chore: update package versions | ||
|
|
4fdd2b3831 | chore: update package versions | ||
|
|
60202f2b09 | Fix build | ||
|
|
3e2f0f9558 |
Fix infinite loop bug in TanStack Start demo
Some checks failed
all-good: Did all the other checks pass? / all-good (push) Has been cancelled
Ensure Prisma migrations are in sync with the schema / check_prisma_migrations (22.x) (push) Has been cancelled
DB migration compat / Check if migrations changed (push) Has been cancelled
Docker Server Build and Push / Docker Build and Push Server (push) Has been cancelled
Docker Server Build and Run / docker (push) Has been cancelled
Runs E2E API Tests (Local Emulator) / E2E Tests (Local Emulator, Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (mock, 22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (prod, 22.x) (push) Has been cancelled
Runs E2E API Tests with custom port prefix / build (22.x) (push) Has been cancelled
Runs E2E Fallback Tests / E2E Fallback Tests (Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Lint & build / lint_and_build (24) (push) Has been cancelled
TOC Generator / TOC Generator (push) Has been cancelled
DB migration compat / Back-compat — Current branch migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Has been cancelled
DB migration compat / Forward-compat — Current branch code with ${{ needs.check-migrations-changed.outputs.base_branch }} branch migrations (push) Has been cancelled
DB migration compat / No migration changes (skipped) (push) Has been cancelled
|
||
|
|
468a221379 | chore: update package versions | ||
|
|
461b9acd68 | chore: update package versions | ||
|
|
10f8348b51 | Fix Vite environment variables | ||
|
|
0c07b4b44e | Show browser alert for missing Hexclave project ID | ||
|
|
c60016226b | Fix nested cross-domain OAuth edge case | ||
|
|
8b78587dac | Fix handler sign out | ||
|
|
1d8babb0e1 | Better UX around hosted component errors | ||
|
|
5cb50721bd | chore: update package versions | ||
|
|
bc45117777
|
Support local dashboard in remote SSH and GH Codespaces (#1538) | ||
|
|
c14a9dd3d0
|
feat(hexclave): PR 5 — internal symbol/path/package renames + brand strings (#1547)
## Stack Auth → Hexclave rename — PR 5 (internal symbols, paths,
packages, brand strings)
PR 5 finishes the **internal / non-wire** half of the Stack→Hexclave
rename. It only touches things where nothing outside the repo depends on
the exact name: internal symbols, file/dir names, the
`@stackframe/template` package, and residual brand strings. Plan +
progress are in `HEXCLAVE-RENAME-PR5-PLAN.md`.
Every step was verified green (`pnpm typecheck` + `pnpm lint`, 28/28)
and committed as its own checkpoint, then a fan-out of review agents
audited all commits and the findings were fixed.
### What changed
- **Internal symbols** (`@hexclave/shared`, `packages/template`, apps):
`stack*`/`Stack*` → `hexclave*`/`Hexclave*` — incl.
`stackGlobalsSymbol`, the `_Stack*AppImpl` classes,
`stackAppInternalsSymbol`, `StackContext`, `getStackStripe`, etc. The
`stack*App` local-variable convention
(`stackServerApp`/`stackClientApp`/…) was renamed across 175
source/example/doc files.
- **File renames**: `hexclave-handler/provider/context.tsx`,
`backend/hexclave.tsx`, `internal-tool/hexclave.ts`,
`hexclave-app-internals.ts`.
- **Directory renames**: `lib/hexclave-app`, `hexclave-companion`,
`[...hexclave]` route segment, `skills/hexclave`,
`dashboard/src/hexclave`, and the package dirs
**`packages/{next,shared,ui,sc,cli}`** (dropping the `stack-` prefix to
match the `@hexclave/*` npm names).
- **Packages**: `@stackframe/template` → `@hexclave/template`; **deleted
`packages/init-stack`** (onboarding lives in `@hexclave/cli init`; the
published npm package is untouched).
- **Brand strings**: reworded `Stack Auth`/`Stack dashboard` prose in
code + docs-mintlify, renamed `hexclave-app.mdx`/`use-hexclave-app.mdx`
with redirects, regenerated OpenAPI, updated coupled e2e assertions;
`doctor`/`init` now prefer `hexclave.config.ts`.
### Intentionally kept (verified, not oversights)
Wire/compat identifiers (`x-stack-*` headers, `stack-*` cookies,
`STACK_*` env names, `*.stack-auth.com`, `stackauth_`, `ask_stack_auth`,
query params), public `Stack*` SDK aliases, crypto/JWT/vault
domain-separation tags, `*-brand-sentinel`s, the
`Symbol.for("StackAuth--…")` string, `_stack_sync_metadata`, Postgres
`stackframe` / docker image names, the `stack-auth-logo*.svg` (used by
the rebrand modal), and `migration.mdx` / "formerly known as Stack Auth"
notes. False positives (Phosphor `StackIcon`/`StackSimple`, `TanStack`,
`OrbStack`, `stackable`/`Stacked` charts) left alone.
### Review pass
Six review agents audited all commits. Found + fixed one real bug — a
build script (`bundle-type-definitions.ts`) hardcoded the old
`lib/stack-app` glob path (not an import, so typecheck/lint were blind),
silently emptying the dashboard AI type bundle — plus stale comments, a
dead CI env var, and stale `.gitignore`/`.dockerignore` entries.
Cross-cutting audit confirmed **zero wire-compat identifiers were
accidentally renamed**.
### ⚠️ Verification note
`typecheck` + `lint` are fully green locally. The **e2e suite was not
run** (needs a live backend+DB), so the brand-string assertion +
OpenAPI-regen changes are verified by grep/codegen only — please let CI
exercise e2e to confirm.
### Base-branch note
This branch was forked from the local-only `cl/friendly-lewin-72293f`
(not on origin, no separate PR), so this PR against `dev` also carries
that branch's ~11 preceding Hexclave-rename commits (config-file rename,
env-var dual-read, AI setup-prompt rebrand). If those should land
separately, re-parent before merge.
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Finishes the internal Stack Auth → Hexclave rename and cleans up
remaining stragglers, including dev-tool and prompt copy. All changes
are internal-only; public/wire APIs remain unchanged. Re-merged `dev`
and resolved the payments create-purchase-url conflict.
- **Refactors**
- Internal symbols: stack*/Stack* → hexclave*/Hexclave* (e.g.,
`getHexclaveServerApp` via `@/hexclave`, `getHexclaveStripe`,
`hexclaveAppInternalsSymbol`, `hexclaveSchemaInfo`, Prisma
`__hexclave_*`, `data-hexclave-handler-page`, Stripe mock
`hexclavePortPrefix`).
- Files/dirs: moved to `lib/hexclave-app`; handler route
`[...hexclave]`; backend entry `src/hexclave.tsx`; dashboard internals
`hexclave-app-internals`; companion `hexclave-companion`; dropped
`stack-` prefix across package dirs
(`packages/{shared,ui,sc,cli,next}`); workflows/emulator paths now
`packages/cli`; Quetzal codegen env at `packages/next/.env.local`.
- Packages/docs: `@stackframe/template` → `@hexclave/template`; removed
`packages/init-stack`; regenerated OpenAPI and updated docs
slugs/redirects for hexclave-app/use-hexclave-app.
- Brand strings/prompts: reworded remaining “Stack” dashboard strings to
Hexclave; updated dev-tool copy and prompts; `doctor/init` now prefer
`hexclave.config.ts`. Kept all wire-compat identifiers and public
aliases (`x-stack-*`, `stack-*` cookies, `STACK_*` env,
`*.stack-auth.com`, `Stack*` SDK names).
- Rebased/merged onto latest `dev`: retained `@hexclave/template`, kept
`src` in published files, refreshed setup-prompt imports and docs JSON,
adopted 1.0.5 version bumps, and re-merged `dev` again (resolved
`create-purchase-url` with `getHexclaveStripe`).
- **Bug Fixes**
- Restored dashboard AI type bundle by pointing the glob to
`packages/template/src/lib/hexclave-app`.
- Addressed rename leftovers: updated lingering `@/stack` imports and
CSS selector, fixed schema/meta and port-prefix expansions, and aligned
emulator commands to `packages/cli`.
- CI/build: removed a dead env var and stale ignore entries; fixed
Docker by renaming `STACK_SKIP_TEMPLATE_GENERATION` →
`HEXCLAVE_SKIP_TEMPLATE_GENERATION`.
<sup>Written for commit
|
||
|
|
760cc4a826 | chore: update package versions | ||
|
|
74e7fb8e9d | Add SDK reminder note | ||
|
|
f7d465f91b
|
Publish src folder alongside dist in all packages (#1545) | ||
|
|
a21ba6b2f5 | chore: update package versions | ||
|
|
fc7174d110 | chore: update package versions | ||
|
|
b946c6fa47 | Fix nested cross domain auth | ||
|
|
a659c7727a | Fix hydration error | ||
|
|
7d08af0db8 | Revert React 19 requirement | ||
|
|
2e1bfecb5f | Fix types | ||
|
|
64a38d0758 | chore: update package versions | ||
|
|
3961a9703e | Bump package versions | ||
|
|
40dd1d2cd7 | Switch default API host to api.hexclave.com | ||
|
|
ce98c44fcf
|
fix(hexclave): rename leftover @stackframe/stack-shared imports in template providers (#1525)
## Why the build fails on `origin/dev`
The `Lint & build` workflow fails at the **Build** step, in
`@hexclave/lovable-react-18-example`'s `vite build`:
```
[vite]: Rollup failed to resolve import "@stackframe/stack-shared/dist/utils/globals"
from ".../packages/react/dist/esm/providers/stack-context.js".
```
(failing run: [job
78596906597](https://github.com/hexclave/stack-auth/actions/runs/26665187298/job/78596906597))
### Root cause
PR 3 (`feat(hexclave): PR 3 — native @hexclave/* source rename + delete
dual-publish wiring`, #1482) renamed `@stackframe/stack-shared` →
`@hexclave/shared` and **deleted the dual-publish wiring**, so
`@stackframe/stack-shared` is no longer a resolvable package.
Two `packages/template/src` provider files were missed in that rename:
- `packages/template/src/providers/stack-context.tsx`
- `packages/template/src/providers/translation-provider-client.tsx`
`packages/react` ships only `package.json`; its `src`/`dist` are
**generated** from `packages/template/src` by `generate-sdks`, which
copies import specifiers verbatim. So the stale
`@stackframe/stack-shared` import propagated into `packages/react/dist`.
The `@hexclave/react` build itself **succeeds** because tsdown/rolldown
externalizes the import (it never has to resolve it). The failure only
surfaces downstream, when `lovable-react-18-example` bundles
`@hexclave/react` with Vite/Rollup and tries to actually resolve
`@stackframe/stack-shared` — a package that no longer exists.
## Fix
Point both imports at the renamed package, matching the convention
already used by sibling files (e.g. `stack-provider-client.tsx`,
`common.ts`):
```diff
-import { createGlobal } from "@stackframe/stack-shared/dist/utils/globals";
+import { createGlobal } from "@hexclave/shared/dist/utils/globals";
```
## Verification
Reproduced and confirmed the fix locally:
1. Edited the two template files.
2. `pnpm -w run generate-sdks` → generated `packages/react/src` now
imports `@hexclave/shared/dist/utils/globals`.
3. `pnpm --filter @hexclave/react run build` →
`dist/esm/providers/stack-context.js` now imports `@hexclave/shared` ✔
4. `pnpm --filter @hexclave/lovable-react-18-example run build` → **`✓
built`** (previously failed at this exact step).
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Rename leftover imports from `@stackframe/stack-shared` to
`@hexclave/shared` in the template providers to fix downstream build
failures. This unblocks Vite/Rollup consumers (e.g.,
`@hexclave/lovable-react-18-example`) by resolving `createGlobal` from
the correct package.
<sup>Written for commit
|
||
|
|
609579abab
|
feat(hexclave): PR 3 — native @hexclave/* source rename + delete dual-publish wiring (#1482)
Some checks failed
all-good: Did all the other checks pass? / all-good (push) Has been cancelled
Ensure Prisma migrations are in sync with the schema / check_prisma_migrations (22.x) (push) Has been cancelled
DB migration compat / Check if migrations changed (push) Has been cancelled
Docker Server Build and Push / Docker Build and Push Server (push) Has been cancelled
Docker Server Build and Run / docker (push) Has been cancelled
Runs E2E API Tests (Local Emulator) / E2E Tests (Local Emulator, Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (mock, 22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (prod, 22.x) (push) Has been cancelled
Runs E2E API Tests with custom port prefix / build (22.x) (push) Has been cancelled
Runs E2E Fallback Tests / E2E Fallback Tests (Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Lint & build / lint_and_build (24) (push) Has been cancelled
TOC Generator / TOC Generator (push) Has been cancelled
DB migration compat / Back-compat — Current branch migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Has been cancelled
DB migration compat / Forward-compat — Current branch code with ${{ needs.check-migrations-changed.outputs.base_branch }} branch migrations (push) Has been cancelled
DB migration compat / No migration changes (skipped) (push) Has been cancelled
|
||
|
|
55e1eb971e | Make StackContext globally unique | ||
|
|
00c8c1954b | Generate package files | ||
|
|
0838a22c14
|
Remove stack emulator CLI commands (#1522) | ||
|
|
70486596f0 | Use jiti for RDE config parsing | ||
|
|
8b2bcbca69 |
Update repository URL back to github.com/hexclave/stack-auth
Some checks failed
all-good: Did all the other checks pass? / all-good (push) Has been cancelled
Ensure Prisma migrations are in sync with the schema / check_prisma_migrations (22.x) (push) Has been cancelled
Docker Server Build and Push / Docker Build and Push Server (push) Has been cancelled
Docker Server Build and Run / docker (push) Has been cancelled
Runs E2E API Tests (Local Emulator) / E2E Tests (Local Emulator, Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (mock, 22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (prod, 22.x) (push) Has been cancelled
Runs E2E API Tests with custom port prefix / build (22.x) (push) Has been cancelled
Runs E2E Fallback Tests / E2E Fallback Tests (Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Lint & build / lint_and_build (24) (push) Has been cancelled
Publish npm packages / publish (push) Has been cancelled
Publish Swift SDK to prerelease repo / publish (push) Has been cancelled
TOC Generator / TOC Generator (push) Has been cancelled
|
||
|
|
80d1530b48 | Update package README |