@schemastud/seam
v0.2.1
Published
The medium-neutral editing socket: a predicate-based widget registry, a form intent bus, uiSchema derivation from schema-carried hints, configurable extension-keyword tolerance, an injected $ref fetcher seam, a base SchemaForm, and the uniform SelectionCh
Readme
seam
The medium-neutral editing socket for the schemastud constellation: a widget registry, a form
intent bus, a base SchemaForm, and the uniform SelectionChrome primitive — the socket that any
skin, dialect, or document model plugs into. (Renamed from @schemastud/rjsf-registry: the old name
described one widget flavor riding the socket, not the socket itself.)
Generally-useful additions over react-jsonschema-form — none of which RJSF core has:
- Predicate widget registry —
registerWidget(predicateOrKey, widget)resolves widgets by predicate over the schema node instead of RJSF's name-based model; first match wins, later registrations take precedence. - Form intent bus —
createFormIntentBus()is the medium-neutral channel a form emits edit intents on, decoupled from any host transport. - Uniform
SelectionChrome— the grammar-blind selection/presence chrome primitive: it draws the same ring/handle/badge/outline around any skin (ridesselectedNodeId, never a document model), so a skin ships zero chrome code. Extracted here from blockdoc so every medium reuses it. - uiSchema derivation —
buildUiSchema(schema, registry)walks a schema and emits an RJSF uiSchema from schema-carried hints (x-widget,x-placeholder), so a form's behavior is declared where its shape is declared. - Configurable extension-keyword tolerance —
createKeywordVocabulary({ keywords, patterns })declares a host'sx-*dialect;createFormValidator()builds an AJV8 validator that never trips strict mode on extension keywords. No vendor dialect is hardcoded. - Injected
$reffetcher seam —resolveExternalRefs(schema, fetcher)pre-resolves external refs through a host-supplied async fetcher. Transport-agnostic: the host passes its own authed client; this package never imports one. SchemaForm— the base component wiring all of the above over@rjsf/shadcn, with registry injection via prop orWidgetRegistryContext.
This package is host-agnostic: it contains no vendor vocabulary beyond the unprefixed form
keywords it consumes itself (x-widget, x-placeholder). Vendor layers (dialects, pre-wired
forms) belong in adapter packages that depend on this one.
Install
npm install @schemastud/seamPeer dependencies: @rjsf/core, @rjsf/shadcn, @rjsf/utils, @rjsf/validator-ajv8 (all ^6),
react (≥18).
Usage
import { SchemaForm, createWidgetRegistry } from '@schemastud/seam';
const registry = createWidgetRegistry();
registry.registerWidget((s) => s['x-widget'] === 'citation', CitationWidget);
<SchemaForm
schema={schema}
registry={registry}
schemaFetcher={(ref) => api.get(ref).then((r) => r.data)}
onSubmit={({ formData }) => save(formData)}
/>;Default resolution chain
x-widgetexplicit override (textarea,radio,file,select)- enum: ≤4 entries → radio; larger → RJSF's select default
- format:
file→ file widget;date/date-time/email/uri→ native inputs - everything else → RJSF defaults (the registry emits nothing)
Quirks handled by SchemaForm
- Backends that serialize an empty associative payload as
[](PHP among them) get their formData coerced to{}against object schemas. - Nullable
$refs ({$ref, nullable: true}) are re-expressed asanyOf[$ref, null](normalizeNullableRefs) — AJV rejectsnullablewithout atypesibling.requiredis passed through untouched: the server's list is the form's list. - Arrays without an
itemsdefinition are hidden rather than rendered as RJSF error blocks.
