Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .changeset/one-origin-dev-configs-5745.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
---
---

Dev/build configuration only — this publishes nothing, declared explicitly with an
empty frontmatter rather than left undeclared. No package `src/` is touched. The
files changed are `apps/console/.env.development`, `examples/console-starter/.env.*`
and `examples/console-starter/vite.config.ts`; `.env.development` is not part of
`@object-ui/console`'s published artifact (`files: ["dist"]`, built from
`.env.production`, which is untouched), and the example is private and on the
changeset `ignore` list.

Converge the dev stacks on one origin: empty `VITE_SERVER_URL` and let the Vite
proxy do the split-host hop.

Both dev env files pointed `VITE_SERVER_URL` at `http://localhost:3000` while the
page was served from `:5180` (console) and `:5173` (starter). Every client in these
apps coalesces an unset value to `''` and then builds a relative `/api/...` URL, so
an empty value routes same-origin through the dev proxy instead. `console-starter`
had no `server` block at all, so it gets the same one-stanza `/api` proxy
(`DEV_PROXY_TARGET` or `http://localhost:3000`) that `apps/console` already carries;
no port is pinned, because its README documents the example on Vite's default 5173.

This is the ruled prerequisite for the `sameOriginOnly` action-runtime default. Under
that default a non-empty `VITE_SERVER_URL` makes every relative-target `type: 'api'`
action resolve cross-origin and be fetched bare — no `Authorization`, no
`X-Tenant-ID`, no `Accept-Language` — i.e. a 401 for the standard `pnpm dev` stack.

`examples/console-starter/.env.production` is emptied too. Its committed
`https://demo.objectstack.ai` was not a deliberate choice for the example: it is the
same pre-convergence value the sibling console carried until 2026-05-24, when
`c351c9604` ("default published SPA to same-origin") cleared it there for CORS-blank
-page reasons and touched only `apps/console/.env.production`. The starter was left
behind by that commit, and as a fork-ready scaffold its committed value is the
production origin every fork inherits. Split-origin deployments inject
`VITE_SERVER_URL` at build time, the way the console's own `.env.production` already
documents.
23 changes: 19 additions & 4 deletions apps/console/.env.development
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,20 @@
# Development: connect to a local or remote ObjectStack server.
# Set VITE_SERVER_URL to your dev server (e.g. http://localhost:3000 or https://demo.objectstack.ai).
# Leave empty to use same-origin (when Console is served by the server itself).
VITE_SERVER_URL=http://localhost:3000
# Development: the Console SPA and the ObjectStack API share ONE origin.
#
# Leave VITE_SERVER_URL EMPTY. Every client in this app coalesces an unset
# value to '' and then builds a RELATIVE `/api/...` URL, which the Vite dev
# server proxies to the backend (`vite.config.ts` -> server.proxy['/api'],
# target `DEV_PROXY_TARGET` or http://localhost:3000). So the page at :5180
# fetches :5180 and the proxy does the split-host hop.
#
# Why empty rather than `http://localhost:3000`: a non-empty value makes every
# relative-target `type: 'api'` action resolve CROSS-origin, and the action
# runtime does not attach Authorization / X-Tenant-ID / Accept-Language to a
# cross-origin fetch — a 401 on the standard `pnpm dev` stack. Same-origin is
# also what `.env.production` already ships, so dev now matches prod.
#
# Pointing at a DIFFERENT backend: set DEV_PROXY_TARGET (keeps one origin) —
# DEV_PROXY_TARGET=https://demo.objectstack.ai pnpm dev
# Setting VITE_SERVER_URL to an absolute origin still works, but it opts this
# app back out of same-origin and into CORS + the 401 above.
VITE_SERVER_URL=
VITE_USE_MOCK_SERVER=false
21 changes: 17 additions & 4 deletions examples/console-starter/.env.development
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
# Development: connect to a local or remote ObjectStack server.
# Set VITE_SERVER_URL to your dev server (e.g. http://localhost:3000 or https://demo.objectstack.ai).
# Leave empty to use same-origin (when Console is served by the server itself).
VITE_SERVER_URL=http://localhost:3000
# Development: the starter SPA and the ObjectStack API share ONE origin.
#
# Leave VITE_SERVER_URL EMPTY. `src/App.tsx`, `src/main.tsx` and everything
# inside @object-ui/app-shell coalesce an unset value to '' and then build a
# RELATIVE `/api/...` URL, which the Vite dev server proxies to the backend
# (`vite.config.ts` -> server.proxy['/api'], target `DEV_PROXY_TARGET` or
# http://localhost:3000). So the page at :5173 fetches :5173 and the proxy
# does the split-host hop.
#
# Why empty rather than `http://localhost:3000`: a non-empty value makes every
# relative-target `type: 'api'` action resolve CROSS-origin, and the action
# runtime does not attach Authorization / X-Tenant-ID / Accept-Language to a
# cross-origin fetch — a 401 on the standard `pnpm dev` stack.
#
# Pointing at a DIFFERENT backend: set DEV_PROXY_TARGET (keeps one origin) —
# DEV_PROXY_TARGET=https://demo.objectstack.ai pnpm dev
VITE_SERVER_URL=
VITE_USE_MOCK_SERVER=false
18 changes: 16 additions & 2 deletions examples/console-starter/.env.production
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,18 @@
# Production: connect to the ObjectStack server.
# Set VITE_SERVER_URL to the server origin, or leave empty for same-origin.
VITE_SERVER_URL=https://demo.objectstack.ai
#
# Defaults to same-origin (empty), matching `apps/console/.env.production`.
# This is a FORK-READY SCAFFOLD: whatever is committed here is the production
# origin every fork inherits, so it must not name one specific deployment.
# A committed `https://demo.objectstack.ai` was cleared from the sibling
# console for exactly that reason — it CORS-blocked the auth / i18n /
# discovery calls and left a blank page on every host but the demo one.
#
# Deployments that genuinely need a split origin inject VITE_SERVER_URL from
# their own deploy environment at build time, rather than committing it:
#
# VITE_SERVER_URL=https://your-objectstack-host pnpm build
#
# A split origin also requires the backend to allow CORS from the SPA origin
# and to issue auth cookies as `SameSite=None; Secure`.
VITE_SERVER_URL=
VITE_USE_MOCK_SERVER=false
20 changes: 20 additions & 0 deletions examples/console-starter/vite.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,4 +60,24 @@ const workspaceAliases: Record<string, string> = {
export default defineConfig({
plugins: [react()],
resolve: { alias: workspaceAliases },
// ONE ORIGIN in dev, proxy for the split host. `.env.development` leaves
// VITE_SERVER_URL empty, so every client here builds a RELATIVE `/api/...`
// URL and this stanza forwards it to the backend. Without it an empty
// VITE_SERVER_URL would make those fetches hit the Vite dev server, which
// has no `/api` route and answers with the SPA's index.html fallback.
//
// Why not just point VITE_SERVER_URL at the backend: a cross-origin value
// makes every relative-target `type: 'api'` action resolve off-origin, and
// the action runtime does not attach Authorization / X-Tenant-ID /
// Accept-Language across origins — a 401 on the standard `pnpm dev` stack.
//
// `DEV_PROXY_TARGET` and the `/api` prefix mirror `apps/console/vite.config.ts`
// deliberately: the two dev stacks are meant to be configured the same way.
// No `port` is set — the README documents this example on Vite's default
// 5173, and pinning one would only collide with a real fork's choice.
server: {
proxy: {
'/api': { target: process.env.DEV_PROXY_TARGET || 'http://localhost:3000', changeOrigin: true },
},
},
});
Loading