@statedelta-axiom/sandbox 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.
@@ -0,0 +1,741 @@
1
+ import * as _statedelta_axiom_realm_system from '@statedelta-axiom/realm-system';
2
+ import { RealmCtx, RealmSystemConfig, RealmPhysicsInput, PersistConfigInput, StepResult, RealmSnapshot, SystemEvents, SystemPhase, RealmPhysics, IRealmSystem, BootOptions, ChildFactory } from '@statedelta-axiom/realm-system';
3
+ import { ISchemaValidator, CompilationService, ProviderRegistry, ProviderBuffer, SourceKey, BootDefinitions, BootResult, BootError } from '@statedelta-axiom/contracts';
4
+ import { HandlerDefinition, DirectivePermissionEntry, AsyncHandlerExecute, SyncHandlerExecute } from '@statedelta-actions/sdk/actions';
5
+ import { FrameLimits } from '@statedelta-actions/sdk/core';
6
+
7
+ /**
8
+ * Capabilities + Environment + cascade — Fase 2.
9
+ *
10
+ * Separação cravada em ADR-032: `environment` (categoria A — ambient,
11
+ * não cascade) e `capabilities` (categoria B — cascade restritiva
12
+ * child ≤ parent, ADR-030).
13
+ *
14
+ * `async` é environment (decisão arquitetural única do ecossistema —
15
+ * ADR-033 RS categoria A refinada). Não cascade.
16
+ */
17
+
18
+ /**
19
+ * Recursos ambient do ecossistema. Compartilhados por referência entre
20
+ * todas as Sandboxes do mesmo runtime. Invisíveis pro realm — preservam
21
+ * determinismo. Trocar não muda lógica do mundo, só performance/mecânica.
22
+ */
23
+ interface SandboxEnvironment {
24
+ readonly schemaValidator?: ISchemaValidator;
25
+ readonly handlers?: Record<string, HandlerDefinition<RealmCtx>>;
26
+ readonly analyzer?: RealmSystemConfig["analyzer"];
27
+ readonly trace?: boolean;
28
+ readonly compilationMode?: "interpret" | "jit" | "auto";
29
+ readonly autoJitThreshold?: number;
30
+ readonly debug?: boolean;
31
+ /** Decisão arquitetural única do ecossistema. Viral — ou todo RS é
32
+ * async, ou todo é sync. Não cascade. */
33
+ readonly async?: boolean;
34
+ /**
35
+ * **CompilationService** — environment capability da Persistência v2
36
+ * (ADR-035 raiz). Compartilhado entre Sandboxes via Manager (por ref).
37
+ *
38
+ * Quando `physics.persist.definitions: true` é declarado, esta capability
39
+ * é **obrigatória** — sem ela, boot falha com `SandboxBootError
40
+ * ENVIRONMENT_INSUFFICIENT`. Com `persist.definitions: false`, é opcional
41
+ * (otimização de dedupe interno — ADR-034 raiz).
42
+ *
43
+ * Pode ser injetado externamente pra compartilhar entre múltiplos Runtimes
44
+ * (multi-tenant); ou o Runtime cria interno como default standalone.
45
+ */
46
+ readonly compilation?: CompilationService<RealmCtx>;
47
+ /**
48
+ * **System Providers** — registry de provider hooks (ADR-038 raiz / ADR-R018).
49
+ * Capability ambient compartilhada por ref entre todas as Sandboxes do
50
+ * mesmo runtime. Cada child herda por default; restringe via
51
+ * `SandboxCapabilities.providers` (whitelist).
52
+ */
53
+ readonly providers?: ProviderRegistry;
54
+ /**
55
+ * **ProviderBuffer** — porta de persistência pra entries `provide`
56
+ * (ADR-039 raiz / ADR-080 RS). Quando ausente, providers funcionam mas
57
+ * não persistem (modo serviço).
58
+ */
59
+ readonly providerBuffer?: ProviderBuffer;
60
+ }
61
+ /**
62
+ * Permissões hierárquicas do parent pra child. Aplicadas via
63
+ * `resolveCascade` no boot. Child > parent = `SandboxBootError`.
64
+ */
65
+ interface SandboxCapabilities {
66
+ readonly enableEvents?: boolean;
67
+ readonly enableViews?: boolean;
68
+ readonly imperatives?: boolean;
69
+ readonly deterministic?: boolean;
70
+ /** RealmSystemConfig.mode (não confundir com SandboxMode closed/governed). */
71
+ readonly mode?: "dev" | "runtime";
72
+ readonly allowedDirectives?: readonly DirectivePermissionEntry[];
73
+ readonly blockedDirectives?: readonly DirectivePermissionEntry[];
74
+ readonly limits?: Partial<FrameLimits>;
75
+ readonly maxEventPasses?: number;
76
+ }
77
+ /**
78
+ * Capabilities **pós-cascade** (ADR-085). Estruturalmente igual a
79
+ * `SandboxCapabilities` — campos permanecem opcionais. Quem produz é
80
+ * `resolveCascade`.
81
+ *
82
+ * **Cascaded ≠ Resolved.** O cascade apenas propaga/valida flags do
83
+ * parent pra child; NÃO aplica defaults. Campos que parent e child
84
+ * ambos omitirem ficam `undefined` aqui — defaults canônicos vivem no
85
+ * RS via `resolveRealmPhysics` (ADR-084).
86
+ *
87
+ * Distinção é semântica: o tipo marca a origem "veio do `resolveCascade`",
88
+ * útil pra leitor saber que cascade já foi validado.
89
+ */
90
+ type CascadedSandboxCapabilities = SandboxCapabilities;
91
+ type CascadeViolationKind = "boolean_restrictive" | "boolean_deterministic" | "enum_tier" | "numeric" | "list_permissive";
92
+ interface CascadeViolation {
93
+ readonly flag: string;
94
+ readonly parent: unknown;
95
+ readonly child: unknown;
96
+ readonly kind: CascadeViolationKind;
97
+ }
98
+ type CascadeResult = {
99
+ readonly ok: true;
100
+ readonly value: CascadedSandboxCapabilities;
101
+ } | {
102
+ readonly ok: false;
103
+ readonly violations: readonly CascadeViolation[];
104
+ };
105
+ /**
106
+ * Erro como dado (ADR-063 RS) — `{ code, message?, data? }`.
107
+ */
108
+ declare class SandboxBootError extends Error {
109
+ readonly code: string;
110
+ readonly data?: Record<string, unknown>;
111
+ constructor(opts: {
112
+ code: string;
113
+ message?: string;
114
+ data?: Record<string, unknown>;
115
+ });
116
+ }
117
+ /**
118
+ * Aplica cascade restritiva — 6 regras genéricas (ADR-030).
119
+ *
120
+ * - parent omitido → declared é resolved (sem cascade — sandbox top-level)
121
+ * - declared omitido → herda integralmente do parent
122
+ * - cada flag declarada é checada contra o parent; violações acumuladas
123
+ *
124
+ * Não throw. Retorna `{ ok, value | violations }` pro caller decidir.
125
+ */
126
+ declare function resolveCascade(parent: CascadedSandboxCapabilities | undefined, declared: SandboxCapabilities | undefined): CascadeResult;
127
+ /**
128
+ * Traduz capabilities resolvidas em `RealmPhysics` (regras de mundo —
129
+ * categoria B) pro RS interno. Campos undefined são omitidos — RS aplica
130
+ * seus defaults. `async` NÃO entra aqui (categoria A — vai pro
131
+ * RealmSystemConfig via `capabilitiesToSystemConfig`).
132
+ */
133
+ declare function capabilitiesToPhysics(caps: CascadedSandboxCapabilities | undefined): RealmPhysicsInput;
134
+ /**
135
+ * Traduz capabilities (mode) + environment (recursos ambient + `async`)
136
+ * em `RealmSystemConfig` pro RS interno. Campos undefined são omitidos.
137
+ *
138
+ * `async` é categoria A (ambient — ADR-032/033 RS): viral no ecossistema,
139
+ * sem cascade. Vive aqui (não em `RealmPhysics`) pra refletir que NÃO é
140
+ * regra de mundo.
141
+ */
142
+ declare function capabilitiesToSystemConfig(caps: CascadedSandboxCapabilities | undefined, env: SandboxEnvironment | undefined): RealmSystemConfig;
143
+ /** Violation do cascade restritivo de `persist` — child reduzindo rigor do parent. */
144
+ interface PersistCascadeViolation {
145
+ readonly flag: "definitions" | "effects";
146
+ readonly parent: true;
147
+ readonly child: false;
148
+ }
149
+ type PersistCascadeResult = {
150
+ readonly ok: true;
151
+ readonly value: PersistConfigInput | undefined;
152
+ } | {
153
+ readonly ok: false;
154
+ readonly violations: readonly PersistCascadeViolation[];
155
+ };
156
+ /**
157
+ * Aplica cascade restritivo de `physics.persist` (ADR-031 raiz).
158
+ *
159
+ * - parent omitido → top-level; declared é o resolvido (sem cascade).
160
+ * - declared omitido → herda integralmente do parent (preserva rigor).
161
+ * - cada flag (`definitions`/`effects`): parent=true + child=false → violation.
162
+ * Outros casos (parent off, child on; ambos on; ambos off) ficam OK.
163
+ *
164
+ * Não throw — retorna `{ ok, value | violations }` pro caller decidir.
165
+ */
166
+ declare function resolvePersistCascade(parent: PersistConfigInput | undefined, declared: PersistConfigInput | undefined): PersistCascadeResult;
167
+
168
+ type DirectiveResult = ReturnType<IRealmSystem["invoke"]>;
169
+ type DispatchResult = ReturnType<IRealmSystem["dispatch"]>;
170
+ type EventProcessingResult = ReturnType<IRealmSystem["processEvents"]>;
171
+ /** Modo de operação da Sandbox (ADR-008). */
172
+ type SandboxMode = "closed" | "governed";
173
+ /** Convenção do projeto pra erros: code obrigatório, message/data opcionais. */
174
+ interface SandboxError {
175
+ readonly code: string;
176
+ readonly message?: string;
177
+ readonly data?: Record<string, unknown>;
178
+ }
179
+ /**
180
+ * Result tagged pra métodos que podem falhar sem throw — Sandbox precisa
181
+ * disso porque parent realm DSL não consegue catch via diretiva. Métodos
182
+ * sem retorno usam SandboxOpResult. Métodos com retorno usam SandboxOpResult&lt;T&gt;.
183
+ */
184
+ type SandboxOpResult<T = void> = (T extends void ? {
185
+ readonly ok: true;
186
+ } : {
187
+ readonly ok: true;
188
+ readonly value: T;
189
+ }) | {
190
+ readonly ok: false;
191
+ readonly error: SandboxError;
192
+ };
193
+ /** Razão pela qual a Sandbox foi finalizada (ADR-025). */
194
+ type SandboxFinishReason = "manual" | "ttl";
195
+ /**
196
+ * Resultado consultável após `finish()`. `undefined` enquanto a Sandbox
197
+ * não tiver finalizado. Pipeline-style: parent finaliza child e consulta
198
+ * o que aconteceu.
199
+ */
200
+ interface SandboxResult {
201
+ readonly phase: "finished";
202
+ readonly reason: SandboxFinishReason;
203
+ readonly finishedAt: number;
204
+ }
205
+ /**
206
+ * Configs de término do realm (ADR-027). Hoje só `ttl`. Sub-objeto
207
+ * existe pra acomodar futuras configs de lifecycle sem poluir top-level.
208
+ */
209
+ interface SandboxLifecycleConfig {
210
+ /** Ticks até `finish()` automático. Boot não conta — começa no 1º step. */
211
+ readonly ttl?: number;
212
+ }
213
+ /**
214
+ * AppPort — Consumer → System → API pública do Realm (ADR-009).
215
+ *
216
+ * Sempre visível, independente do modo. Controle raso: lifecycle (step/
217
+ * snapshot/restore), input via effects, leitura **limitada à superfície
218
+ * pública do child** quando `mode === "closed"` (exports — ADR-031,
219
+ * implementado em `ports.ts:isFilteredOut`). Em `governed`, exports é
220
+ * ignorado. Imperativas e mutações diretas **não** estão aqui — isso é
221
+ * SystemPort.
222
+ */
223
+ interface IAppPort {
224
+ step(): StepResult;
225
+ stepAsync(): Promise<StepResult>;
226
+ get(stateId: string, field?: string): unknown;
227
+ applyEffect(id: string, data: unknown): void;
228
+ /**
229
+ * Captura snapshot do realm. Falha se phase é "created" — `{ code:
230
+ * "SNAPSHOT_PHASE_INVALID" }`. Após dispose: `SANDBOX_DISPOSED`.
231
+ */
232
+ snapshot(): SandboxOpResult<SandboxSnapshot>;
233
+ /**
234
+ * Restaura realm a partir de snapshot. Falha em mismatch de id/mode/
235
+ * capabilities (`SNAPSHOT_*_MISMATCH`) ou phase inválida do RS
236
+ * (`RESTORE_PHASE_INVALID`). Após dispose: `SANDBOX_DISPOSED`.
237
+ */
238
+ restore(snapshot: SandboxSnapshot): SandboxOpResult;
239
+ on<K extends keyof SystemEvents & string>(event: K, listener: (data: SystemEvents[K]) => void): () => void;
240
+ /**
241
+ * Subscribe pra mudanças efetivas no realm — canal de persistência
242
+ * observacional. Listener recebe `SandboxChange` agregado por tick.
243
+ *
244
+ * Lazy subscribe: agregador só assina hooks do RS interno quando o
245
+ * primeiro watcher entra; desassina quando o último sai. Sem watcher
246
+ * = zero overhead efetivo.
247
+ *
248
+ * Watch só dispatcha em ticks que **efetivamente mudaram o realm**:
249
+ * STEP_LOCKED não dispatcha; TICK_EXCEPTION em deterministic
250
+ * (rolled back) não dispatcha; throw declarativo em deterministic
251
+ * idem. Halt mantém mutações → dispatcha.
252
+ *
253
+ * Retorna unsubscribe idempotente.
254
+ */
255
+ watch(listener: (change: SandboxChange) => void): () => void;
256
+ getPhase(): SystemPhase;
257
+ getTick(): number;
258
+ isIdle(): boolean;
259
+ isAsync(): boolean;
260
+ hasState(id: string): boolean;
261
+ getStateIds(): string[];
262
+ /** Leis imutáveis do mundo (delega ao RS interno — ADR-032 RS). */
263
+ getPhysics(): Readonly<RealmPhysics>;
264
+ }
265
+ /**
266
+ * Mudança no realm dispatchada pelo Watch (ADR-026 refinada). Schema
267
+ * enxuto pra replay determinístico — Runtime aplica `effects` + reproduz
268
+ * rules → state derivado idêntico. Sem `stateChanges` (state é derivado,
269
+ * não input).
270
+ *
271
+ * Granularidade: 1 chamada por tick. Sem buffer interno — Runtime
272
+ * decide se acumula em memória, log append-only, etc.
273
+ */
274
+ interface SandboxChange {
275
+ readonly tick: number;
276
+ /** Inputs pra replay — effects aplicados durante este tick. */
277
+ readonly effects?: readonly {
278
+ id: string;
279
+ data: unknown;
280
+ }[];
281
+ /** Lifecycle markers — booted no boot, finished em finish(). */
282
+ readonly lifecycle?: "booted" | "finished";
283
+ /** Erro do tick (TICK_EXCEPTION). Em deterministic já houve rollback. */
284
+ readonly error?: _statedelta_axiom_realm_system.TickError;
285
+ /** Outputs — eventos `notify` emitidos durante o tick. */
286
+ readonly notify?: readonly {
287
+ event: string;
288
+ data?: unknown;
289
+ }[];
290
+ /** Outputs — requests emitidos durante o tick (pra observação). */
291
+ readonly requests?: readonly {
292
+ id: string;
293
+ }[];
294
+ }
295
+ /** Listener de `app.watch()`. */
296
+ type SandboxChangeListener = (change: SandboxChange) => void;
297
+ /**
298
+ * SystemPort — Consumer → System direto (ADR-009).
299
+ *
300
+ * Visível apenas em modo `governed`. Bypassa o contrato público do Realm
301
+ * (exports). Expõe tudo do AppPort + imperativas (invoke/dispatch/emit/
302
+ * processEvents) + consultas internas (actions, rules, events). É a porta
303
+ * "hack legítimo" — quem detém ela controla o System diretamente, como se
304
+ * fosse o próprio System.
305
+ *
306
+ * Naming nota: NÃO é a "RealmPort" do ADR-011 do RS. Aquela é a porta
307
+ * INTERNA do Realm pro System (via diretivas, sem objeto JS). Aqui é a
308
+ * porta EXTERNA do consumer pro System bypassando o contrato público.
309
+ */
310
+ interface ISystemPort extends IAppPort {
311
+ invoke(id: string, params?: Record<string, unknown>): DirectiveResult;
312
+ invokeAsync(id: string, params?: Record<string, unknown>): Promise<DirectiveResult>;
313
+ dispatch(stateId: string, op: string, params?: Record<string, unknown>): DispatchResult;
314
+ emit(event: string, data?: unknown): void;
315
+ processEvents(): EventProcessingResult;
316
+ processEventsAsync(): Promise<EventProcessingResult>;
317
+ getActionIds(): string[];
318
+ hasAction(id: string): boolean;
319
+ getRuleCount(): number;
320
+ hasRule(id: string): boolean;
321
+ }
322
+ /**
323
+ * Snapshot da Sandbox — wrappa o RealmSnapshot com metadados.
324
+ *
325
+ * ADR-006: não carrega o DSL fonte literalmente. DSL vive no
326
+ * `CompilationService` do environment (ADR-035 raiz); o snapshot carrega
327
+ * apenas referências (`bootSourceKey`) pra ele.
328
+ * ADR-032: capabilities resolvidas serializadas pra validação no restore.
329
+ */
330
+ interface SandboxSnapshot {
331
+ readonly id: string;
332
+ readonly mode: SandboxMode;
333
+ readonly capabilities?: CascadedSandboxCapabilities;
334
+ readonly realm: RealmSnapshot;
335
+ /**
336
+ * **Persistência v2 — metadados pra restore via CompilationService**
337
+ * (ADRs 029/035/036 raiz). Presente apenas quando o realm declarou
338
+ * `physics.persist.definitions: true` e o boot conhecia o `bootSourceKey`
339
+ * (boot via DSL via Runtime, ou boot via diretiva `sandbox create` com
340
+ * `boot:<DSL>`). Ausente em snapshots state-only (`persist.definitions`
341
+ * off, ou boot via JS direto sem source).
342
+ */
343
+ readonly persistence?: SandboxPersistenceSnapshot;
344
+ }
345
+ /**
346
+ * Metadados de Persistência v2 anexados ao `SandboxSnapshot` quando o realm
347
+ * declarou `physics.persist.definitions: true`.
348
+ *
349
+ * `bootSourceKey` aponta pra DSL fonte no `CompilationService` do environment.
350
+ * Runtime recover faz `service.getSource(bootSourceKey)` → `service.compile(dsl)`
351
+ * → re-boot da Sandbox com a artifact retornada → `restore(snap.realm)`.
352
+ */
353
+ interface SandboxPersistenceSnapshot {
354
+ readonly bootSourceKey: SourceKey;
355
+ }
356
+ /**
357
+ * Opções estendidas do `Sandbox.boot()` — superset de `BootOptions` do RS.
358
+ * `bootSourceKey` é metadado opcional consumido pela Sandbox (não vai pro RS)
359
+ * — permite que o snapshot subsequente referencie o source no
360
+ * `CompilationService` (ADR-034 raiz).
361
+ */
362
+ interface SandboxBootOptions extends BootOptions {
363
+ /** Hash content-addressable do BootDSL que originou estas Definitions. */
364
+ readonly bootSourceKey?: SourceKey;
365
+ }
366
+ /**
367
+ * Config de criação de uma Sandbox.
368
+ *
369
+ * `environment` (categoria A) e `capabilities` (categoria B) são os
370
+ * caminhos preferenciais — separados por semântica (ADR-032).
371
+ * `physics` e `systemConfig` continuam como **escape hatches** low-level
372
+ * pra casos específicos; quando passados, têm precedência sobre os
373
+ * campos derivados de `environment`/`capabilities`.
374
+ */
375
+ interface SandboxConfig {
376
+ /** Identificador único dentro do parent scope. */
377
+ readonly id: string;
378
+ /** Modo de operação (ADR-008). Default: "closed". */
379
+ readonly mode?: SandboxMode;
380
+ /** Recursos ambient do ecossistema (categoria A — ADR-032). */
381
+ readonly environment?: SandboxEnvironment;
382
+ /** Permissões hierárquicas declaradas pela child (categoria B — ADR-030). */
383
+ readonly capabilities?: SandboxCapabilities;
384
+ /** Configs de término (ADR-027). Hoje só `ttl`. */
385
+ readonly lifecycle?: SandboxLifecycleConfig;
386
+ /** Sandbox parent — habilita cascade no boot (ADR-035, ADR-067 RS). */
387
+ readonly parent?: ISandbox;
388
+ /**
389
+ * Lookup pra path syntax cross-sandbox (ADR-041). Quando definido,
390
+ * Sandbox monta o `crossRealmGet` (ADR-071 RS) que split o `/`,
391
+ * resolve childId via lookup e delega pro `child.app.get(intra)` —
392
+ * recursão pra profundidade arbitrária. Manager injeta automaticamente
393
+ * em cada Sandbox criada; Sandbox standalone fica sem (paths com `/`
394
+ * retornam undefined).
395
+ */
396
+ readonly lookup?: SandboxLookup;
397
+ /**
398
+ * Factory de child sandboxes — injetado pelo Manager (fecha sobre Manager +
399
+ * esta Sandbox como parent). A Sandbox o repassa pro `systemConfig.childFactory`
400
+ * do RS interno, que o usa pra processar `BootDefinitions.sandboxes` (e a
401
+ * directive `sandbox create`) — `def.boot` já é `BootDefinitions` compilado,
402
+ * o factory só cria+boota. Sandbox standalone (sem Manager) fica sem —
403
+ * `sandboxes` declaradas → BootError.
404
+ */
405
+ readonly childFactory?: ChildFactory;
406
+ /** Escape hatch — physics direto. Tem precedência sobre tradução de capabilities. */
407
+ readonly physics?: RealmPhysicsInput;
408
+ /** Escape hatch — systemConfig direto. Tem precedência sobre tradução de environment/capabilities. */
409
+ readonly systemConfig?: RealmSystemConfig;
410
+ }
411
+ /** Lookup de Sandbox por id (provido pelo Manager — ADR-037, ADR-041). */
412
+ type SandboxLookup = (id: string) => ISandbox | undefined;
413
+ /**
414
+ * Sandbox — wrapper de child realm (ADR-001, ADR-028).
415
+ *
416
+ * Wrappa um RealmSystem interno. Expõe AppPort sempre e SystemPort apenas
417
+ * em modo governed. A assimetria é por encapsulamento (ADR-009) — em modo
418
+ * closed, `system` é `undefined` e não há referência pra acessar.
419
+ *
420
+ * O **realm interno** se comunica com o System via diretivas (RealmPort
421
+ * conceitual do ADR-011 do RS) — sem objeto JS exposto. Realm não conhece
422
+ * AppPort/SystemPort.
423
+ */
424
+ interface ISandbox {
425
+ readonly id: string;
426
+ readonly mode: SandboxMode;
427
+ /** Consumer → API pública do Realm via System. Sempre disponível. */
428
+ readonly app: IAppPort;
429
+ /** Consumer → API do System direto (bypass do contrato público).
430
+ * Presente apenas em modo `governed`; `undefined` em `closed`. */
431
+ readonly system: ISystemPort | undefined;
432
+ /** Boota definitions no RS interno. Idempotente quanto a phase "created". */
433
+ boot(definitions: BootDefinitions<RealmCtx>, options?: SandboxBootOptions): BootResult;
434
+ /** Phase do RS interno. */
435
+ getPhase(): SystemPhase;
436
+ /** Capabilities resolvidas pós-cascade (ADR-030). Self-aware. */
437
+ getEffectiveCapabilities(): CascadedSandboxCapabilities;
438
+ /**
439
+ * Encerra o realm — phase vai pra "finished". Reads/snapshot/restore
440
+ * continuam ok. Falha se phase != "running" (`SANDBOX_FINISH_PHASE_INVALID`)
441
+ * ou já disposed (`SANDBOX_DISPOSED`).
442
+ */
443
+ finish(): SandboxOpResult;
444
+ /**
445
+ * Destrói a Sandbox — derruba listeners, libera RS interno. Após
446
+ * dispose, todas as chamadas em sandbox/app/system retornam
447
+ * `SANDBOX_DISPOSED`. Idempotente: 2º dispose retorna o mesmo erro.
448
+ */
449
+ dispose(): SandboxOpResult;
450
+ /** True se `dispose()` já foi chamado. */
451
+ isDisposed(): boolean;
452
+ /**
453
+ * Resultado consultável após `finish()`. Retorna `undefined` enquanto
454
+ * a Sandbox não tiver finalizado.
455
+ */
456
+ getResult(): SandboxResult | undefined;
457
+ }
458
+
459
+ /**
460
+ * Sandbox — factory do contexto de execução isolado.
461
+ *
462
+ * Fase 1: wrappa um RealmSystem e expõe ports.
463
+ * Fase 2: aceita `environment` + `capabilities` + `parent` na config,
464
+ * resolve cascade restritiva (ADR-030), traduz pra physics +
465
+ * systemConfig do RS interno.
466
+ * Fase 3: lifecycle terminal (`finish`/`dispose`/TTL/getResult — ADR-025,
467
+ * ADR-027). Métodos diretos na ISandbox, não nos ports — encerrar
468
+ * a Sandbox é semântica do wrapper, não do realm.
469
+ */
470
+
471
+ declare function createSandbox(config: SandboxConfig): ISandbox;
472
+
473
+ /**
474
+ * Construção dos ports (ADR-009).
475
+ *
476
+ * AppPort e SystemPort são wrappers finos sobre o RS. A assimetria é
477
+ * implementada por encapsulamento: em modo closed, `buildSystemPort` nunca
478
+ * é chamado e a referência simplesmente não existe pro consumer.
479
+ *
480
+ * AppPort = consumer → API pública do Realm via System (sempre).
481
+ * SystemPort = consumer → System direto, bypass do contrato público
482
+ * (só em governed).
483
+ * RealmPort (ADR-011 do RS) = realm interno → System via diretivas
484
+ * (não tem objeto JS — não é construído aqui).
485
+ *
486
+ * Fase 3: snapshot/restore retornam `SandboxOpResult` (erro como dado —
487
+ * ADR-029). Demais métodos lançam `Error` se a Sandbox foi disposed
488
+ * (ADR-025). Quando handler `sandbox` (Fase 5) vier, ele traduz throw
489
+ * residuais pro DSL parent — consumer JS é o único que enxerga throw.
490
+ */
491
+
492
+ /**
493
+ * Exports resolvidos — superfície pública do realm filtrada por
494
+ * AppPort em closed (ADR-031). Lookups O(1) via Set.
495
+ *
496
+ * `accessible` é a união de states + views — usada por `get` que aceita
497
+ * paths intra-realm de qualquer tipo (split por `:` antes do lookup).
498
+ */
499
+ interface ResolvedExports {
500
+ readonly states: ReadonlySet<string>;
501
+ readonly views: ReadonlySet<string>;
502
+ readonly actions: ReadonlySet<string>;
503
+ readonly accessible: ReadonlySet<string>;
504
+ }
505
+ interface PortContext {
506
+ readonly id: string;
507
+ readonly mode: SandboxMode;
508
+ readonly rs: IRealmSystem;
509
+ readonly capabilities: CascadedSandboxCapabilities;
510
+ /** Vivo enquanto a Sandbox não foi disposed (ADR-025). */
511
+ readonly isDisposed: () => boolean;
512
+ /**
513
+ * Getter de exports resolvidos — preenchido pela Sandbox no boot
514
+ * quando `BootDefinitions.exports` declarado. AppPort filtra leitura
515
+ * em modo closed; SystemPort ignora (sempre vê tudo — ADR-031).
516
+ * Retorna undefined quando exports não declarado (default permissivo).
517
+ */
518
+ readonly getExports?: () => ResolvedExports | undefined;
519
+ /**
520
+ * Getter do `bootSourceKey` capturado no boot (Persistência v2, ADR-034 raiz).
521
+ * Lido por `snapshot()` pra anexar `SandboxPersistenceSnapshot` quando
522
+ * `physics.persist.definitions: true` + key conhecida.
523
+ * Retorna undefined quando o boot não trouxe a key (boot via JS cru,
524
+ * ou realm sem `persist.definitions`).
525
+ */
526
+ readonly getBootSourceKey?: () => string | undefined;
527
+ /**
528
+ * Hook de path syntax cross-sandbox (ADR-041 + ADR-071 RS). Quando
529
+ * definido, `AppPort.get` em paths com `/` desvia diretamente —
530
+ * mesmo caminho que o RS interno usaria via `ctx.get`. Permite que
531
+ * consumer JS (`parent.app.get("child/hp:value")`) bata na child
532
+ * sem passar pelo Router (que retornaria undefined).
533
+ */
534
+ readonly crossRealmGet?: (path: string, field?: string) => unknown;
535
+ /**
536
+ * Watch Layer (ADR-026). Função do agregador interno da Sandbox —
537
+ * AppPort.watch delega aqui. Retorna unsubscribe idempotente.
538
+ */
539
+ readonly watch?: (listener: SandboxChangeListener) => () => void;
540
+ /**
541
+ * Notifica o agregador do Watch quando consumer aplica effect via
542
+ * AppPort. Agregador acumula no buffer do tick em curso.
543
+ */
544
+ readonly recordEffect?: (id: string, data: unknown) => void;
545
+ }
546
+ declare function buildAppPort(ctx: PortContext): IAppPort;
547
+ declare function buildSystemPort(ctx: PortContext, app: IAppPort): ISystemPort;
548
+
549
+ /**
550
+ * Lifecycle terminal — Fase 3 (ADR-025, ADR-027).
551
+ *
552
+ * Wrappa um RS interno e provê:
553
+ * - `finish()` — encerra realm via `rs.finish()`; preserva snapshot.
554
+ * - `dispose()` — destrói sandbox; desfaz listeners; libera RS.
555
+ * - TTL automático — decrementa após cada `step()` executado (boot
556
+ * não conta); em `ttl <= 0` dispara `finish("ttl")`.
557
+ * - `getResult()` — consulta consultável de razão/tick após finish.
558
+ *
559
+ * Ver ADR-040 — eventos do realm child pro parent ficam deferred. Por
560
+ * enquanto, consumer JS observa via `rs.on("finished", ...)` (sobe direto
561
+ * do RS interno) e consulta `getResult()` pra saber a razão.
562
+ */
563
+
564
+ interface LifecycleHandle {
565
+ finish(): SandboxOpResult;
566
+ dispose(): SandboxOpResult;
567
+ isDisposed(): boolean;
568
+ getResult(): SandboxResult | undefined;
569
+ }
570
+ declare function createLifecycle(rs: IRealmSystem, config: SandboxLifecycleConfig | undefined): LifecycleHandle;
571
+
572
+ /**
573
+ * SandboxManager — registry + factory wrapper + cascade destroy (ADR-037).
574
+ *
575
+ * Provê hierarquia de Sandboxes. Sandbox standalone (sem Manager) NÃO cria
576
+ * children (ADR-036). Manager NÃO cria RS (Sandbox cria — ADR-034); apenas
577
+ * orquestra graph + propaga environment compartilhado (ADR-R002 do Runtime).
578
+ *
579
+ * Estrutura interna (3 Maps inspirados no legado SandboxRegistry):
580
+ * - sandboxes: Map<id, ISandbox> — flat storage O(1)
581
+ * - parents: Map<childId, parentId> — quem é pai
582
+ * - children: Map<parentId, Set<childId>> — quem são filhos
583
+ *
584
+ * Hierarquia mora **só no graph**, nunca no id (ADR-035 — link simbólico).
585
+ *
586
+ * Cascade dispose depth-first: folhas primeiro, depois o pai (ADR-025).
587
+ * Copy-before-iterate evita mutation-during-iteration.
588
+ */
589
+
590
+ interface SandboxManagerConfig {
591
+ /**
592
+ * Environment compartilhado entre todas as Sandboxes registradas.
593
+ * Categoria A — propagado por referência, sem cascade (ADR-032,
594
+ * ADR-R002 Runtime).
595
+ */
596
+ readonly environment?: SandboxEnvironment;
597
+ }
598
+ /**
599
+ * Config aceita por `Manager.createSandbox`. Manager resolve `parent`
600
+ * via `parentId` (lookup no graph), em vez de aceitar ref direta.
601
+ * `parent` direto não é aceito — Manager é a fonte de verdade da
602
+ * hierarquia (ADR-035).
603
+ */
604
+ interface ManagerSandboxConfig extends Omit<SandboxConfig, "parent"> {
605
+ /** Id do parent registrado no Manager. Manager resolve a ref. */
606
+ readonly parentId?: string;
607
+ }
608
+ interface ChildSpec {
609
+ readonly id: string;
610
+ readonly mode?: SandboxMode;
611
+ readonly capabilities?: SandboxCapabilities;
612
+ /** Boot da child — `BootDefinitions` já compilado. Ausente → child vazia. */
613
+ readonly definitions?: BootDefinitions<RealmCtx>;
614
+ /**
615
+ * Persistência v2 (ADR-034 raiz) — hash do BootDSL fonte. Emitido pelo
616
+ * compiler do Runtime quando a diretiva `sandbox create` carregava `boot:<DSL>`.
617
+ * Propagado pra Sandbox child via `SandboxBootOptions.bootSourceKey`. Ausente
618
+ * quando a child foi criada via JS direto (sem DSL fonte).
619
+ */
620
+ readonly bootSourceKey?: SourceKey;
621
+ readonly bootOptions?: BootOptions;
622
+ }
623
+ /**
624
+ * Cria + boota uma child sandbox sob `parentId`. Retorna `{ success, errors }`
625
+ * — o RS agrega `errors` no `BootResult` do parent; o handler `sandbox` adapta
626
+ * pra `ApplyResult`. Em qualquer falha, a child é disposed (sem órfã).
627
+ */
628
+ declare function createAndBootChild(manager: ISandboxManager, parentId: string, spec: ChildSpec): {
629
+ readonly success: boolean;
630
+ readonly errors: readonly BootError[];
631
+ };
632
+ interface ISandboxManager {
633
+ /**
634
+ * Cria + registra Sandbox. Aplica cascade se `parentId` declarado.
635
+ * Throw em validações (id inválido, duplicado, parent não existe,
636
+ * cascade violation).
637
+ */
638
+ createSandbox(config: ManagerSandboxConfig): ISandbox;
639
+ /**
640
+ * **Dynamic Boot** (ADR-035 raiz) — `CompilationService` do environment
641
+ * compartilhado. Exposto pra handlers que precisam compilar DSL em runtime
642
+ * (ex.: `sandbox create` com `boot` expression). `undefined` quando o
643
+ * Manager foi criado sem `environment.compilation` (caminho legado).
644
+ */
645
+ readonly compilation?: CompilationService<RealmCtx>;
646
+ getSandbox(id: string): ISandbox | undefined;
647
+ getParent(id: string): ISandbox | undefined;
648
+ getChildren(id: string): readonly ISandbox[];
649
+ getAncestors(id: string): readonly ISandbox[];
650
+ getDescendants(id: string): readonly ISandbox[];
651
+ getDepth(id: string): number;
652
+ has(id: string): boolean;
653
+ ids(): readonly string[];
654
+ readonly size: number;
655
+ /**
656
+ * Dispose cascade depth-first. `id` inexistente retorna
657
+ * `SANDBOX_NOT_FOUND` como dado.
658
+ */
659
+ dispose(id: string): SandboxOpResult;
660
+ /** Dispose tudo. Itera roots; cascade pega children. */
661
+ clear(): void;
662
+ }
663
+ declare function createSandboxManager(config?: SandboxManagerConfig): ISandboxManager;
664
+
665
+ /**
666
+ * Handler `sandbox` — protocolo DSL → JS API entre Sandboxes (ADR-017, ADR-033).
667
+ *
668
+ * Injetado pelo Manager em `systemConfig.handlers.sandbox` do RS interno
669
+ * de cada Sandbox criada. Permite que rules do realm parent comandem
670
+ * Sandboxes children via diretivas — caminho declarativo equivalente
671
+ * às chamadas JS `manager.getSandbox(id).app.X()`.
672
+ *
673
+ * Owner = parent automático. Handler captura o id da Sandbox dona via
674
+ * closure. Quando rule emite `{ type: "sandbox", op: "create", id }`,
675
+ * a child criada vira filha da Sandbox dona — não há `parentId` na
676
+ * diretiva.
677
+ *
678
+ * Erro como dado (ADR-029): `error` é string concisa (code curto);
679
+ * `data` carrega `SandboxError` estruturado quando aplicável. Catch da
680
+ * rule dispara em `ok: false`.
681
+ *
682
+ * Dual-mode (ADR-032):
683
+ * - `options.async = false` (default) — `execute` é função sync.
684
+ * Apropriado pra ecossistema sync (FPS, game loops). Ops em variantes sync.
685
+ * - `options.async = true` — `execute` é `async function`. O SDK detecta via
686
+ * `isAsyncFunction(execute)`, JIT compila wrapper async per-op. Variantes
687
+ * async (`stepAsync`/`processEventsAsync`) preferidas. Apropriado pra
688
+ * ecossistema com I/O (ETL, business com fetch/DB).
689
+ *
690
+ * Decisão arquitetural: a flag vem do Manager (categoria A — ambient,
691
+ * viral no ecossistema; ADR-032). Manager async → todas Sandboxes
692
+ * async; Manager sync → todas sync. Sem mistura no mesmo Manager.
693
+ *
694
+ * Diretiva = só **métodos de container/porta** — 8 ops:
695
+ * create / step / applyEffect / snapshot / restore / finish / dispose
696
+ * processEvents (governed-only — método do SystemPort)
697
+ *
698
+ * Operar recurso interno de uma child (mutar state, invocar action, emitir
699
+ * evento) NÃO usa esta diretiva — é a diretiva normal do realm com path
700
+ * prefixado (`{ state: "c/hp", op }`, `{ action: "c/heal", ... }`, `{ emit:
701
+ * "c/fire", ... }`), processada pelos handlers `crossDispatch`/`crossInvoke`/
702
+ * `crossEmit` do RS (ADR-074 / §1.7).
703
+ *
704
+ * Estrutura modular:
705
+ * - `handlers/types.ts` — `SandboxDirective` shape
706
+ * - `handlers/with-child.ts` — wrappers de pre-checks (lookup, liveness, requireSystem)
707
+ * - `handlers/actions/{lifecycle,runtime,imperative}.ts` — implementação por categoria
708
+ * - `handlers/dispatch.ts` — `OP_TABLE` unificada sync/async
709
+ */
710
+
711
+ interface SandboxDirectiveHandlerOptions {
712
+ /**
713
+ * Flag de ecossistema async (ADR-032 — categoria A, ambient). Quando
714
+ * true, `execute` é `async function` e variantes async são preferidas
715
+ * pelas actions que oferecem (step/invoke/processEvents). Setado pelo
716
+ * Manager via `environment.async`.
717
+ */
718
+ readonly async?: boolean;
719
+ }
720
+ /**
721
+ * Cria handler de diretiva `sandbox` ligado a um Manager + um owner.
722
+ *
723
+ * Tipo unificado (`HandlerDefinition`) — o actions@0.8.0 consolidou as três
724
+ * variantes (ver README §Handler Multi-Variant). Overloads narrowem `execute`
725
+ * sync/async pelo `options.async` pra ergonomia de testes/consumidores.
726
+ *
727
+ * @param manager Registry de Sandboxes do ecossistema.
728
+ * @param ownerSandboxId Id da Sandbox dona deste handler. Children criados
729
+ * via diretiva têm essa Sandbox como parent automático.
730
+ * @param options Flags do ecossistema (ex: `async`).
731
+ */
732
+ declare function createSandboxDirectiveHandler(manager: ISandboxManager, ownerSandboxId: string, options: SandboxDirectiveHandlerOptions & {
733
+ async: true;
734
+ }): Omit<HandlerDefinition<RealmCtx>, "execute"> & {
735
+ execute: AsyncHandlerExecute<RealmCtx>;
736
+ };
737
+ declare function createSandboxDirectiveHandler(manager: ISandboxManager, ownerSandboxId: string, options?: SandboxDirectiveHandlerOptions): Omit<HandlerDefinition<RealmCtx>, "execute"> & {
738
+ execute: SyncHandlerExecute<RealmCtx>;
739
+ };
740
+
741
+ export { type CascadeResult, type CascadeViolation, type CascadeViolationKind, type CascadedSandboxCapabilities, type ChildSpec, type IAppPort, type ISandbox, type ISandboxManager, type ISystemPort, type LifecycleHandle, type ManagerSandboxConfig, type PersistCascadeResult, type PersistCascadeViolation, SandboxBootError, type SandboxBootOptions, type SandboxCapabilities, type SandboxChange, type SandboxChangeListener, type SandboxConfig, type SandboxEnvironment, type SandboxError, type SandboxFinishReason, type SandboxLifecycleConfig, type SandboxLookup, type SandboxManagerConfig, type SandboxMode, type SandboxOpResult, type SandboxPersistenceSnapshot, type SandboxResult, type SandboxSnapshot, buildAppPort, buildSystemPort, capabilitiesToPhysics, capabilitiesToSystemConfig, createAndBootChild, createLifecycle, createSandbox, createSandboxDirectiveHandler, createSandboxManager, resolveCascade, resolvePersistCascade };