npm package: @statedelta-actions/analyzer
Description: Static analysis layer for ActionEngine — capabilities, dependency graph, access control
Author: Anderson D. Rosa
Latest version: 0.3.0

# @statedelta-actions/analyzer

Camada de analise estatica para o ActionEngine. Graph queries, deteccao de ciclos, inferencia de capabilities, composition control e deteccao de conflitos de declaracao.

---

## Filosofia

**O engine e o V8. O analyzer e o TypeScript.**

O ActionEngine e runtime puro: valida, armazena, compila, executa. Nao sabe o que "capabilities", "ciclos" ou "access control" significam.

O ActionAnalyzer e o type checker externo: observa o engine (read-only), extrai fatos, valida contratos e expoe consultas. Nunca muta o engine.

Tres modos de operacao:

| Modo | O que usa | Analogia |
|------|-----------|----------|
| **JS mode** | Engine only | JavaScript puro — sem checagem |
| **TS mode** | Engine + Analyzer | TypeScript — analise estatica, graph queries |
| **TS strict** | Engine + Analyzer + Composition manifest | `strict: true` — analise + composition control + contratos |

O consumer decide o nivel de analise. O engine funciona sozinho. O analyzer e opt-in.

---

## Instalacao

```bash
pnpm add @statedelta-actions/analyzer
```

Peer dependencies: `@statedelta-actions/core`, `@statedelta-actions/graph`, `@statedelta-actions/actions`.

---

## Inicio Rapido

```typescript
import { createActionEngine } from "@statedelta-actions/actions";
import { createActionAnalyzer } from "@statedelta-actions/analyzer";

// 1. Criar engine
const engine = createActionEngine({ handlers });

// 2. Criar analyzer
const analyzer = createActionAnalyzer({ engine });

// 3. Wire lifecycle events
const unsubs = [
  engine.on("register",   (e) => analyzer.processRegistration(e)),
  engine.on("unregister", (e) => analyzer.processUnregistration(e)),
];

// 4. Usar normalmente — engine opera, analyzer observa
engine.register([
  { id: "heal", directives: [{ type: "state", path: "hp", value: 100 }] },
  { id: "combat", directives: [{ type: "invoke", action: "heal" }] },
]);

// 5. Consultar o analyzer
analyzer.capabilitiesOf("combat");  // Set { "invoke", "write" } (transitivo)
analyzer.dependenciesOf("combat");  // Set { "heal" }
analyzer.dependentsOf("heal");      // Set { "combat" }
analyzer.inCycle("heal");           // false

// 6. Cleanup
unsubs.forEach((fn) => fn());
analyzer.dispose();
```

---

## Tres Modos de Operacao

### JS mode — Engine only

```typescript
const engine = createActionEngine({ handlers });
engine.register([...]);
engine.invoke("myAction", ctx);
// Sem analise. Runtime puro. Zero overhead.
```

Funciona: registration, invoke, JIT.
Nao funciona: ciclos, capabilities, composition control, declarations, dependency queries.

### TS mode — Engine + Analyzer

```typescript
const engine = createActionEngine({ handlers });
const analyzer = createActionAnalyzer({ engine });

// Wire events...
engine.on("register", (e) => analyzer.processRegistration(e));
engine.on("unregister", (e) => analyzer.processUnregistration(e));

engine.register([...]);

// Graph queries disponiveis
analyzer.capabilitiesOf("myAction");
analyzer.inCycle("myAction");
analyzer.getStats();
```

Tudo do JS mode + ciclos, capabilities transitivas, declarations, dependency queries.

### TS strict — Engine + Analyzer + Composition Control

```typescript
const analyzer = createActionAnalyzer({
  engine,
  accessManifest: {
    rules: [
      {
        source: { tags: { include: ["write"] } },
        target: { tags: { include: ["readonly"] } },
        effect: "deny",
      },
    ],
  },
});

// Wire events...
const result = analyzer.processRegistration(event);
result.accessViolations; // AccessViolation[] — edges que violam regras
```

Tudo do TS mode + composition control com manifest, validacao de edges em register-time.

---

## Wiring

O analyzer nao auto-subscribe. O consumer decide a estrategia de wiring (direto, batched, tick-boundary).

Dois lifecycle events:

| Evento | Metodo do Analyzer | Quando o engine emite |
|--------|-------------------|----------------------|
| `"register"` | `processRegistration(e)` | Apos `register()` ou `endBatch()`, se houve registros |
| `"unregister"` | `processUnregistration(e)` | Apos `unregister()`, se a action existia |

```typescript
const unsubs = [
  engine.on("register",   (e) => analyzer.processRegistration(e)),
  engine.on("unregister", (e) => analyzer.processUnregistration(e)),
];

// Cleanup
unsubs.forEach((fn) => fn());
```

**Um listener por evento.** Multiplos listeners causam processamento duplicado.

---

## Graph Queries

Todas delegam diretamente pro `DependencyGraph`. Zero logica extra.

| Metodo | Retorno | Descricao |
|--------|---------|-----------|
| `capabilitiesOf(id)` | `ReadonlySet<string> \| undefined` | Capabilities resolvidas (own + transitivas) |
| `propertyOf<T>(id, name)` | `T \| undefined` | Valor resolved de qualquer propriedade |
| `inferredOf<T>(id, name)` | `T \| undefined` | Valor inferido (ignora declarations) |
| `dependenciesOf(id)` | `ReadonlySet<string>` | Dependencias diretas |
| `dependentsOf(id)` | `ReadonlySet<string>` | Quem depende dessa action |
| `conflictsOf(id)` | `readonly DeclarationConflict[]` | Conflitos inferred vs declared |
| `inCycle(id)` | `boolean` | Se participa de ciclo |
| `getStats()` | `GraphStats` | Estatisticas do grafo |

```typescript
analyzer.capabilitiesOf("combat");
// Set { "invoke", "write" } — transitivo via heal

analyzer.propertyOf<number>("combat", "maxDepth");
// 1

analyzer.inferredOf<boolean>("action", "readonly");
// valor inferido, ignora declarations

analyzer.dependenciesOf("combat");  // Set { "heal" }
analyzer.dependentsOf("heal");      // Set { "combat" }
analyzer.inCycle("A");              // true/false

analyzer.conflictsOf("action");
// [{ property: "readonly", declared: true, inferred: false }]

analyzer.getStats();
// { totalActions, totalEdges, leafCount, branchCount, cycleCount, maxDepth, isolatedCount }
```

Acesso direto ao grafo: `analyzer.graph` (readonly `DependencyGraph`).

---

## Composition Control

Controle de dependencias arquiteturais baseado em tags com pattern matching. Avalia edges (source -> target) contra regras declarativas. Define quem pode depender de quem — dev tooling pra manter a arquitetura limpa.

```typescript
const analyzer = createActionAnalyzer({
  engine,
  accessManifest: {
    rules: [
      {
        source: { tags: { include: ["write"] } },
        target: { ids: ["protected-*"] },
        effect: "deny",
      },
    ],
  },
});
```

Violacoes aparecem no `AnalysisResult.accessViolations`. A action **e registrada** — o consumer decide a politica.

### setAccessManifest — Manifest mutavel

```typescript
// Aplica novo manifest em runtime (recompila + revalida tudo)
const result = analyzer.setAccessManifest({
  rules: [{ source: { tags: { include: ["read"] } }, target: { tags: { include: ["write"] } }, effect: "deny" }],
});
result.accessViolations; // Violacoes das edges existentes

// Desativa composition control (zero overhead)
analyzer.setAccessManifest(null);
```

Para detalhes de `TagMatcher`, `NodeMatcher`, `AccessRule`, pipeline de compilacao e semantica de matching, veja [ARCHITECTURE.md](./ARCHITECTURE.md).

> **Nota sobre nomenclatura:** O codigo usa `accessManifest`, `AccessRule`, `AccessViolation` etc.
> Conceitualmente, isso e **composition control** — controle de dependencias arquiteturais
> (quem pode depender de quem), nao controle de acesso de usuario. ACL real (RBAC, autenticacao)
> e extensao futura, completamente separada. Ver [ARCHITECTURE-VISION.md](../../ARCHITECTURE-VISION.md).

---

## Tier Validation

Controle hierarquico por nivel numerico. Actions com tier declarado participam de uma hierarquia: action com tier menor nao pode invocar action com tier maior. Actions sem tier sao livres.

```typescript
const analyzer = createActionAnalyzer({
  engine,
  tierValidation: true, // injeta tierPropagator + valida edges
});

// Wire events...
engine.on("register", (e) => analyzer.processRegistration(e));
engine.on("unregister", (e) => analyzer.processUnregistration(e));

engine.register([
  { id: "admin-action", directives: [...], tier: 500 },
  { id: "user-action", directives: [{ type: "action", id: "admin-action" }], tier: 100 },
]);

// result.tierViolations:
// [{ sourceId: "user-action", targetId: "admin-action", sourceTier: 100, targetTier: 500, message: "..." }]
```

### Semantica

| Source tier | Target tier | Resultado |
|------------|-------------|-----------|
| undefined | undefined | Livre |
| undefined | 500 | Livre |
| 100 | undefined | Livre |
| 100 | 100 | OK (igual) |
| 100 | 500 | **Violacao** |
| 500 | 100 | OK (higher → lower) |

**`undefined` = livre.** Actions sem tier nao participam da validacao. Tier e declaracao explicita de intencao — so tem efeito quando o dev declara.

### Dois mecanismos complementares

| Mecanismo | O que detecta | Exemplo |
|-----------|--------------|---------|
| **Edge validation** | Invocacao direta com tier insuficiente | A(100) → B(500) |
| **DECLARATION_CONFLICT** | Tier declarado menor que inferido da sub-arvore | A declara 100, sub-arvore exige 500 |

Edge validation pega violacoes diretas. DECLARATION_CONFLICT pega violacoes transitivas (A → B → C onde C exige tier alto). Juntos cobrem todos os cenarios.

### Consulta de tier

```typescript
// Tier inferido (max da sub-arvore, incluindo declarado proprio)
analyzer.propertyOf<number>("action-A", "tier"); // 500

// Tier declarado (so o que a action declarou explicitamente)
analyzer.graph.get("action-A")?.declarations.get("tier"); // undefined (nao declarou)
```

### Quando usar

- **Actions com tier**: actions de dominio que participam de hierarquia arquitetural (admin, system, domain-specific).
- **Actions sem tier**: utility actions (log, format, validate) — qualquer um pode chamar.
- **Rules**: o RuleEngine pode injetar `tier` via `priority`. Com `tierValidation: true`, o analyzer valida que uma rule de priority 300 nao invoca (direta ou transitivamente) actions que exijam tier > 300.

---

## sync() — Hot-Plug

O analyzer pode ser criado **depois** de actions ja registradas. `sync()` le o registry inteiro do engine e reconstroi a analise completa:

```typescript
// Actions registradas antes do analyzer existir
engine.register([...]);

// Analyzer criado depois — perdeu os eventos
const analyzer = createActionAnalyzer({ engine });
const result = analyzer.sync(); // Le tudo: registry completo
// result: AnalysisResult

// Wire pra eventos futuros
engine.on("register", (e) => analyzer.processRegistration(e));
// ...
```

`sync()` e idempotente. Chamar duas vezes reconstroi do zero com resultado identico. Util como escape hatch.

---

## dispose()

Limpa estado interno (cache do validator, grafo). Chamadas subsequentes sao no-op.

```typescript
// Cancelar listeners primeiro
unsubs.forEach((fn) => fn());

// Cleanup do analyzer
analyzer.dispose();
```

---

## Referencia de Configuracao

```typescript
interface ActionAnalyzerConfig<TCtx = unknown> {
  /** Engine pra observar (read-only). Obrigatorio. */
  engine: IAnalyzableEngine<TCtx>;

  /**
   * Grafo de dependencias.
   * Se fornecido: usa direto (consumer controla propagators).
   * Se omitido: cria internamente com DEFAULT_PROPAGATORS.
   */
  graph?: DependencyGraph;

  /**
   * Propagators pro grafo.
   * So usado se `graph` nao foi fornecido.
   * Default: DEFAULT_PROPAGATORS (capabilities, leaf, maxDepth).
   */
  propagators?: Record<string, Propagator<any>>;

  /**
   * Manifest de composition control (controle de dependencias arquiteturais).
   * Se omitido: sem composition control (zero overhead).
   * Pode ser alterado em runtime via setAccessManifest().
   */
  accessManifest?: AccessManifest;
}
```

| Config | Comportamento |
|--------|---------------|
| `graph` fornecido | Usa direto. Consumer controla propagators. |
| `graph` omitido, `propagators` fornecido | Cria graph com propagators fornecidos. |
| `graph` e `propagators` omitidos | Cria graph com DEFAULT_PROPAGATORS. |

---

## Exports

```typescript
// Types: Analyzer
IActionAnalyzer, ActionAnalyzerConfig, IAnalyzableEngine,
AnalysisResult, AnalysisWarning, AnalysisConflict,
UnregistrationResult

// Types: Lifecycle Events
RegisterEvent, UnregisterEvent

// Factory
createActionAnalyzer

// Analysis (pure function)
analyzeAction

// Composition Control (code still uses "access" naming)
AccessValidator, AccessOrchestrator,
AccessOrchestratorDeps, TagMatcher, NodeMatcher,
AccessRule, AccessManifest, AccessViolation, AccessValidationResult

// Composition Matching (pure functions — advanced usage / testing)
compilePattern, compileTagMatcher, compileNodeMatcher,
compileRule, compileManifest
```

---

## Licenca

MIT

---

Para arquitetura interna, detalhes de implementacao e decisoes de design, veja [ARCHITECTURE.md](./ARCHITECTURE.md).