@nzelajs/studio
v0.1.0
Published
Nzela no-code circuit studio: a palette-driven React component library that authors and validates BlueprintGraph documents, fail-closed, before they are published.
Maintainers
Readme
@nzelajs/studio
English below - Version francaise plus bas.
The Nzela no-code circuit studio: a palette-driven React component library that lets an app's own
clients draw and edit process circuits without code. It PRODUCES and CONSUMES exactly the
BlueprintGraph schema the engine executes, and validates every edit FAIL-CLOSED against the app
palette before anything can be saved.
English
Install
npm add @nzelajs/studio @nzelajs/blueprint @nzelajs/ports react @xyflow/reactreact is a peer dependency (^18 || ^19). Import the stylesheet once:
import "@nzelajs/studio/styles.css";The idea: PALETTE + NodeRenderer, a mirror of the engine
The studio knows NO business terms. The host app declares its vocabulary through a StudioPalette:
what can be dragged (nodeTypes), the transition verbs (actions), the named guards and effects,
and the approver kinds. What you can drop into the studio is exactly what the app declared, which is
exactly what the engine can execute. The same catalog drives the side palette AND fail-closed
validation.
import { CircuitEditor, type StudioPalette } from "@nzelajs/studio";
import "@nzelajs/studio/styles.css";
const palette: StudioPalette = {
nodeTypes: ["state", "auto", "approval"],
actions: [
{ id: "submit", label: "Submit" },
{ id: "approve", label: "Approve" },
{ id: "return", label: "Return" },
],
guards: [{ id: "isUrgent", label: "Is urgent" }],
effects: [{ id: "notify", label: "Notify requester" }],
approverKinds: [{ kind: "ORG_CHAIN", label: "Hierarchy chain" }],
};
function Editor({ graph }) {
return (
<CircuitEditor
value={graph}
palette={palette}
onChange={(next) => console.log("edited", next)}
onValidate={(result) => console.log("validation", result)}
/>
);
}What it provides
<CircuitEditor>: a drag/drop editor (@xyflow/react) with a side palette, an inspector for the selected node/edge (type, id, terminal, approval mode/quorum/approver, action, guards, effects, permission), and aValidatebutton. Structural edits are emitted throughonChange(graph).- Graph <-> blueprint mapping (pure, DOM-free, in
graph-mapping.ts):blueprintToFlow(graph)andflowToBlueprint(entityType, nodes, edges). Positions are a studio concern, never stored in the blueprint;flowToBlueprint(blueprintToFlow(g))yields a graph equivalent tog. - Immutable graph edits (
addNode,removeNode,renameNode,updateNode,addTransition,removeTransition,updateTransition, ...): pure helpers that keep a valid blueprint. validateGraph(graph, palette): runs the engine'sparseBlueprintwith the palette projected onto the engine catalog. FAIL-CLOSED and never throws; an invalid circuit is never saved.- NodeRenderer registry: one renderer per node type (a mirror of the engine NodeHandler). Defaults ship for the five built-ins; the app can add or override renderers.
- Persistence binding:
useBlueprintStore(store, palette)(or the pureloadActiveGraph/publishGraph) over aPick<BlueprintInstallStore, "getActive" | "publish">slice the app supplies.publishis always preceded by validation.
Wiring a store
The studio never talks to a database. The host passes the getActive / publish slice of its
BlueprintInstallStore (from @nzelajs/ports, e.g. backed by @nzelajs/persistence-prisma):
const { load, save, busy, error } = useBlueprintStore(installStore, palette);
const graph = await load(requestTypeId);
const record = await save(graph, { requestTypeId, slug: "purchase" }); // validated firstFrancais
Installation
npm add @nzelajs/studio @nzelajs/blueprint @nzelajs/ports react @xyflow/reactreact est une peer dependency (^18 || ^19). Importez la feuille de style une fois :
import "@nzelajs/studio/styles.css";L'idee : PALETTE + NodeRenderer, un miroir du moteur
Le studio ne connait AUCUN terme metier. L'app declare son vocabulaire via une StudioPalette : ce
qu'on peut glisser (nodeTypes), les verbes de transition (actions), les guards et effects
nommes, les types d'approbateur. Ce qu'on depose dans le studio = ce que l'app a declare = ce que le
moteur sait executer. Le meme catalogue alimente la palette laterale ET la validation fail-closed.
Ce que ca fournit
<CircuitEditor>: un editeur en glisser-deposer (@xyflow/react) avec palette laterale, panneau d'inspection du noeud/arete selectionne (type, id, terminal, mode/quorum/approbateur, action, gardes, effets, permission) et un boutonValider. Les editions structurelles sont remontees viaonChange(graph).- Mapping graphe <-> blueprint (pur, sans DOM, dans
graph-mapping.ts) :blueprintToFlow(graph)etflowToBlueprint(entityType, nodes, edges). Les positions sont une preoccupation studio, jamais stockees dans le blueprint ;flowToBlueprint(blueprintToFlow(g))redonne un graphe equivalent ag. - Editions immuables du graphe (
addNode,removeNode,renameNode,updateNode,addTransition,removeTransition,updateTransition...) : helpers purs qui conservent un blueprint valide. validateGraph(graph, palette): lance leparseBlueprintdu moteur avec la palette projetee sur le catalogue moteur. FAIL-CLOSED, ne leve jamais ; un circuit invalide n'est jamais sauvegarde.- Registre de NodeRenderer : un renderer par type de noeud (miroir du NodeHandler du moteur). Les cinq types integres ont des rendus par defaut ; l'app peut en ajouter ou surcharger.
- Liaison de persistance :
useBlueprintStore(store, palette)(ou les fonctions puresloadActiveGraph/publishGraph) sur la tranchePick<BlueprintInstallStore, "getActive" | "publish">fournie par l'app. Lepublishest toujours precede d'une validation.
Cabler un store
Le studio ne parle jamais a une base. L'app fournit la tranche getActive / publish de son
BlueprintInstallStore (de @nzelajs/ports, ex. via @nzelajs/persistence-prisma). Le circuit est
valide avant toute ecriture.
License
Apache-2.0
