-
Notifications
You must be signed in to change notification settings - Fork 0
CORS
Source of truth:
docs/cors.md· ADR:ADR-0022
First-party, app-level, opt-in CORS. The safe default is no cross-origin access: an app without a cors key sends no CORS headers and behaves byte-identically to a pre-CORS app. When configured, the policy is enforced at the compile boundary — Bun's native router stays authoritative, no routes are synthesized.
import { defineApp, json, route } from "lugas";
defineApp({
cors: {
origin: "https://app.example.com", // exact string, allowlist, "*", or callback
methods: ["GET", "POST"], // optional; default GET/HEAD/POST/PUT/PATCH/DELETE
allowedHeaders: ["Content-Type"], // optional; default reflects the request
exposedHeaders: ["X-Request-Id"], // optional
credentials: true, // optional; never with "*"
maxAge: 600, // optional preflight cache (seconds)
},
routes: { /* ... */ },
});-
Exact string — echoed when the request
Originmatches exactly (scheme + host + port). -
Allowlist array —
"*"cannot be mixed with concrete origins (LUGAS_CORS_002). -
Wildcard
"*"— sendsAccess-Control-Allow-Origin: *; never combinable withcredentials: true(LUGAS_CORS_003). -
Callback
(origin, request) => boolean | string(sync or async) —trueechoes the request origin; a non-empty string echoes that canonical origin;false(or"", or"*"under credentials) denies.
Denied requests — and requests without an Origin header — receive no Access-Control-* headers. The response keeps its normal status; the browser enforces the denial.
-
Vary: Originon every response — merged with any handler-setVary, never duplicated — including denies,onError500s, not-found 404s, and startup-gate 503s. -
Preflight interception:
OPTIONS+Access-Control-Request-Methodanswered204before application handlers (including paths with no declaredOPTIONS— they reach the wrapped fallback through Bun's own routing). Allowed → full header set; denied →204withVaryonly. PlainOPTIONSpasses through with headers applied. -
Coverage:
route()descriptors, native function handlers (path-level and per-method), any-method entries, modules, the not-found fallback, and a user-suppliedserve({ fetch }).
Static route values bypass the framework response pipeline, so configuring cors alongside them is rejected at startup (LUGAS_CORS_004): static Response values, Bun.file()/Blob values, { dir } mounts, and assets configuration. Convert such routes to handlers or serve static content from a separate app without cors.
-
lugas-manifest-v1records routing, not policy — CORS adds no routes and changes no facts. - Preflight
Access-Control-Allow-Methodsreflects the configured/default method set, not the target route's own methods (authorization is unaffected). - Pairs with Server-Sent Events (stream responses carry the policy) and the
lugas/client/browserartifact (same-origin lane; cross-origin frontends need this policy).
Behavior and diagnostics are pinned by tests/cors/cors.test.ts and tests/cors/config.test.ts; evidence in docs/reports/issues/M8-001.md.
LugasJS Wiki
Server facilities
- CORS
- Server-Sent Events
- Structured Logging
- OpenAPI and Scalar
- Drizzle
- Service Lifecycle
- Assets and Body Limits
Reference
Repository