Imported from DanielFGray/fibrae (
AGENTS.md). Install upstream withnpx skills add DanielFGray/fibrae. Copyright stays with the author.
fibrae
Effect-native JSX browser renderer with SSR, SSG, routing, live SSE sync, and automatic reactivity.
Branches
develop— default branch, day-to-day developmentmain— release branch (merge develop → main triggers release-please → npm publish)smol— v4 rewrite (Effect 4.0.0-beta.8, useseffect/unstable/reactivity)
Commands
bun run build # TypeScript compile (packages/fibrae)
bun run types:check # Type check all workspaces
oxlint # Lint
cd packages/demo && bun cypress:run # E2E tests
Style
Write idiomatic Effect code. See AGENTS.md for full coding guidelines.
- FP patterns: map/reduce/pipe, not imperative loops
- Effect stdlib: forEach, all, reduce, iterate, Mailbox, RcMap — don't reinvent
- Atom APIs: prefer
Atom.get/set/update(Effect-based) over manual registry access Schema.TaggedErrorfor all errors — neverData.TaggedError, neverthrow new ErrorSchema.decodeUnknown(Effect-returning) — never sync decode variants- Pipe style, no console.log, minimize
ascasts — fix inference rather than casting - Casts belong at type-erasure boundaries in the library, never in consumer code
Key Patterns
- JSX uses classic transform with
jsxInjectfor fibrae/jsx-runtime - JSX event handlers accept both
onClickandonclick, can return Effects Layer.fresh()prevents Layer memoization sharing state across render() callsresubscribeFiber()closes alternate's componentScope to prevent subscription leaks- Event handlers can return Effects — forked with full app context via
runForkWithRuntime - Components return
VElement | Effect<VElement> | Stream<VElement> - Fiber.componentScope is
Scope.Closeable(not justScope) FiberRef.currentContextreturnsContext<never>— store and pass asContext<never>- Link uses real paths:
<Link href="/posts/42">— type-safe viaRegisteredRouter+PatternToHref - Router/RouteGroup accumulate route paths as literal types —
<Link href="/typo">is a type error RouterBuilder.group()takes RouteGroup directly (not string name) for type-safehandle()Route.match/interpolatereturn Effects —RouteErrorfor failuresRouteHandlerstores Effects with R=never (type erasure boundary inhandle())