Skip to content

Commit b0208ed

Browse files
committed
Merge remote-tracking branch 'origin/dev' into stack-cli-bugs
# Conflicts: # apps/backend/src/app/api/latest/internal/local-emulator/project/route.tsx # packages/stack-cli/src/commands/emulator.ts # pnpm-lock.yaml
2 parents c3425e5 + 647883c commit b0208ed

184 files changed

Lines changed: 39742 additions & 2709 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/CLAUDE-KNOWLEDGE.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,9 @@ A: Check the error location:
217217
- Callback endpoint (400 error) - Validation failed during callback
218218
- Token endpoint (400 error) - Validation failed during token exchange
219219

220+
### Q: How should connected-account OAuth access-token refresh errors be classified?
221+
A: In `apps/backend/src/oauth/providers/base.tsx`, invalid/revoked refresh-token provider errors such as `invalid_grant` return `Result.error({ type: "invalid-refresh-token", ... })` so `access-token-helpers.tsx` can invalidate that stored refresh token and try another. Transient provider/network failures such as openid-client `RPError: outgoing request timed out after 3500ms` return `Result.error({ type: "temporarily-unavailable", cause })`; the connected-account helper converts that to `OAuthProviderTemporarilyUnavailable` without invalidating the refresh token. Expected refresh outcomes should be represented in the provider return type instead of thrown as known/status errors. Refresh requests use a 6s openid-client HTTP timeout and retry transient failures once. If a retry sees `invalid_grant` after an ambiguous transient failure, keep treating it as temporarily unavailable rather than invalidating the refresh token, because the first request may have reached the provider and rotated the token before our client timed out. Sentry should capture non-revocation refresh issues (temporary provider failure, invalid client, unexpected) with provider id/class, attempts, retry count, ambiguity state, final cause, and all provider errors seen during attempts; normal revoked/expired refresh tokens should not be reported.
222+
220223
## Git and Development Workflow
221224

222225
### Q: How should you format git commit messages in this project?
@@ -356,6 +359,33 @@ Then restart the dev server. This rebuilds all packages and generates the necess
356359
## Q: How is backwards compatibility for the offer→product rename handled in the payments purchase APIs?
357360
A: API v1 requests are routed through the `v2beta1` migration. The migration wraps the latest handlers, accepts legacy `offer_id`/`offer_inline` request fields, translates product-related errors back to the old offer error codes/messages, and augments responses (like `validate-code`) with `offer`/`conflicting_group_offers` aliases alongside the new `product` fields. Newer API versions keep the product-only contract.
358361

362+
### Q: What's the reliable way to run targeted tests across backend, dashboard, stack-shared, and e2e at once?
363+
A: Run from the monorepo root with explicit file paths: `pnpm test run "<path1>" "<path2>" ...`. This works even when individual packages do not define a local `test` script. Also avoid passing an extra `run` argument to package-level `test` scripts that already execute `vitest run`.
364+
365+
### Q: What's the new Authorization header format for Stack token forwarding?
366+
A: Use `getAuthorizationHeader()`, which returns `Bearer stackauth_<base64(getAuthJson())>`. The payload encodes both `accessToken` and `refreshToken`, and request-like token stores should parse this format first, with legacy `x-stack-auth` remaining as a backward-compatible fallback.
367+
368+
### Q: What RequestLike header shapes are supported by tokenStore overrides?
369+
A: `RequestLike` accepts both `{ headers: { get(name): string | null } }` and `{ headers: Record<string, string | null> }`. Header lookup is case-insensitive for record-style headers, and supports `authorization`, `x-stack-auth`, and `cookie`.
370+
371+
### Q: Which env var should emulator onboarding URLs use for dashboard port?
372+
A: Use `EMULATOR_DASHBOARD_PORT` (default `26700`) or explicit `STACK_LOCAL_EMULATOR_DASHBOARD_URL`. Do not derive emulator URLs from `NEXT_PUBLIC_STACK_PORT_PREFIX`, because that points to the host dev environment ports (e.g. `92xx`) rather than the emulator host-forwarded ports.
373+
374+
### Q: Why does `PATCH /api/v1/internal/projects/current` fail in local emulator when updating only `onboarding_state`?
375+
A: `createOrUpdateProjectWithLegacyConfig` always called `overrideEnvironmentConfigOverride`, even when there were zero config override keys to apply. In local emulator mode, environment config overrides are intentionally blocked, so this threw `Environment configuration overrides cannot be changed in the local emulator` and returned 500. The fix is to skip `overrideEnvironmentConfigOverride` unless `configOverrideOverride` has at least one key.
376+
377+
### Q: Why might local emulator UI changes in `apps/dashboard` not appear immediately at `localhost:26700`?
378+
A: The QEMU local emulator serves the dashboard from the Docker image bundled inside the VM, not from the host repo's live source tree. Source edits in `apps/dashboard` are reflected in lint/typecheck/tests immediately, but you need an updated emulator image/runtime to see the visual change on `26700`.
379+
380+
### Q: Why can local emulator onboarding break with `ParseError` on non-`.ts` config files (e.g. `test-config.untracked`)?
381+
A: The emulator writes TypeScript-style config source (`import type ...` and `config: StackConfig`) and later evaluates it with Jiti. If the filename has a non-TS extension, Jiti may parse it as plain JS and fail. Fix by evaluating unknown extensions as TypeScript (use a `.ts` eval filename fallback) and add regression coverage for non-`.ts` config paths.
382+
383+
### Q: How should docs fetch the canonical AI setup prompt text?
384+
A: Expose an unauthenticated backend endpoint at `/api/v1/setup-prompt` that returns `getSdkSetupPrompt("ai-prompt", { tanstackQuery: false })` as plain text and sets `Cache-Control: public, max-age=60`. Mintlify docs should fetch `https://api.stack-auth.com/api/v1/setup-prompt` directly when docs and API are on different origins.
385+
386+
### Q: Can Mintlify snippets import other snippets?
387+
A: No. Keep snippet logic inline within each snippet file; avoid snippet-to-snippet imports. For setup prompt fetching, point directly to `https://api.stack-auth.com/api/v1/setup-prompt` when docs run on a different origin/port than the API.
388+
359389
## Q: How does `/api/v1/ai/query/generate` reject invalid AI tool names?
360390
A: Invalid `tools` entries are rejected by `requestBodySchema` in `apps/backend/src/lib/ai/schema.ts` via `yupString().oneOf(TOOL_NAMES)`, so the endpoint returns a structured `SCHEMA_ERROR` object mentioning `body.tools[n]` rather than a custom `"Invalid tool names"` string from handler logic.
361391

@@ -364,3 +394,15 @@ A: The `/api/v1/internal/metrics` response now intentionally includes `analytics
364394

365395
## Q: Why can environment config override writes fail with a product/product-line customer type warning after creating a preview project?
366396
A: The environment override endpoint validates the new environment override against the rendered branch config. Preview dummy payments data must therefore be internally coherent: products assigned to a product line need the same `customerType` as that product line, otherwise unrelated environment patches can fail with warnings like `Product "growth" has customer type "user" but its product line "workspace" has customer type "team"`.
397+
398+
## Q: Why can `pnpm run dev` fail with `ERR_MODULE_NOT_FOUND` for `@stackframe/stack/dist/esm/index.js` during OpenAPI docs generation?
399+
A: Root `dev` starts the OpenAPI docs watcher at the same time as package `dev` watchers. If a package `dev` script removes `dist` before `tsdown --watch` recreates it, the docs generator can import `apps/backend/src/stack.tsx` while `@stackframe/stack`'s ESM entrypoint is temporarily missing. Package watch scripts should update `dist` in place, and eager generators should wait for package imports to resolve before running.
400+
401+
## Q: How do SDK source tests replace the compile-time client version sentinel?
402+
A: `packages/template/vitest.config.ts` installs a Vite transform plugin for Vitest that replaces `STACK_COMPILE_TIME_CLIENT_PACKAGE_VERSION_SENTINEL` with `js <package-name>@<version>` from the local package.json. Keep the plugin in `packages/template` so `pnpm pre`/`scripts/generate-sdks.ts` propagates it to `packages/js`, `packages/react`, and `packages/stack`; otherwise tests importing `common.ts` throw `Client version was not replaced` before test collection.
403+
404+
## Q: How does the Mintlify apps sidebar filter stay in sync with theme changes?
405+
A: `docs-mintlify/apps-sidebar-filter.js` injects the Apps filter with inline styles, so the MutationObserver must reapply `applySidebarAppsFilterTheme` when an existing input is found. Theme detection should handle both `html.dark` and `data-theme="dark"` signals.
406+
407+
## Q: How should `StackAssertionError` preserve an underlying thrown error?
408+
A: Pass the underlying error as the `cause` property in the second argument. The `StackAssertionError` constructor only forwards `cause` into `ErrorOptions`, so storing a caught error under an `error` property captures it as ordinary metadata instead of preserving the error cause chain.

.github/workflows/db-migration-backwards-compatibility.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,16 @@ jobs:
169169
wait-for: 30s
170170
log-output-if: true
171171

172+
- name: Start stack-mcp in background
173+
uses: JarvusInnovations/background-action@v1.0.7
174+
with:
175+
run: pnpm run start:mcp --log-order=stream &
176+
wait-on: |
177+
http://localhost:8144/health
178+
tail: true
179+
wait-for: 30s
180+
log-output-if: true
181+
172182
- name: Start stack-dashboard in background
173183
uses: JarvusInnovations/background-action@v1.0.7
174184
with:
@@ -366,6 +376,16 @@ jobs:
366376
wait-for: 30s
367377
log-output-if: true
368378

379+
- name: Start stack-mcp in background
380+
uses: JarvusInnovations/background-action@v1.0.7
381+
with:
382+
run: pnpm run start:mcp --log-order=stream &
383+
wait-on: |
384+
http://localhost:8144/health
385+
tail: true
386+
wait-for: 30s
387+
log-output-if: true
388+
369389
- name: Start stack-dashboard in background
370390
uses: JarvusInnovations/background-action@v1.0.7
371391
with:

.github/workflows/e2e-api-tests-local-emulator.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,15 @@ jobs:
119119
tail: true
120120
wait-for: 30s
121121
log-output-if: true
122+
- name: Start stack-mcp in background
123+
uses: JarvusInnovations/background-action@v1.0.7
124+
with:
125+
run: pnpm run start:mcp --log-order=stream &
126+
wait-on: |
127+
http://localhost:8144/health
128+
tail: true
129+
wait-for: 30s
130+
log-output-if: true
122131

123132
- name: Start stack-dashboard in background
124133
uses: JarvusInnovations/background-action@v1.0.7

.github/workflows/e2e-api-tests.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,15 @@ jobs:
125125
tail: true
126126
wait-for: 30s
127127
log-output-if: true
128+
- name: Start stack-mcp in background
129+
uses: JarvusInnovations/background-action@v1.0.7
130+
with:
131+
run: pnpm run start:mcp --log-order=stream &
132+
wait-on: |
133+
http://localhost:8144/health
134+
tail: true
135+
wait-for: 30s
136+
log-output-if: true
128137
- name: Start stack-dashboard in background
129138
uses: JarvusInnovations/background-action@v1.0.7
130139
with:

.github/workflows/e2e-custom-base-port-api-tests.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,15 @@ jobs:
118118
tail: true
119119
wait-for: 30s
120120
log-output-if: true
121+
- name: Start stack-mcp in background
122+
uses: JarvusInnovations/background-action@v1.0.7
123+
with:
124+
run: pnpm run start:mcp --log-order=stream &
125+
wait-on: |
126+
http://localhost:6744/health
127+
tail: true
128+
wait-for: 30s
129+
log-output-if: true
121130
- name: Start stack-dashboard in background
122131
uses: JarvusInnovations/background-action@v1.0.7
123132
with:

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ To see all development ports, refer to the index.html of `apps/dev-launchpad/pub
7474
- Always run typecheck, lint, and test to make sure your changes are working as expected. You can save time by only linting and testing the files you've changed (and/or related E2E tests).
7575
- The project uses a custom route handler system in the backend for consistent API responses
7676
- When writing tests, prefer .toMatchInlineSnapshot over other selectors, if possible. You can check (and modify) the snapshot-serializer.ts file to see how the snapshots are formatted and how non-deterministic values are handled.
77-
- Whenever you learn something new, or at the latest right before you call the `Stop` tool, write whatever you learned into the ./claude/CLAUDE-KNOWLEDGE.md file, in the Q&A format in there. You will later be able to look up knowledge from there (based on the question you asked).
77+
- Whenever you learn something new, or at the latest right before you call the `Stop` tool, write whatever you learned into the .claude/CLAUDE-KNOWLEDGE.md file, in the Q&A format in there. You will later be able to look up knowledge from there (based on the question you asked).
7878
- Animations: Keep hover/click transitions snappy and fast. Don't delay the action with a pre-transition (e.g. no fade-in when hovering a button) — it makes the UI feel sluggish. Instead, apply transitions after the action, like a smooth fade-out when the hover ends.
7979
- Whenever you make changes in the dashboard, provide the user with a deep link to the dashboard page that you've just changed. Usually, this takes the form of `http://localhost:<whatever-is-in-$NEXT_PUBLIC_STACK_PORT_PREFIX>01/projects/-selector-/...`, although sometimes it's different. If $NEXT_PUBLIC_STACK_PORT_PREFIX is set to 91, 92, or 93, use `a.localhost`, `b.localhost`, and `c.localhost` for the domains, respectively.
8080
- To update the list of apps available, edit `apps-frontend.tsx` and `apps-config.ts`. When you're tasked to implement a new app or a new page, always check existing apps for inspiration on how you could implement the new app or page.

apps/backend/.env

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ STACK_SEED_INTERNAL_PROJECT_USER_EMAIL=# default user added to the dashboard
1414
STACK_SEED_INTERNAL_PROJECT_USER_PASSWORD=# default user's password, paired with STACK_SEED_INTERNAL_PROJECT_USER_EMAIL
1515
STACK_SEED_INTERNAL_PROJECT_USER_INTERNAL_ACCESS=# if the default user has access to the internal dashboard project
1616
STACK_SEED_INTERNAL_PROJECT_USER_GITHUB_ID=# add github oauth id to the default user
17-
STACK_SEED_INTERNAL_PROJECT_PUBLISHABLE_CLIENT_KEY=# default publishable client key for the internal project
18-
STACK_SEED_INTERNAL_PROJECT_SECRET_SERVER_KEY=# default secret server key for the internal project
17+
STACK_INTERNAL_PROJECT_PUBLISHABLE_CLIENT_KEY=# default publishable client key for the internal project
18+
STACK_INTERNAL_PROJECT_SECRET_SERVER_KEY=# default secret server key for the internal project
1919
STACK_SEED_INTERNAL_PROJECT_SUPER_SECRET_ADMIN_KEY=# default super secret admin key for the internal project
2020

2121
# OAuth mock provider settings
@@ -34,6 +34,8 @@ STACK_SPOTIFY_CLIENT_SECRET=# client secret
3434

3535
STACK_ALLOW_SHARED_OAUTH_ACCESS_TOKENS=# allow shared oauth provider to also use connected account access token, this should only be used for development and testing
3636

37+
STACK_DISABLE_PLAN_LIMITS=# set to "true" to bypass enforcement of Stack Auth's own internal-tenancy plan limits (analytics_events, session_replays, emails_per_month, dashboard_admins seat cap, auth_users soft cap, analytics_timeout_seconds). Default unset/false preserves enforcement. Intended as a temporary cutover safety net while the plan-limits infrastructure rolls out — customer projects' own item APIs are unaffected by this flag.
38+
3739
# Email
3840
# For local development, you can spin up a local SMTP server like inbucket
3941
STACK_EMAIL_HOST=# for local inbucket: 127.0.0.1

apps/backend/.env.development

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ STACK_SEED_INTERNAL_PROJECT_ALLOW_LOCALHOST=true
1313
STACK_SEED_INTERNAL_PROJECT_OAUTH_PROVIDERS=github,spotify,google,microsoft
1414
STACK_SEED_INTERNAL_PROJECT_USER_GITHUB_ID=admin@example.com
1515
STACK_SEED_INTERNAL_PROJECT_USER_INTERNAL_ACCESS=true
16-
STACK_SEED_INTERNAL_PROJECT_PUBLISHABLE_CLIENT_KEY=this-publishable-client-key-is-for-local-development-only
17-
STACK_SEED_INTERNAL_PROJECT_SECRET_SERVER_KEY=this-secret-server-key-is-for-local-development-only
16+
STACK_INTERNAL_PROJECT_PUBLISHABLE_CLIENT_KEY=this-publishable-client-key-is-for-local-development-only
17+
STACK_INTERNAL_PROJECT_SECRET_SERVER_KEY=this-secret-server-key-is-for-local-development-only
1818
STACK_SEED_INTERNAL_PROJECT_SUPER_SECRET_ADMIN_KEY=this-super-secret-admin-key-is-for-local-development-only
1919

2020
STACK_OAUTH_MOCK_URL=http://localhost:${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}14
@@ -43,6 +43,12 @@ STACK_SPOTIFY_CLIENT_SECRET=MOCK
4343

4444
STACK_ALLOW_SHARED_OAUTH_ACCESS_TOKENS=true
4545

46+
# Default to enforcing plan limits in local dev so behavior matches prod.
47+
# Flip to "true" to bypass every Stack-Auth-internal plan-limit enforcement
48+
# site (e.g. session_replays, analytics_events, emails_per_month). See
49+
# apps/backend/src/lib/plan-entitlements.ts:arePlanLimitsEnforced.
50+
STACK_DISABLE_PLAN_LIMITS=false
51+
4652
STACK_DATABASE_CONNECTION_STRING=postgres://postgres:PASSWORD-PLACEHOLDER--uqfEC1hmmv@localhost:${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}28/stackframe
4753
STACK_DATABASE_REPLICA_CONNECTION_STRING=postgres://postgres:PASSWORD-PLACEHOLDER--uqfEC1hmmv@localhost:${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}34/stackframe
4854
STACK_DATABASE_REPLICATION_WAIT_STRATEGY=pg-stat-replication

apps/backend/package.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@stackframe/backend",
3-
"version": "2.8.87",
4-
"repository": "https://github.com/stack-auth/stack-auth",
3+
"version": "2.8.88",
4+
"repository": "https://github.com/hexclave/stack-auth",
55
"private": true,
66
"type": "module",
77
"scripts": {
@@ -87,7 +87,6 @@
8787
"@stackframe/stack-shared": "workspace:*",
8888
"@upstash/qstash": "^2.8.2",
8989
"@vercel/functions": "^2.0.0",
90-
"@vercel/mcp-adapter": "^1.0.0",
9190
"@vercel/otel": "^1.10.4",
9291
"@vercel/sandbox": "^1.2.0",
9392
"ai": "^6.0.0",
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ALTER TABLE "Project"
2+
ADD COLUMN "onboardingState" JSONB;

0 commit comments

Comments
 (0)