@dfosco/statefully
v0.2.0
Published
Portable prototype state runtime with React bindings and a Vite data plugin
Readme
Statefully
Statefully is a portable prototype state runtime. The core owns data resolution, session state, typed knobs, theme primitives, and optional history. React bindings are separate. The Vite plugin discovers only the roots passed to it.
Install @dfosco/statefully in a React and Vite application. The package provides a framework-neutral core, optional React bindings, and an opt-in Vite plugin for discovering prototype data.
Standalone Vite setup
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'
import statefully from '@dfosco/statefully/vite'
export default defineConfig({
plugins: [statefully({ roots: ['source'] }), react()],
})Import the virtual module once before mounting the provider. It installs the discovered index into the compatibility default store.
import 'virtual:statefully-data-index'
import { PrototypeProvider } from '@dfosco/statefully/react'
root.render(
<PrototypeProvider prototype="Dashboard" flow="Dashboard/default">
<Dashboard />
</PrototypeProvider>,
)The old virtual:prototype-data ID and @dfosco/statefully/vite-plugin entry remain aliases for source migration.
Data files
By default, the plugin scans only configured roots. State files use .flow.json, .scene.json, .object.json, .record.json, .prototype.json, and .component.json; .jsonc is also supported. A root descriptor may specify path, scope, identityRoot, and identityProfile. The hypercanvas identity profile recognizes .folder organization and nested prototype paths.
Files inside prototypes/<name>/ are scoped to that prototype. Files elsewhere in a standalone root use their basename as a global name. _-prefixed path segments are ignored. drafts/ files are visible in development and omitted in production. Duplicate identities fail production builds; development returns structured diagnostics and indexes valid files.
Supported template variables in string values are ${currentDir}, ${currentProto}, and ${currentProtoDir}. $ref resolves objects by name. $global deep-merges shared objects into a flow, with flow values taking precedence.
Runtime and React APIs
import { createPrototypeStore, createSessionStore, createKnobStore } from '@dfosco/statefully/core'import {
PrototypeProvider,
useFlowData,
useObject,
useRecords,
useRecord,
useRouteRecord,
useOverride,
useKnob,
useKnobValue,
} from '@dfosco/statefully/react'createPrototypeStore(index) provides an isolated index with atomic replaceIndex and subscriptions. Returned data is cloned. PrototypeProvider accepts a store or index, explicit prototype/dataset/flow values, a route binding, and optional session/theme adapters. It does not infer router semantics unless configured to select flows from the route.
useRecord(name, id) matches an explicit record ID. useRouteRecord(name, { param, field }) handles a route parameter. useOverride returns [value, setValue, clearValue]; URL values remain strings. useKnob returns [value, setValue, clearValue]; useKnobValue returns the scalar value. Route-scoped knob keys use knob.<route>.<id>.
Core module imports do not read browser globals or install listeners. Create a session store to bind a window, storage object, and namespace. Theme DOM and persistence behavior is explicit. installHistorySync is opt-in and returns cleanup.
Entrypoints
@dfosco/statefullyand/core: framework-neutral public runtime./react: React provider, hooks, and optionalKnobPanelUI./vite: standalone Vite discovery and plugin./vite-plugin: legacy alias for/vite./initializer: explicit project scaffold utility.
See docs/integration.md for core, React, and Vite usage. docs/migration-hypercanvas.md describes the opt-in adapter boundary for Hypercanvas.
