@statedelta-actions/actions 0.5.0 → 0.7.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/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { Directive, ExecutionFrame, RuleEngineRef, ApplyResult, RegisterWarning, ActionEngineRef, DirectiveResult, HookFn, InterceptResult, AbortDecision, FrameLimits, SlotAnalysis } from '@statedelta-actions/core';
1
+ import { Directive, ExecutionFrame, ActionEngineRef, ApplyResult, RuleEngineRef, RegisterWarning, DirectiveResult, HookFn, InterceptResult, AbortDecision, FrameLimits, SlotAnalysis } from '@statedelta-actions/core';
2
2
  export { RegisterWarning } from '@statedelta-actions/core';
3
3
 
4
4
  type DirectiveHandler<TCtx> = (directive: Directive, frame: ExecutionFrame<TCtx>, engine: RuleEngineRef<TCtx>) => ApplyResult | Promise<ApplyResult>;
@@ -12,7 +12,65 @@ interface ValidationResult {
12
12
  error?: string;
13
13
  warnings?: string[];
14
14
  }
15
- interface HandlerDefinition<TCtx> {
15
+ /**
16
+ * Configuração de um campo da diretiva que contém sub-array de Directive[].
17
+ *
18
+ * Comportamento (afeta o engine):
19
+ * - `required`: campo precisa estar presente e ser array. Default: false.
20
+ * - `graph`: se false, ignorado por walks de grafo (mini-graph, analyzer).
21
+ * Default: true.
22
+ *
23
+ * Inspeção (metadata pura — não lida pelo engine, exposta via accessor):
24
+ * - `description`, `purpose`, `examples`, `tags`
25
+ */
26
+ interface SubDirectiveFieldConfig {
27
+ /**
28
+ * Se true, o campo precisa estar presente na diretiva e ser um array
29
+ * de Directive. Ausente ou não-array → throw em register-time.
30
+ * Default: false.
31
+ */
32
+ readonly required?: boolean;
33
+ /**
34
+ * Se false, o campo é ignorado por todos os walks de grafo
35
+ * (mini-graph, analyzer denied-scan, propagators). Útil pra campos
36
+ * de metadata/preview que não representam path de execução real.
37
+ * Default: true.
38
+ */
39
+ readonly graph?: boolean;
40
+ /**
41
+ * Descrição do campo. Hover docs em IDEs, descoberta de esquema
42
+ * por DSL JSON externo, documentação auto-gerada.
43
+ */
44
+ readonly description?: string;
45
+ /**
46
+ * Label semântico machine-readable. Tooling/analyzer pode dar
47
+ * tratamento diferente sem hardcode no engine.
48
+ *
49
+ * Valores comuns (não exaustivo, string livre):
50
+ * - "body" — caminho principal de execução
51
+ * - "catch" — caminho de fallback em erro
52
+ * - "branch" — caminho condicional
53
+ * - "finalizer" — caminho garantido (try/finally)
54
+ * - "metadata" — não é execução real (combine com graph: false)
55
+ */
56
+ readonly purpose?: string;
57
+ /**
58
+ * Exemplos de sub-arrays válidos. DSL/IDE pode usar pra autocomplete
59
+ * e docs auto-geradas.
60
+ */
61
+ readonly examples?: ReadonlyArray<ReadonlyArray<Directive>>;
62
+ /**
63
+ * Tags livres pra categorização cruzada em tooling/analyzer.
64
+ * Ex: ["transaction", "rollback-safe"].
65
+ */
66
+ readonly tags?: ReadonlyArray<string>;
67
+ }
68
+ /**
69
+ * Campos comuns a todo handler V2, independente do modo de execução.
70
+ * `execute` fica nos subtipos — cada modo refina seu retorno. Não é genérico
71
+ * porque nenhuma das fases (`analyze`/`validate`/`compile`) usa `TCtx`.
72
+ */
73
+ interface HandlerBaseDefinition {
16
74
  /** Register-time: extrai capabilities e dependências. */
17
75
  analyze?: (directive: Directive) => HandlerAnalysis;
18
76
  /** Register-time: valida estrutura da directive. */
@@ -20,44 +78,113 @@ interface HandlerDefinition<TCtx> {
20
78
  /** JIT-time: contribui código inline per-action (Fase 2c). */
21
79
  compile?: (directive: Directive, compiler: unknown) => string | void;
22
80
  /**
23
- * Marca o handler como assíncrono (opt-in explícito).
24
- *
25
- * Quando true (ou quando `execute` é uma `async function`), o engine:
26
- * - inclui este handler no async handler set;
27
- * - força `isAsync = true` no engine inteiro;
28
- * - emite `await` ao invocar este handler no interpreter async e nos JITs.
81
+ * Declara campos da diretiva que contêm sub-arrays de Directive[].
82
+ * O engine usa essa informação para:
83
+ * - Validação recursiva em register-time (com `required`)
84
+ * - Normalização recursiva (catch internos aninhados)
85
+ * - Walks do mini-graph (deps, async/interactive transitivo) — quando `graph !== false`
86
+ * - Walks do analyzer (denied-scan, capabilities, composition)
29
87
  *
30
- * Auto-detecção via `isAsyncFunction(execute)` cobre `async function`.
31
- * Use a flag para wrappers que retornam `Promise<ApplyResult>` sem
32
- * serem `async function` (ex: factory que retorna função normal que
33
- * delega pra um await internamente via `.then`).
88
+ * NÃO afeta execução. Handler.execute é responsável por rodar
89
+ * as sub-directives via engine.runDirectives() ou runDirectivesAsync().
34
90
  *
35
- * Sem flag e sem `async function`: handler é tratado como sync.
91
+ * Ver: docs/proposals/REFACTOR-SUBDIRECTIVES-DESCRIPTOR.md
36
92
  */
37
- async?: boolean;
93
+ readonly subDirectives?: {
94
+ readonly [fieldName: string]: SubDirectiveFieldConfig;
95
+ };
38
96
  /**
39
- * Marca o handler como interativo (opt-in explícito).
40
- *
41
- * Quando true (ou quando `execute` é uma `function*` / `async function*`),
42
- * o engine:
43
- * - inclui este handler no interactive handler set;
44
- * - propaga transitividade `_interactiveActions` no mini-graph;
45
- * - emite `yield* handler(...)` ao invocar nas actions interactive;
46
- * - exige `engine.invokeInteractive()` em actions cuja sub-árvore use o handler.
47
- *
48
- * Auto-detecção via `isGeneratorFunction(execute)` cobre `function*` e
49
- * `async function*`. Use a flag para wrappers que retornam iterator sem
50
- * serem generator function.
51
- *
52
- * Handler com `interactive: true` em engine sem `interactive` config →
53
- * erro no constructor (fail-fast).
54
- *
55
- * Sem flag e sem generator function: handler é tratado como regular.
97
+ * Descrição do handler. Hover docs, descoberta de esquema por DSL,
98
+ * documentação auto-gerada.
56
99
  */
57
- interactive?: boolean;
58
- /** Runtime: executa a directive (obrigatório). */
59
- execute: (directive: Directive, frame: ExecutionFrame<TCtx>, engine: ActionEngineRef<TCtx>) => ApplyResult | Promise<ApplyResult>;
100
+ readonly description?: string;
101
+ /**
102
+ * Tags livres pra categorização cruzada (control-flow, transaction,
103
+ * io, query, etc.). Tooling pode agrupar/filtrar handlers por tag.
104
+ */
105
+ readonly tags?: ReadonlyArray<string>;
106
+ /**
107
+ * Marca o handler como obsoleto. boolean = deprecated sem motivo;
108
+ * string = mensagem de migração ("use 'try' since v0.5").
109
+ * Tooling/analyzer pode emitir warning quando handler é usado.
110
+ */
111
+ readonly deprecated?: boolean | string;
112
+ /**
113
+ * Versão em que o handler foi introduzido. SemVer ou string livre.
114
+ * Útil pra versionamento de DSL e compat checks no analyzer.
115
+ */
116
+ readonly since?: string;
117
+ /**
118
+ * Declara que esse handler é alias semântico de outro
119
+ * (mesma forma estrutural, mesma intenção). Tooling pode dedup
120
+ * referências e documentação. NÃO afeta dispatch — cada handler
121
+ * permanece independente em runtime.
122
+ */
123
+ readonly aliasOf?: string;
124
+ }
125
+ /** Função `execute` de um handler sync — retorna `ApplyResult` direto. */
126
+ type SyncHandlerExecute<TCtx> = (directive: Directive, frame: ExecutionFrame<TCtx>, engine: ActionEngineRef<TCtx>) => ApplyResult;
127
+ /** Função `execute` de um handler async — retorna `Promise<ApplyResult>`. */
128
+ type AsyncHandlerExecute<TCtx> = (directive: Directive, frame: ExecutionFrame<TCtx>, engine: ActionEngineRef<TCtx>) => Promise<ApplyResult>;
129
+ /**
130
+ * Função `execute` de um handler interactive — generator (sync ou async).
131
+ * O yield é `unknown`: o handler controla o protocol (o que yielda, o que
132
+ * recebe via `next`). O engine apenas empacota o yield como `PauseEvent.payload`
133
+ * pro consumer no nível raiz (ver ADR-024). O return é o `ApplyResult` final.
134
+ */
135
+ type InteractiveHandlerExecute<TCtx> = (directive: Directive, frame: ExecutionFrame<TCtx>, engine: ActionEngineRef<TCtx>) => Generator<unknown, ApplyResult, unknown> | AsyncGenerator<unknown, ApplyResult, unknown>;
136
+ /**
137
+ * Handler sync — `execute` retorna `ApplyResult` direto. É o modo default:
138
+ * sem flag e sem `async function`/`function*`, o handler é tratado como sync.
139
+ */
140
+ interface SyncHandlerDefinition<TCtx> extends HandlerBaseDefinition {
141
+ async?: false;
142
+ interactive?: false;
143
+ execute: SyncHandlerExecute<TCtx>;
144
+ }
145
+ /**
146
+ * Handler async — `execute` retorna `Promise<ApplyResult>`. Marcado com
147
+ * `async: true` ou detectado via `isAsyncFunction(execute)` (cobre
148
+ * `async function`). Use a flag pra wrappers que retornam Promise sem serem
149
+ * `async function` (ex: factory que delega via `.then`).
150
+ *
151
+ * Quando o engine vê um handler async: inclui no async handler set, força
152
+ * `isAsync = true` no engine inteiro, e emite `await` ao invocá-lo no
153
+ * interpreter async e nos JITs.
154
+ */
155
+ interface AsyncHandlerDefinition<TCtx> extends HandlerBaseDefinition {
156
+ async: true;
157
+ interactive?: false;
158
+ execute: AsyncHandlerExecute<TCtx>;
60
159
  }
160
+ /**
161
+ * Handler interactive — `execute` é generator (sync ou async). Marcado com
162
+ * `interactive: true` ou detectado via `isGeneratorFunction(execute)` (cobre
163
+ * `function*` e `async function*`). Use a flag pra wrappers que retornam
164
+ * iterator sem serem generator function. Pode também ser `async` (generator
165
+ * async) — daí `async: true` junto.
166
+ *
167
+ * Quando o engine vê um handler interactive: inclui no interactive handler set,
168
+ * propaga `_interactiveActions` transitivamente no mini-graph, emite
169
+ * `yield* handler(...)` ao invocá-lo nas actions interactive, e exige
170
+ * `engine.invokeInteractive()` em actions cuja sub-árvore o use. Handler
171
+ * `interactive: true` em engine sem `interactive` config → erro no constructor
172
+ * (fail-fast).
173
+ */
174
+ interface InteractiveHandlerDefinition<TCtx> extends HandlerBaseDefinition {
175
+ interactive: true;
176
+ async?: boolean;
177
+ execute: InteractiveHandlerExecute<TCtx>;
178
+ }
179
+ /**
180
+ * Handler V2 (objeto com fases). Discriminated union pelo modo de execução —
181
+ * `async` e `interactive` discriminam, `execute` refina seu retorno conforme
182
+ * o modo. Tipar como `HandlerDefinition<TCtx>` continua válido (é o agregado);
183
+ * narrowing pelos flags refina pro subtipo. Quem sabe o modo do seu handler
184
+ * pode tipar diretamente como `SyncHandlerDefinition` etc. e chamar
185
+ * `handler.execute(...)` sem `await`/narrowing.
186
+ */
187
+ type HandlerDefinition<TCtx> = SyncHandlerDefinition<TCtx> | AsyncHandlerDefinition<TCtx> | InteractiveHandlerDefinition<TCtx>;
61
188
  /**
62
189
  * Aceita handler V1 (função) ou V2 (objeto com fases).
63
190
  * Backwards compat: função simples é tratada como { execute: fn }.
@@ -191,6 +318,7 @@ type DirectivePermissionEntry = string | DirectivePermissionConfig;
191
318
  * - diretiva `type: "pause"` → erro no register
192
319
  */
193
320
  interface InteractiveConfig {
321
+ readonly [key: string]: unknown;
194
322
  }
195
323
  /**
196
324
  * Evento yieldado durante execução de uma action interactive.
@@ -361,6 +489,13 @@ interface IActionEngine<TCtx> {
361
489
  getActionDefinition(id: string): ActionDefinition<TCtx> | undefined;
362
490
  /** Campo de tipo pra dispatch de diretivas (default: "type"). */
363
491
  readonly typeField: string;
492
+ /**
493
+ * Mapa `type → readonly fieldNames[]` dos campos declarados pelo
494
+ * `subDirectives` dos handlers que entram no grafo (graph !== false).
495
+ * Pré-computado no construct-time. Consumido pelo mini-graph interno
496
+ * e por walks externos (analyzer denied-scan, capabilities, composition).
497
+ */
498
+ readonly subDirectiveFieldsForGraph: ReadonlyMap<string, readonly string[]>;
364
499
  /**
365
500
  * Mapa de permissões de diretivas, computado no boot (imutável).
366
501
  * Contém apenas handlers registrados (available ou denied).
@@ -505,7 +640,7 @@ declare class SimpleEmitter<TEvents = Record<string, unknown>> {
505
640
  }
506
641
 
507
642
  declare const RESERVED_TYPES: Set<string>;
508
- declare function normalizeDirectives<TCtx>(directives: readonly Directive<TCtx>[], typeField: string): Directive<TCtx>[];
643
+ declare function normalizeDirectives<TCtx>(directives: readonly Directive<TCtx>[], typeField: string, subDirectiveFieldsAll?: ReadonlyMap<string, readonly string[]>): Directive<TCtx>[];
509
644
 
510
645
  declare function createDirectiveInterpreter<TCtx>(analysis: SlotAnalysis, isAsync: boolean): DirectiveExecutorFn<TCtx>;
511
646
 
@@ -527,9 +662,22 @@ interface GeneratedDirectiveExecutor {
527
662
  */
528
663
  declare function buildDirectiveExecutor(analysis: SlotAnalysis, isAsync?: boolean): GeneratedDirectiveExecutor;
529
664
 
530
- type RuntimeActionExecutorFn = (frame: unknown, scope: unknown, $: unknown) => unknown;
665
+ /**
666
+ * Assinatura da função compilada pelo JIT per-action.
667
+ *
668
+ * Os parâmetros são tipados como `unknown` propositalmente: a função é
669
+ * criada via `new Function(...)` e recebe `frame`/`scope`/`$` que o
670
+ * caller (engine) já tem em tipos concretos. O retorno é `unknown`
671
+ * porque o JIT compila 4 shapes possíveis (sync/async/generator/
672
+ * async-generator) e o caller faz narrowing baseado em
673
+ * `GeneratedActionExecutor.{isAsync,isInteractive}`.
674
+ *
675
+ * Exportada pra que o engine possa tipar `CompiledAction.fn` sem
676
+ * cair em `Function` (que perde toda checagem de assinatura).
677
+ */
678
+ type GeneratedActionExecutorFn = (frame: unknown, scope: unknown, $: unknown) => unknown;
531
679
  interface GeneratedActionExecutor {
532
- fn: RuntimeActionExecutorFn;
680
+ fn: GeneratedActionExecutorFn;
533
681
  /** Wrapper async (`async function`/`async function*`)? */
534
682
  isAsync: boolean;
535
683
  /** Wrapper generator (`function*`/`async function*`)? Quando true, `fn(...)` retorna Generator/AsyncGenerator. */
@@ -565,4 +713,4 @@ interface GeneratedActionExecutor {
565
713
  */
566
714
  declare function buildActionExecutor(directives: readonly Directive[], analysis: SlotAnalysis, typeField: string, asyncHandlerSet?: ReadonlySet<string>, actionUsesAsyncOverride?: boolean, actionIsInteractive?: boolean, interactiveHandlerSet?: ReadonlySet<string>): GeneratedActionExecutor;
567
715
 
568
- export { type ActionDefinition, type ActionHooks, type ActionInterceptResult, type AsyncInteractiveSession, 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 InteractiveApplyResult, type InteractiveConfig, type InteractiveSession, type Listener, type PauseEvent, RESERVED_TYPES, type RegisterError, type RegisterEvent, type RegisterResult, type Responder, SimpleEmitter, type UnregisterEvent, type ValidationResult, buildActionExecutor, buildDirectiveExecutor, createActionEngine, createDirectiveInterpreter, drainAsync, drainSync, normalizeDirectives, replayResponder };
716
+ export { type ActionDefinition, type ActionHooks, type ActionInterceptResult, type AsyncHandlerDefinition, type AsyncHandlerExecute, type AsyncInteractiveSession, 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 GeneratedActionExecutorFn, type GeneratedDirectiveExecutor, type HandlerAnalysis, type HandlerBaseDefinition, type HandlerDefinition, type HandlerInput, type HandlerInputMap, type IActionEngine, type IActionEngineConfig, type InteractiveApplyResult, type InteractiveConfig, type InteractiveHandlerDefinition, type InteractiveHandlerExecute, type InteractiveSession, type Listener, type PauseEvent, RESERVED_TYPES, type RegisterError, type RegisterEvent, type RegisterResult, type Responder, SimpleEmitter, type SubDirectiveFieldConfig, type SyncHandlerDefinition, type SyncHandlerExecute, type UnregisterEvent, type ValidationResult, buildActionExecutor, buildDirectiveExecutor, createActionEngine, createDirectiveInterpreter, drainAsync, drainSync, normalizeDirectives, replayResponder };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Directive, ExecutionFrame, RuleEngineRef, ApplyResult, RegisterWarning, ActionEngineRef, DirectiveResult, HookFn, InterceptResult, AbortDecision, FrameLimits, SlotAnalysis } from '@statedelta-actions/core';
1
+ import { Directive, ExecutionFrame, ActionEngineRef, ApplyResult, RuleEngineRef, RegisterWarning, DirectiveResult, HookFn, InterceptResult, AbortDecision, FrameLimits, SlotAnalysis } from '@statedelta-actions/core';
2
2
  export { RegisterWarning } from '@statedelta-actions/core';
3
3
 
4
4
  type DirectiveHandler<TCtx> = (directive: Directive, frame: ExecutionFrame<TCtx>, engine: RuleEngineRef<TCtx>) => ApplyResult | Promise<ApplyResult>;
@@ -12,7 +12,65 @@ interface ValidationResult {
12
12
  error?: string;
13
13
  warnings?: string[];
14
14
  }
15
- interface HandlerDefinition<TCtx> {
15
+ /**
16
+ * Configuração de um campo da diretiva que contém sub-array de Directive[].
17
+ *
18
+ * Comportamento (afeta o engine):
19
+ * - `required`: campo precisa estar presente e ser array. Default: false.
20
+ * - `graph`: se false, ignorado por walks de grafo (mini-graph, analyzer).
21
+ * Default: true.
22
+ *
23
+ * Inspeção (metadata pura — não lida pelo engine, exposta via accessor):
24
+ * - `description`, `purpose`, `examples`, `tags`
25
+ */
26
+ interface SubDirectiveFieldConfig {
27
+ /**
28
+ * Se true, o campo precisa estar presente na diretiva e ser um array
29
+ * de Directive. Ausente ou não-array → throw em register-time.
30
+ * Default: false.
31
+ */
32
+ readonly required?: boolean;
33
+ /**
34
+ * Se false, o campo é ignorado por todos os walks de grafo
35
+ * (mini-graph, analyzer denied-scan, propagators). Útil pra campos
36
+ * de metadata/preview que não representam path de execução real.
37
+ * Default: true.
38
+ */
39
+ readonly graph?: boolean;
40
+ /**
41
+ * Descrição do campo. Hover docs em IDEs, descoberta de esquema
42
+ * por DSL JSON externo, documentação auto-gerada.
43
+ */
44
+ readonly description?: string;
45
+ /**
46
+ * Label semântico machine-readable. Tooling/analyzer pode dar
47
+ * tratamento diferente sem hardcode no engine.
48
+ *
49
+ * Valores comuns (não exaustivo, string livre):
50
+ * - "body" — caminho principal de execução
51
+ * - "catch" — caminho de fallback em erro
52
+ * - "branch" — caminho condicional
53
+ * - "finalizer" — caminho garantido (try/finally)
54
+ * - "metadata" — não é execução real (combine com graph: false)
55
+ */
56
+ readonly purpose?: string;
57
+ /**
58
+ * Exemplos de sub-arrays válidos. DSL/IDE pode usar pra autocomplete
59
+ * e docs auto-geradas.
60
+ */
61
+ readonly examples?: ReadonlyArray<ReadonlyArray<Directive>>;
62
+ /**
63
+ * Tags livres pra categorização cruzada em tooling/analyzer.
64
+ * Ex: ["transaction", "rollback-safe"].
65
+ */
66
+ readonly tags?: ReadonlyArray<string>;
67
+ }
68
+ /**
69
+ * Campos comuns a todo handler V2, independente do modo de execução.
70
+ * `execute` fica nos subtipos — cada modo refina seu retorno. Não é genérico
71
+ * porque nenhuma das fases (`analyze`/`validate`/`compile`) usa `TCtx`.
72
+ */
73
+ interface HandlerBaseDefinition {
16
74
  /** Register-time: extrai capabilities e dependências. */
17
75
  analyze?: (directive: Directive) => HandlerAnalysis;
18
76
  /** Register-time: valida estrutura da directive. */
@@ -20,44 +78,113 @@ interface HandlerDefinition<TCtx> {
20
78
  /** JIT-time: contribui código inline per-action (Fase 2c). */
21
79
  compile?: (directive: Directive, compiler: unknown) => string | void;
22
80
  /**
23
- * Marca o handler como assíncrono (opt-in explícito).
24
- *
25
- * Quando true (ou quando `execute` é uma `async function`), o engine:
26
- * - inclui este handler no async handler set;
27
- * - força `isAsync = true` no engine inteiro;
28
- * - emite `await` ao invocar este handler no interpreter async e nos JITs.
81
+ * Declara campos da diretiva que contêm sub-arrays de Directive[].
82
+ * O engine usa essa informação para:
83
+ * - Validação recursiva em register-time (com `required`)
84
+ * - Normalização recursiva (catch internos aninhados)
85
+ * - Walks do mini-graph (deps, async/interactive transitivo) — quando `graph !== false`
86
+ * - Walks do analyzer (denied-scan, capabilities, composition)
29
87
  *
30
- * Auto-detecção via `isAsyncFunction(execute)` cobre `async function`.
31
- * Use a flag para wrappers que retornam `Promise<ApplyResult>` sem
32
- * serem `async function` (ex: factory que retorna função normal que
33
- * delega pra um await internamente via `.then`).
88
+ * NÃO afeta execução. Handler.execute é responsável por rodar
89
+ * as sub-directives via engine.runDirectives() ou runDirectivesAsync().
34
90
  *
35
- * Sem flag e sem `async function`: handler é tratado como sync.
91
+ * Ver: docs/proposals/REFACTOR-SUBDIRECTIVES-DESCRIPTOR.md
36
92
  */
37
- async?: boolean;
93
+ readonly subDirectives?: {
94
+ readonly [fieldName: string]: SubDirectiveFieldConfig;
95
+ };
38
96
  /**
39
- * Marca o handler como interativo (opt-in explícito).
40
- *
41
- * Quando true (ou quando `execute` é uma `function*` / `async function*`),
42
- * o engine:
43
- * - inclui este handler no interactive handler set;
44
- * - propaga transitividade `_interactiveActions` no mini-graph;
45
- * - emite `yield* handler(...)` ao invocar nas actions interactive;
46
- * - exige `engine.invokeInteractive()` em actions cuja sub-árvore use o handler.
47
- *
48
- * Auto-detecção via `isGeneratorFunction(execute)` cobre `function*` e
49
- * `async function*`. Use a flag para wrappers que retornam iterator sem
50
- * serem generator function.
51
- *
52
- * Handler com `interactive: true` em engine sem `interactive` config →
53
- * erro no constructor (fail-fast).
54
- *
55
- * Sem flag e sem generator function: handler é tratado como regular.
97
+ * Descrição do handler. Hover docs, descoberta de esquema por DSL,
98
+ * documentação auto-gerada.
56
99
  */
57
- interactive?: boolean;
58
- /** Runtime: executa a directive (obrigatório). */
59
- execute: (directive: Directive, frame: ExecutionFrame<TCtx>, engine: ActionEngineRef<TCtx>) => ApplyResult | Promise<ApplyResult>;
100
+ readonly description?: string;
101
+ /**
102
+ * Tags livres pra categorização cruzada (control-flow, transaction,
103
+ * io, query, etc.). Tooling pode agrupar/filtrar handlers por tag.
104
+ */
105
+ readonly tags?: ReadonlyArray<string>;
106
+ /**
107
+ * Marca o handler como obsoleto. boolean = deprecated sem motivo;
108
+ * string = mensagem de migração ("use 'try' since v0.5").
109
+ * Tooling/analyzer pode emitir warning quando handler é usado.
110
+ */
111
+ readonly deprecated?: boolean | string;
112
+ /**
113
+ * Versão em que o handler foi introduzido. SemVer ou string livre.
114
+ * Útil pra versionamento de DSL e compat checks no analyzer.
115
+ */
116
+ readonly since?: string;
117
+ /**
118
+ * Declara que esse handler é alias semântico de outro
119
+ * (mesma forma estrutural, mesma intenção). Tooling pode dedup
120
+ * referências e documentação. NÃO afeta dispatch — cada handler
121
+ * permanece independente em runtime.
122
+ */
123
+ readonly aliasOf?: string;
124
+ }
125
+ /** Função `execute` de um handler sync — retorna `ApplyResult` direto. */
126
+ type SyncHandlerExecute<TCtx> = (directive: Directive, frame: ExecutionFrame<TCtx>, engine: ActionEngineRef<TCtx>) => ApplyResult;
127
+ /** Função `execute` de um handler async — retorna `Promise<ApplyResult>`. */
128
+ type AsyncHandlerExecute<TCtx> = (directive: Directive, frame: ExecutionFrame<TCtx>, engine: ActionEngineRef<TCtx>) => Promise<ApplyResult>;
129
+ /**
130
+ * Função `execute` de um handler interactive — generator (sync ou async).
131
+ * O yield é `unknown`: o handler controla o protocol (o que yielda, o que
132
+ * recebe via `next`). O engine apenas empacota o yield como `PauseEvent.payload`
133
+ * pro consumer no nível raiz (ver ADR-024). O return é o `ApplyResult` final.
134
+ */
135
+ type InteractiveHandlerExecute<TCtx> = (directive: Directive, frame: ExecutionFrame<TCtx>, engine: ActionEngineRef<TCtx>) => Generator<unknown, ApplyResult, unknown> | AsyncGenerator<unknown, ApplyResult, unknown>;
136
+ /**
137
+ * Handler sync — `execute` retorna `ApplyResult` direto. É o modo default:
138
+ * sem flag e sem `async function`/`function*`, o handler é tratado como sync.
139
+ */
140
+ interface SyncHandlerDefinition<TCtx> extends HandlerBaseDefinition {
141
+ async?: false;
142
+ interactive?: false;
143
+ execute: SyncHandlerExecute<TCtx>;
144
+ }
145
+ /**
146
+ * Handler async — `execute` retorna `Promise<ApplyResult>`. Marcado com
147
+ * `async: true` ou detectado via `isAsyncFunction(execute)` (cobre
148
+ * `async function`). Use a flag pra wrappers que retornam Promise sem serem
149
+ * `async function` (ex: factory que delega via `.then`).
150
+ *
151
+ * Quando o engine vê um handler async: inclui no async handler set, força
152
+ * `isAsync = true` no engine inteiro, e emite `await` ao invocá-lo no
153
+ * interpreter async e nos JITs.
154
+ */
155
+ interface AsyncHandlerDefinition<TCtx> extends HandlerBaseDefinition {
156
+ async: true;
157
+ interactive?: false;
158
+ execute: AsyncHandlerExecute<TCtx>;
60
159
  }
160
+ /**
161
+ * Handler interactive — `execute` é generator (sync ou async). Marcado com
162
+ * `interactive: true` ou detectado via `isGeneratorFunction(execute)` (cobre
163
+ * `function*` e `async function*`). Use a flag pra wrappers que retornam
164
+ * iterator sem serem generator function. Pode também ser `async` (generator
165
+ * async) — daí `async: true` junto.
166
+ *
167
+ * Quando o engine vê um handler interactive: inclui no interactive handler set,
168
+ * propaga `_interactiveActions` transitivamente no mini-graph, emite
169
+ * `yield* handler(...)` ao invocá-lo nas actions interactive, e exige
170
+ * `engine.invokeInteractive()` em actions cuja sub-árvore o use. Handler
171
+ * `interactive: true` em engine sem `interactive` config → erro no constructor
172
+ * (fail-fast).
173
+ */
174
+ interface InteractiveHandlerDefinition<TCtx> extends HandlerBaseDefinition {
175
+ interactive: true;
176
+ async?: boolean;
177
+ execute: InteractiveHandlerExecute<TCtx>;
178
+ }
179
+ /**
180
+ * Handler V2 (objeto com fases). Discriminated union pelo modo de execução —
181
+ * `async` e `interactive` discriminam, `execute` refina seu retorno conforme
182
+ * o modo. Tipar como `HandlerDefinition<TCtx>` continua válido (é o agregado);
183
+ * narrowing pelos flags refina pro subtipo. Quem sabe o modo do seu handler
184
+ * pode tipar diretamente como `SyncHandlerDefinition` etc. e chamar
185
+ * `handler.execute(...)` sem `await`/narrowing.
186
+ */
187
+ type HandlerDefinition<TCtx> = SyncHandlerDefinition<TCtx> | AsyncHandlerDefinition<TCtx> | InteractiveHandlerDefinition<TCtx>;
61
188
  /**
62
189
  * Aceita handler V1 (função) ou V2 (objeto com fases).
63
190
  * Backwards compat: função simples é tratada como { execute: fn }.
@@ -191,6 +318,7 @@ type DirectivePermissionEntry = string | DirectivePermissionConfig;
191
318
  * - diretiva `type: "pause"` → erro no register
192
319
  */
193
320
  interface InteractiveConfig {
321
+ readonly [key: string]: unknown;
194
322
  }
195
323
  /**
196
324
  * Evento yieldado durante execução de uma action interactive.
@@ -361,6 +489,13 @@ interface IActionEngine<TCtx> {
361
489
  getActionDefinition(id: string): ActionDefinition<TCtx> | undefined;
362
490
  /** Campo de tipo pra dispatch de diretivas (default: "type"). */
363
491
  readonly typeField: string;
492
+ /**
493
+ * Mapa `type → readonly fieldNames[]` dos campos declarados pelo
494
+ * `subDirectives` dos handlers que entram no grafo (graph !== false).
495
+ * Pré-computado no construct-time. Consumido pelo mini-graph interno
496
+ * e por walks externos (analyzer denied-scan, capabilities, composition).
497
+ */
498
+ readonly subDirectiveFieldsForGraph: ReadonlyMap<string, readonly string[]>;
364
499
  /**
365
500
  * Mapa de permissões de diretivas, computado no boot (imutável).
366
501
  * Contém apenas handlers registrados (available ou denied).
@@ -505,7 +640,7 @@ declare class SimpleEmitter<TEvents = Record<string, unknown>> {
505
640
  }
506
641
 
507
642
  declare const RESERVED_TYPES: Set<string>;
508
- declare function normalizeDirectives<TCtx>(directives: readonly Directive<TCtx>[], typeField: string): Directive<TCtx>[];
643
+ declare function normalizeDirectives<TCtx>(directives: readonly Directive<TCtx>[], typeField: string, subDirectiveFieldsAll?: ReadonlyMap<string, readonly string[]>): Directive<TCtx>[];
509
644
 
510
645
  declare function createDirectiveInterpreter<TCtx>(analysis: SlotAnalysis, isAsync: boolean): DirectiveExecutorFn<TCtx>;
511
646
 
@@ -527,9 +662,22 @@ interface GeneratedDirectiveExecutor {
527
662
  */
528
663
  declare function buildDirectiveExecutor(analysis: SlotAnalysis, isAsync?: boolean): GeneratedDirectiveExecutor;
529
664
 
530
- type RuntimeActionExecutorFn = (frame: unknown, scope: unknown, $: unknown) => unknown;
665
+ /**
666
+ * Assinatura da função compilada pelo JIT per-action.
667
+ *
668
+ * Os parâmetros são tipados como `unknown` propositalmente: a função é
669
+ * criada via `new Function(...)` e recebe `frame`/`scope`/`$` que o
670
+ * caller (engine) já tem em tipos concretos. O retorno é `unknown`
671
+ * porque o JIT compila 4 shapes possíveis (sync/async/generator/
672
+ * async-generator) e o caller faz narrowing baseado em
673
+ * `GeneratedActionExecutor.{isAsync,isInteractive}`.
674
+ *
675
+ * Exportada pra que o engine possa tipar `CompiledAction.fn` sem
676
+ * cair em `Function` (que perde toda checagem de assinatura).
677
+ */
678
+ type GeneratedActionExecutorFn = (frame: unknown, scope: unknown, $: unknown) => unknown;
531
679
  interface GeneratedActionExecutor {
532
- fn: RuntimeActionExecutorFn;
680
+ fn: GeneratedActionExecutorFn;
533
681
  /** Wrapper async (`async function`/`async function*`)? */
534
682
  isAsync: boolean;
535
683
  /** Wrapper generator (`function*`/`async function*`)? Quando true, `fn(...)` retorna Generator/AsyncGenerator. */
@@ -565,4 +713,4 @@ interface GeneratedActionExecutor {
565
713
  */
566
714
  declare function buildActionExecutor(directives: readonly Directive[], analysis: SlotAnalysis, typeField: string, asyncHandlerSet?: ReadonlySet<string>, actionUsesAsyncOverride?: boolean, actionIsInteractive?: boolean, interactiveHandlerSet?: ReadonlySet<string>): GeneratedActionExecutor;
567
715
 
568
- export { type ActionDefinition, type ActionHooks, type ActionInterceptResult, type AsyncInteractiveSession, 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 InteractiveApplyResult, type InteractiveConfig, type InteractiveSession, type Listener, type PauseEvent, RESERVED_TYPES, type RegisterError, type RegisterEvent, type RegisterResult, type Responder, SimpleEmitter, type UnregisterEvent, type ValidationResult, buildActionExecutor, buildDirectiveExecutor, createActionEngine, createDirectiveInterpreter, drainAsync, drainSync, normalizeDirectives, replayResponder };
716
+ export { type ActionDefinition, type ActionHooks, type ActionInterceptResult, type AsyncHandlerDefinition, type AsyncHandlerExecute, type AsyncInteractiveSession, 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 GeneratedActionExecutorFn, type GeneratedDirectiveExecutor, type HandlerAnalysis, type HandlerBaseDefinition, type HandlerDefinition, type HandlerInput, type HandlerInputMap, type IActionEngine, type IActionEngineConfig, type InteractiveApplyResult, type InteractiveConfig, type InteractiveHandlerDefinition, type InteractiveHandlerExecute, type InteractiveSession, type Listener, type PauseEvent, RESERVED_TYPES, type RegisterError, type RegisterEvent, type RegisterResult, type Responder, SimpleEmitter, type SubDirectiveFieldConfig, type SyncHandlerDefinition, type SyncHandlerExecute, type UnregisterEvent, type ValidationResult, buildActionExecutor, buildDirectiveExecutor, createActionEngine, createDirectiveInterpreter, drainAsync, drainSync, normalizeDirectives, replayResponder };