API
Casing tells you where a decorator goes. PascalCase is a class decorator —
@Host, @StableProps. camelCase goes on a member — @state, @compute, @mounted,
@watchProp. Nothing else distinguishes them at a glance, and the two groups are used in
different places, so the convention carries its weight.
Everything every package exports. Each entry links to the page that explains it.
@ramonda/core
Classes
Component<P> | The base class. Extend it and implement render(). |
Hook<O> | State and lifecycle with no element. |
Ref<T> / createRef<T>() | Holds a real DOM node. Refs |
SAME_ITEM | The option for a lens set that replaces a list item with the same item rebuilt, so it keeps its element and its component. Lens |
merge(previous, next, identity?) | Keeps the previous value where the new one equals it, so a refetch is not a change. With identity, rows are paired across a reorder or a resize and a changed row keeps its identity. Lists |
list<T>(each, render) | Renders a list, minting identity from the items. render is a function taking one item. Lists |
@StableProps(...names) | Declares which props are values, so a caller writes the plain literal. On a hook and on a component alike. Writing a hook · A prop that is a value |
Head | Per-page <title> and <meta>. Head and metadata |
Timeout / Interval | A scheduled call the app starts and the framework clears: this.use(Timeout, () => ({ run })), then start(ms) and stop(). One instance is one timer; start returns whether it started, and teardown clears it. Timers |
Portal | Renders a subtree into a DOM target elsewhere — e.g. document.head. Portal |
portalTarget(name) | Names a portal target outside the app's root, so it exists on the server too. PORTAL_TARGET_ATTR marks the container a shell emits. Portal |
AsyncLoad | Loads a module the first time it is rendered. Lazy loading |
ErrorBoundary | Catches what a subtree throws while rendering. Error boundaries |
createContext(default, options?) | Returns [Provider, Consumer]. Options: label names the pair in devtools; optional: true says the default is a real answer, so a consumer with no provider above it is not reported. Context |
Entry points
bootstrap(vnode, element) | Mounts an app. |
unmount(element) | Tears down everything bootstrap mounted, running @destroyed throughout. Removing the element is not a substitute. |
__h(tag, props, ...children) | The vnode factory, for a tag that is a value or children you have to spread. Compiled JSX does not call it — that goes through the @ramonda/core/jsx-runtime import the compiler writes. JSX |
Server rendering
renderToString(vnode, opts?) | An app → HTML, awaiting async lifecycle. opts.request makes it a per-request render so requestContext() returns real values. Details |
renderPage(vnode) | The same, plus { title, head }. Head |
renderStatic(vnode, url) | A build-time render with the request context poisoned — returns { html } to bake or { blockedBy } if the route read per-request data. |
renderDocument(page, options?) | Wraps a rendered page in a complete document. Static builds |
hydrateRoot(vnode, element) | Adopts the server's DOM instead of rebuilding it. |
ServerRedirect | Thrown by renderToString when a render asks to redirect; catch it and answer with a 302. Guards |
captureServerRedirect() | Low-level hook to record a server redirect for the current render. Guards |
requestContext() | Per-request data — url, cookies, headers, get(key). Read it synchronously: on the server the scope is cleared before the render's first await (RMD053). Per-request reads throw during a static build (that guards prerendering). |
requestKey<T>(label) | Declares a typed per-request slot for requestContext().get. |
seedRequest(key, value) | Server-only: fills a slot for the render already under way. Values known beforehand go to renderToString as request.values, keyed by the key itself. Details |
RequestReadDuringBuild | Thrown when per-request data is read during a static build — the route cannot be prerendered. |
ServerRequestInit.values takes pairs of KEY and value — an array literal is the natural spelling,
since new Map([…]) cannot infer a heterogeneous one. A label is never written twice.
The types beside them: RenderToStringOptions and ServerRequestInit are what renderToString takes,
StaticRender is what renderStatic returns ({ html } or { blockedBy }), and the per-request family
is RequestContext with RequestCookies, RequestMode, plus RequestKey<T> and RequestKeyOptions
for a declared slot.
Decorators — state
@state | Turns a field into a signal. State |
@compute | Caches a derived value; method or getter. Derived values |
@persist | Marks a non-reactive field as part of the hydration payload. env |
@memoized | Caches a function by its arguments, per instance. |
Decorators — lifecycle
@created(options?) | Runs while building; no DOM yet. Lifecycle |
@mounted(options?) | Runs once the element is in the document. Returning a promise makes a server render wait. Async on the server |
@updated | Runs after every commit after the first, with the new DOM in place. No deps, no previous values, no cleanup. Lifecycle |
@destroyed | Runs on teardown, while state is still readable. |
createSubscriptionDecorator(name, connect) | Your own subscription decorator: connect after the commit, and what it returns is the cleanup. Subscriptions |
@created, @mounted and @destroyed take { env: "client" | "server" | "shared" }, and shared is
the default. Which to use — @updated has no env, because a server render commits
once and so never produces the update it reacts to.
That options bag is LifecycleOptions, and the side itself is RenderEnv — the argument a lifecycle
method receives, so a shared method can branch without a typeof window check, which is unreliable
anyway under a server DOM shim.
Which decorator runs where, works on what, and may repeat: the decorator table.
Decorators — reacting
@watchProp(selector) | Runs when one prop changes, before the render. Props |
@ShouldUpdateOnPropsChange | A CLASS decorator taking (self, previous, next) => boolean: gates whether new props from the parent are taken up (their signals update + a render). For ignoring a change that really happened — a prop merely rebuilt with equal contents is @StableProps' job. Components only. |
@deferHydration | Keeps the server's markup while a promise settles. Async on the server |
@catchError | Declares the method that handles an error thrown anywhere below this component. Return false to decline it and let the next one above take over. Components only; one per class (RMD032). |
Decorators — the DOM
@Host(tag, props?) | The element a component is. tag may be a callback of props. Components only, once per class. The host element |
@onElement(type, options?) | Listener on the component's host. Components only — a hook has no element. Events |
@onWindow(type, options?) / @onDocument(...) | Listeners on window / document. Work on a Hook too. |
@interval(ms) / @timeout(ms) | Timers cleared on unmount. Client only; work on a Hook. Timers |
Building your own
createSubscriptionDecorator(name, connect, validate?) | Turns "subscribe, and unsubscribe on unmount" into a decorator. Your own decorators |
Development switches
configureDev({ strictRender }) | Turns off the double render behind RMD020. A no-op in production. |
INSPECT | A symbol. Define [INSPECT]() on a component or hook and the devtools panel shows what it returns, under Holds. Devtools |
Types
All 32, grouped by what they belong to. The server and per-request ones are explained under Server rendering; the rest are the shape of whatever they are named for.
Markup — VNode · RamondaNode · ComponentChild · ComponentClassKind
Hooks and options — HookMeta · HeadOptions · MetaTag · LinkTag · PortalProps · PortalTarget · Each · ItemRender · ItemComponent · Identity ·
AsyncLoadProps · AsyncLoadFailure · Lazy · ContextOptions · ErrorBoundaryFallbackProps
Refs and subscriptions — RefCallback · RefTarget · SubscriptionOwner · Disconnect
Lifecycle — LifecycleOptions · RenderEnv
Server rendering — RenderedPage · DocumentOptions · RenderToStringOptions ·
ServerRequestInit · StaticRender · RequestContext · RequestCookies · RequestKey ·
RequestKeyOptions · RequestMode
Development — DevFlags
HookMeta is the third argument to this.use() — what a use() says about a hook rather than
what it passes into one. One field today, label, which devtools adds to the hook's class name:
Form (Sign Up). Development-only, and the hook never sees it. See
naming a hook.
@ramonda/router
Router | A hook on the app root; owns the store, adds no element. Also exposes the Navigator surface (minus params()). Setup |
RouteOutlet | Renders the matched route. |
Navigator | pathname · params<T>() · searchParams · hashTags · push · replace · updateSearchParams · updateHashTags · back · forward. Reading the URL |
Link | A real <a href> that intercepts a plain left click. Links |
createRoutes(map) | Compiles a route table once, capturing its paths in the type. Call it at module scope. |
createRouter(routes) | Returns { Router, RouteOutlet, Navigator, Link, route } bound to the table, so <Link href> is type-checked. Setup |
route(pattern, params) | Builds a :param href — the only way to make one; params are typed. Links |
routePaths(config, extra?) | { paths, needsData } for a static build. Static builds |
matchRoute · matchParams · matchCompiled | Matching, for tooling. |
parseUrl · parseUrlString · buildUrl · sanitizeHref | URL helpers. |
Types: RouteConfig · PathOf · Href · TypedRouterKit · TypedLinkProps · TypedNavigator ·
RouteParams · RoutePaths · RouterState · RouterNavigator · NavigateOptions ·
PartialNavigateOptions · SearchParamsUpdater · HashTagsUpdater · HashTag · StateUpdater ·
RouteOutletProps · LinkProps
@ramonda/router/server
Server-only — never imported from client code. See rendering modes.
defineServer(routes, config, opts?) | Per-route render modes, keyed exhaustively by path. config — { prerender?, revalidate? } per route; opts.defaultMode. |
routePlan(server, paths?) | Partitions the routes into { static, isr, server, needsData } for the build. static holds paths, never patterns: a route with a :param marked prerender needs its concrete paths in paths, and throws without them. Rendering modes |
createIsrCache({ plan, store, render, maxPages?, onerror?, now? }) | The ISR cache. serve(path) gives { html, mode } — fresh, stale-while-revalidate, or a cold render — and undefined for a path that is not an ISR route. A route with a :param caches each page separately and needs maxPages, which drops the page nobody has asked for longest. Where ISR pages are kept |
memoryStore() | Keeps baked pages in this process. One instance, or development. |
fileStore({ dir }) | Keeps baked pages in a directory: survives a restart, shared by instances that mount it. Writes atomically. |
Types: ServerRoute · ServerConfig · ServerOptions · ServerRoutes · RoutePlan ·
IsrCache · IsrCacheOptions · IsrStore · IsrEntry · IsrPage · IsrMode · FileStoreOptions
@ramonda/query
Cached, deduplicated, race-free async state. Async data
QueryClientProvider | A hook on the app root; owns the cache and publishes it, adds no element. Takes { client?, defaults? }. Setup |
Query | Reads one query. Write Query<TData> to type its callbacks — see typing. status · data · error · isPending · isFetching · isSuccess · isError · failureCount · updatedAt · isRestored · result · refetch(). Queries |
InfiniteQuery | Reads one paginated query, pages under one key. pages · pageParams · fetchNextPage() · fetchPreviousPage() · hasNextPage · hasPreviousPage · isFetchingNextPage · isFetchingPreviousPage · maxPages, plus everything Query has. Infinite queries |
Mutation | Writes. mutate(vars) · mutateAsync(vars) · reset() · cancel() · isIdle · isPending · isSuccess · isError · data · error. Mutations |
QueryClientAccess | A hook that hands you the client, for imperative work. Reaching the cache |
QueryClient | The cache itself: fetch · prefetch · setData · peek · getEntry · all · isStale · invalidate · cancel · remove · sweep · subscribe · dehydrate · hydrate |
ServerQueryError | What a failure from a server render arrives as. A real Error. On the server |
hashKey(key) · keyStartsWith(key, prefix) | Key hashing and prefix matching, for tooling. |
Types: QueryKey · QueryStatus · FetchStatus · FetchContext · QueryFetcher ·
QueryProps · QueryResult · QuerySnapshot · QueryEntry · QueryBehaviour ·
ObserverBehaviour · QueryDefaults · RefetchOnMount · RetryPolicy ·
RetryDelayPolicy · QueryEvent · QueryObserver · QueryClientOptions ·
QueryClientProviderProps · MutationProps · MutationContext · MutationStatus ·
InfiniteQueryProps · InfiniteData · PageContext ·
Rollback · DehydratedQuery · DehydratedState · SerializedError
@ramonda/form
Typed field paths, Standard Schema validation, and array rows that keep their identity. Forms
Form | A hook — this.use(Form<typeof schema>, () => ({ schema, defaultValues, onSubmit })). Adds no element; the <form> stays your JSX. fields · values · formErrors · isValid · isDirty · isSubmitting · submitCount · submit(event?) · reset(values?) · setError(path, message). Your first form |
defaultValues may move after the form exists — an untouched field takes the new value, an edited one
keeps what was typed. Editing a record you had to fetch
Every field is reached by property access and its API sits behind $:
f.address.street.$.value. Fields
FieldApi | What every field has: value · error · errors · touched · dirty · path · name · set(next) · reset() · at(key). |
LeafApi | A field holding a single value. Adds bind. Binding an input |
ArrayApi | A field holding a list. Adds length · rows · append(item) · insert(at, item) · remove(at) · move(from, to). Array fields |
Row | One member of rows: id · index · field. The id is what keeps a row stable as it moves. |
Field | A hook, for a component that watches ONE field — this.use(Field<string>, () => ({ of: this.props.of })). Answers everything FieldApi and LeafApi do, plus the list members. Required for a field in its own component: a field node is one cached object for the form's life, so without this the component's props never change and it never re-renders. Also what makes an edit wake one field rather than the form. A field in its own component |
FormState | A hook, for a component that watches the FORM rather than a field — this.use(FormState), no props. isValid · isDirty · isSubmitting · submitCount · formErrors · submit(event?) · reset(). Wakes only when a fact it reads actually MOVED, so a save button sleeps through typing that does not change the answer. The form publishes itself on the context, so it works at any depth. A button that watches the form |
Types: FieldNode · FieldTarget · LeafNode · ObjectNode · ArrayNode · FormProps · ValidateOn ·
Bind · CommonBind · TextBind · NumberBind · CheckboxBind · DateBind · Collision ·
InferIn · InferOut · StandardSchemaV1 · StandardResult · StandardIssue
StandardSchemaV1 is the Standard Schema interface, vendored so
the package depends on no validator. Anything implementing it works unchanged — bguard, zod,
valibot, arktype. Validation
@ramonda/form/bguard
What Standard Schema cannot express. bguard is an optional peer dependency, and the main entry never reaches this module. The bguard submodule
htmlConstraints(schema) | Returns a lookup by field path giving required · minlength · maxlength · pattern · min · max · type, derived from the schema. Answers are cached, so the same path is the same object every render. HTML attributes |
unknownRefPaths(schema, values) | Every ctx.ref path that names no field — the typo that otherwise passes silently for ever. Belongs in a test. Cross-field rules |
Types: HtmlConstraints · UnknownRef
@ramonda/lens
Immutable updates by path. Zero dependencies, usable on its own. Immutable updates
focusOn(root) | Starts a path into root. Nothing runs until a terminal operation does. |
Walking — details
.get(key) | Descends into a property. __proto__, constructor and prototype are refused. |
.at(index) | Descends into one element. Negative counts from the end. |
.where(pred) | Descends into every element the predicate accepts. Narrow explicitly: where<T>(…). |
Writing — each returns the new root. Details
.set(value, opts?) | Replaces the focused value. An equal value copies nothing. Creates an absent key. opts.keepSymbols carries hidden symbols off the old value — true, or exactly the ones listed. |
.update(fn) | Replaces it with fn(current). |
.merge(partial) | Copies the focused object and assigns over it. Does not create a missing object. |
.remove() | Drops the property or element from its container. |
.push(...items) | Appends to the focused array. A missing or null array counts as an empty one. |
.insert(i, ...items) | Inserts at a position. i === length appends; negative counts from the end. |
.and(...branches) | Forks the path: several edits, one walk of the shared prefix. |
Reading
.value() | The first focused value, or undefined. |
.values() | Every focused value. |
Types: Focus · FocusCommon · FocusArray · ElementOf · KeepSymbols
@ramonda/build
The three bundler settings an app needs, so the app names none of them. Configuring your build
RAMONDA_TRANSFORM | The three settings themselves — jsx, jsxImportSource, target — for a bundler with no adapter here. |
lowersDecorators(target) | Whether a target compiles the decorators away. A list lowers if even one entry is something other than esnext. |
PUBLIC_ENV_PREFIX | "RAMONDA_PUBLIC_" — the prefix that marks an environment variable safe to ship to the browser. |
publicEnv(env) | The variables in env that may travel, and nothing else. |
@ramonda/build/vite
ramonda() | The Vite plugin. Fills in what the config left unsaid, and refuses what disagrees. |
@ramonda/build/esbuild
ramondaOptions | The settings, ready to spread into a build you call yourself. |
ramondaDefine(own?) | The define entries that make import.meta.env.RAMONDA_PUBLIC_* work, merged with your own. Call it — a plain define after the spread would drop them. |
ramonda() | The same settings as a plugin, for a build assembled somewhere you cannot reach. |
@ramonda/check
The analyzer behind ramonda-check, as an import. Checking your app
analyzeProject(tsconfig) | Reads a project and answers with everything below — the context issues, every rule's findings, and the graph they are computed from. |
ruleCatalogue() | Every rule as four strings: its id, its severity, when it reports, and the runtime diagnostic that reports the same fault. The rule tables on the check page are built from this. |
splitOf(graph) | What loads before anything, what each split point brings, and what they share. |
filesOf(declarations) | How many files a set of declarations lives in. |
diffGraphs(before, after) | What moved between two graphs — nodes, edges, and the size of the first payload. |
refuseToDiff(before, after) | Why two graphs cannot be compared, or undefined when they can. |
typescript is a peer dependency: the analyzer uses your compiler, so it reads your syntax and
your config rather than guessing at them.
The types
AnalyzeResult | Everything one run found. |
Findings | What each rule found, keyed by the rule's id and typed as that rule's own issue. |
RuleSummary | One rule as ruleCatalogue() describes it. |
ContextIssue | A consumer with no provider above it — the check this package was written for. |
ComponentGraph, GraphNode, GraphEdge, Where | What can mount what, and where each fact was written. |
Split, SplitPoint | What splitOf answers. |
GraphDiff | What diffGraphs answers. |
Generated by scripts/build-rule-tables.mjs from what the package exports — edit the rule, not this.
Every rule publishes its own issue shape, named for the rule: AccessKeyIssue,
AriaHiddenOnFocusableIssue, AriaValueIssue, AriaWithNoSubjectIssue, ArrowFieldIssue,
AsyncRenderIssue, AttributeThatDoesNothingIssue, BrowserUrlIssue,
CachedReadOfAPlainFieldIssue, ClassInsteadOfClassNameIssue, ClickWithNoKeyboardPathIssue,
ClientOnlyRequestReadIssue, ClockReadWhileRenderingIssue, ComputeTakesNoArgumentsIssue,
ContextConsumedAboveItsProviderIssue, ControlWithNoLabelIssue, DecoratorThatAddsNothingIssue,
DevGuardAsAnExpressionIssue, DomWriteIssue, DuplicateDecoratorIssue, DuplicateIdIssue,
DuplicateKeyAmongSiblingsIssue, EmptyHeadingOrLinkIssue, FragmentLinkToNowhereIssue,
FreshObjectInHookPropsIssue, FreshObjectInPropsIssue, FreshValueFromAWatchSelectorIssue,
HeadTagsCollideIssue, HeadingSkipsALevelIssue, IndexAsKeyIssue,
InteractiveInsideInteractiveIssue, IntervalWithNoCleanupIssue, LateRequestReadIssue,
LinkWithoutADestinationIssue, ListenerAddedByHandIssue, ListenerOnTheDefaultHostIssue,
MediaWithNoCaptionsIssue, NamedOnlyByAPlaceholderIssue, OneProviderPerComponentIssue,
PersistOfALossyValueIssue, PositiveTabIndexIssue, ReferenceToAnIdThatIsNotThereIssue,
RoleMissingRequiredAriaIssue, RoleTakesNoNameIssue, RowReadsAPlainFieldIssue,
RowWithoutAKeyIssue, ServerEnvInSharedCodeIssue, StateMutatedInPlaceIssue,
StateWrittenWhileRenderingIssue, TagNeedsItsParentIssue, UnexposedEnvReadIssue,
UnguardedAsyncLifecycleIssue, UnkeyableMemoizedArgumentIssue, UnknownAriaAttributeIssue,
UnknownRoleIssue, UnnamedFrameIssue, UnnamedImageIssue, UnserializableStateIssue,
UnsplittableImportIssue, UnwatchedFieldIssue, WatchOfAPropThatIsNotThereIssue.
The rules themselves are not exported, and that is a decision. A rule carries functions over its
own issue type and a read that takes a compiler node, so publishing one would make this package's
internals somebody's dependency and every change to a rule's shape a breaking change.
ruleCatalogue() is what a caller actually wants from them.
@ramonda/testing-library
render(ui, options?) | Mounts and returns the DOM plus bound queries. Rendering |
renderHook(hook, options?) | Mounts a hook on a throwaway host. Testing hooks |
act(callback?) | Commits everything the callback caused. act |
fireEvent | The DOM library's, wrapped so the render is committed. |
cleanup() | Unmounts everything. Registered automatically. |
Everything from @testing-library/dom is re-exported — screen, waitFor, within,
prettyDOM, every query.
@ramonda/core/testing
A separate entry point, for building a test harness rather than for apps.
flushSync() | Runs every pending update and mount now. |
rerenderRoot(vnode, container) | Diffs new JSX into an already-rendered container. |
getComponentInstance(node) | The component a DOM node belongs to. |
It exists so the main entry does not have to be widened for a test utility: a separate, narrow door that a harness reaches for and an app never sees.