@statedelta-actions/sdk
v0.1.0
Published
Barrel package — re-exports all @statedelta-actions packages with synchronized versions
Readme
@statedelta-actions/sdk
Ponto de entrada unificado para o ecossistema
@statedelta-actions. Uma dependência, todos os packages, versões sincronizadas.
Table of Contents
- Filosofia
- Instalação
- Quick Start — Full Stack
- Os 4 Componentes
- Composição — Como Tudo se Conecta
- Dois Modos de Import
- Packages Incluídos
- Três Modos de Operação
- Por que usar o SDK
- Documentação dos Packages
- Licença
Filosofia
O @statedelta-actions é um sistema de execução declarativa. Ações são objetos JS com diretivas. Handlers dão significado às diretivas. O engine orquestra execução. O analyzer observa e valida.
O SDK não é um framework. É um kit de composição. O consumer monta o sistema que precisa:
- Só precisa de actions? Use o engine.
- Quer análise estática? Adicione o analyzer.
- Precisa de triggers condicionais? Adicione rules.
- Quer event-driven? Adicione events.
- Quer tudo junto com versões garantidas? Use o SDK.
Cada componente funciona sozinho. Nenhum assume a existência dos outros. O consumer é quem decide a composição e faz o wiring.
Três metáforas
| Componente | Metáfora | Papel |
|-----------|----------|-------|
| ActionEngine | V8 | Runtime puro — executa diretivas |
| ActionAnalyzer | TypeScript | Análise estática — observa, valida, consulta |
| RuleEngine | Cron | Triggers condicionais — when() → invoke() |
| EventProcessor | EventEmitter | Dispatch de listeners — on(event) → invoke() |
O engine nunca analisa. O analyzer nunca muta. O RuleEngine nunca executa diretivas (delega pro engine). O EventProcessor é o mesmo — registro de listeners como hidden actions, dispatch via engine.
Instalação
pnpm add @statedelta-actions/sdkQuick Start — Full Stack
O setup completo: engine processa diretivas, analyzer observa o grafo, rules avaliam triggers, events despacham listeners.
import {
createActionEngine,
createActionAnalyzer,
createRuleEngine,
createEventProcessor,
} from "@statedelta-actions/sdk";
import { HALT_HANDLER } from "@statedelta-actions/sdk/rules";
// ── 1. Handlers — dão significado aos tipos de diretiva ──
const handlers = {
state: {
analyze: () => ({ capabilities: ["write"], dependencies: [] }),
execute: (directive, frame) => {
frame.ctx[directive.target] = directive.value;
return { ok: true, data: directive.value };
},
},
emit: {
analyze: () => ({ capabilities: ["emit"], dependencies: [] }),
execute: (directive, frame) => {
frame.ctx.events.push(directive.event);
return { ok: true };
},
},
action: {
analyze: (d) => ({
capabilities: ["invoke"],
dependencies: d.id ? [d.id] : [],
}),
execute: (directive, frame, engine) => {
const result = engine.invoke(directive.id, directive.params, frame);
return { ok: result.success, data: result.data };
},
},
halt: HALT_HANDLER,
};
// ── 2. Engine — runtime de execução ──
const engine = createActionEngine({ handlers });
// ── 3. Analyzer — análise estática (opt-in) ──
const analyzer = createActionAnalyzer({ engine });
engine.on("register", (e) => analyzer.processRegistration(e));
engine.on("unregister", (e) => analyzer.processUnregistration(e));
// ── 4. Rules — triggers condicionais ──
const rules = createRuleEngine({ actionEngine: engine });
// ── 5. Events — dispatch de listeners ──
const events = createEventProcessor({ actionEngine: engine });
// ── 6. Registrar e usar ──
engine.register([
{ id: "heal", directives: [{ type: "state", target: "hp", value: 100 }] },
]);
rules.register([{
id: "auto-heal",
priority: 100,
when: (ctx) => ctx.hp < 50,
then: [
{ type: "action", id: "heal" },
{ type: "emit", event: "healed" },
],
}]);
events.register([{
id: "on-healed",
priority: 100,
on: "healed",
then: [{ type: "state", target: "healCount", value: 1 }],
}]);
// Runtime
const ctx = { hp: 30, events: [], healCount: 0 };
rules.evaluate(ctx); // auto-heal fires → heal + emit
events.processEvents([{ event: "healed" }], ctx); // on-healed fires
// Análise estática
analyzer.capabilitiesOf("rule:auto-heal"); // Set { "invoke", "write", "emit" }
analyzer.dependenciesOf("rule:auto-heal"); // Set { "heal" }Os 4 Componentes
ActionEngine — Runtime
Motor de execução. Registra ações (ID + diretivas), valida em register-time, compila executores (interpret/JIT), invoca em O(1). Handlers dão semântica — o engine orquestra.
const engine = createActionEngine({ handlers });
engine.register([{
id: "checkout",
directives: [
{ type: "state", target: "status", value: "paid" },
{ type: "emit", event: "payment-processed" },
],
}]);
engine.setContext(ctx);
const result = engine.invoke("checkout");
// result.success, result.appliedCount, result.errorsLifecycle events (register, unregister) permitem que camadas externas (analyzer) observem sem acoplamento.
ActionAnalyzer — Análise Estática
Observa o engine via read-only accessors e lifecycle events. Constrói grafo de dependências, infere capabilities transitivas, detecta ciclos, valida composition control.
const analyzer = createActionAnalyzer({ engine });
// Wire lifecycle events
engine.on("register", (e) => analyzer.processRegistration(e));
engine.on("unregister", (e) => analyzer.processUnregistration(e));
// Queries
analyzer.capabilitiesOf("checkout"); // Set { "write", "emit" }
analyzer.dependenciesOf("checkout"); // Set { ... }
analyzer.inCycle("checkout"); // false
analyzer.getStats(); // { totalActions, totalEdges, ... }Opt-in: o engine funciona sem analyzer. Quando presente, valida contratos sem afetar runtime.
RuleEngine — Triggers Condicionais
Loop de when(ctx) → invoke(). Rules são normalizadas em hidden actions (rule:{id}) no engine. O RuleEngine não executa diretivas — delega tudo pro engine.
const rules = createRuleEngine({ actionEngine: engine });
rules.register([{
id: "heal-when-low",
priority: 100,
when: (ctx) => ctx.hp < 50,
then: [{ type: "state", target: "hp", value: 100 }],
}]);
const result = rules.evaluate(ctx);
// result.matched, result.skipped, result.countersSub-rules habilitam branching condicional. Hooks (beforeRule, afterRule) habilitam governança. Halt para a avaliação de forma controlada.
EventProcessor — Dispatch de Listeners
Listeners escutam eventos por nome. Normalizados em hidden actions (event:{id}) no engine. Match por nome, execução por prioridade desc. Halt é scoped ao evento — não contamina o próximo da fila.
const events = createEventProcessor({ actionEngine: engine });
events.defineEvents([{ event: "save", tags: ["domain:billing"] }]);
events.register([{
id: "on-save",
priority: 100,
on: "save",
then: [{ type: "state", target: "saved", value: true }],
}]);
const result = events.processEvents([{ event: "save" }], ctx);
// result.processedEvents, result.eventResults, result.unprocessedEventsComposição — Como Tudo se Conecta
Hidden Actions
Rules e listeners são hidden actions no engine. O RuleEngine registra rule:{id}. O EventProcessor registra event:{id}. O engine não sabe a origem — trata todas como actions normais. Isso significa que o analyzer valida tudo no mesmo grafo:
engine.registeredIds:
"checkout" ← action direta
"rule:auto-heal" ← hidden action da rule
"event:on-payment" ← hidden action do listener
"eventdef:payment" ← nó de definição de evento
analyzer.graph:
rule:auto-heal → checkout → eventdef:payment
↑
event:on-payment ──────────┘Wiring
O SDK não faz wiring automático. O consumer conecta os componentes:
// Analyzer observa engine
engine.on("register", (e) => analyzer.processRegistration(e));
engine.on("unregister", (e) => analyzer.processUnregistration(e));
// Rules e Events recebem o engine
const rules = createRuleEngine({ actionEngine: engine });
const events = createEventProcessor({ actionEngine: engine });Máxima flexibilidade: o consumer decide quando e como conectar. O engine não sabe quem escuta. O analyzer não sabe quem o alimenta.
Fluxo Típico de Execução
1. rules.evaluate(ctx)
│
├─ when(ctx) === true → engine.invoke("rule:{id}")
│ ├─ handler "state" → muta ctx
│ ├─ handler "emit" → registra evento
│ └─ handler "action" → engine.invoke(child)
│
└─ result.matched = ["rule-a", "rule-b"]
2. events.processEvents(queue, ctx)
│
├─ match listeners by event name
│ └─ engine.invoke("event:{id}")
│ └─ handlers executam diretivas
│
└─ result.processedEvents = 2
3. analyzer.capabilitiesOf("rule:rule-a")
└─ Set { "write", "emit", "invoke" }Rules avaliam. Events processam. Engine executa. Analyzer observa. Cada um no seu papel.
Batch — Registro Cruzado
Batch permite registrar actions, rules, events e eventdefs em uma única transação. O engine adia lifecycle events até endBatch(). Forward references resolvem:
engine.beginBatch();
rules.register([...]); // Usa action que ainda não existe
events.defineEvents([...]); // Define eventdefs
engine.register([...]); // Registra a action referenciada
events.register([...]); // Registra listeners
engine.endBatch(); // Analyzer recebe tudo de uma vezDois Modos de Import
Root — factories e tipos essenciais
Para começar rápido. Exporta as 4 factories e os tipos mais usados, sem colisão de nomes entre packages.
// Factories
import {
createActionEngine,
createActionAnalyzer,
createRuleEngine,
createEventProcessor,
} from "@statedelta-actions/sdk";
// Types
import type {
HandlerDefinition,
ActionDefinition,
IActionEngine,
IActionAnalyzer,
RuleDefinition,
EventListenerDefinition,
Directive,
ExecutionFrame,
} from "@statedelta-actions/sdk";Subpath — acesso completo por package
Para quem precisa de exports específicos (propagators, matching, JIT, etc). Cada subpath re-exporta tudo do package original.
// Core — primitivas (ExecutionFrame, hooks, slots, intercept)
import { createRootFrame, composeHooks } from "@statedelta-actions/sdk/core";
// Graph — grafo de dependências (ciclos, topo sort, propagators)
import { DependencyGraph, DEFAULT_PROPAGATORS } from "@statedelta-actions/sdk/graph";
// Actions — ActionEngine (validação, compilação, invoke)
import { createActionEngine, RESERVED_TYPES } from "@statedelta-actions/sdk/actions";
// Analyzer — análise estática (graph queries, composition control, permissions)
import { createActionAnalyzer, compileManifest } from "@statedelta-actions/sdk/analyzer";
// Rules — RuleEngine (invocação condicional por triggers)
import { createRuleEngine, HALT_HANDLER } from "@statedelta-actions/sdk/rules";
// Events — EventProcessor (dispatch de listeners por evento)
import { createEventProcessor, createEmitAnalyzer } from "@statedelta-actions/sdk/events";Packages Incluídos
| Subpath | Package | Descrição |
|---------|---------|-----------|
| /core | @statedelta-actions/core | Primitivas: ExecutionFrame, hooks, slots, intercept |
| /graph | @statedelta-actions/graph | Grafo de dependências: ciclos, topo sort, propagators, dirty tracking |
| /actions | @statedelta-actions/actions | ActionEngine: validação, compilação JIT, invoke O(1) |
| /analyzer | @statedelta-actions/analyzer | ActionAnalyzer: análise estática, capabilities, composition control |
| /rules | @statedelta-actions/rules | RuleEngine: invocação condicional por triggers e sub-rules |
| /events | @statedelta-actions/events | EventProcessor: dispatch de listeners por nome de evento |
Três Modos de Operação
O consumer escolhe o nível de análise. Cada modo é uma composição incremental:
JS mode — Engine only
const engine = createActionEngine({ handlers });
engine.register([...]);
engine.invoke("action");Runtime puro. Zero overhead de análise. Funciona: registration, invoke, JIT, hooks, batch.
TS mode — Engine + Analyzer
const engine = createActionEngine({ handlers });
const analyzer = createActionAnalyzer({ engine });
// wire events...Tudo do JS mode + grafo de dependências, capabilities transitivas, detecção de ciclos, declaration conflicts.
TS strict — Engine + Analyzer + Composition Control
const analyzer = createActionAnalyzer({
engine,
accessManifest: {
rules: [{
source: { tags: { include: ["write"] } },
target: { tags: { include: ["readonly"] } },
effect: "deny",
}],
},
tierValidation: true,
});Tudo do TS mode + composition control declarativo (quem pode depender de quem) + tier validation (hierarquia numérica de acesso).
Por que usar o SDK
Packages individuais têm versões independentes. Se você instala @statedelta-actions/[email protected] e @statedelta-actions/[email protected], pode haver incompatibilidade de tipos (ex: IAnalyzableEngine mudou entre versões).
O SDK garante que todos os packages estão na versão que foi testada junta. Uma única dependência, zero risco de mismatch.
{
"dependencies": {
"@statedelta-actions/sdk": "^0.1.0"
}
}Em vez de:
{
"dependencies": {
"@statedelta-actions/core": "^0.1.0",
"@statedelta-actions/graph": "^0.1.0",
"@statedelta-actions/actions": "^0.1.0",
"@statedelta-actions/analyzer": "^0.1.0",
"@statedelta-actions/rules": "^0.1.0",
"@statedelta-actions/events": "^0.1.0"
}
}Além de versões sincronizadas, o SDK inclui uma suite de 91 testes de integração que validam a composição cross-package — cenários que nenhum package individual consegue testar sozinho.
Documentação dos Packages
Cada package tem documentação completa de API, arquitetura interna e decisões de design:
| Package | README | Architecture | Decisions | |---------|--------|-------------|-----------| | actions | README | ARCHITECTURE | DECISIONS | | analyzer | README | ARCHITECTURE | DECISIONS | | rules | README | ARCHITECTURE | DECISIONS | | events | README | ARCHITECTURE | DECISIONS | | graph | — | ARCHITECTURE | DECISIONS | | core | — | — | — |
Para a test architecture do SDK (helpers, test map, decisões), ver docs/ARCHITECTURE.md.
Licença
MIT
