@statedelta-actions/actions 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +541 -0
- package/dist/index.cjs +9 -0
- package/dist/index.d.cts +277 -0
- package/dist/index.d.ts +277 -0
- package/dist/index.js +9 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
# @statedelta-actions/actions
|
|
2
|
+
|
|
3
|
+
Motor de execução de diretivas com validação em register-time e compilação JIT.
|
|
4
|
+
|
|
5
|
+
Defina ações como objetos JS declarativos. Registre. O engine valida estrutura, normaliza diretivas pra forma canônica, compila executores otimizados. Em runtime, invoke é O(1) lookup + execução — zero validação, zero análise.
|
|
6
|
+
|
|
7
|
+
## Filosofia
|
|
8
|
+
|
|
9
|
+
**Engine = V8. Runtime puro.**
|
|
10
|
+
|
|
11
|
+
O engine é um executor. Não analisa dependências, não computa grafos, não valida contratos. Registro valida estrutura e compila. Invocação executa. Análise estática (grafo, capabilities, ciclos, composition control) é responsabilidade do `@statedelta-actions/analyzer` — uma camada externa opcional.
|
|
12
|
+
|
|
13
|
+
Ações são **declarativas**. Uma action é um ID mais uma lista ordenada de diretivas — objetos JS declarativos despachados por um campo de tipo (`type` por padrão). O engine não sabe o que "state", "emit" ou "action" significam. Handlers dão significado às diretivas. O engine orquestra a execução.
|
|
14
|
+
|
|
15
|
+
**Handlers têm fases.** Um handler não é só um executor. Ele pode validar estrutura da diretiva em register-time, contribuir código JIT, e executar em runtime. Cada fase é opcional — uma função simples funciona como handler (compat V1). Uma definição completa desbloqueia todo o pipeline. A fase `analyze` existe na interface mas é consumida pelo `ActionAnalyzer`, não pelo engine.
|
|
16
|
+
|
|
17
|
+
## Instalação
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pnpm add @statedelta-actions/actions
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Início Rápido
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { createActionEngine } from "@statedelta-actions/actions";
|
|
27
|
+
|
|
28
|
+
// 1. Defina handlers — dão significado aos tipos de diretiva
|
|
29
|
+
const handlers = {
|
|
30
|
+
state: {
|
|
31
|
+
execute: (directive, frame) => {
|
|
32
|
+
const { target, value } = directive;
|
|
33
|
+
frame.ctx.state[target] = value;
|
|
34
|
+
return { ok: true, data: value };
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
emit: {
|
|
38
|
+
execute: (directive, frame) => {
|
|
39
|
+
frame.ctx.events.push(directive.event);
|
|
40
|
+
return { ok: true };
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
action: {
|
|
44
|
+
execute: (directive, frame, engine) => {
|
|
45
|
+
const result = engine.invoke(directive.id, directive.params, frame);
|
|
46
|
+
return { ok: result.success, data: result.data };
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// 2. Crie o engine
|
|
52
|
+
const engine = createActionEngine({ handlers });
|
|
53
|
+
|
|
54
|
+
// 3. Registre ações
|
|
55
|
+
const result = engine.register([
|
|
56
|
+
{
|
|
57
|
+
id: "heal",
|
|
58
|
+
directives: [
|
|
59
|
+
{ type: "state", target: "hp", value: 100 },
|
|
60
|
+
{ type: "emit", event: "healed" },
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: "combat",
|
|
65
|
+
directives: [
|
|
66
|
+
{ type: "action", id: "heal" },
|
|
67
|
+
{ type: "emit", event: "combat:done" },
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
]);
|
|
71
|
+
|
|
72
|
+
// result.registered → ["heal", "combat"]
|
|
73
|
+
// result.errors → []
|
|
74
|
+
// result.warnings → []
|
|
75
|
+
|
|
76
|
+
// 4. Defina contexto e invoque
|
|
77
|
+
engine.setContext({ state: {}, events: [] });
|
|
78
|
+
|
|
79
|
+
const r = engine.invoke("heal");
|
|
80
|
+
// r.success → true
|
|
81
|
+
// r.appliedCount → 2
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Handlers
|
|
85
|
+
|
|
86
|
+
Um handler processa diretivas de um tipo específico. Dois formatos:
|
|
87
|
+
|
|
88
|
+
### V1 — Função simples
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
const handlers = {
|
|
92
|
+
log: (directive, frame, engine) => {
|
|
93
|
+
console.log(directive.message);
|
|
94
|
+
return { ok: true };
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Compatível com versões anteriores. Sem análise em register-time. Emite warning `NO_ANALYZE`.
|
|
100
|
+
|
|
101
|
+
### V2 — Definição completa
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
const handlers = {
|
|
105
|
+
state: {
|
|
106
|
+
// Register-time: valida estrutura da diretiva
|
|
107
|
+
validate(directive) {
|
|
108
|
+
if (!directive.target) return { valid: false, error: "missing target" };
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
// Runtime: executa a diretiva
|
|
112
|
+
execute(directive, frame, engine) {
|
|
113
|
+
frame.ctx.state[directive.target] = directive.value;
|
|
114
|
+
return { ok: true, data: directive.value };
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
// Usado pelo ActionAnalyzer (não pelo engine):
|
|
118
|
+
// analyze(directive) {
|
|
119
|
+
// return { capabilities: ["write"], dependencies: [] };
|
|
120
|
+
// },
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
| Fase | Quando | Propósito |
|
|
126
|
+
|------|--------|-----------|
|
|
127
|
+
| `validate` | Register | Validação estrutural (rejeita diretivas malformadas) |
|
|
128
|
+
| `execute` | Runtime | Processa a diretiva e retorna resultado |
|
|
129
|
+
| `analyze` | — | Consumido pelo `ActionAnalyzer` externo, não pelo engine |
|
|
130
|
+
|
|
131
|
+
## Ações
|
|
132
|
+
|
|
133
|
+
Uma ação é um ID mais diretivas:
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
engine.register([{
|
|
137
|
+
id: "checkout",
|
|
138
|
+
directives: [
|
|
139
|
+
{ type: "validate", schema: "cart" },
|
|
140
|
+
{ type: "state", target: "status", value: "processing" },
|
|
141
|
+
{ type: "action", id: "checkout/charge" },
|
|
142
|
+
{ type: "emit", event: "checkout:complete" },
|
|
143
|
+
],
|
|
144
|
+
}]);
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Categorias de Diretiva
|
|
148
|
+
|
|
149
|
+
Três categorias. Todas têm campo `type` na forma canônica. O consumer pode usar a forma sugar (shorthand) — o engine normaliza em register-time.
|
|
150
|
+
|
|
151
|
+
**Binding** — escrita no scope de execução:
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
// Sugar (authoring)
|
|
155
|
+
{ const: "tax", value: 0.1 }
|
|
156
|
+
{ let: "total", value: "$", resolve: (ctx, scope) => ({ value: scope.subtotal * 1.1 }) }
|
|
157
|
+
|
|
158
|
+
// Forma canônica (após normalização no register)
|
|
159
|
+
{ type: "const", name: "tax", value: 0.1 }
|
|
160
|
+
{ type: "let", name: "total", value: "$", resolve: (ctx, scope) => ({ value: scope.subtotal * 1.1 }) }
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**Control** — saída antecipada:
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
// Sugar (authoring)
|
|
167
|
+
{ return: "done" }
|
|
168
|
+
{ throw: "saldo insuficiente" }
|
|
169
|
+
|
|
170
|
+
// Forma canônica (após normalização no register)
|
|
171
|
+
{ type: "return", value: "done" } // success: true, data: "done"
|
|
172
|
+
{ type: "throw", message: "saldo insuficiente" } // success: false
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**Handler** — dispatch pro handler registrado:
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
{ type: "state", target: "hp", value: 100, as: "prev" }
|
|
179
|
+
{ type: "action", id: "heal", catch: [{ type: "emit", event: "heal:failed" }] }
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Após normalização, **toda diretiva tem `type`**. O interpreter e JIT operam sobre formato uniforme — um único dispatch via `type` field, sem branching de categorias.
|
|
183
|
+
|
|
184
|
+
Os nomes `const`, `let`, `return`, `throw` são **tipos reservados** — o engine rejeita handlers com esses nomes.
|
|
185
|
+
|
|
186
|
+
Diretivas suportam:
|
|
187
|
+
- **`as`** — captura `result.data` no scope: `scope["prev"] = result.data`
|
|
188
|
+
- **`catch`** — em caso de falha, executa sub-diretivas com `scope.$exception`
|
|
189
|
+
- **`halt`** — handler retorna `{ ok: true, halt: true }` para saída antecipada
|
|
190
|
+
- **`resolve`** — campos dinâmicos mesclados antes da chamada: `resolve(ctx, scope) → campos mesclados`
|
|
191
|
+
|
|
192
|
+
## Registro
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
const result = engine.register(actions);
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
O pipeline de registro:
|
|
199
|
+
|
|
200
|
+
1. **Validate** — valida estrutura via `handler.validate()` (rejeita diretivas malformadas)
|
|
201
|
+
2. **Normalize** — converte sugar forms (`{ const: ... }`, `{ return: ... }`) pra forma canônica (`{ type: "const", ... }`)
|
|
202
|
+
3. **Store** — armazena no registry com diretivas normalizadas
|
|
203
|
+
4. **Compile** — compila executor (interpret/JIT) sobre formato canônico
|
|
204
|
+
5. **Emit** — emite evento `register` via lifecycle events
|
|
205
|
+
|
|
206
|
+
Retorna `RegisterResult`:
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
{
|
|
210
|
+
registered: string[]; // IDs registrados com sucesso
|
|
211
|
+
errors: RegisterError[]; // Falhas de validação
|
|
212
|
+
warnings: RegisterWarning[]; // NO_ANALYZE, ANALYZE_ERROR
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Desregistrar
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
engine.unregister("checkout");
|
|
220
|
+
// Também remove filhas: "checkout/charge", "checkout/validate", etc.
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## Modo Batch
|
|
224
|
+
|
|
225
|
+
Batch agrupa múltiplos registros e adia emissão de eventos pro `endBatch()`:
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
engine.beginBatch();
|
|
229
|
+
engine.register([actionA]);
|
|
230
|
+
engine.register([actionB]);
|
|
231
|
+
const result = engine.endBatch(); // Único evento "register" emitido
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Suporta aninhamento — `endBatch()` interno é no-op. Processamento acontece na profundidade 0.
|
|
235
|
+
|
|
236
|
+
## Invocação
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
// Defina contexto primeiro
|
|
240
|
+
engine.setContext(ctx);
|
|
241
|
+
const result = engine.invoke("heal");
|
|
242
|
+
const result = engine.invoke("heal", { amount: 50 });
|
|
243
|
+
|
|
244
|
+
// Ou contexto com escopo
|
|
245
|
+
engine.context(ctx, () => {
|
|
246
|
+
engine.invoke("heal");
|
|
247
|
+
engine.invoke("combat");
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
// Async (obrigatório se algum hook for async)
|
|
251
|
+
const result = await engine.invokeAsync("heal");
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Retorna `DirectiveResult`:
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
{
|
|
258
|
+
success: boolean;
|
|
259
|
+
aborted: boolean;
|
|
260
|
+
abortedBy?: string; // "halt" | "throw" | "maxDepth"
|
|
261
|
+
appliedCount: number;
|
|
262
|
+
skippedCount: number;
|
|
263
|
+
errors: DirectiveError[];
|
|
264
|
+
data?: unknown; // De return directive ou halt
|
|
265
|
+
counters: FrameCounters;
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
**Nunca lança exceções.** Erros são coletados em `result.errors`. Exceções de handlers são capturadas e reportadas.
|
|
270
|
+
|
|
271
|
+
### Sub-Actions (Visibilidade)
|
|
272
|
+
|
|
273
|
+
Ações com `/` no ID são privadas:
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
engine.register([
|
|
277
|
+
{ id: "checkout/validate", directives: [...] }, // privada
|
|
278
|
+
{ id: "checkout", directives: [
|
|
279
|
+
{ type: "action", id: "checkout/validate" }, // ok — escopo do pai
|
|
280
|
+
]},
|
|
281
|
+
]);
|
|
282
|
+
|
|
283
|
+
engine.invoke("checkout/validate"); // erro — não acessível da raiz
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## Lifecycle Events
|
|
287
|
+
|
|
288
|
+
O engine emite eventos de ciclo de vida via `on()`. Retorna função de unsubscribe idempotente:
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
const unsub = engine.on("register", (event) => {
|
|
292
|
+
console.log("Registrados:", event.registered);
|
|
293
|
+
console.log("Resultado:", event.result);
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
engine.on("unregister", (event) => {
|
|
297
|
+
console.log("Removido:", event.id);
|
|
298
|
+
console.log("Cascata:", event.cascaded);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
// Cancelar subscription
|
|
302
|
+
unsub();
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
| Evento | Payload | Quando |
|
|
306
|
+
|--------|---------|--------|
|
|
307
|
+
| `register` | `{ actions, result, registered }` | Após `register()` ou `endBatch()` |
|
|
308
|
+
| `unregister` | `{ id, cascaded }` | Após `unregister()` |
|
|
309
|
+
|
|
310
|
+
Em batch mode, o evento `register` é emitido uma única vez no `endBatch()` com todas as actions do batch.
|
|
311
|
+
|
|
312
|
+
## Read-Only Accessors
|
|
313
|
+
|
|
314
|
+
Acessores de leitura para introspection por camadas externas (e.g. analyzer):
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
// Map de handler definitions V2 registradas
|
|
318
|
+
engine.handlerDefinitions; // ReadonlyMap<string, HandlerDefinition>
|
|
319
|
+
|
|
320
|
+
// Set de IDs de todas as actions no registry
|
|
321
|
+
engine.registeredIds; // ReadonlySet<string>
|
|
322
|
+
|
|
323
|
+
// Lê uma action definition pelo ID
|
|
324
|
+
engine.getActionDefinition("heal"); // ActionDefinition | undefined
|
|
325
|
+
|
|
326
|
+
// Campo de tipo pra dispatch de diretivas
|
|
327
|
+
engine.typeField; // string (default: "type")
|
|
328
|
+
|
|
329
|
+
// Mapa de permissões de diretivas (computado no boot, imutável)
|
|
330
|
+
engine.directivePermissions; // ReadonlyMap<string, DirectivePermission>
|
|
331
|
+
|
|
332
|
+
// Slots de hooks preenchidos (nomes dos hooks registrados)
|
|
333
|
+
engine.directiveHookSlots; // ReadonlySet<string>
|
|
334
|
+
|
|
335
|
+
// Slots async (nomes dos hooks que são async)
|
|
336
|
+
engine.asyncSlots; // ReadonlySet<string>
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
## Modos de Compilação
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
createActionEngine({ handlers, mode: "interpret" }); // Interpretador loop-based (dev)
|
|
343
|
+
createActionEngine({ handlers, mode: "jit" }); // Compila tudo no register (prod)
|
|
344
|
+
createActionEngine({ handlers, mode: "auto" }); // Interpreta primeiro, promove após N invocações (padrão)
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
| Modo | Register | Runtime | Ideal para |
|
|
348
|
+
|------|----------|---------|------------|
|
|
349
|
+
| `interpret` | Rápido | Loop interpretado | Desenvolvimento, debug |
|
|
350
|
+
| `jit` | Mais lento (compila) | `new Function` compilado | Produção, ações estáveis |
|
|
351
|
+
| `auto` | Rápido | Promove per-action após threshold | Uso geral (padrão) |
|
|
352
|
+
|
|
353
|
+
Threshold de auto-promote (padrão: 8):
|
|
354
|
+
|
|
355
|
+
```typescript
|
|
356
|
+
createActionEngine({ handlers, mode: "auto", autoJitThreshold: 4 });
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
Forçar compilação de todas as ações registradas:
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
engine.compile();
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
Informações de compilação:
|
|
366
|
+
|
|
367
|
+
```typescript
|
|
368
|
+
engine.isAsync; // true se hooks async detectados
|
|
369
|
+
engine.compilationMode; // "interpret" | "jit" (modo atual, não o requestado)
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
## Hooks
|
|
373
|
+
|
|
374
|
+
Três pontos de hook em nível de diretiva:
|
|
375
|
+
|
|
376
|
+
```typescript
|
|
377
|
+
createActionEngine({
|
|
378
|
+
handlers,
|
|
379
|
+
directiveHooks: {
|
|
380
|
+
beforeDirective(directive, frame) {
|
|
381
|
+
// Retorne "skip" pra pular, "abort" pra abortar
|
|
382
|
+
// Retorne { directive } pra substituir, { ctx } pra sobrescrever contexto
|
|
383
|
+
},
|
|
384
|
+
afterDirective(directive, result, frame) {
|
|
385
|
+
// Retorne "abort" pra parar execução
|
|
386
|
+
},
|
|
387
|
+
onDirectivesComplete(result) {
|
|
388
|
+
// Fire-and-forget — observa resultado final
|
|
389
|
+
},
|
|
390
|
+
},
|
|
391
|
+
});
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
Hooks podem ser sync ou async. Hooks async tornam o engine async (`engine.isAsync === true`), exigindo `invokeAsync()`.
|
|
395
|
+
|
|
396
|
+
**Custo zero quando ausente.** A compilação JIT não emite código de hook para hooks não registrados.
|
|
397
|
+
|
|
398
|
+
## Handler Permissions
|
|
399
|
+
|
|
400
|
+
Controle declarativo de quais diretivas uma instância do engine pode usar. Útil pra governança (sandbox parent→child) e arquitetura (query actions não devem mutar estado).
|
|
401
|
+
|
|
402
|
+
```typescript
|
|
403
|
+
// Whitelist: só estes tipos permitidos
|
|
404
|
+
createActionEngine({
|
|
405
|
+
handlers,
|
|
406
|
+
allowedDirectives: ["state:query", "emit", "action"],
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
// Blacklist: estes tipos bloqueados
|
|
410
|
+
createActionEngine({
|
|
411
|
+
handlers,
|
|
412
|
+
blockedDirectives: ["state:dispatch", "emit"],
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
// Blacklist com motivações
|
|
416
|
+
createActionEngine({
|
|
417
|
+
handlers,
|
|
418
|
+
blockedDirectives: [
|
|
419
|
+
{ pattern: "state:dispatch", reason: "sandbox read-only", source: "parent:root" },
|
|
420
|
+
{ pattern: "emit", reason: "events disabled", source: "config" },
|
|
421
|
+
],
|
|
422
|
+
});
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
**Regras:**
|
|
426
|
+
- `allowedDirectives` e `blockedDirectives` são **mutuamente exclusivos**. Se ambos fornecidos: erro no constructor.
|
|
427
|
+
- Se nenhum fornecido: tudo permitido (default, zero overhead).
|
|
428
|
+
- Diretivas estáticas (`const`, `let`, `return`, `throw`) são **sempre permitidas** — não podem ser bloqueadas.
|
|
429
|
+
- Patterns com wildcard `*` são suportados: `"state:*"`, `"*:dispatch"`, `"*"`.
|
|
430
|
+
|
|
431
|
+
**O engine não bloqueia execução.** Permissions são config, não enforcement. Actions com handler denied **registram e executam normalmente**. Quem valida violations é o `ActionAnalyzer`. Quem decide a política (rejeitar boot, warning, ignorar) é o consumer.
|
|
432
|
+
|
|
433
|
+
```typescript
|
|
434
|
+
// Consultar permissões
|
|
435
|
+
const perms = engine.directivePermissions;
|
|
436
|
+
|
|
437
|
+
perms.get("emit");
|
|
438
|
+
// → { status: "available" }
|
|
439
|
+
|
|
440
|
+
perms.get("state:dispatch");
|
|
441
|
+
// → { status: "denied", reason: "sandbox read-only", source: "parent:root" }
|
|
442
|
+
|
|
443
|
+
// Handler inexistente: não está no mapa (unavailable é derivado)
|
|
444
|
+
perms.has("notify");
|
|
445
|
+
// → false
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
## Referência de Configuração
|
|
449
|
+
|
|
450
|
+
```typescript
|
|
451
|
+
interface IActionEngineConfig<TCtx> {
|
|
452
|
+
handlers: HandlerInputMap<TCtx>; // Obrigatório — handlers de diretivas
|
|
453
|
+
typeField?: string; // Campo de dispatch (padrão: "type")
|
|
454
|
+
directiveHooks?: DirectiveHooks<TCtx>; // Hooks de diretiva
|
|
455
|
+
limits?: Partial<FrameLimits>; // maxDepth (10), maxRules, maxDirectives
|
|
456
|
+
mode?: "interpret" | "jit" | "auto"; // Modo de compilação (padrão: "auto")
|
|
457
|
+
autoJitThreshold?: number; // Auto-promote após N invocações (padrão: 8)
|
|
458
|
+
allowedDirectives?: DirectivePermissionEntry[]; // Whitelist com pattern matching
|
|
459
|
+
blockedDirectives?: DirectivePermissionEntry[]; // Blacklist com pattern matching
|
|
460
|
+
}
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
## Exports
|
|
464
|
+
|
|
465
|
+
```typescript
|
|
466
|
+
// Factory
|
|
467
|
+
import { createActionEngine } from "@statedelta-actions/actions";
|
|
468
|
+
|
|
469
|
+
// Tipos (V1)
|
|
470
|
+
import type {
|
|
471
|
+
DirectiveHandler,
|
|
472
|
+
DirectiveHandlerMap,
|
|
473
|
+
DirectiveHooks,
|
|
474
|
+
DirectiveExecutorFn,
|
|
475
|
+
DirectiveRunnerFn,
|
|
476
|
+
} from "@statedelta-actions/actions";
|
|
477
|
+
|
|
478
|
+
// Tipos (V2)
|
|
479
|
+
import type {
|
|
480
|
+
HandlerDefinition,
|
|
481
|
+
HandlerAnalysis,
|
|
482
|
+
ValidationResult,
|
|
483
|
+
HandlerInput,
|
|
484
|
+
HandlerInputMap,
|
|
485
|
+
ActionDefinition,
|
|
486
|
+
RegisterResult,
|
|
487
|
+
RegisterError,
|
|
488
|
+
RegisterWarning,
|
|
489
|
+
IActionEngineConfig,
|
|
490
|
+
IActionEngine,
|
|
491
|
+
} from "@statedelta-actions/actions";
|
|
492
|
+
|
|
493
|
+
// Lifecycle Events
|
|
494
|
+
import type {
|
|
495
|
+
RegisterEvent,
|
|
496
|
+
UnregisterEvent,
|
|
497
|
+
EngineEventMap,
|
|
498
|
+
EngineEventName,
|
|
499
|
+
} from "@statedelta-actions/actions";
|
|
500
|
+
|
|
501
|
+
// Directive Permissions
|
|
502
|
+
import type {
|
|
503
|
+
DirectivePermissionStatus,
|
|
504
|
+
DirectivePermission,
|
|
505
|
+
DirectivePermissionConfig,
|
|
506
|
+
DirectivePermissionEntry,
|
|
507
|
+
} from "@statedelta-actions/actions";
|
|
508
|
+
|
|
509
|
+
// Register Pipeline
|
|
510
|
+
import { normalizeDirectives, RESERVED_TYPES } from "@statedelta-actions/actions";
|
|
511
|
+
|
|
512
|
+
// Emitter
|
|
513
|
+
import { SimpleEmitter } from "@statedelta-actions/actions";
|
|
514
|
+
import type { Listener } from "@statedelta-actions/actions";
|
|
515
|
+
|
|
516
|
+
// Interpreter
|
|
517
|
+
import { createDirectiveInterpreter } from "@statedelta-actions/actions";
|
|
518
|
+
|
|
519
|
+
// JIT (generic)
|
|
520
|
+
import { buildDirectiveExecutor } from "@statedelta-actions/actions";
|
|
521
|
+
import type { GeneratedDirectiveExecutor } from "@statedelta-actions/actions";
|
|
522
|
+
|
|
523
|
+
// JIT (per-action)
|
|
524
|
+
import { buildActionExecutor } from "@statedelta-actions/actions";
|
|
525
|
+
import type { GeneratedActionExecutor } from "@statedelta-actions/actions";
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
## Analyzer
|
|
529
|
+
|
|
530
|
+
Funcionalidades de análise estática foram extraídas para o pacote `@statedelta-actions/analyzer`:
|
|
531
|
+
|
|
532
|
+
- **Grafo de dependências** — capabilities, leaf, maxDepth, ciclos
|
|
533
|
+
- **Declarations** — trust boundaries, conflitos de contrato
|
|
534
|
+
- **Propagators** — propriedades computadas e propagadas pelo grafo
|
|
535
|
+
- **Composition Control** — manifest declarativo, tags, matchers, transitividade
|
|
536
|
+
|
|
537
|
+
O analyzer consome os read-only accessors e lifecycle events do engine. Consulte a documentação do `@statedelta-actions/analyzer` para detalhes.
|
|
538
|
+
|
|
539
|
+
## Licença
|
|
540
|
+
|
|
541
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
'use strict';var core=require('@statedelta-actions/core');var F=class{_listeners=new Map;on(e,i){let n=this._listeners.get(e);n||(n=new Set,this._listeners.set(e,n)),n.add(i);let s=false;return ()=>{s||(s=true,n.delete(i),n.size===0&&this._listeners.delete(e));}}once(e,i){let n=this.on(e,s=>{n(),i(s);});return n}emit(e,i){let n=this._listeners.get(e);if(!n||n.size===0)return;let s=[...n];for(let r=0;r<s.length;r++)try{s[r](i);}catch(c){console.error(`[SimpleEmitter] Listener error on "${e}":`,c);}}listenerCount(e){return this._listeners.get(e)?.size??0}hasListeners(e){let i=this._listeners.get(e);return i!==void 0&&i.size>0}off(e){this._listeners.delete(e);}removeAllListeners(){this._listeners.clear();}};var S=new Set(["const","let","return","throw"]);function W(t,e,i,n){let s=[];for(let r=0;r<t.directives.length;r++){let c=t.directives[r];if("const"in c||"let"in c||"return"in c||"throw"in c)continue;let h=c[n];if(!h){s.push(`directive[${r}]: missing type field "${n}"`);continue}if(!e[h]){s.push(`directive[${r}]: no handler for type "${h}"`);continue}let x=i.get(h);if(x?.validate)try{let D=x.validate(c);D&&!D.valid&&s.push(`directive[${r}]: ${D.error??"validation failed"}`);}catch(D){s.push(`directive[${r}]: validate threw: ${D}`);}let m=c.catch;if(m&&Array.isArray(m))for(let D=0;D<m.length;D++){let R=m[D];if("const"in R||"let"in R||"return"in R||"throw"in R)continue;let v=R[n];v&&!e[v]&&s.push(`directive[${r}].catch[${D}]: no handler for type "${v}"`);}}return s}function H(t,e){let i=t.length,n=new Array(i);for(let s=0;s<i;s++){let r=t[s];if("const"in r)n[s]=q(r,"const");else if("let"in r)n[s]=q(r,"let");else if("return"in r)n[s]=te(r);else if("throw"in r)n[s]=re(r);else {let c=r.catch;Array.isArray(c)&&c.length>0?n[s]={...r,catch:H(c)}:n[s]=r;}}return n}function q(t,e){let{[e]:i,...n}=t;return {...n,type:e,name:i}}function te(t){let{return:e,...i}=t;return {...i,type:"return",value:e}}function re(t){let{throw:e,...i}=t;return {...i,type:"throw",message:e}}function M(t,e,i,n,s,r,c){return {success:false,aborted:true,abortedBy:t,appliedCount:e,skippedCount:i,errors:n,processedCount:s,totalCount:r,counters:c}}function ie(t){let e=t.filledNames.has("beforeDirective"),i=t.filledNames.has("afterDirective"),n=t.filledNames.has("onDirectivesComplete");return function(r,c,h,x,m,D,R){let v=Math.min(r.length,R),l=[],f=0,_=0,{counters:u,scope:C}=c,A=c.ctx;for(let a=0;a<v;a++){let p=r[a],$=p[m];if($==="const"||$==="let"){let o=p.name;if(typeof p.resolve=="function")try{C[o]=p.resolve(A,C).value;}catch(d){l.push({directiveIndex:a,error:`Binding resolve: ${d}`}),u.errors++;}else C[o]=p.value;continue}if($==="return"){let o=a+1;if(typeof p.resolve=="function")try{let d=p.resolve(A,C),b="value"in d?d.value:"return"in d?d.return:p.value;return {success:!0,aborted:!1,appliedCount:f,skippedCount:_,errors:l,processedCount:o,totalCount:v,counters:u,data:b}}catch(d){l.push({directiveIndex:a,error:`Control resolve: ${d}`}),u.errors++;}else return {success:true,aborted:false,appliedCount:f,skippedCount:_,errors:l,processedCount:o,totalCount:v,counters:u,data:p.value};continue}if($==="throw"){let o=a+1;if(typeof p.resolve=="function")try{let d=p.resolve(A,C),b="message"in d?d.message:"throw"in d?d.throw:p.message;return {success:!1,aborted:!0,abortedBy:"throw",appliedCount:f,skippedCount:_,errors:l,processedCount:o,totalCount:v,counters:u,data:b}}catch(d){l.push({directiveIndex:a,error:`Control resolve: ${d}`}),u.errors++;}else return {success:false,aborted:true,abortedBy:"throw",appliedCount:f,skippedCount:_,errors:l,processedCount:o,totalCount:v,counters:u,data:p.message};continue}let y=p,E=c;if(e)try{let o=core.processIntercept(x.beforeDirective(y,c),"beforeDirective");if(o.action===core.Intercept.SKIP){_++,u.directivesSkipped++;continue}if(o.action===core.Intercept.ABORT)return M(o.abortReason,f,_,l,a,v,u);o.ctx!==void 0&&(E=c.withCtx(o.ctx)),o.directive!==void 0&&(y=o.directive);}catch(o){l.push({directiveIndex:a,error:`Hook beforeDirective: ${o}`}),u.errors++;}if(typeof y.resolve=="function")try{let o=y.resolve(E.ctx,E.scope);y={...y,...o};}catch(o){l.push({directiveIndex:a,error:`Directive resolve: ${o}`}),u.errors++;continue}let w=y[m],I=w?h[w]:void 0;if(!I){l.push({directiveIndex:a,error:`No handler for directive type: ${w??"undefined"}`}),u.errors++;continue}let g;try{g=I(y,E,D);}catch(o){g={ok:false,error:String(o)};}let T=a+1;if(g.ok){if(f++,u.directivesApplied++,typeof y.as=="string"&&(C[y.as]=g.data),g.halt)return {success:true,aborted:true,abortedBy:"halt",appliedCount:f,skippedCount:_,errors:l,processedCount:T,totalCount:v,counters:u,data:g.data}}else {if(g.halt)return {success:false,aborted:true,abortedBy:"halt",appliedCount:f,skippedCount:_,errors:l,processedCount:T,totalCount:v,counters:u,data:g.data};let o=y.catch;if(Array.isArray(o)&&o.length>0){C.$exception=g.error??"handler failed";let d=D.runDirectives(o,E);f+=d.appliedCount;for(let b=0;b<d.errors.length;b++)l.push(d.errors[b]);if(d.aborted)return {success:d.success,aborted:true,abortedBy:d.abortedBy,appliedCount:f,skippedCount:_,errors:l,processedCount:T,totalCount:v,counters:u,data:d.data}}else l.push({directiveIndex:a,error:g.error??"handler failed"}),u.errors++;}if(i)try{if(x.afterDirective(y,g,E)==="abort")return M("afterDirective",f,_,l,T,v,u)}catch(o){l.push({directiveIndex:a,error:`Hook afterDirective: ${o}`}),u.errors++;}}let k={success:true,aborted:false,appliedCount:f,skippedCount:_,errors:l,processedCount:v,totalCount:v,counters:u};if(n)try{x.onDirectivesComplete(k);}catch{}return k}}function ne(t){let e=t.filledNames.has("beforeDirective"),i=t.filledNames.has("afterDirective"),n=t.filledNames.has("onDirectivesComplete");return async function(r,c,h,x,m,D,R){let v=Math.min(r.length,R),l=[],f=0,_=0,{counters:u,scope:C}=c,A=c.ctx;for(let a=0;a<v;a++){let p=r[a],$=p[m];if($==="const"||$==="let"){let o=p.name;if(typeof p.resolve=="function")try{C[o]=p.resolve(A,C).value;}catch(d){l.push({directiveIndex:a,error:`Binding resolve: ${d}`}),u.errors++;}else C[o]=p.value;continue}if($==="return"){let o=a+1;if(typeof p.resolve=="function")try{let d=p.resolve(A,C),b="value"in d?d.value:"return"in d?d.return:p.value;return {success:!0,aborted:!1,appliedCount:f,skippedCount:_,errors:l,processedCount:o,totalCount:v,counters:u,data:b}}catch(d){l.push({directiveIndex:a,error:`Control resolve: ${d}`}),u.errors++;}else return {success:true,aborted:false,appliedCount:f,skippedCount:_,errors:l,processedCount:o,totalCount:v,counters:u,data:p.value};continue}if($==="throw"){let o=a+1;if(typeof p.resolve=="function")try{let d=p.resolve(A,C),b="message"in d?d.message:"throw"in d?d.throw:p.message;return {success:!1,aborted:!0,abortedBy:"throw",appliedCount:f,skippedCount:_,errors:l,processedCount:o,totalCount:v,counters:u,data:b}}catch(d){l.push({directiveIndex:a,error:`Control resolve: ${d}`}),u.errors++;}else return {success:false,aborted:true,abortedBy:"throw",appliedCount:f,skippedCount:_,errors:l,processedCount:o,totalCount:v,counters:u,data:p.message};continue}let y=p,E=c;if(e)try{let o=core.processIntercept(await x.beforeDirective(y,c),"beforeDirective");if(o.action===core.Intercept.SKIP){_++,u.directivesSkipped++;continue}if(o.action===core.Intercept.ABORT)return M(o.abortReason,f,_,l,a,v,u);o.ctx!==void 0&&(E=c.withCtx(o.ctx)),o.directive!==void 0&&(y=o.directive);}catch(o){l.push({directiveIndex:a,error:`Hook beforeDirective: ${o}`}),u.errors++;}if(typeof y.resolve=="function")try{let o=y.resolve(E.ctx,E.scope);y={...y,...o};}catch(o){l.push({directiveIndex:a,error:`Directive resolve: ${o}`}),u.errors++;continue}let w=y[m],I=w?h[w]:void 0;if(!I){l.push({directiveIndex:a,error:`No handler for directive type: ${w??"undefined"}`}),u.errors++;continue}let g;try{g=I(y,E,D);}catch(o){g={ok:false,error:String(o)};}let T=a+1;if(g.ok){if(f++,u.directivesApplied++,typeof y.as=="string"&&(C[y.as]=g.data),g.halt)return {success:true,aborted:true,abortedBy:"halt",appliedCount:f,skippedCount:_,errors:l,processedCount:T,totalCount:v,counters:u,data:g.data}}else {if(g.halt)return {success:false,aborted:true,abortedBy:"halt",appliedCount:f,skippedCount:_,errors:l,processedCount:T,totalCount:v,counters:u,data:g.data};let o=y.catch;if(Array.isArray(o)&&o.length>0){C.$exception=g.error??"handler failed";let d=await D.runDirectivesAsync(o,E);f+=d.appliedCount;for(let b=0;b<d.errors.length;b++)l.push(d.errors[b]);if(d.aborted)return {success:d.success,aborted:true,abortedBy:d.abortedBy,appliedCount:f,skippedCount:_,errors:l,processedCount:T,totalCount:v,counters:u,data:d.data}}else l.push({directiveIndex:a,error:g.error??"handler failed"}),u.errors++;}if(i)try{if(await x.afterDirective(y,g,E)==="abort")return M("afterDirective",f,_,l,T,v,u)}catch(o){l.push({directiveIndex:a,error:`Hook afterDirective: ${o}`}),u.errors++;}}let k={success:true,aborted:false,appliedCount:f,skippedCount:_,errors:l,processedCount:v,totalCount:v,counters:u};if(n)try{await x.onDirectivesComplete(k);}catch{}return k}}function B(t,e){return e?ne(t):ie(t)}function j(t){let{filledNames:e,hasAnyAsync:i}=t,n=m=>e.has(m),s=m=>t.asyncNames.has(m)?"await ":"",r=[];for(let m of e)r.push(`const ${m} = directiveHooks.${m};`);r.push("const len = Math.min(directives.length, maxDirectives);"),r.push("const errors = []; let appliedCount = 0; let skippedCount = 0;"),r.push("const counters = frame.counters;"),r.push("for (let i = 0; i < len; i++) {"),r.push(" let directive = directives[i];"),n("beforeDirective")?(r.push(" let _df = frame;"),r.push(" try {"),r.push(` const _bd = ${s("beforeDirective")}beforeDirective(directive, frame);`),r.push(' if (_bd === "skip") { skippedCount++; counters.directivesSkipped++; continue; }'),r.push(' if (_bd === "abort") return { success: false, aborted: true, abortedBy: "beforeDirective", appliedCount, skippedCount, errors, processedCount: i, totalCount: len, counters };'),r.push(' if (typeof _bd === "object" && _bd !== null) {'),r.push(' if ("abort" in _bd) return { success: false, aborted: true, abortedBy: _bd.abort, appliedCount, skippedCount, errors, processedCount: i, totalCount: len, counters };'),r.push(' if ("ctx" in _bd) _df = frame.withCtx(_bd.ctx);'),r.push(' if ("directive" in _bd) directive = _bd.directive;'),r.push(" }"),r.push(' } catch (_e) { errors.push({ directiveIndex: i, error: "Hook beforeDirective: " + _e }); counters.errors++; }')):r.push(" const _df = frame;"),r.push(' if (typeof directive.resolve === "function") {'),r.push(" try { const _r = directive.resolve(_df.ctx); directive = Object.assign({}, directive, _r); }"),r.push(' catch (_e) { errors.push({ directiveIndex: i, error: "Directive resolve: " + _e }); counters.errors++; continue; }'),r.push(" }"),r.push(" const _type = directive[typeField];"),r.push(" const _handler = _type ? handlers[_type] : undefined;"),r.push(' if (!_handler) { errors.push({ directiveIndex: i, error: "No handler for directive type: " + (_type || "undefined") }); counters.errors++; continue; }'),r.push(" let _result;"),r.push(" try { _result = _handler(directive, _df, engine); } catch (_e) { errors.push({ directiveIndex: i, error: String(_e) }); counters.errors++; continue; }"),r.push(" if (_result.ok) { appliedCount++; counters.directivesApplied++; }"),r.push(' else { errors.push({ directiveIndex: i, error: _result.error || "handler failed" }); counters.errors++; }'),n("afterDirective")&&(r.push(" try {"),r.push(` const _ad = ${s("afterDirective")}afterDirective(directive, _result, _df);`),r.push(' if (_ad === "abort") return { success: false, aborted: true, abortedBy: "afterDirective", appliedCount, skippedCount, errors, processedCount: i + 1, totalCount: len, counters };'),r.push(' } catch (_e) { errors.push({ directiveIndex: i, error: "Hook afterDirective: " + _e }); counters.errors++; }')),r.push("}"),r.push("const _finalResult = { success: true, aborted: false, appliedCount, skippedCount, errors, processedCount: len, totalCount: len, counters };"),n("onDirectivesComplete")&&r.push(`try { ${s("onDirectivesComplete")}onDirectivesComplete(_finalResult); } catch (_e) {}`),r.push("return _finalResult;");let c=r.join(`
|
|
2
|
+
`),h=i?"async ":"";return {fn:new Function("directives","frame","handlers","directiveHooks","typeField","engine","maxDirectives",`"use strict";
|
|
3
|
+
return (${h}() => {
|
|
4
|
+
${c}
|
|
5
|
+
})();`),isAsync:i}}function N(t,e,i){return `return{success:false,aborted:true,abortedBy:${t},appliedCount,skippedCount,errors,processedCount:${e},totalCount:${i},counters};`}function se(t,e){return `return{success:true,aborted:true,abortedBy:"halt",appliedCount,skippedCount,errors,processedCount:${t},totalCount:${e},counters,data:_result.data};`}function oe(t,e){return `return{success:false,aborted:true,abortedBy:"halt",appliedCount,skippedCount,errors,processedCount:${t},totalCount:${e},counters,data:_result.data};`}function ce(t,e,i){let n=JSON.stringify(e.name);typeof e.resolve=="function"?(t.push("try{"),t.push(`scope[${n}]=$.d[${i}].resolve(ctx,scope).value;`),t.push(`}catch(_e){errors.push({directiveIndex:${i},error:"Binding resolve: "+_e});counters.errors++;}`)):t.push(`scope[${n}]=$.d[${i}].value;`);}function ae(t,e,i,n){let s=typeof e.resolve=="function",r=i+1;s?(t.push("try{"),t.push(`const _rv=$.d[${i}].resolve(ctx,scope);`),t.push(`return{success:true,aborted:false,appliedCount,skippedCount,errors,processedCount:${r},totalCount:${n},counters,data:"value" in _rv?_rv.value:"return" in _rv?_rv.return:$.d[${i}].value};`),t.push(`}catch(_e){errors.push({directiveIndex:${i},error:"Control resolve: "+_e});counters.errors++;}`)):t.push(`return{success:true,aborted:false,appliedCount,skippedCount,errors,processedCount:${r},totalCount:${n},counters,data:$.d[${i}].value};`);}function ue(t,e,i,n){let s=typeof e.resolve=="function",r=i+1;s?(t.push("try{"),t.push(`const _rv=$.d[${i}].resolve(ctx,scope);`),t.push(`return{success:false,aborted:true,abortedBy:"throw",appliedCount,skippedCount,errors,processedCount:${r},totalCount:${n},counters,data:"message" in _rv?_rv.message:"throw" in _rv?_rv.throw:$.d[${i}].message};`),t.push(`}catch(_e){errors.push({directiveIndex:${i},error:"Control resolve: "+_e});counters.errors++;}`)):t.push(`return{success:false,aborted:true,abortedBy:"throw",appliedCount,skippedCount,errors,processedCount:${r},totalCount:${n},counters,data:$.d[${i}].message};`);}function de(t,e,i,n,s,r,c,h,x,m){let D=e[s],R=r.get(D),v=typeof e.resolve=="function",l=typeof e.as=="string",f=Array.isArray(e.catch)&&e.catch.length>0,_=l?JSON.stringify(e.as):"",u=i+1;t.push(`_b${i}:{`);let C=c||v,A=c;C&&t.push(`let _dir=$.d[${i}];`),A&&t.push("let _df=frame;"),c&&(t.push("try{"),t.push(`const _bd=${x}_hookBefore($.d[${i}],frame);`),t.push(`if(_bd==="skip"){skippedCount++;counters.directivesSkipped++;break _b${i};}`),t.push(`if(_bd==="abort")${N('"beforeDirective"',i,n)}`),t.push('if(typeof _bd==="object"&&_bd!==null){'),t.push(`if("abort" in _bd)${N("_bd.abort",i,n)}`),t.push('if("ctx" in _bd)_df=frame.withCtx(_bd.ctx);'),t.push('if("directive" in _bd)_dir=_bd.directive;'),t.push("}"),t.push(`}catch(_e){errors.push({directiveIndex:${i},error:"Hook beforeDirective: "+_e});counters.errors++;}`)),c?(t.push('if(typeof _dir.resolve==="function"){'),t.push("try{const _r=_dir.resolve(_df.ctx,scope);_dir=Object.assign({},_dir,_r);}"),t.push(`catch(_e){errors.push({directiveIndex:${i},error:"Directive resolve: "+_e});counters.errors++;break _b${i};}}`)):v&&(t.push(`try{const _r=$.d[${i}].resolve(ctx,scope);_dir=Object.assign({},$.d[${i}],_r);}`),t.push(`catch(_e){errors.push({directiveIndex:${i},error:"Directive resolve: "+_e});counters.errors++;break _b${i};}`));let k=C?"_dir":`$.d[${i}]`,a=A?"_df":"frame";t.push("let _result;"),t.push(`try{_result=${R}(${k},${a},$.engine);}`),t.push("catch(_e){_result={ok:false,error:String(_e)};}"),t.push("if(_result.ok){"),t.push("appliedCount++;counters.directivesApplied++;"),l&&t.push(`scope[${_}]=_result.data;`),t.push(`if(_result.halt)${se(u,n)}`),t.push("}else{"),t.push(`if(_result.halt)${oe(u,n)}`),f?(t.push('scope.$exception=_result.error||"handler failed";'),t.push(`const _cr=$.runner($.d[${i}].catch,${a});`),t.push("appliedCount+=_cr.appliedCount;"),t.push("for(let _j=0;_j<_cr.errors.length;_j++)errors.push(_cr.errors[_j]);"),t.push(`if(_cr.aborted)return{success:_cr.success,aborted:true,abortedBy:_cr.abortedBy,appliedCount,skippedCount,errors,processedCount:${u},totalCount:${n},counters,data:_cr.data};`)):t.push(`errors.push({directiveIndex:${i},error:_result.error||"handler failed"});counters.errors++;`),t.push("}"),h&&(t.push("try{"),t.push(`const _ad=${m}_hookAfter(${k},_result,${a});`),t.push(`if(_ad==="abort")${N('"afterDirective"',u,n)}`),t.push(`}catch(_e){errors.push({directiveIndex:${i},error:"Hook afterDirective: "+_e});counters.errors++;}`)),t.push("}");}function O(t,e,i){let n=[],s=t.length,{filledNames:r,hasAnyAsync:c,asyncNames:h}=e,x=r.has("beforeDirective"),m=r.has("afterDirective"),D=r.has("onDirectivesComplete"),R=h.has("beforeDirective")?"await ":"",v=h.has("afterDirective")?"await ":"",l=h.has("onDirectivesComplete")?"await ":"",f=new Set;for(let a of t){let p=a[i];p&&!S.has(p)&&f.add(p);}let _=new Map,u=0;for(let a of f)_.set(a,`_h${u++}`);n.push("const counters=frame.counters;"),n.push("const ctx=frame.ctx;"),n.push("const errors=[];"),n.push("let appliedCount=0;"),n.push("let skippedCount=0;");for(let[a,p]of _)n.push(`const ${p}=$.h[${JSON.stringify(a)}];`);x&&n.push("const _hookBefore=$.hooks.beforeDirective;"),m&&n.push("const _hookAfter=$.hooks.afterDirective;");for(let a=0;a<t.length;a++){let p=t[a];switch(p[i]){case "const":case "let":ce(n,p,a);break;case "return":ae(n,p,a,s);break;case "throw":ue(n,p,a,s);break;default:de(n,p,a,s,i,_,x,m,R,v);break}}n.push(`const _finalResult={success:true,aborted:false,appliedCount,skippedCount,errors,processedCount:${s},totalCount:${s},counters};`),D&&n.push(`try{${l}$.hooks.onDirectivesComplete(_finalResult);}catch(_e){}`),n.push("return _finalResult;");let C=n.join(`
|
|
6
|
+
`),A=c?"async ":"";return {fn:new Function("frame","scope","$",`"use strict";
|
|
7
|
+
return(${A}()=>{
|
|
8
|
+
${C}
|
|
9
|
+
})();`),isAsync:c}}function K(t,e,i){return {success:false,aborted:false,appliedCount:0,skippedCount:0,processedCount:0,totalCount:0,errors:[{directiveIndex:-1,error:e}],counters:t,...i}}function U(t,e){return K(e,`Action not found: "${t}"`)}function G(t,e,i){return K(i,`Max depth ${e} exceeded invoking "${t}"`,{aborted:true,abortedBy:"maxDepth"})}function Y(t,e){return K(e,`Action "${t}" is private (sub-action). Can only be invoked from within its parent scope.`)}function le(t){return typeof t=="object"&&t!==null&&"execute"in t}function Q(t){let e=Object.create(null),i=new Map;for(let n of Object.keys(t)){let s=t[n];le(s)?(e[n]=s.execute,i.set(n,s)):(e[n]=s,i.set(n,{execute:s}));}return {handlers:e,definitions:i}}function X(t,e){if(t==="*")return true;if(!t.includes("*"))return t===e;let i=t.replace(/[.+^${}()|[\]\\]/g,"\\$&");return new RegExp("^"+i.replace(/\*/g,".*")+"$").test(e)}function Z(t){return typeof t=="string"?{pattern:t}:t}function L(t,e,i){let n=new Map;if(!e&&!i){for(let s of t)n.set(s,{status:"available"});return n}if(e){let s=e.map(Z);for(let r of t){let c=s.some(h=>X(h.pattern,r));n.set(r,{status:c?"available":"denied"});}}else {let s=i.map(Z);for(let r of t){let c=s.find(h=>X(h.pattern,r));c?n.set(r,{status:"denied",reason:c.reason,source:c.source}):n.set(r,{status:"available"});}}return n}var ve=8,fe="type",z={maxDepth:10,maxRules:1e4,maxDirectives:1e5},V=class{_directiveExecutor;_mode;_ctx;_requestedMode;_threshold;_limits;_typeField;_handlers;_definitions;_directiveHooks;_directiveAnalysis;_isAsync;_directivePermissions;_registry=new Map;_emitter=new F;_batchDepth=0;_batchErrors=[];_batchWarnings=[];_batchActions=[];_batchRegistered=[];_registeredIds=new Set;_directiveRunner;_engineRef;constructor(e){this._requestedMode=e.mode??"auto",this._threshold=e.autoJitThreshold??ve,this._typeField=e.typeField??fe,this._limits={maxDepth:e.limits?.maxDepth??z.maxDepth,maxRules:e.limits?.maxRules??z.maxRules,maxDirectives:e.limits?.maxDirectives??z.maxDirectives};let{handlers:i,definitions:n}=Q(e.handlers);for(let s of Object.keys(e.handlers))if(S.has(s))throw new Error(`Handler type "${s}" is reserved for engine-internal directives`);if(e.allowedDirectives&&e.blockedDirectives)throw new Error("allowedDirectives and blockedDirectives are mutually exclusive. Provide one or neither.");this._handlers=i,this._definitions=n,this._directivePermissions=L(Object.keys(i),e.allowedDirectives,e.blockedDirectives),this._directiveHooks=e.directiveHooks??{},this._directiveAnalysis=core.analyzeSlots(this._directiveHooks,core.DIRECTIVE_SLOT_NAMES),this._isAsync=this._directiveAnalysis.hasAnyAsync,this._engineRef={runDirectives:(s,r)=>this._executeDirectives(s,r),runDirectivesAsync:(s,r)=>this._executeDirectivesAsync(s,r),invoke:(s,r,c)=>this._invokeInternal(s,r,c),invokeAsync:(s,r,c)=>this._invokeInternalAsync(s,r,c),evaluateRules:()=>{throw new Error("evaluateRules() not available in ActionEngine context. Use createRuleEngine().")},evaluateRulesAsync:()=>{throw new Error("evaluateRulesAsync() not available in ActionEngine context. Use createRuleEngine().")}},this._directiveRunner=(s,r)=>this._directiveExecutor(s,r,this._handlers,this._directiveHooks,this._typeField,this._engineRef,this._limits.maxDirectives),this._requestedMode==="jit"?(this._directiveExecutor=this._buildJitDirectiveExecutor(),this._mode="jit"):(this._directiveExecutor=B(this._directiveAnalysis,this._isAsync),this._mode="interpret");}register(e){let i=[],n=[],s=[],r=[];for(let h of e){if(!h.id){n.push({actionId:"",error:"Action must have an id"});continue}let x=W(h,this._handlers,this._definitions,this._typeField);if(x.length>0){for(let m of x)n.push({actionId:h.id,error:m});continue}r.push(h);}for(let h of r){let x=H(h.directives,this._typeField),m={definition:h,directives:x,compiled:null,invokeCount:0};this._requestedMode==="jit"&&(m.compiled=this._compileAction(x)),this._registry.set(h.id,m),this._registeredIds.add(h.id),i.push(h.id);}if(this._batchDepth>0){this._batchErrors.push(...n),this._batchWarnings.push(...s);for(let h of r)this._batchActions.push(h);return this._batchRegistered.push(...i),{registered:i,errors:n,warnings:s}}let c={registered:i,errors:n,warnings:s};return i.length>0&&this._emitter.emit("register",{actions:r,result:c,registered:i}),c}unregister(e){let i=this._registry.delete(e);i&&this._registeredIds.delete(e);let n=e+"/",s=[];for(let r of this._registry.keys())r.startsWith(n)&&(this._registry.delete(r),this._registeredIds.delete(r),s.push(r));return i&&this._emitter.emit("unregister",{id:e,cascaded:s}),i}invoke(e,i){if(this._isAsync)throw new Error("Cannot call invoke() with async hooks. Use invokeAsync() instead.");let n=this._requireCtx(),s=core.createRootFrame(n,this._limits);return this._invokeInternal(e,i,s)}async invokeAsync(e,i){let n=this._requireCtx(),s=core.createRootFrame(n,this._limits);return this._invokeInternalAsync(e,i,s)}setContext(e){this._ctx=e;}context(e,i){let n=this._ctx;this._ctx=e;try{return i()}finally{this._ctx=n;}}beginBatch(){this._batchDepth++,this._batchDepth===1&&(this._batchErrors=[],this._batchWarnings=[],this._batchActions=[],this._batchRegistered=[]);}endBatch(){if(this._batchDepth<=0)throw new Error("endBatch() called without matching beginBatch()");if(this._batchDepth--,this._batchDepth>0)return {registered:[],errors:[],warnings:[]};let e={registered:this._batchRegistered,errors:this._batchErrors,warnings:this._batchWarnings};return this._batchRegistered.length>0&&this._emitter.emit("register",{actions:this._batchActions,result:e,registered:this._batchRegistered}),e}on(e,i){return this._emitter.on(e,i)}get handlerDefinitions(){return this._definitions}get registeredIds(){return this._registeredIds}getActionDefinition(e){return this._registry.get(e)?.definition}get typeField(){return this._typeField}get directivePermissions(){return this._directivePermissions}get isAsync(){return this._isAsync}get compilationMode(){return this._mode}get directiveHookSlots(){return this._directiveAnalysis.filledNames}get asyncSlots(){return this._directiveAnalysis.asyncNames}compile(){if(this._requestedMode!=="interpret"){this._mode!=="jit"&&this._promote();for(let e of this._registry.values())e.compiled||(e.compiled=this._compileAction(e.directives));}}_invokeInternal(e,i,n){let s=this._prepareInvoke(e,i,n);if("success"in s)return s;let{stored:r,childFrame:c,childScope:h}=s;if(r.compiled)return r.compiled.fn(c,h,r.compiled.$);let x=this._directiveRunner(r.directives,c);return this._maybeAutoPromote(r),x}async _invokeInternalAsync(e,i,n){let s=this._prepareInvoke(e,i,n);if("success"in s)return s;let{stored:r,childFrame:c,childScope:h}=s;if(r.compiled)return r.compiled.fn(c,h,r.compiled.$);let x=this._directiveRunner(r.directives,c);return this._maybeAutoPromote(r),x}_prepareInvoke(e,i,n){let s=this._registry.get(e);if(!s)return U(e,n.counters);if(e.includes("/")&&!this._isAccessible(e,n))return Y(e,n.counters);if(n.depth>=n.limits.maxDepth)return G(e,n.limits.maxDepth,n.counters);let r=Object.create(n.scope);i&&Object.assign(r,i);let c=n.child("action:"+e,0,e).withScope(r);return {stored:s,childFrame:c,childScope:r}}_maybeAutoPromote(e){this._requestedMode==="auto"&&++e.invokeCount>=this._threshold&&(e.compiled=this._compileAction(e.directives),this._mode!=="jit"&&this._promote());}_executeDirectives(e,i){return this._directiveExecutor(e,i,this._handlers,this._directiveHooks,this._typeField,this._engineRef,this._limits.maxDirectives)}async _executeDirectivesAsync(e,i){return this._directiveExecutor(e,i,this._handlers,this._directiveHooks,this._typeField,this._engineRef,this._limits.maxDirectives)}_compileAction(e){let{fn:i}=O(e,this._directiveAnalysis,this._typeField),n={d:e,h:this._handlers,hooks:this._directiveHooks,engine:this._engineRef,runner:this._directiveRunner};return {fn:i,$:n}}_isAccessible(e,i){let s="action:"+e.slice(0,e.lastIndexOf("/"));return i.path.includes(s)}_requireCtx(){if(this._ctx===void 0)throw new Error("No context set. Call setContext(ctx) or use context(ctx, fn) before invoke.");return this._ctx}_promote(){this._directiveExecutor=this._buildJitDirectiveExecutor(),this._mode="jit";}_buildJitDirectiveExecutor(){let{fn:e}=j(this._directiveAnalysis);return e}};function _e(t){return new V(t)}exports.RESERVED_TYPES=S;exports.SimpleEmitter=F;exports.buildActionExecutor=O;exports.buildDirectiveExecutor=j;exports.createActionEngine=_e;exports.createDirectiveInterpreter=B;exports.normalizeDirectives=H;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { Directive, ExecutionFrame, RuleEngineRef, ApplyResult, HookFn, InterceptResult, AbortDecision, DirectiveResult, ActionEngineRef, FrameLimits, SlotAnalysis } from '@statedelta-actions/core';
|
|
2
|
+
|
|
3
|
+
type DirectiveHandler<TCtx> = (directive: Directive, frame: ExecutionFrame<TCtx>, engine: RuleEngineRef<TCtx>) => ApplyResult;
|
|
4
|
+
type DirectiveHandlerMap<TCtx> = Record<string, DirectiveHandler<TCtx>>;
|
|
5
|
+
interface DirectiveHooks<TCtx> {
|
|
6
|
+
beforeDirective?: HookFn<[
|
|
7
|
+
directive: Directive,
|
|
8
|
+
frame: ExecutionFrame<TCtx>
|
|
9
|
+
], InterceptResult<TCtx>>;
|
|
10
|
+
afterDirective?: HookFn<[
|
|
11
|
+
directive: Directive,
|
|
12
|
+
result: ApplyResult,
|
|
13
|
+
frame: ExecutionFrame<TCtx>
|
|
14
|
+
], AbortDecision>;
|
|
15
|
+
onDirectivesComplete?: HookFn<[result: DirectiveResult], void>;
|
|
16
|
+
}
|
|
17
|
+
type DirectiveExecutorFn<TCtx> = (directives: readonly Directive[], frame: ExecutionFrame<TCtx>, handlers: DirectiveHandlerMap<TCtx>, directiveHooks: DirectiveHooks<TCtx>, typeField: string, engine: RuleEngineRef<TCtx>, maxDirectives: number) => DirectiveResult | Promise<DirectiveResult>;
|
|
18
|
+
type DirectiveRunnerFn<TCtx> = (directives: readonly Directive[], frame: ExecutionFrame<TCtx>) => DirectiveResult | Promise<DirectiveResult>;
|
|
19
|
+
interface HandlerAnalysis {
|
|
20
|
+
capabilities: string[];
|
|
21
|
+
dependencies: string[];
|
|
22
|
+
}
|
|
23
|
+
interface ValidationResult {
|
|
24
|
+
valid: boolean;
|
|
25
|
+
error?: string;
|
|
26
|
+
warnings?: string[];
|
|
27
|
+
}
|
|
28
|
+
interface HandlerDefinition<TCtx> {
|
|
29
|
+
/** Register-time: extrai capabilities e dependências. */
|
|
30
|
+
analyze?: (directive: Directive) => HandlerAnalysis;
|
|
31
|
+
/** Register-time: valida estrutura da directive. */
|
|
32
|
+
validate?: (directive: Directive) => ValidationResult | void;
|
|
33
|
+
/** JIT-time: contribui código inline per-action (Fase 2c). */
|
|
34
|
+
compile?: (directive: Directive, compiler: unknown) => string | void;
|
|
35
|
+
/** Runtime: executa a directive (obrigatório). */
|
|
36
|
+
execute: (directive: Directive, frame: ExecutionFrame<TCtx>, engine: ActionEngineRef<TCtx>) => ApplyResult;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Aceita handler V1 (função) ou V2 (objeto com fases).
|
|
40
|
+
* Backwards compat: função simples é tratada como { execute: fn }.
|
|
41
|
+
*/
|
|
42
|
+
type HandlerInput<TCtx> = DirectiveHandler<TCtx> | HandlerDefinition<TCtx>;
|
|
43
|
+
type HandlerInputMap<TCtx> = Record<string, HandlerInput<TCtx>>;
|
|
44
|
+
interface ActionDefinition<TCtx = unknown> {
|
|
45
|
+
/** ID único da Action. */
|
|
46
|
+
id: string;
|
|
47
|
+
/** Directives estáticas. */
|
|
48
|
+
directives: readonly Directive<TCtx>[];
|
|
49
|
+
/**
|
|
50
|
+
* Tags categóricas explícitas (identidade).
|
|
51
|
+
* O que a action É: "controller", "service", "admin", "ui".
|
|
52
|
+
* Não propagam via grafo. Usadas pelo composition control.
|
|
53
|
+
*/
|
|
54
|
+
tags?: readonly string[];
|
|
55
|
+
/** Schema de params aceitos (opcional, Fase 2d). */
|
|
56
|
+
params?: Record<string, unknown>;
|
|
57
|
+
/** Metadata opaca pro consumer (opcional). */
|
|
58
|
+
metadata?: Record<string, unknown>;
|
|
59
|
+
/**
|
|
60
|
+
* Tier — nível mínimo necessário pra invocar esta action.
|
|
61
|
+
* Propagado transitivamente via Math.max pelo analyzer.
|
|
62
|
+
*
|
|
63
|
+
* Se declarado, o analyzer valida: tier declarado >= tier inferido (max da sub-árvore).
|
|
64
|
+
* Se não declarado, apenas infere (sem restrição).
|
|
65
|
+
*
|
|
66
|
+
* O engine ignora este campo — é puramente declarativo pro analyzer.
|
|
67
|
+
* Requer tierPropagator habilitado no grafo.
|
|
68
|
+
*/
|
|
69
|
+
tier?: number;
|
|
70
|
+
/**
|
|
71
|
+
* Declarações — contratos públicos (trust boundaries).
|
|
72
|
+
* Cada key = nome de um propagator registrado no grafo.
|
|
73
|
+
*
|
|
74
|
+
* O engine infere capabilities/propriedades via handler.analyze() + grafo.
|
|
75
|
+
* Declarations são o contrato que o autor assina.
|
|
76
|
+
* Se inferência contradiz declaração → DECLARATION_CONFLICT reportado.
|
|
77
|
+
* Dependentes veem o valor DECLARADO (barreira de propagação).
|
|
78
|
+
*
|
|
79
|
+
* Ex: { readonly: true, leaf: true }
|
|
80
|
+
*/
|
|
81
|
+
declarations?: Record<string, unknown>;
|
|
82
|
+
}
|
|
83
|
+
/** Emitido após register() ou endBatch(). */
|
|
84
|
+
interface RegisterEvent<TCtx = unknown> {
|
|
85
|
+
readonly actions: readonly ActionDefinition<TCtx>[];
|
|
86
|
+
readonly result: RegisterResult;
|
|
87
|
+
readonly registered: readonly string[];
|
|
88
|
+
}
|
|
89
|
+
/** Emitido após unregister(). */
|
|
90
|
+
interface UnregisterEvent {
|
|
91
|
+
readonly id: string;
|
|
92
|
+
readonly cascaded: readonly string[];
|
|
93
|
+
}
|
|
94
|
+
/** Mapa de eventos tipados do engine. */
|
|
95
|
+
interface EngineEventMap<TCtx = unknown> {
|
|
96
|
+
register: RegisterEvent<TCtx>;
|
|
97
|
+
unregister: UnregisterEvent;
|
|
98
|
+
}
|
|
99
|
+
/** Union dos nomes de lifecycle events. */
|
|
100
|
+
type EngineEventName = keyof EngineEventMap;
|
|
101
|
+
interface RegisterError {
|
|
102
|
+
actionId: string;
|
|
103
|
+
error: string;
|
|
104
|
+
code?: string;
|
|
105
|
+
}
|
|
106
|
+
interface RegisterWarning {
|
|
107
|
+
actionId: string;
|
|
108
|
+
code: string;
|
|
109
|
+
message: string;
|
|
110
|
+
}
|
|
111
|
+
interface RegisterResult {
|
|
112
|
+
registered: readonly string[];
|
|
113
|
+
errors: readonly RegisterError[];
|
|
114
|
+
warnings: readonly RegisterWarning[];
|
|
115
|
+
}
|
|
116
|
+
/** Status de uma diretiva no contexto da instância. */
|
|
117
|
+
type DirectivePermissionStatus = "available" | "denied";
|
|
118
|
+
/**
|
|
119
|
+
* Informação completa de permissão de uma diretiva.
|
|
120
|
+
* O mapa só contém handlers que existem (available ou denied).
|
|
121
|
+
* `unavailable` é derivado: key referenciada mas sem handler registrado.
|
|
122
|
+
*/
|
|
123
|
+
interface DirectivePermission {
|
|
124
|
+
readonly status: DirectivePermissionStatus;
|
|
125
|
+
/** Motivação do bloqueio (humana). Presente apenas em denied. */
|
|
126
|
+
readonly reason?: string;
|
|
127
|
+
/** Quem bloqueou (auditoria). Presente apenas em denied. */
|
|
128
|
+
readonly source?: string;
|
|
129
|
+
}
|
|
130
|
+
/** Forma estruturada de entrada de permissão na config. */
|
|
131
|
+
interface DirectivePermissionConfig {
|
|
132
|
+
readonly pattern: string;
|
|
133
|
+
readonly reason?: string;
|
|
134
|
+
readonly source?: string;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Entrada de permissão: string (shorthand) ou objeto com pattern + motivação.
|
|
138
|
+
* String pura é equivalente a `{ pattern: string }`.
|
|
139
|
+
*/
|
|
140
|
+
type DirectivePermissionEntry = string | DirectivePermissionConfig;
|
|
141
|
+
interface IActionEngineConfig<TCtx> {
|
|
142
|
+
/** Handlers com fases (V2) ou funções simples (V1 compat). */
|
|
143
|
+
handlers: HandlerInputMap<TCtx>;
|
|
144
|
+
/** Campo de tipo pra dispatch (default: "type"). */
|
|
145
|
+
typeField?: string;
|
|
146
|
+
/** Hooks de directive. */
|
|
147
|
+
directiveHooks?: DirectiveHooks<TCtx>;
|
|
148
|
+
/** Limites de execução. */
|
|
149
|
+
limits?: Partial<FrameLimits>;
|
|
150
|
+
/** Modo de compilação. */
|
|
151
|
+
mode?: "interpret" | "jit" | "auto";
|
|
152
|
+
/** Threshold pra auto-promote (default: 8). */
|
|
153
|
+
autoJitThreshold?: number;
|
|
154
|
+
/**
|
|
155
|
+
* Whitelist de diretivas permitidas (pattern matching com wildcard `*`).
|
|
156
|
+
* Mutuamente exclusivo com `blockedDirectives`.
|
|
157
|
+
* Se nenhum fornecido: tudo permitido (default).
|
|
158
|
+
* Diretivas estáticas (const/let/return/throw) são sempre permitidas.
|
|
159
|
+
*/
|
|
160
|
+
allowedDirectives?: readonly DirectivePermissionEntry[];
|
|
161
|
+
/**
|
|
162
|
+
* Blacklist de diretivas bloqueadas (pattern matching com wildcard `*`).
|
|
163
|
+
* Mutuamente exclusivo com `allowedDirectives`.
|
|
164
|
+
* Se nenhum fornecido: tudo permitido (default).
|
|
165
|
+
* Diretivas estáticas (const/let/return/throw) são sempre permitidas.
|
|
166
|
+
*/
|
|
167
|
+
blockedDirectives?: readonly DirectivePermissionEntry[];
|
|
168
|
+
}
|
|
169
|
+
interface IActionEngine<TCtx> {
|
|
170
|
+
/** Registra actions. Valida, analisa, indexa no grafo, compila. */
|
|
171
|
+
register(actions: readonly ActionDefinition<TCtx>[]): RegisterResult;
|
|
172
|
+
/** Remove action do registry. Cascade: remove children (path-based). */
|
|
173
|
+
unregister(id: string): boolean;
|
|
174
|
+
/** Inicia batch. Registros acumulados, análise adiada pro endBatch. */
|
|
175
|
+
beginBatch(): void;
|
|
176
|
+
/** Finaliza batch. Executa análise, typecheck, indexação. */
|
|
177
|
+
endBatch(): RegisterResult;
|
|
178
|
+
/** Invoca action por ID. Lookup O(1). */
|
|
179
|
+
invoke(id: string, params?: Record<string, unknown>): DirectiveResult;
|
|
180
|
+
/** Invoca action async. */
|
|
181
|
+
invokeAsync(id: string, params?: Record<string, unknown>): Promise<DirectiveResult>;
|
|
182
|
+
/** Seta ctx global. */
|
|
183
|
+
setContext(ctx: TCtx): void;
|
|
184
|
+
/** Override temporário de ctx dentro do callback. */
|
|
185
|
+
context<R>(ctx: TCtx, fn: () => R): R;
|
|
186
|
+
/**
|
|
187
|
+
* Subscribe em lifecycle events do engine.
|
|
188
|
+
* Retorna função de unsubscribe idempotente.
|
|
189
|
+
*/
|
|
190
|
+
on<K extends keyof EngineEventMap<TCtx> & string>(event: K, listener: (payload: EngineEventMap<TCtx>[K]) => void): () => void;
|
|
191
|
+
/** Handler definitions registradas (V2). Leitura por camadas externas. */
|
|
192
|
+
readonly handlerDefinitions: ReadonlyMap<string, HandlerDefinition<TCtx>>;
|
|
193
|
+
/** IDs de todas as actions no registry. */
|
|
194
|
+
readonly registeredIds: ReadonlySet<string>;
|
|
195
|
+
/** Lê uma action definition pelo ID. undefined se não registrada. */
|
|
196
|
+
getActionDefinition(id: string): ActionDefinition<TCtx> | undefined;
|
|
197
|
+
/** Campo de tipo pra dispatch de diretivas (default: "type"). */
|
|
198
|
+
readonly typeField: string;
|
|
199
|
+
/**
|
|
200
|
+
* Mapa de permissões de diretivas, computado no boot (imutável).
|
|
201
|
+
* Contém apenas handlers registrados (available ou denied).
|
|
202
|
+
* `unavailable` é derivado: key não presente = handler inexistente.
|
|
203
|
+
*/
|
|
204
|
+
readonly directivePermissions: ReadonlyMap<string, DirectivePermission>;
|
|
205
|
+
readonly isAsync: boolean;
|
|
206
|
+
readonly compilationMode: "interpret" | "jit";
|
|
207
|
+
compile(): void;
|
|
208
|
+
readonly directiveHookSlots: ReadonlySet<string>;
|
|
209
|
+
readonly asyncSlots: ReadonlySet<string>;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Type-safe event emitter. Zero deps. Browser-compatible.
|
|
214
|
+
*
|
|
215
|
+
* Usage:
|
|
216
|
+
* ```ts
|
|
217
|
+
* type Events = {
|
|
218
|
+
* register: { ids: string[] };
|
|
219
|
+
* disable: { id: string };
|
|
220
|
+
* };
|
|
221
|
+
* const emitter = new SimpleEmitter<Events>();
|
|
222
|
+
* const unsub = emitter.on("register", (e) => console.log(e.ids));
|
|
223
|
+
* emitter.emit("register", { ids: ["a"] });
|
|
224
|
+
* unsub();
|
|
225
|
+
* ```
|
|
226
|
+
*/
|
|
227
|
+
type Listener<T> = (payload: T) => void;
|
|
228
|
+
declare class SimpleEmitter<TEvents = Record<string, unknown>> {
|
|
229
|
+
private readonly _listeners;
|
|
230
|
+
/**
|
|
231
|
+
* Subscribe to an event. Returns idempotent unsubscribe function.
|
|
232
|
+
* Same listener on same event is deduplicated (Set semantics).
|
|
233
|
+
*/
|
|
234
|
+
on<K extends keyof TEvents & string>(event: K, listener: Listener<TEvents[K]>): () => void;
|
|
235
|
+
/**
|
|
236
|
+
* Subscribe for a single emission, then auto-unsubscribe.
|
|
237
|
+
* Returns unsubscribe (can cancel before first emission).
|
|
238
|
+
*/
|
|
239
|
+
once<K extends keyof TEvents & string>(event: K, listener: Listener<TEvents[K]>): () => void;
|
|
240
|
+
/**
|
|
241
|
+
* Emit event to all listeners.
|
|
242
|
+
* - Iterates a snapshot — safe if listeners add/remove during emit.
|
|
243
|
+
* - Listener errors are isolated: caught, logged, don't block others.
|
|
244
|
+
*/
|
|
245
|
+
emit<K extends keyof TEvents & string>(event: K, payload: TEvents[K]): void;
|
|
246
|
+
/** Number of listeners for a specific event (0 if none). */
|
|
247
|
+
listenerCount<K extends keyof TEvents & string>(event: K): number;
|
|
248
|
+
/** True if at least one listener exists for the event. */
|
|
249
|
+
hasListeners<K extends keyof TEvents & string>(event: K): boolean;
|
|
250
|
+
/** Remove all listeners for a specific event. */
|
|
251
|
+
off<K extends keyof TEvents & string>(event: K): void;
|
|
252
|
+
/** Remove ALL listeners from ALL events. */
|
|
253
|
+
removeAllListeners(): void;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
declare const RESERVED_TYPES: Set<string>;
|
|
257
|
+
declare function normalizeDirectives<TCtx>(directives: readonly Directive<TCtx>[], typeField: string): Directive<TCtx>[];
|
|
258
|
+
|
|
259
|
+
declare function createDirectiveInterpreter<TCtx>(analysis: SlotAnalysis, isAsync: boolean): DirectiveExecutorFn<TCtx>;
|
|
260
|
+
|
|
261
|
+
declare function createActionEngine<TCtx>(config: IActionEngineConfig<TCtx>): IActionEngine<TCtx>;
|
|
262
|
+
|
|
263
|
+
type RuntimeDirectiveExecutorFn = (directives: readonly unknown[], frame: unknown, handlers: unknown, directiveHooks: unknown, typeField: string, engine: unknown, maxDirectives: number) => unknown;
|
|
264
|
+
interface GeneratedDirectiveExecutor {
|
|
265
|
+
fn: RuntimeDirectiveExecutorFn;
|
|
266
|
+
isAsync: boolean;
|
|
267
|
+
}
|
|
268
|
+
declare function buildDirectiveExecutor(analysis: SlotAnalysis): GeneratedDirectiveExecutor;
|
|
269
|
+
|
|
270
|
+
type RuntimeActionExecutorFn = (frame: unknown, scope: unknown, $: unknown) => unknown;
|
|
271
|
+
interface GeneratedActionExecutor {
|
|
272
|
+
fn: RuntimeActionExecutorFn;
|
|
273
|
+
isAsync: boolean;
|
|
274
|
+
}
|
|
275
|
+
declare function buildActionExecutor(directives: readonly Directive[], analysis: SlotAnalysis, typeField: string): GeneratedActionExecutor;
|
|
276
|
+
|
|
277
|
+
export { type ActionDefinition, type DirectiveExecutorFn, type DirectiveHandler, type DirectiveHandlerMap, type DirectiveHooks, type DirectivePermission, type DirectivePermissionConfig, type DirectivePermissionEntry, type DirectivePermissionStatus, type DirectiveRunnerFn, type EngineEventMap, type EngineEventName, type GeneratedActionExecutor, type GeneratedDirectiveExecutor, type HandlerAnalysis, type HandlerDefinition, type HandlerInput, type HandlerInputMap, type IActionEngine, type IActionEngineConfig, type Listener, RESERVED_TYPES, type RegisterError, type RegisterEvent, type RegisterResult, type RegisterWarning, SimpleEmitter, type UnregisterEvent, type ValidationResult, buildActionExecutor, buildDirectiveExecutor, createActionEngine, createDirectiveInterpreter, normalizeDirectives };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { Directive, ExecutionFrame, RuleEngineRef, ApplyResult, HookFn, InterceptResult, AbortDecision, DirectiveResult, ActionEngineRef, FrameLimits, SlotAnalysis } from '@statedelta-actions/core';
|
|
2
|
+
|
|
3
|
+
type DirectiveHandler<TCtx> = (directive: Directive, frame: ExecutionFrame<TCtx>, engine: RuleEngineRef<TCtx>) => ApplyResult;
|
|
4
|
+
type DirectiveHandlerMap<TCtx> = Record<string, DirectiveHandler<TCtx>>;
|
|
5
|
+
interface DirectiveHooks<TCtx> {
|
|
6
|
+
beforeDirective?: HookFn<[
|
|
7
|
+
directive: Directive,
|
|
8
|
+
frame: ExecutionFrame<TCtx>
|
|
9
|
+
], InterceptResult<TCtx>>;
|
|
10
|
+
afterDirective?: HookFn<[
|
|
11
|
+
directive: Directive,
|
|
12
|
+
result: ApplyResult,
|
|
13
|
+
frame: ExecutionFrame<TCtx>
|
|
14
|
+
], AbortDecision>;
|
|
15
|
+
onDirectivesComplete?: HookFn<[result: DirectiveResult], void>;
|
|
16
|
+
}
|
|
17
|
+
type DirectiveExecutorFn<TCtx> = (directives: readonly Directive[], frame: ExecutionFrame<TCtx>, handlers: DirectiveHandlerMap<TCtx>, directiveHooks: DirectiveHooks<TCtx>, typeField: string, engine: RuleEngineRef<TCtx>, maxDirectives: number) => DirectiveResult | Promise<DirectiveResult>;
|
|
18
|
+
type DirectiveRunnerFn<TCtx> = (directives: readonly Directive[], frame: ExecutionFrame<TCtx>) => DirectiveResult | Promise<DirectiveResult>;
|
|
19
|
+
interface HandlerAnalysis {
|
|
20
|
+
capabilities: string[];
|
|
21
|
+
dependencies: string[];
|
|
22
|
+
}
|
|
23
|
+
interface ValidationResult {
|
|
24
|
+
valid: boolean;
|
|
25
|
+
error?: string;
|
|
26
|
+
warnings?: string[];
|
|
27
|
+
}
|
|
28
|
+
interface HandlerDefinition<TCtx> {
|
|
29
|
+
/** Register-time: extrai capabilities e dependências. */
|
|
30
|
+
analyze?: (directive: Directive) => HandlerAnalysis;
|
|
31
|
+
/** Register-time: valida estrutura da directive. */
|
|
32
|
+
validate?: (directive: Directive) => ValidationResult | void;
|
|
33
|
+
/** JIT-time: contribui código inline per-action (Fase 2c). */
|
|
34
|
+
compile?: (directive: Directive, compiler: unknown) => string | void;
|
|
35
|
+
/** Runtime: executa a directive (obrigatório). */
|
|
36
|
+
execute: (directive: Directive, frame: ExecutionFrame<TCtx>, engine: ActionEngineRef<TCtx>) => ApplyResult;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Aceita handler V1 (função) ou V2 (objeto com fases).
|
|
40
|
+
* Backwards compat: função simples é tratada como { execute: fn }.
|
|
41
|
+
*/
|
|
42
|
+
type HandlerInput<TCtx> = DirectiveHandler<TCtx> | HandlerDefinition<TCtx>;
|
|
43
|
+
type HandlerInputMap<TCtx> = Record<string, HandlerInput<TCtx>>;
|
|
44
|
+
interface ActionDefinition<TCtx = unknown> {
|
|
45
|
+
/** ID único da Action. */
|
|
46
|
+
id: string;
|
|
47
|
+
/** Directives estáticas. */
|
|
48
|
+
directives: readonly Directive<TCtx>[];
|
|
49
|
+
/**
|
|
50
|
+
* Tags categóricas explícitas (identidade).
|
|
51
|
+
* O que a action É: "controller", "service", "admin", "ui".
|
|
52
|
+
* Não propagam via grafo. Usadas pelo composition control.
|
|
53
|
+
*/
|
|
54
|
+
tags?: readonly string[];
|
|
55
|
+
/** Schema de params aceitos (opcional, Fase 2d). */
|
|
56
|
+
params?: Record<string, unknown>;
|
|
57
|
+
/** Metadata opaca pro consumer (opcional). */
|
|
58
|
+
metadata?: Record<string, unknown>;
|
|
59
|
+
/**
|
|
60
|
+
* Tier — nível mínimo necessário pra invocar esta action.
|
|
61
|
+
* Propagado transitivamente via Math.max pelo analyzer.
|
|
62
|
+
*
|
|
63
|
+
* Se declarado, o analyzer valida: tier declarado >= tier inferido (max da sub-árvore).
|
|
64
|
+
* Se não declarado, apenas infere (sem restrição).
|
|
65
|
+
*
|
|
66
|
+
* O engine ignora este campo — é puramente declarativo pro analyzer.
|
|
67
|
+
* Requer tierPropagator habilitado no grafo.
|
|
68
|
+
*/
|
|
69
|
+
tier?: number;
|
|
70
|
+
/**
|
|
71
|
+
* Declarações — contratos públicos (trust boundaries).
|
|
72
|
+
* Cada key = nome de um propagator registrado no grafo.
|
|
73
|
+
*
|
|
74
|
+
* O engine infere capabilities/propriedades via handler.analyze() + grafo.
|
|
75
|
+
* Declarations são o contrato que o autor assina.
|
|
76
|
+
* Se inferência contradiz declaração → DECLARATION_CONFLICT reportado.
|
|
77
|
+
* Dependentes veem o valor DECLARADO (barreira de propagação).
|
|
78
|
+
*
|
|
79
|
+
* Ex: { readonly: true, leaf: true }
|
|
80
|
+
*/
|
|
81
|
+
declarations?: Record<string, unknown>;
|
|
82
|
+
}
|
|
83
|
+
/** Emitido após register() ou endBatch(). */
|
|
84
|
+
interface RegisterEvent<TCtx = unknown> {
|
|
85
|
+
readonly actions: readonly ActionDefinition<TCtx>[];
|
|
86
|
+
readonly result: RegisterResult;
|
|
87
|
+
readonly registered: readonly string[];
|
|
88
|
+
}
|
|
89
|
+
/** Emitido após unregister(). */
|
|
90
|
+
interface UnregisterEvent {
|
|
91
|
+
readonly id: string;
|
|
92
|
+
readonly cascaded: readonly string[];
|
|
93
|
+
}
|
|
94
|
+
/** Mapa de eventos tipados do engine. */
|
|
95
|
+
interface EngineEventMap<TCtx = unknown> {
|
|
96
|
+
register: RegisterEvent<TCtx>;
|
|
97
|
+
unregister: UnregisterEvent;
|
|
98
|
+
}
|
|
99
|
+
/** Union dos nomes de lifecycle events. */
|
|
100
|
+
type EngineEventName = keyof EngineEventMap;
|
|
101
|
+
interface RegisterError {
|
|
102
|
+
actionId: string;
|
|
103
|
+
error: string;
|
|
104
|
+
code?: string;
|
|
105
|
+
}
|
|
106
|
+
interface RegisterWarning {
|
|
107
|
+
actionId: string;
|
|
108
|
+
code: string;
|
|
109
|
+
message: string;
|
|
110
|
+
}
|
|
111
|
+
interface RegisterResult {
|
|
112
|
+
registered: readonly string[];
|
|
113
|
+
errors: readonly RegisterError[];
|
|
114
|
+
warnings: readonly RegisterWarning[];
|
|
115
|
+
}
|
|
116
|
+
/** Status de uma diretiva no contexto da instância. */
|
|
117
|
+
type DirectivePermissionStatus = "available" | "denied";
|
|
118
|
+
/**
|
|
119
|
+
* Informação completa de permissão de uma diretiva.
|
|
120
|
+
* O mapa só contém handlers que existem (available ou denied).
|
|
121
|
+
* `unavailable` é derivado: key referenciada mas sem handler registrado.
|
|
122
|
+
*/
|
|
123
|
+
interface DirectivePermission {
|
|
124
|
+
readonly status: DirectivePermissionStatus;
|
|
125
|
+
/** Motivação do bloqueio (humana). Presente apenas em denied. */
|
|
126
|
+
readonly reason?: string;
|
|
127
|
+
/** Quem bloqueou (auditoria). Presente apenas em denied. */
|
|
128
|
+
readonly source?: string;
|
|
129
|
+
}
|
|
130
|
+
/** Forma estruturada de entrada de permissão na config. */
|
|
131
|
+
interface DirectivePermissionConfig {
|
|
132
|
+
readonly pattern: string;
|
|
133
|
+
readonly reason?: string;
|
|
134
|
+
readonly source?: string;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Entrada de permissão: string (shorthand) ou objeto com pattern + motivação.
|
|
138
|
+
* String pura é equivalente a `{ pattern: string }`.
|
|
139
|
+
*/
|
|
140
|
+
type DirectivePermissionEntry = string | DirectivePermissionConfig;
|
|
141
|
+
interface IActionEngineConfig<TCtx> {
|
|
142
|
+
/** Handlers com fases (V2) ou funções simples (V1 compat). */
|
|
143
|
+
handlers: HandlerInputMap<TCtx>;
|
|
144
|
+
/** Campo de tipo pra dispatch (default: "type"). */
|
|
145
|
+
typeField?: string;
|
|
146
|
+
/** Hooks de directive. */
|
|
147
|
+
directiveHooks?: DirectiveHooks<TCtx>;
|
|
148
|
+
/** Limites de execução. */
|
|
149
|
+
limits?: Partial<FrameLimits>;
|
|
150
|
+
/** Modo de compilação. */
|
|
151
|
+
mode?: "interpret" | "jit" | "auto";
|
|
152
|
+
/** Threshold pra auto-promote (default: 8). */
|
|
153
|
+
autoJitThreshold?: number;
|
|
154
|
+
/**
|
|
155
|
+
* Whitelist de diretivas permitidas (pattern matching com wildcard `*`).
|
|
156
|
+
* Mutuamente exclusivo com `blockedDirectives`.
|
|
157
|
+
* Se nenhum fornecido: tudo permitido (default).
|
|
158
|
+
* Diretivas estáticas (const/let/return/throw) são sempre permitidas.
|
|
159
|
+
*/
|
|
160
|
+
allowedDirectives?: readonly DirectivePermissionEntry[];
|
|
161
|
+
/**
|
|
162
|
+
* Blacklist de diretivas bloqueadas (pattern matching com wildcard `*`).
|
|
163
|
+
* Mutuamente exclusivo com `allowedDirectives`.
|
|
164
|
+
* Se nenhum fornecido: tudo permitido (default).
|
|
165
|
+
* Diretivas estáticas (const/let/return/throw) são sempre permitidas.
|
|
166
|
+
*/
|
|
167
|
+
blockedDirectives?: readonly DirectivePermissionEntry[];
|
|
168
|
+
}
|
|
169
|
+
interface IActionEngine<TCtx> {
|
|
170
|
+
/** Registra actions. Valida, analisa, indexa no grafo, compila. */
|
|
171
|
+
register(actions: readonly ActionDefinition<TCtx>[]): RegisterResult;
|
|
172
|
+
/** Remove action do registry. Cascade: remove children (path-based). */
|
|
173
|
+
unregister(id: string): boolean;
|
|
174
|
+
/** Inicia batch. Registros acumulados, análise adiada pro endBatch. */
|
|
175
|
+
beginBatch(): void;
|
|
176
|
+
/** Finaliza batch. Executa análise, typecheck, indexação. */
|
|
177
|
+
endBatch(): RegisterResult;
|
|
178
|
+
/** Invoca action por ID. Lookup O(1). */
|
|
179
|
+
invoke(id: string, params?: Record<string, unknown>): DirectiveResult;
|
|
180
|
+
/** Invoca action async. */
|
|
181
|
+
invokeAsync(id: string, params?: Record<string, unknown>): Promise<DirectiveResult>;
|
|
182
|
+
/** Seta ctx global. */
|
|
183
|
+
setContext(ctx: TCtx): void;
|
|
184
|
+
/** Override temporário de ctx dentro do callback. */
|
|
185
|
+
context<R>(ctx: TCtx, fn: () => R): R;
|
|
186
|
+
/**
|
|
187
|
+
* Subscribe em lifecycle events do engine.
|
|
188
|
+
* Retorna função de unsubscribe idempotente.
|
|
189
|
+
*/
|
|
190
|
+
on<K extends keyof EngineEventMap<TCtx> & string>(event: K, listener: (payload: EngineEventMap<TCtx>[K]) => void): () => void;
|
|
191
|
+
/** Handler definitions registradas (V2). Leitura por camadas externas. */
|
|
192
|
+
readonly handlerDefinitions: ReadonlyMap<string, HandlerDefinition<TCtx>>;
|
|
193
|
+
/** IDs de todas as actions no registry. */
|
|
194
|
+
readonly registeredIds: ReadonlySet<string>;
|
|
195
|
+
/** Lê uma action definition pelo ID. undefined se não registrada. */
|
|
196
|
+
getActionDefinition(id: string): ActionDefinition<TCtx> | undefined;
|
|
197
|
+
/** Campo de tipo pra dispatch de diretivas (default: "type"). */
|
|
198
|
+
readonly typeField: string;
|
|
199
|
+
/**
|
|
200
|
+
* Mapa de permissões de diretivas, computado no boot (imutável).
|
|
201
|
+
* Contém apenas handlers registrados (available ou denied).
|
|
202
|
+
* `unavailable` é derivado: key não presente = handler inexistente.
|
|
203
|
+
*/
|
|
204
|
+
readonly directivePermissions: ReadonlyMap<string, DirectivePermission>;
|
|
205
|
+
readonly isAsync: boolean;
|
|
206
|
+
readonly compilationMode: "interpret" | "jit";
|
|
207
|
+
compile(): void;
|
|
208
|
+
readonly directiveHookSlots: ReadonlySet<string>;
|
|
209
|
+
readonly asyncSlots: ReadonlySet<string>;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Type-safe event emitter. Zero deps. Browser-compatible.
|
|
214
|
+
*
|
|
215
|
+
* Usage:
|
|
216
|
+
* ```ts
|
|
217
|
+
* type Events = {
|
|
218
|
+
* register: { ids: string[] };
|
|
219
|
+
* disable: { id: string };
|
|
220
|
+
* };
|
|
221
|
+
* const emitter = new SimpleEmitter<Events>();
|
|
222
|
+
* const unsub = emitter.on("register", (e) => console.log(e.ids));
|
|
223
|
+
* emitter.emit("register", { ids: ["a"] });
|
|
224
|
+
* unsub();
|
|
225
|
+
* ```
|
|
226
|
+
*/
|
|
227
|
+
type Listener<T> = (payload: T) => void;
|
|
228
|
+
declare class SimpleEmitter<TEvents = Record<string, unknown>> {
|
|
229
|
+
private readonly _listeners;
|
|
230
|
+
/**
|
|
231
|
+
* Subscribe to an event. Returns idempotent unsubscribe function.
|
|
232
|
+
* Same listener on same event is deduplicated (Set semantics).
|
|
233
|
+
*/
|
|
234
|
+
on<K extends keyof TEvents & string>(event: K, listener: Listener<TEvents[K]>): () => void;
|
|
235
|
+
/**
|
|
236
|
+
* Subscribe for a single emission, then auto-unsubscribe.
|
|
237
|
+
* Returns unsubscribe (can cancel before first emission).
|
|
238
|
+
*/
|
|
239
|
+
once<K extends keyof TEvents & string>(event: K, listener: Listener<TEvents[K]>): () => void;
|
|
240
|
+
/**
|
|
241
|
+
* Emit event to all listeners.
|
|
242
|
+
* - Iterates a snapshot — safe if listeners add/remove during emit.
|
|
243
|
+
* - Listener errors are isolated: caught, logged, don't block others.
|
|
244
|
+
*/
|
|
245
|
+
emit<K extends keyof TEvents & string>(event: K, payload: TEvents[K]): void;
|
|
246
|
+
/** Number of listeners for a specific event (0 if none). */
|
|
247
|
+
listenerCount<K extends keyof TEvents & string>(event: K): number;
|
|
248
|
+
/** True if at least one listener exists for the event. */
|
|
249
|
+
hasListeners<K extends keyof TEvents & string>(event: K): boolean;
|
|
250
|
+
/** Remove all listeners for a specific event. */
|
|
251
|
+
off<K extends keyof TEvents & string>(event: K): void;
|
|
252
|
+
/** Remove ALL listeners from ALL events. */
|
|
253
|
+
removeAllListeners(): void;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
declare const RESERVED_TYPES: Set<string>;
|
|
257
|
+
declare function normalizeDirectives<TCtx>(directives: readonly Directive<TCtx>[], typeField: string): Directive<TCtx>[];
|
|
258
|
+
|
|
259
|
+
declare function createDirectiveInterpreter<TCtx>(analysis: SlotAnalysis, isAsync: boolean): DirectiveExecutorFn<TCtx>;
|
|
260
|
+
|
|
261
|
+
declare function createActionEngine<TCtx>(config: IActionEngineConfig<TCtx>): IActionEngine<TCtx>;
|
|
262
|
+
|
|
263
|
+
type RuntimeDirectiveExecutorFn = (directives: readonly unknown[], frame: unknown, handlers: unknown, directiveHooks: unknown, typeField: string, engine: unknown, maxDirectives: number) => unknown;
|
|
264
|
+
interface GeneratedDirectiveExecutor {
|
|
265
|
+
fn: RuntimeDirectiveExecutorFn;
|
|
266
|
+
isAsync: boolean;
|
|
267
|
+
}
|
|
268
|
+
declare function buildDirectiveExecutor(analysis: SlotAnalysis): GeneratedDirectiveExecutor;
|
|
269
|
+
|
|
270
|
+
type RuntimeActionExecutorFn = (frame: unknown, scope: unknown, $: unknown) => unknown;
|
|
271
|
+
interface GeneratedActionExecutor {
|
|
272
|
+
fn: RuntimeActionExecutorFn;
|
|
273
|
+
isAsync: boolean;
|
|
274
|
+
}
|
|
275
|
+
declare function buildActionExecutor(directives: readonly Directive[], analysis: SlotAnalysis, typeField: string): GeneratedActionExecutor;
|
|
276
|
+
|
|
277
|
+
export { type ActionDefinition, type DirectiveExecutorFn, type DirectiveHandler, type DirectiveHandlerMap, type DirectiveHooks, type DirectivePermission, type DirectivePermissionConfig, type DirectivePermissionEntry, type DirectivePermissionStatus, type DirectiveRunnerFn, type EngineEventMap, type EngineEventName, type GeneratedActionExecutor, type GeneratedDirectiveExecutor, type HandlerAnalysis, type HandlerDefinition, type HandlerInput, type HandlerInputMap, type IActionEngine, type IActionEngineConfig, type Listener, RESERVED_TYPES, type RegisterError, type RegisterEvent, type RegisterResult, type RegisterWarning, SimpleEmitter, type UnregisterEvent, type ValidationResult, buildActionExecutor, buildDirectiveExecutor, createActionEngine, createDirectiveInterpreter, normalizeDirectives };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import {processIntercept,Intercept,analyzeSlots,DIRECTIVE_SLOT_NAMES,createRootFrame}from'@statedelta-actions/core';var F=class{_listeners=new Map;on(e,i){let n=this._listeners.get(e);n||(n=new Set,this._listeners.set(e,n)),n.add(i);let s=false;return ()=>{s||(s=true,n.delete(i),n.size===0&&this._listeners.delete(e));}}once(e,i){let n=this.on(e,s=>{n(),i(s);});return n}emit(e,i){let n=this._listeners.get(e);if(!n||n.size===0)return;let s=[...n];for(let r=0;r<s.length;r++)try{s[r](i);}catch(c){console.error(`[SimpleEmitter] Listener error on "${e}":`,c);}}listenerCount(e){return this._listeners.get(e)?.size??0}hasListeners(e){let i=this._listeners.get(e);return i!==void 0&&i.size>0}off(e){this._listeners.delete(e);}removeAllListeners(){this._listeners.clear();}};var S=new Set(["const","let","return","throw"]);function W(t,e,i,n){let s=[];for(let r=0;r<t.directives.length;r++){let c=t.directives[r];if("const"in c||"let"in c||"return"in c||"throw"in c)continue;let h=c[n];if(!h){s.push(`directive[${r}]: missing type field "${n}"`);continue}if(!e[h]){s.push(`directive[${r}]: no handler for type "${h}"`);continue}let x=i.get(h);if(x?.validate)try{let D=x.validate(c);D&&!D.valid&&s.push(`directive[${r}]: ${D.error??"validation failed"}`);}catch(D){s.push(`directive[${r}]: validate threw: ${D}`);}let m=c.catch;if(m&&Array.isArray(m))for(let D=0;D<m.length;D++){let R=m[D];if("const"in R||"let"in R||"return"in R||"throw"in R)continue;let v=R[n];v&&!e[v]&&s.push(`directive[${r}].catch[${D}]: no handler for type "${v}"`);}}return s}function H(t,e){let i=t.length,n=new Array(i);for(let s=0;s<i;s++){let r=t[s];if("const"in r)n[s]=q(r,"const");else if("let"in r)n[s]=q(r,"let");else if("return"in r)n[s]=te(r);else if("throw"in r)n[s]=re(r);else {let c=r.catch;Array.isArray(c)&&c.length>0?n[s]={...r,catch:H(c)}:n[s]=r;}}return n}function q(t,e){let{[e]:i,...n}=t;return {...n,type:e,name:i}}function te(t){let{return:e,...i}=t;return {...i,type:"return",value:e}}function re(t){let{throw:e,...i}=t;return {...i,type:"throw",message:e}}function M(t,e,i,n,s,r,c){return {success:false,aborted:true,abortedBy:t,appliedCount:e,skippedCount:i,errors:n,processedCount:s,totalCount:r,counters:c}}function ie(t){let e=t.filledNames.has("beforeDirective"),i=t.filledNames.has("afterDirective"),n=t.filledNames.has("onDirectivesComplete");return function(r,c,h,x,m,D,R){let v=Math.min(r.length,R),l=[],f=0,_=0,{counters:u,scope:C}=c,A=c.ctx;for(let a=0;a<v;a++){let p=r[a],$=p[m];if($==="const"||$==="let"){let o=p.name;if(typeof p.resolve=="function")try{C[o]=p.resolve(A,C).value;}catch(d){l.push({directiveIndex:a,error:`Binding resolve: ${d}`}),u.errors++;}else C[o]=p.value;continue}if($==="return"){let o=a+1;if(typeof p.resolve=="function")try{let d=p.resolve(A,C),b="value"in d?d.value:"return"in d?d.return:p.value;return {success:!0,aborted:!1,appliedCount:f,skippedCount:_,errors:l,processedCount:o,totalCount:v,counters:u,data:b}}catch(d){l.push({directiveIndex:a,error:`Control resolve: ${d}`}),u.errors++;}else return {success:true,aborted:false,appliedCount:f,skippedCount:_,errors:l,processedCount:o,totalCount:v,counters:u,data:p.value};continue}if($==="throw"){let o=a+1;if(typeof p.resolve=="function")try{let d=p.resolve(A,C),b="message"in d?d.message:"throw"in d?d.throw:p.message;return {success:!1,aborted:!0,abortedBy:"throw",appliedCount:f,skippedCount:_,errors:l,processedCount:o,totalCount:v,counters:u,data:b}}catch(d){l.push({directiveIndex:a,error:`Control resolve: ${d}`}),u.errors++;}else return {success:false,aborted:true,abortedBy:"throw",appliedCount:f,skippedCount:_,errors:l,processedCount:o,totalCount:v,counters:u,data:p.message};continue}let y=p,E=c;if(e)try{let o=processIntercept(x.beforeDirective(y,c),"beforeDirective");if(o.action===Intercept.SKIP){_++,u.directivesSkipped++;continue}if(o.action===Intercept.ABORT)return M(o.abortReason,f,_,l,a,v,u);o.ctx!==void 0&&(E=c.withCtx(o.ctx)),o.directive!==void 0&&(y=o.directive);}catch(o){l.push({directiveIndex:a,error:`Hook beforeDirective: ${o}`}),u.errors++;}if(typeof y.resolve=="function")try{let o=y.resolve(E.ctx,E.scope);y={...y,...o};}catch(o){l.push({directiveIndex:a,error:`Directive resolve: ${o}`}),u.errors++;continue}let w=y[m],I=w?h[w]:void 0;if(!I){l.push({directiveIndex:a,error:`No handler for directive type: ${w??"undefined"}`}),u.errors++;continue}let g;try{g=I(y,E,D);}catch(o){g={ok:false,error:String(o)};}let T=a+1;if(g.ok){if(f++,u.directivesApplied++,typeof y.as=="string"&&(C[y.as]=g.data),g.halt)return {success:true,aborted:true,abortedBy:"halt",appliedCount:f,skippedCount:_,errors:l,processedCount:T,totalCount:v,counters:u,data:g.data}}else {if(g.halt)return {success:false,aborted:true,abortedBy:"halt",appliedCount:f,skippedCount:_,errors:l,processedCount:T,totalCount:v,counters:u,data:g.data};let o=y.catch;if(Array.isArray(o)&&o.length>0){C.$exception=g.error??"handler failed";let d=D.runDirectives(o,E);f+=d.appliedCount;for(let b=0;b<d.errors.length;b++)l.push(d.errors[b]);if(d.aborted)return {success:d.success,aborted:true,abortedBy:d.abortedBy,appliedCount:f,skippedCount:_,errors:l,processedCount:T,totalCount:v,counters:u,data:d.data}}else l.push({directiveIndex:a,error:g.error??"handler failed"}),u.errors++;}if(i)try{if(x.afterDirective(y,g,E)==="abort")return M("afterDirective",f,_,l,T,v,u)}catch(o){l.push({directiveIndex:a,error:`Hook afterDirective: ${o}`}),u.errors++;}}let k={success:true,aborted:false,appliedCount:f,skippedCount:_,errors:l,processedCount:v,totalCount:v,counters:u};if(n)try{x.onDirectivesComplete(k);}catch{}return k}}function ne(t){let e=t.filledNames.has("beforeDirective"),i=t.filledNames.has("afterDirective"),n=t.filledNames.has("onDirectivesComplete");return async function(r,c,h,x,m,D,R){let v=Math.min(r.length,R),l=[],f=0,_=0,{counters:u,scope:C}=c,A=c.ctx;for(let a=0;a<v;a++){let p=r[a],$=p[m];if($==="const"||$==="let"){let o=p.name;if(typeof p.resolve=="function")try{C[o]=p.resolve(A,C).value;}catch(d){l.push({directiveIndex:a,error:`Binding resolve: ${d}`}),u.errors++;}else C[o]=p.value;continue}if($==="return"){let o=a+1;if(typeof p.resolve=="function")try{let d=p.resolve(A,C),b="value"in d?d.value:"return"in d?d.return:p.value;return {success:!0,aborted:!1,appliedCount:f,skippedCount:_,errors:l,processedCount:o,totalCount:v,counters:u,data:b}}catch(d){l.push({directiveIndex:a,error:`Control resolve: ${d}`}),u.errors++;}else return {success:true,aborted:false,appliedCount:f,skippedCount:_,errors:l,processedCount:o,totalCount:v,counters:u,data:p.value};continue}if($==="throw"){let o=a+1;if(typeof p.resolve=="function")try{let d=p.resolve(A,C),b="message"in d?d.message:"throw"in d?d.throw:p.message;return {success:!1,aborted:!0,abortedBy:"throw",appliedCount:f,skippedCount:_,errors:l,processedCount:o,totalCount:v,counters:u,data:b}}catch(d){l.push({directiveIndex:a,error:`Control resolve: ${d}`}),u.errors++;}else return {success:false,aborted:true,abortedBy:"throw",appliedCount:f,skippedCount:_,errors:l,processedCount:o,totalCount:v,counters:u,data:p.message};continue}let y=p,E=c;if(e)try{let o=processIntercept(await x.beforeDirective(y,c),"beforeDirective");if(o.action===Intercept.SKIP){_++,u.directivesSkipped++;continue}if(o.action===Intercept.ABORT)return M(o.abortReason,f,_,l,a,v,u);o.ctx!==void 0&&(E=c.withCtx(o.ctx)),o.directive!==void 0&&(y=o.directive);}catch(o){l.push({directiveIndex:a,error:`Hook beforeDirective: ${o}`}),u.errors++;}if(typeof y.resolve=="function")try{let o=y.resolve(E.ctx,E.scope);y={...y,...o};}catch(o){l.push({directiveIndex:a,error:`Directive resolve: ${o}`}),u.errors++;continue}let w=y[m],I=w?h[w]:void 0;if(!I){l.push({directiveIndex:a,error:`No handler for directive type: ${w??"undefined"}`}),u.errors++;continue}let g;try{g=I(y,E,D);}catch(o){g={ok:false,error:String(o)};}let T=a+1;if(g.ok){if(f++,u.directivesApplied++,typeof y.as=="string"&&(C[y.as]=g.data),g.halt)return {success:true,aborted:true,abortedBy:"halt",appliedCount:f,skippedCount:_,errors:l,processedCount:T,totalCount:v,counters:u,data:g.data}}else {if(g.halt)return {success:false,aborted:true,abortedBy:"halt",appliedCount:f,skippedCount:_,errors:l,processedCount:T,totalCount:v,counters:u,data:g.data};let o=y.catch;if(Array.isArray(o)&&o.length>0){C.$exception=g.error??"handler failed";let d=await D.runDirectivesAsync(o,E);f+=d.appliedCount;for(let b=0;b<d.errors.length;b++)l.push(d.errors[b]);if(d.aborted)return {success:d.success,aborted:true,abortedBy:d.abortedBy,appliedCount:f,skippedCount:_,errors:l,processedCount:T,totalCount:v,counters:u,data:d.data}}else l.push({directiveIndex:a,error:g.error??"handler failed"}),u.errors++;}if(i)try{if(await x.afterDirective(y,g,E)==="abort")return M("afterDirective",f,_,l,T,v,u)}catch(o){l.push({directiveIndex:a,error:`Hook afterDirective: ${o}`}),u.errors++;}}let k={success:true,aborted:false,appliedCount:f,skippedCount:_,errors:l,processedCount:v,totalCount:v,counters:u};if(n)try{await x.onDirectivesComplete(k);}catch{}return k}}function B(t,e){return e?ne(t):ie(t)}function j(t){let{filledNames:e,hasAnyAsync:i}=t,n=m=>e.has(m),s=m=>t.asyncNames.has(m)?"await ":"",r=[];for(let m of e)r.push(`const ${m} = directiveHooks.${m};`);r.push("const len = Math.min(directives.length, maxDirectives);"),r.push("const errors = []; let appliedCount = 0; let skippedCount = 0;"),r.push("const counters = frame.counters;"),r.push("for (let i = 0; i < len; i++) {"),r.push(" let directive = directives[i];"),n("beforeDirective")?(r.push(" let _df = frame;"),r.push(" try {"),r.push(` const _bd = ${s("beforeDirective")}beforeDirective(directive, frame);`),r.push(' if (_bd === "skip") { skippedCount++; counters.directivesSkipped++; continue; }'),r.push(' if (_bd === "abort") return { success: false, aborted: true, abortedBy: "beforeDirective", appliedCount, skippedCount, errors, processedCount: i, totalCount: len, counters };'),r.push(' if (typeof _bd === "object" && _bd !== null) {'),r.push(' if ("abort" in _bd) return { success: false, aborted: true, abortedBy: _bd.abort, appliedCount, skippedCount, errors, processedCount: i, totalCount: len, counters };'),r.push(' if ("ctx" in _bd) _df = frame.withCtx(_bd.ctx);'),r.push(' if ("directive" in _bd) directive = _bd.directive;'),r.push(" }"),r.push(' } catch (_e) { errors.push({ directiveIndex: i, error: "Hook beforeDirective: " + _e }); counters.errors++; }')):r.push(" const _df = frame;"),r.push(' if (typeof directive.resolve === "function") {'),r.push(" try { const _r = directive.resolve(_df.ctx); directive = Object.assign({}, directive, _r); }"),r.push(' catch (_e) { errors.push({ directiveIndex: i, error: "Directive resolve: " + _e }); counters.errors++; continue; }'),r.push(" }"),r.push(" const _type = directive[typeField];"),r.push(" const _handler = _type ? handlers[_type] : undefined;"),r.push(' if (!_handler) { errors.push({ directiveIndex: i, error: "No handler for directive type: " + (_type || "undefined") }); counters.errors++; continue; }'),r.push(" let _result;"),r.push(" try { _result = _handler(directive, _df, engine); } catch (_e) { errors.push({ directiveIndex: i, error: String(_e) }); counters.errors++; continue; }"),r.push(" if (_result.ok) { appliedCount++; counters.directivesApplied++; }"),r.push(' else { errors.push({ directiveIndex: i, error: _result.error || "handler failed" }); counters.errors++; }'),n("afterDirective")&&(r.push(" try {"),r.push(` const _ad = ${s("afterDirective")}afterDirective(directive, _result, _df);`),r.push(' if (_ad === "abort") return { success: false, aborted: true, abortedBy: "afterDirective", appliedCount, skippedCount, errors, processedCount: i + 1, totalCount: len, counters };'),r.push(' } catch (_e) { errors.push({ directiveIndex: i, error: "Hook afterDirective: " + _e }); counters.errors++; }')),r.push("}"),r.push("const _finalResult = { success: true, aborted: false, appliedCount, skippedCount, errors, processedCount: len, totalCount: len, counters };"),n("onDirectivesComplete")&&r.push(`try { ${s("onDirectivesComplete")}onDirectivesComplete(_finalResult); } catch (_e) {}`),r.push("return _finalResult;");let c=r.join(`
|
|
2
|
+
`),h=i?"async ":"";return {fn:new Function("directives","frame","handlers","directiveHooks","typeField","engine","maxDirectives",`"use strict";
|
|
3
|
+
return (${h}() => {
|
|
4
|
+
${c}
|
|
5
|
+
})();`),isAsync:i}}function N(t,e,i){return `return{success:false,aborted:true,abortedBy:${t},appliedCount,skippedCount,errors,processedCount:${e},totalCount:${i},counters};`}function se(t,e){return `return{success:true,aborted:true,abortedBy:"halt",appliedCount,skippedCount,errors,processedCount:${t},totalCount:${e},counters,data:_result.data};`}function oe(t,e){return `return{success:false,aborted:true,abortedBy:"halt",appliedCount,skippedCount,errors,processedCount:${t},totalCount:${e},counters,data:_result.data};`}function ce(t,e,i){let n=JSON.stringify(e.name);typeof e.resolve=="function"?(t.push("try{"),t.push(`scope[${n}]=$.d[${i}].resolve(ctx,scope).value;`),t.push(`}catch(_e){errors.push({directiveIndex:${i},error:"Binding resolve: "+_e});counters.errors++;}`)):t.push(`scope[${n}]=$.d[${i}].value;`);}function ae(t,e,i,n){let s=typeof e.resolve=="function",r=i+1;s?(t.push("try{"),t.push(`const _rv=$.d[${i}].resolve(ctx,scope);`),t.push(`return{success:true,aborted:false,appliedCount,skippedCount,errors,processedCount:${r},totalCount:${n},counters,data:"value" in _rv?_rv.value:"return" in _rv?_rv.return:$.d[${i}].value};`),t.push(`}catch(_e){errors.push({directiveIndex:${i},error:"Control resolve: "+_e});counters.errors++;}`)):t.push(`return{success:true,aborted:false,appliedCount,skippedCount,errors,processedCount:${r},totalCount:${n},counters,data:$.d[${i}].value};`);}function ue(t,e,i,n){let s=typeof e.resolve=="function",r=i+1;s?(t.push("try{"),t.push(`const _rv=$.d[${i}].resolve(ctx,scope);`),t.push(`return{success:false,aborted:true,abortedBy:"throw",appliedCount,skippedCount,errors,processedCount:${r},totalCount:${n},counters,data:"message" in _rv?_rv.message:"throw" in _rv?_rv.throw:$.d[${i}].message};`),t.push(`}catch(_e){errors.push({directiveIndex:${i},error:"Control resolve: "+_e});counters.errors++;}`)):t.push(`return{success:false,aborted:true,abortedBy:"throw",appliedCount,skippedCount,errors,processedCount:${r},totalCount:${n},counters,data:$.d[${i}].message};`);}function de(t,e,i,n,s,r,c,h,x,m){let D=e[s],R=r.get(D),v=typeof e.resolve=="function",l=typeof e.as=="string",f=Array.isArray(e.catch)&&e.catch.length>0,_=l?JSON.stringify(e.as):"",u=i+1;t.push(`_b${i}:{`);let C=c||v,A=c;C&&t.push(`let _dir=$.d[${i}];`),A&&t.push("let _df=frame;"),c&&(t.push("try{"),t.push(`const _bd=${x}_hookBefore($.d[${i}],frame);`),t.push(`if(_bd==="skip"){skippedCount++;counters.directivesSkipped++;break _b${i};}`),t.push(`if(_bd==="abort")${N('"beforeDirective"',i,n)}`),t.push('if(typeof _bd==="object"&&_bd!==null){'),t.push(`if("abort" in _bd)${N("_bd.abort",i,n)}`),t.push('if("ctx" in _bd)_df=frame.withCtx(_bd.ctx);'),t.push('if("directive" in _bd)_dir=_bd.directive;'),t.push("}"),t.push(`}catch(_e){errors.push({directiveIndex:${i},error:"Hook beforeDirective: "+_e});counters.errors++;}`)),c?(t.push('if(typeof _dir.resolve==="function"){'),t.push("try{const _r=_dir.resolve(_df.ctx,scope);_dir=Object.assign({},_dir,_r);}"),t.push(`catch(_e){errors.push({directiveIndex:${i},error:"Directive resolve: "+_e});counters.errors++;break _b${i};}}`)):v&&(t.push(`try{const _r=$.d[${i}].resolve(ctx,scope);_dir=Object.assign({},$.d[${i}],_r);}`),t.push(`catch(_e){errors.push({directiveIndex:${i},error:"Directive resolve: "+_e});counters.errors++;break _b${i};}`));let k=C?"_dir":`$.d[${i}]`,a=A?"_df":"frame";t.push("let _result;"),t.push(`try{_result=${R}(${k},${a},$.engine);}`),t.push("catch(_e){_result={ok:false,error:String(_e)};}"),t.push("if(_result.ok){"),t.push("appliedCount++;counters.directivesApplied++;"),l&&t.push(`scope[${_}]=_result.data;`),t.push(`if(_result.halt)${se(u,n)}`),t.push("}else{"),t.push(`if(_result.halt)${oe(u,n)}`),f?(t.push('scope.$exception=_result.error||"handler failed";'),t.push(`const _cr=$.runner($.d[${i}].catch,${a});`),t.push("appliedCount+=_cr.appliedCount;"),t.push("for(let _j=0;_j<_cr.errors.length;_j++)errors.push(_cr.errors[_j]);"),t.push(`if(_cr.aborted)return{success:_cr.success,aborted:true,abortedBy:_cr.abortedBy,appliedCount,skippedCount,errors,processedCount:${u},totalCount:${n},counters,data:_cr.data};`)):t.push(`errors.push({directiveIndex:${i},error:_result.error||"handler failed"});counters.errors++;`),t.push("}"),h&&(t.push("try{"),t.push(`const _ad=${m}_hookAfter(${k},_result,${a});`),t.push(`if(_ad==="abort")${N('"afterDirective"',u,n)}`),t.push(`}catch(_e){errors.push({directiveIndex:${i},error:"Hook afterDirective: "+_e});counters.errors++;}`)),t.push("}");}function O(t,e,i){let n=[],s=t.length,{filledNames:r,hasAnyAsync:c,asyncNames:h}=e,x=r.has("beforeDirective"),m=r.has("afterDirective"),D=r.has("onDirectivesComplete"),R=h.has("beforeDirective")?"await ":"",v=h.has("afterDirective")?"await ":"",l=h.has("onDirectivesComplete")?"await ":"",f=new Set;for(let a of t){let p=a[i];p&&!S.has(p)&&f.add(p);}let _=new Map,u=0;for(let a of f)_.set(a,`_h${u++}`);n.push("const counters=frame.counters;"),n.push("const ctx=frame.ctx;"),n.push("const errors=[];"),n.push("let appliedCount=0;"),n.push("let skippedCount=0;");for(let[a,p]of _)n.push(`const ${p}=$.h[${JSON.stringify(a)}];`);x&&n.push("const _hookBefore=$.hooks.beforeDirective;"),m&&n.push("const _hookAfter=$.hooks.afterDirective;");for(let a=0;a<t.length;a++){let p=t[a];switch(p[i]){case "const":case "let":ce(n,p,a);break;case "return":ae(n,p,a,s);break;case "throw":ue(n,p,a,s);break;default:de(n,p,a,s,i,_,x,m,R,v);break}}n.push(`const _finalResult={success:true,aborted:false,appliedCount,skippedCount,errors,processedCount:${s},totalCount:${s},counters};`),D&&n.push(`try{${l}$.hooks.onDirectivesComplete(_finalResult);}catch(_e){}`),n.push("return _finalResult;");let C=n.join(`
|
|
6
|
+
`),A=c?"async ":"";return {fn:new Function("frame","scope","$",`"use strict";
|
|
7
|
+
return(${A}()=>{
|
|
8
|
+
${C}
|
|
9
|
+
})();`),isAsync:c}}function K(t,e,i){return {success:false,aborted:false,appliedCount:0,skippedCount:0,processedCount:0,totalCount:0,errors:[{directiveIndex:-1,error:e}],counters:t,...i}}function U(t,e){return K(e,`Action not found: "${t}"`)}function G(t,e,i){return K(i,`Max depth ${e} exceeded invoking "${t}"`,{aborted:true,abortedBy:"maxDepth"})}function Y(t,e){return K(e,`Action "${t}" is private (sub-action). Can only be invoked from within its parent scope.`)}function le(t){return typeof t=="object"&&t!==null&&"execute"in t}function Q(t){let e=Object.create(null),i=new Map;for(let n of Object.keys(t)){let s=t[n];le(s)?(e[n]=s.execute,i.set(n,s)):(e[n]=s,i.set(n,{execute:s}));}return {handlers:e,definitions:i}}function X(t,e){if(t==="*")return true;if(!t.includes("*"))return t===e;let i=t.replace(/[.+^${}()|[\]\\]/g,"\\$&");return new RegExp("^"+i.replace(/\*/g,".*")+"$").test(e)}function Z(t){return typeof t=="string"?{pattern:t}:t}function L(t,e,i){let n=new Map;if(!e&&!i){for(let s of t)n.set(s,{status:"available"});return n}if(e){let s=e.map(Z);for(let r of t){let c=s.some(h=>X(h.pattern,r));n.set(r,{status:c?"available":"denied"});}}else {let s=i.map(Z);for(let r of t){let c=s.find(h=>X(h.pattern,r));c?n.set(r,{status:"denied",reason:c.reason,source:c.source}):n.set(r,{status:"available"});}}return n}var ve=8,fe="type",z={maxDepth:10,maxRules:1e4,maxDirectives:1e5},V=class{_directiveExecutor;_mode;_ctx;_requestedMode;_threshold;_limits;_typeField;_handlers;_definitions;_directiveHooks;_directiveAnalysis;_isAsync;_directivePermissions;_registry=new Map;_emitter=new F;_batchDepth=0;_batchErrors=[];_batchWarnings=[];_batchActions=[];_batchRegistered=[];_registeredIds=new Set;_directiveRunner;_engineRef;constructor(e){this._requestedMode=e.mode??"auto",this._threshold=e.autoJitThreshold??ve,this._typeField=e.typeField??fe,this._limits={maxDepth:e.limits?.maxDepth??z.maxDepth,maxRules:e.limits?.maxRules??z.maxRules,maxDirectives:e.limits?.maxDirectives??z.maxDirectives};let{handlers:i,definitions:n}=Q(e.handlers);for(let s of Object.keys(e.handlers))if(S.has(s))throw new Error(`Handler type "${s}" is reserved for engine-internal directives`);if(e.allowedDirectives&&e.blockedDirectives)throw new Error("allowedDirectives and blockedDirectives are mutually exclusive. Provide one or neither.");this._handlers=i,this._definitions=n,this._directivePermissions=L(Object.keys(i),e.allowedDirectives,e.blockedDirectives),this._directiveHooks=e.directiveHooks??{},this._directiveAnalysis=analyzeSlots(this._directiveHooks,DIRECTIVE_SLOT_NAMES),this._isAsync=this._directiveAnalysis.hasAnyAsync,this._engineRef={runDirectives:(s,r)=>this._executeDirectives(s,r),runDirectivesAsync:(s,r)=>this._executeDirectivesAsync(s,r),invoke:(s,r,c)=>this._invokeInternal(s,r,c),invokeAsync:(s,r,c)=>this._invokeInternalAsync(s,r,c),evaluateRules:()=>{throw new Error("evaluateRules() not available in ActionEngine context. Use createRuleEngine().")},evaluateRulesAsync:()=>{throw new Error("evaluateRulesAsync() not available in ActionEngine context. Use createRuleEngine().")}},this._directiveRunner=(s,r)=>this._directiveExecutor(s,r,this._handlers,this._directiveHooks,this._typeField,this._engineRef,this._limits.maxDirectives),this._requestedMode==="jit"?(this._directiveExecutor=this._buildJitDirectiveExecutor(),this._mode="jit"):(this._directiveExecutor=B(this._directiveAnalysis,this._isAsync),this._mode="interpret");}register(e){let i=[],n=[],s=[],r=[];for(let h of e){if(!h.id){n.push({actionId:"",error:"Action must have an id"});continue}let x=W(h,this._handlers,this._definitions,this._typeField);if(x.length>0){for(let m of x)n.push({actionId:h.id,error:m});continue}r.push(h);}for(let h of r){let x=H(h.directives,this._typeField),m={definition:h,directives:x,compiled:null,invokeCount:0};this._requestedMode==="jit"&&(m.compiled=this._compileAction(x)),this._registry.set(h.id,m),this._registeredIds.add(h.id),i.push(h.id);}if(this._batchDepth>0){this._batchErrors.push(...n),this._batchWarnings.push(...s);for(let h of r)this._batchActions.push(h);return this._batchRegistered.push(...i),{registered:i,errors:n,warnings:s}}let c={registered:i,errors:n,warnings:s};return i.length>0&&this._emitter.emit("register",{actions:r,result:c,registered:i}),c}unregister(e){let i=this._registry.delete(e);i&&this._registeredIds.delete(e);let n=e+"/",s=[];for(let r of this._registry.keys())r.startsWith(n)&&(this._registry.delete(r),this._registeredIds.delete(r),s.push(r));return i&&this._emitter.emit("unregister",{id:e,cascaded:s}),i}invoke(e,i){if(this._isAsync)throw new Error("Cannot call invoke() with async hooks. Use invokeAsync() instead.");let n=this._requireCtx(),s=createRootFrame(n,this._limits);return this._invokeInternal(e,i,s)}async invokeAsync(e,i){let n=this._requireCtx(),s=createRootFrame(n,this._limits);return this._invokeInternalAsync(e,i,s)}setContext(e){this._ctx=e;}context(e,i){let n=this._ctx;this._ctx=e;try{return i()}finally{this._ctx=n;}}beginBatch(){this._batchDepth++,this._batchDepth===1&&(this._batchErrors=[],this._batchWarnings=[],this._batchActions=[],this._batchRegistered=[]);}endBatch(){if(this._batchDepth<=0)throw new Error("endBatch() called without matching beginBatch()");if(this._batchDepth--,this._batchDepth>0)return {registered:[],errors:[],warnings:[]};let e={registered:this._batchRegistered,errors:this._batchErrors,warnings:this._batchWarnings};return this._batchRegistered.length>0&&this._emitter.emit("register",{actions:this._batchActions,result:e,registered:this._batchRegistered}),e}on(e,i){return this._emitter.on(e,i)}get handlerDefinitions(){return this._definitions}get registeredIds(){return this._registeredIds}getActionDefinition(e){return this._registry.get(e)?.definition}get typeField(){return this._typeField}get directivePermissions(){return this._directivePermissions}get isAsync(){return this._isAsync}get compilationMode(){return this._mode}get directiveHookSlots(){return this._directiveAnalysis.filledNames}get asyncSlots(){return this._directiveAnalysis.asyncNames}compile(){if(this._requestedMode!=="interpret"){this._mode!=="jit"&&this._promote();for(let e of this._registry.values())e.compiled||(e.compiled=this._compileAction(e.directives));}}_invokeInternal(e,i,n){let s=this._prepareInvoke(e,i,n);if("success"in s)return s;let{stored:r,childFrame:c,childScope:h}=s;if(r.compiled)return r.compiled.fn(c,h,r.compiled.$);let x=this._directiveRunner(r.directives,c);return this._maybeAutoPromote(r),x}async _invokeInternalAsync(e,i,n){let s=this._prepareInvoke(e,i,n);if("success"in s)return s;let{stored:r,childFrame:c,childScope:h}=s;if(r.compiled)return r.compiled.fn(c,h,r.compiled.$);let x=this._directiveRunner(r.directives,c);return this._maybeAutoPromote(r),x}_prepareInvoke(e,i,n){let s=this._registry.get(e);if(!s)return U(e,n.counters);if(e.includes("/")&&!this._isAccessible(e,n))return Y(e,n.counters);if(n.depth>=n.limits.maxDepth)return G(e,n.limits.maxDepth,n.counters);let r=Object.create(n.scope);i&&Object.assign(r,i);let c=n.child("action:"+e,0,e).withScope(r);return {stored:s,childFrame:c,childScope:r}}_maybeAutoPromote(e){this._requestedMode==="auto"&&++e.invokeCount>=this._threshold&&(e.compiled=this._compileAction(e.directives),this._mode!=="jit"&&this._promote());}_executeDirectives(e,i){return this._directiveExecutor(e,i,this._handlers,this._directiveHooks,this._typeField,this._engineRef,this._limits.maxDirectives)}async _executeDirectivesAsync(e,i){return this._directiveExecutor(e,i,this._handlers,this._directiveHooks,this._typeField,this._engineRef,this._limits.maxDirectives)}_compileAction(e){let{fn:i}=O(e,this._directiveAnalysis,this._typeField),n={d:e,h:this._handlers,hooks:this._directiveHooks,engine:this._engineRef,runner:this._directiveRunner};return {fn:i,$:n}}_isAccessible(e,i){let s="action:"+e.slice(0,e.lastIndexOf("/"));return i.path.includes(s)}_requireCtx(){if(this._ctx===void 0)throw new Error("No context set. Call setContext(ctx) or use context(ctx, fn) before invoke.");return this._ctx}_promote(){this._directiveExecutor=this._buildJitDirectiveExecutor(),this._mode="jit";}_buildJitDirectiveExecutor(){let{fn:e}=j(this._directiveAnalysis);return e}};function _e(t){return new V(t)}export{S as RESERVED_TYPES,F as SimpleEmitter,O as buildActionExecutor,j as buildDirectiveExecutor,_e as createActionEngine,B as createDirectiveInterpreter,H as normalizeDirectives};
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@statedelta-actions/actions",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Directive execution engine with JIT compilation and BailHook interception",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@statedelta-actions/core": "0.1.0"
|
|
22
|
+
},
|
|
23
|
+
"author": "Anderson D. Rosa <andersondrosa@outlook.com>",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "https://github.com/andersondrosa/statedelta-actions.git",
|
|
28
|
+
"directory": "packages/actions"
|
|
29
|
+
},
|
|
30
|
+
"sideEffects": false,
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=18"
|
|
33
|
+
},
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsup",
|
|
39
|
+
"dev": "tsup --watch",
|
|
40
|
+
"test": "vitest run",
|
|
41
|
+
"test:watch": "vitest",
|
|
42
|
+
"test:coverage": "vitest run --coverage",
|
|
43
|
+
"lint": "eslint src/",
|
|
44
|
+
"typecheck": "tsc --noEmit",
|
|
45
|
+
"clean": "rm -rf dist"
|
|
46
|
+
}
|
|
47
|
+
}
|