Architecture Overview
StateLoom is organized as a layered monorepo. Each layer has a clear responsibility boundary, explicit dependency direction, and independent versioning under the @stateloom/* npm scope.
Layer Diagram
Dependency Rules
- Downward only: Dependencies flow strictly downward. A lower layer never imports from a higher layer.
- Core is the only shared dependency: All packages depend on
@stateloom/coreas a peer dependency. - Framework adapters peer-depend on their framework:
@stateloom/reactpeer-depends onreact,@stateloom/vueonvue, etc. - Paradigm packages are independent:
@stateloom/store,@stateloom/atom, and@stateloom/proxydo not depend on each other. - Middleware packages are independent: Each middleware package depends only on core. They do not depend on each other or on paradigm packages.
- Platform backends depend on their parent:
@stateloom/persist-redisdepends on@stateloom/persist.
Package Dependency Graph
Build Order
Turborepo resolves this automatically via "dependsOn": ["^build"]:
| Phase | Packages | Dependencies |
|---|---|---|
| 1 | @stateloom/core | None |
| 2 | store, atom, proxy | core |
| 3 | devtools, persist, tab-sync, history, immer, telemetry, server, testing | core |
| 4 | react, vue, solid, svelte, angular | core + paradigms |
| 5 | persist-redis | persist |
| 6 | examples/* | all packages |
Phases 2 and 3 execute in parallel. Turborepo's caching means only changed packages and their dependents rebuild.
Package Naming Convention
All packages are published under the @stateloom npm organization:
| Category | Pattern | Examples |
|---|---|---|
| Core | @stateloom/core | @stateloom/core |
| Paradigms | @stateloom/<paradigm> | store, atom, proxy |
| Framework adapters | @stateloom/<framework> | react, vue, solid, svelte, angular |
| Middleware | @stateloom/<feature> | devtools, persist, tab-sync, history |
| Platform backends | @stateloom/persist-<platform> | persist-redis |
| Utilities | @stateloom/<utility> | server, testing, immer, telemetry |
Monorepo Layout
stateloom/
├── packages/
│ ├── core/ # Layer 1
│ ├── store/ # Layer 2
│ ├── atom/ # Layer 2
│ ├── proxy/ # Layer 2
│ ├── react/ # Layer 3
│ ├── vue/ # Layer 3
│ ├── solid/ # Layer 3
│ ├── svelte/ # Layer 3
│ ├── angular/ # Layer 3
│ ├── devtools/ # Layer 4
│ ├── persist/ # Layer 4
│ ├── persist-redis/ # Layer 5
│ ├── tab-sync/ # Layer 4
│ ├── history/ # Layer 4
│ ├── immer/ # Layer 4
│ ├── telemetry/ # Layer 4
│ ├── server/ # Layer 4
│ └── testing/ # Layer 4
├── examples/
├── docs/
├── scripts/
└── [root config files]Reactive Data Flow
This sequence diagram shows how a signal write propagates through the system from a user action to a framework re-render:
Build Pipeline
Turborepo resolves build order automatically via "dependsOn": ["^build"]. Phases 2 and 3 execute in parallel:
Turborepo's caching means only changed packages and their dependents rebuild.
Key Interfaces
The entire architecture rests on a small number of shared interfaces:
Subscribable<T> — The Universal Contract
interface Subscribable<T> {
get(): T;
subscribe(callback: (value: T) => void): () => void;
}Every signal, computed, store, and atom implements this. Framework adapters bridge this single interface to framework-specific reactivity.
Middleware<T> — The Extension Point
interface Middleware<T> {
name: string;
init?: (api: MiddlewareAPI<T>) => void;
onSet?: (api: MiddlewareAPI<T>, next: SetFn<T>, partial: Partial<T>) => void;
onGet?: (api: MiddlewareAPI<T>, key: keyof T) => void;
onSubscribe?: (api: MiddlewareAPI<T>, listener: Listener<T>) => Listener<T>;
onDestroy?: (api: MiddlewareAPI<T>) => void;
}All cross-cutting concerns (persistence, devtools, tab-sync, history) implement this interface.
Scope — SSR Isolation
interface Scope {
fork(): Scope;
get<T>(subscribable: Subscribable<T>): T;
set<T>(signal: Signal<T>, value: T): void;
serialize(): Record<string, unknown>;
}Scopes provide per-request isolation for SSR environments.
Interface Relationships
This class diagram shows how the key interfaces connect across layers:
When to Use Each Layer
| Need | Layer | Package |
|---|---|---|
| Raw reactive primitives | Core | @stateloom/core |
| Zustand-like single-object store | Paradigm | @stateloom/store |
| Jotai-like bottom-up atoms | Paradigm | @stateloom/atom |
| Valtio-like mutable proxy | Paradigm | @stateloom/proxy |
| React hooks for stores/atoms/proxies | Framework | @stateloom/react |
| Vue composables | Framework | @stateloom/vue |
| Persist to localStorage/Redis | Middleware | @stateloom/persist + backend |
| Time-travel debugging | Middleware | @stateloom/devtools |
| Undo/redo | Middleware | @stateloom/history |
| Cross-tab sync | Middleware | @stateloom/tab-sync |
| SSR per-request isolation | Core + Adapter | @stateloom/core + framework adapter |
Cross-References
- Design Philosophy — rationale behind architectural decisions
- Layer Scoping — exact responsibility boundaries per package
- Core Design — reactive kernel internals
- Store Design — store paradigm internals
- Atom Design — atom paradigm internals
- Proxy Design — proxy paradigm internals
- Adapters Overview — framework adapter internals
- Middleware Overview — middleware system internals
- Testing Design — testing infrastructure internals