@kraken-e2e/core 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.
Files changed (50) hide show
  1. package/LICENSE +674 -0
  2. package/dist/ctk.d.ts +57 -0
  3. package/dist/ctk.d.ts.map +1 -0
  4. package/dist/ctk.js +137 -0
  5. package/dist/ctk.js.map +1 -0
  6. package/dist/event-bus.d.ts +21 -0
  7. package/dist/event-bus.d.ts.map +1 -0
  8. package/dist/event-bus.js +55 -0
  9. package/dist/event-bus.js.map +1 -0
  10. package/dist/event-schemas.d.ts +13 -0
  11. package/dist/event-schemas.d.ts.map +1 -0
  12. package/dist/event-schemas.js +202 -0
  13. package/dist/event-schemas.js.map +1 -0
  14. package/dist/host.d.ts +10 -0
  15. package/dist/host.d.ts.map +1 -0
  16. package/dist/host.js +23 -0
  17. package/dist/host.js.map +1 -0
  18. package/dist/index.d.ts +19 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +19 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/logger.d.ts +16 -0
  23. package/dist/logger.d.ts.map +1 -0
  24. package/dist/logger.js +13 -0
  25. package/dist/logger.js.map +1 -0
  26. package/dist/registry.d.ts +50 -0
  27. package/dist/registry.d.ts.map +1 -0
  28. package/dist/registry.js +244 -0
  29. package/dist/registry.js.map +1 -0
  30. package/dist/runner.d.ts +60 -0
  31. package/dist/runner.d.ts.map +1 -0
  32. package/dist/runner.js +363 -0
  33. package/dist/runner.js.map +1 -0
  34. package/dist/scenario.d.ts +25 -0
  35. package/dist/scenario.d.ts.map +1 -0
  36. package/dist/scenario.js +85 -0
  37. package/dist/scenario.js.map +1 -0
  38. package/dist/scheduler.d.ts +87 -0
  39. package/dist/scheduler.d.ts.map +1 -0
  40. package/dist/scheduler.js +190 -0
  41. package/dist/scheduler.js.map +1 -0
  42. package/dist/testing/fake-driver.d.ts +54 -0
  43. package/dist/testing/fake-driver.d.ts.map +1 -0
  44. package/dist/testing/fake-driver.js +209 -0
  45. package/dist/testing/fake-driver.js.map +1 -0
  46. package/dist/testing/index.d.ts +2 -0
  47. package/dist/testing/index.d.ts.map +1 -0
  48. package/dist/testing/index.js +2 -0
  49. package/dist/testing/index.js.map +1 -0
  50. package/package.json +53 -0
package/dist/runner.js ADDED
@@ -0,0 +1,363 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { mkdirSync, writeFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { KrakenError, serializeError } from '@kraken-e2e/contracts';
5
+ import { SignalTimeoutError, } from '@kraken-e2e/signaling';
6
+ import { EventBus } from './event-bus.js';
7
+ import { createLogger, silentLogger } from './logger.js';
8
+ import { executePlan } from './scheduler.js';
9
+ /**
10
+ * Wraps an ActorSignals handle so every publish/wait surfaces in the event
11
+ * stream (signalSent / signalWaitStarted / signalReceived / signalTimedOut —
12
+ * the live UI's signature moments, ADR-0001 §5.11/§5.12), and so scenario
13
+ * aborts cancel pending waits by default.
14
+ */
15
+ class InstrumentedActorSignals {
16
+ inner;
17
+ events;
18
+ scenarioId;
19
+ abort;
20
+ constructor(inner, events, scenarioId, abort) {
21
+ this.inner = inner;
22
+ this.events = events;
23
+ this.scenarioId = scenarioId;
24
+ this.abort = abort;
25
+ }
26
+ get subscriberId() {
27
+ return this.inner.subscriberId;
28
+ }
29
+ async publish(name, payload) {
30
+ const record = await this.inner.publish(name, payload);
31
+ this.events.emit({
32
+ type: 'signalSent',
33
+ scenarioId: this.scenarioId,
34
+ signal: name,
35
+ from: this.subscriberId,
36
+ recordSeq: record.seq,
37
+ });
38
+ return record;
39
+ }
40
+ async waitFor(name, opts) {
41
+ this.events.emit({
42
+ type: 'signalWaitStarted',
43
+ scenarioId: this.scenarioId,
44
+ signal: name,
45
+ actorId: this.subscriberId,
46
+ timeoutMs: opts.timeoutMs,
47
+ });
48
+ const startedAt = Date.now();
49
+ try {
50
+ const record = await this.inner.waitFor(name, {
51
+ ...opts,
52
+ signal: opts.signal ?? this.abort,
53
+ });
54
+ this.events.emit({
55
+ type: 'signalReceived',
56
+ scenarioId: this.scenarioId,
57
+ signal: name,
58
+ by: this.subscriberId,
59
+ from: record.from,
60
+ latencyMs: Date.now() - startedAt,
61
+ });
62
+ return record;
63
+ }
64
+ catch (error) {
65
+ if (error instanceof SignalTimeoutError) {
66
+ this.events.emit({
67
+ type: 'signalTimedOut',
68
+ scenarioId: this.scenarioId,
69
+ signal: name,
70
+ actorId: this.subscriberId,
71
+ timeoutMs: opts.timeoutMs,
72
+ });
73
+ }
74
+ throw error;
75
+ }
76
+ }
77
+ async barrier(name, opts) {
78
+ await this.publish(`${name}:${this.subscriberId}`);
79
+ await Promise.all(opts.participants
80
+ .filter((participant) => participant !== this.subscriberId)
81
+ .map((participant) => this.waitFor(`${name}:${participant}`, {
82
+ timeoutMs: opts.timeoutMs,
83
+ ...(opts.signal !== undefined ? { signal: opts.signal } : {}),
84
+ })));
85
+ }
86
+ }
87
+ function driverServices(deps, source, abort) {
88
+ const logger = deps.logSink ? createLogger(source, deps.logSink) : (deps.logger ?? silentLogger);
89
+ return {
90
+ runId: deps.events.runId,
91
+ logger,
92
+ artifactsDir: deps.artifactsDir,
93
+ abort,
94
+ emit: (emission) => {
95
+ if (emission.type === 'driverLog') {
96
+ deps.events.emit({
97
+ type: 'driverLog',
98
+ source,
99
+ level: emission.level,
100
+ message: emission.message,
101
+ });
102
+ }
103
+ else {
104
+ deps.events.emit({
105
+ type: 'artifactCaptured',
106
+ kind: emission.kind,
107
+ path: emission.path,
108
+ ...(emission.actorId !== undefined ? { actorId: emission.actorId } : {}),
109
+ });
110
+ }
111
+ },
112
+ };
113
+ }
114
+ const DISPOSE_TIMEOUT_MS = 15_000;
115
+ async function withTimeout(work, ms) {
116
+ let timer;
117
+ const timeout = new Promise((resolve) => {
118
+ timer = setTimeout(() => resolve('timeout'), ms);
119
+ });
120
+ const outcome = await Promise.race([
121
+ work.then(() => 'ok', () => 'error'),
122
+ timeout,
123
+ ]);
124
+ clearTimeout(timer);
125
+ return outcome;
126
+ }
127
+ /**
128
+ * Runs one scenario end to end (ADR-0002 D7): boot all actor sessions
129
+ * (allSettled + rollback — never leak a booted device), open the signal scope,
130
+ * execute the plan, capture artifacts from ALL actors on failure, and tear
131
+ * everything down in finally with timeout guards.
132
+ */
133
+ export class ScenarioRunner {
134
+ deps;
135
+ constructor(deps) {
136
+ this.deps = deps;
137
+ }
138
+ async run(plan) {
139
+ const { deps } = this;
140
+ const startedAt = Date.now();
141
+ const abortController = new AbortController();
142
+ // Resolve every actor's driver BEFORE any session boots: an iOS actor on a
143
+ // non-macOS host fails here, fast and explicit (C4b).
144
+ const bindings = plan.actors.map((actor) => ({
145
+ actor,
146
+ driver: deps.registry.driverFor(actor.platform),
147
+ }));
148
+ deps.events.emit({
149
+ type: 'scenarioStarted',
150
+ scenarioId: plan.scenarioId,
151
+ name: plan.name,
152
+ ...(plan.featureUri !== undefined ? { featureUri: plan.featureUri } : {}),
153
+ actors: bindings.map(({ actor, driver }) => ({
154
+ id: actor.id,
155
+ platform: actor.platform,
156
+ driverId: driver.manifest.id,
157
+ })),
158
+ });
159
+ const finish = (status, error) => {
160
+ deps.events.emit({
161
+ type: 'scenarioFinished',
162
+ scenarioId: plan.scenarioId,
163
+ status,
164
+ durationMs: Date.now() - startedAt,
165
+ ...(error !== undefined ? { error: serializeError(error) } : {}),
166
+ });
167
+ return {
168
+ scenarioId: plan.scenarioId,
169
+ name: plan.name,
170
+ status,
171
+ durationMs: Date.now() - startedAt,
172
+ ...(error !== undefined ? { error } : {}),
173
+ };
174
+ };
175
+ // ── Boot sessions: allSettled, roll back the booted ones on any failure ──
176
+ const settled = await Promise.allSettled(bindings.map(async ({ actor, driver }) => {
177
+ const services = driverServices(deps, `driver:${driver.manifest.id}/${actor.id}`, abortController.signal);
178
+ const session = await driver.createSession(actor, services);
179
+ return { actor, driver, session };
180
+ }));
181
+ const booted = settled.flatMap((result) => result.status === 'fulfilled' ? [result.value] : []);
182
+ const bootFailures = settled.flatMap((result) => result.status === 'rejected' ? [result.reason] : []);
183
+ if (bootFailures.length > 0) {
184
+ await Promise.all(booted.map((b) => withTimeout(b.session.dispose(), DISPOSE_TIMEOUT_MS)));
185
+ const cause = bootFailures[0];
186
+ return finish('failed', KrakenError.is(cause)
187
+ ? cause
188
+ : KrakenError.wrap(cause, 'KRK-SESSION-CREATE-FAILED', 'Actor session boot failed'));
189
+ }
190
+ for (const { actor, driver } of booted) {
191
+ deps.events.emit({
192
+ type: 'actorSessionStarted',
193
+ scenarioId: plan.scenarioId,
194
+ actorId: actor.id,
195
+ driverId: driver.manifest.id,
196
+ platformLabel: driver.manifest.platformLabel,
197
+ });
198
+ }
199
+ // ── Signal scope + actor runtimes ──
200
+ const scoped = deps.signalBus.scope({
201
+ runId: deps.events.runId,
202
+ scenarioId: plan.scenarioId,
203
+ });
204
+ await scoped.open();
205
+ const actors = new Map(booted.map(({ actor, session }) => [
206
+ actor.id,
207
+ {
208
+ id: actor.id,
209
+ platform: actor.platform,
210
+ session,
211
+ signals: new InstrumentedActorSignals(scoped.forActor(actor.id), deps.events, plan.scenarioId, abortController.signal),
212
+ log: deps.logSink
213
+ ? createLogger(`actor:${actor.id}`, deps.logSink)
214
+ : (deps.logger ?? silentLogger),
215
+ },
216
+ ]));
217
+ let result;
218
+ try {
219
+ const execution = await executePlan(plan, {
220
+ actors,
221
+ events: deps.events,
222
+ abortController,
223
+ });
224
+ if (execution.status === 'failed') {
225
+ await this.#captureFailureArtifacts(plan.scenarioId, actors);
226
+ result = finish('failed', execution.error);
227
+ }
228
+ else {
229
+ result = finish('passed');
230
+ }
231
+ }
232
+ finally {
233
+ // Sessions first: device teardown must never depend on transport health.
234
+ await Promise.all(booted.map(async ({ actor, session }) => {
235
+ const outcome = await withTimeout(session.dispose(), DISPOSE_TIMEOUT_MS);
236
+ deps.events.emit({
237
+ type: 'actorSessionFinished',
238
+ scenarioId: plan.scenarioId,
239
+ actorId: actor.id,
240
+ status: outcome === 'ok' ? 'ok' : 'failed',
241
+ });
242
+ }));
243
+ try {
244
+ await scoped.destroy();
245
+ }
246
+ catch {
247
+ // A failing transport must not mask the scenario result.
248
+ }
249
+ }
250
+ return result;
251
+ }
252
+ /** Best-effort screenshots from EVERY actor — the all-actors snapshot (ADR-0002 D7). */
253
+ async #captureFailureArtifacts(scenarioId, actors) {
254
+ await Promise.all([...actors.values()].map(async (actor) => {
255
+ try {
256
+ const artifact = await actor.session.screenshot();
257
+ this.deps.events.emit({
258
+ type: 'artifactCaptured',
259
+ kind: artifact.kind,
260
+ path: artifact.path,
261
+ scenarioId,
262
+ actorId: actor.id,
263
+ });
264
+ }
265
+ catch {
266
+ // Artifact capture must never mask the real failure.
267
+ }
268
+ try {
269
+ // ADR-0002 D7: screenshot + SOURCE from every actor, best-effort.
270
+ const dump = await actor.session.source();
271
+ const path = join(this.deps.artifactsDir, `${scenarioId}-${actor.id}-source.txt`.replace(/[^\w.-]+/g, '_'));
272
+ writeFileSync(path, dump);
273
+ this.deps.events.emit({
274
+ type: 'artifactCaptured',
275
+ kind: 'source',
276
+ path,
277
+ scenarioId,
278
+ actorId: actor.id,
279
+ });
280
+ }
281
+ catch {
282
+ // Best-effort, same rule.
283
+ }
284
+ }));
285
+ }
286
+ }
287
+ /**
288
+ * The run coordinator: starts each involved driver once, runs scenarios
289
+ * sequentially, stops drivers and flushes reporters in finally
290
+ * (ADR-0002 D7; Phase 1 runs scenarios serially — parallel scenario
291
+ * scheduling is a later concern).
292
+ */
293
+ export async function runScenarios(options) {
294
+ const runId = options.runId ?? randomUUID();
295
+ const events = new EventBus(runId, {
296
+ ...(options.onReporterError !== undefined ? { onReporterError: options.onReporterError } : {}),
297
+ });
298
+ for (const reporter of options.reporters ?? []) {
299
+ events.subscribe(reporter);
300
+ }
301
+ const artifactsDir = options.artifactsDir ?? join(process.cwd(), '.kraken', 'runs', runId);
302
+ mkdirSync(artifactsDir, { recursive: true });
303
+ const runAbort = new AbortController();
304
+ const deps = {
305
+ registry: options.registry,
306
+ signalBus: options.signalBus,
307
+ events,
308
+ hostContext: options.hostContext,
309
+ artifactsDir,
310
+ ...(options.logSink !== undefined ? { logSink: options.logSink } : {}),
311
+ };
312
+ events.emit({ type: 'runStarted', protocol: 1, scenarioCount: options.plans.length });
313
+ const startedAt = Date.now();
314
+ const scenarios = [];
315
+ // Start each involved driver exactly once per run (ADR-0002 D2).
316
+ const startedDrivers = [];
317
+ try {
318
+ const uniqueDrivers = new Map();
319
+ for (const plan of options.plans) {
320
+ for (const actor of plan.actors) {
321
+ const driver = options.registry.driverFor(actor.platform);
322
+ uniqueDrivers.set(driver.manifest.id, driver);
323
+ }
324
+ }
325
+ for (const driver of uniqueDrivers.values()) {
326
+ try {
327
+ await driver.start(options.hostContext, driverServices(deps, `driver:${driver.manifest.id}`, runAbort.signal));
328
+ startedDrivers.push(driver);
329
+ }
330
+ catch (cause) {
331
+ throw KrakenError.wrap(cause, 'KRK-DRIVER-START-FAILED', `Driver "${driver.manifest.id}" failed to start`);
332
+ }
333
+ }
334
+ const runner = new ScenarioRunner(deps);
335
+ for (const plan of options.plans) {
336
+ scenarios.push(await runner.run(plan));
337
+ }
338
+ }
339
+ finally {
340
+ runAbort.abort();
341
+ for (const driver of startedDrivers.reverse()) {
342
+ try {
343
+ await driver.stop();
344
+ }
345
+ catch {
346
+ // stop() is declared idempotent/best-effort; a failing stop must not mask results.
347
+ }
348
+ }
349
+ const status = scenarios.length === options.plans.length && scenarios.every((s) => s.status === 'passed')
350
+ ? 'passed'
351
+ : 'failed';
352
+ events.emit({ type: 'runFinished', status, durationMs: Date.now() - startedAt });
353
+ await events.flush();
354
+ }
355
+ return {
356
+ runId,
357
+ status: scenarios.every((s) => s.status === 'passed') ? 'passed' : 'failed',
358
+ scenarios,
359
+ };
360
+ }
361
+ /** Boot-failure sessions and similar cleanup share this constant. */
362
+ export const SESSION_DISPOSE_TIMEOUT_MS = DISPOSE_TIMEOUT_MS;
363
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { ResolvedActor } from '@kraken-e2e/contracts';
2
+ import type { ScenarioPlan, StepRunContext } from './scheduler.js';
3
+ /**
4
+ * The programmatic scenario API (ADR-0001 §7 Phase 1): a plan builder
5
+ * mirroring the DSL's screenplay semantics — same orchestrator, no Gherkin.
6
+ *
7
+ * const plan = scenario('direct message')
8
+ * .step('alice', 'opens the conversation', async ({ actor }) => { ... })
9
+ * .step('bob', 'sees the message', async ({ actor }) => { ... })
10
+ * .build({ actors });
11
+ */
12
+ export declare function scenario(name: string): ScenarioBuilder;
13
+ export declare class ScenarioBuilder {
14
+ #private;
15
+ constructor(name: string);
16
+ step(actorId: string, title: string, run: (ctx: StepRunContext) => Promise<void>): this;
17
+ /** Starts a named background task; MUST be joined before the scenario ends. */
18
+ detach(actorId: string, title: string, handle: string, run: (ctx: StepRunContext) => Promise<void>): this;
19
+ join(actorId: string, title: string, handle: string, timeoutMs: number): this;
20
+ build(options: {
21
+ actors: readonly ResolvedActor[];
22
+ scenarioId?: string;
23
+ }): ScenarioPlan;
24
+ }
25
+ //# sourceMappingURL=scenario.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scenario.d.ts","sourceRoot":"","sources":["../src/scenario.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAG3D,OAAO,KAAK,EAAY,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAE7E;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAEtD;AAED,qBAAa,eAAe;;gBAKd,IAAI,EAAE,MAAM;IAcxB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IAYvF,+EAA+E;IAC/E,MAAM,CACJ,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,GAC1C,IAAI;IAaP,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAc7E,KAAK,CAAC,OAAO,EAAE;QAAE,MAAM,EAAE,SAAS,aAAa,EAAE,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,YAAY;CAmBxF"}
@@ -0,0 +1,85 @@
1
+ import { KrakenError } from '@kraken-e2e/contracts';
2
+ /**
3
+ * The programmatic scenario API (ADR-0001 §7 Phase 1): a plan builder
4
+ * mirroring the DSL's screenplay semantics — same orchestrator, no Gherkin.
5
+ *
6
+ * const plan = scenario('direct message')
7
+ * .step('alice', 'opens the conversation', async ({ actor }) => { ... })
8
+ * .step('bob', 'sees the message', async ({ actor }) => { ... })
9
+ * .build({ actors });
10
+ */
11
+ export function scenario(name) {
12
+ return new ScenarioBuilder(name);
13
+ }
14
+ export class ScenarioBuilder {
15
+ #name;
16
+ #nodes = [];
17
+ #counter = 0;
18
+ constructor(name) {
19
+ this.#name = name;
20
+ }
21
+ #nextId() {
22
+ this.#counter += 1;
23
+ return `node-${this.#counter}`;
24
+ }
25
+ #chainDependency() {
26
+ const last = this.#nodes.at(-1);
27
+ return last ? [last.id] : [];
28
+ }
29
+ step(actorId, title, run) {
30
+ this.#nodes.push({
31
+ id: this.#nextId(),
32
+ actorId,
33
+ kind: 'step',
34
+ title,
35
+ dependsOn: this.#chainDependency(),
36
+ run,
37
+ });
38
+ return this;
39
+ }
40
+ /** Starts a named background task; MUST be joined before the scenario ends. */
41
+ detach(actorId, title, handle, run) {
42
+ this.#nodes.push({
43
+ id: this.#nextId(),
44
+ actorId,
45
+ kind: 'detach',
46
+ title,
47
+ dependsOn: this.#chainDependency(),
48
+ taskHandle: handle,
49
+ run,
50
+ });
51
+ return this;
52
+ }
53
+ join(actorId, title, handle, timeoutMs) {
54
+ this.#nodes.push({
55
+ id: this.#nextId(),
56
+ actorId,
57
+ kind: 'join',
58
+ title,
59
+ dependsOn: this.#chainDependency(),
60
+ taskHandle: handle,
61
+ joinTimeoutMs: timeoutMs,
62
+ run: async () => { },
63
+ });
64
+ return this;
65
+ }
66
+ build(options) {
67
+ const declared = new Set(options.actors.map((actor) => actor.id));
68
+ for (const node of this.#nodes) {
69
+ if (!declared.has(node.actorId)) {
70
+ const known = [...declared].join(', ');
71
+ throw new KrakenError('KRK-STEP-UNKNOWN-ACTOR', `Step "${node.title}" is addressed to undeclared actor "${node.actorId}". Declared actors: ${known}.`, { fix: 'Declare the actor in the actors list, or fix the actor name in the step.' });
72
+ }
73
+ }
74
+ return {
75
+ scenarioId: options.scenarioId ?? `scenario-${cryptoRandomId()}`,
76
+ name: this.#name,
77
+ actors: options.actors,
78
+ nodes: this.#nodes,
79
+ };
80
+ }
81
+ }
82
+ function cryptoRandomId() {
83
+ return Math.random().toString(36).slice(2, 10);
84
+ }
85
+ //# sourceMappingURL=scenario.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scenario.js","sourceRoot":"","sources":["../src/scenario.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,87 @@
1
+ import type { Logger, ResolvedActor, UserSession } from '@kraken-e2e/contracts';
2
+ import type { SignalHandle } from '@kraken-e2e/signaling';
3
+ import type { EventSink } from './event-bus.js';
4
+ /** What a step's run() receives for its addressed actor. */
5
+ export interface ActorRuntime {
6
+ readonly id: string;
7
+ readonly platform: string;
8
+ readonly session: UserSession;
9
+ readonly signals: SignalHandle;
10
+ readonly log: Logger;
11
+ }
12
+ export interface StepRunContext {
13
+ readonly actor: ActorRuntime;
14
+ /** Per-scenario shared state across all actors' steps. */
15
+ readonly world: Record<string, unknown>;
16
+ readonly tasks: TaskRegistry;
17
+ /** Fired on failFast — long operations must honor it. */
18
+ readonly abort: AbortSignal;
19
+ /** All actor runtimes, for the rare step that must inspect another actor. */
20
+ readonly actors: ReadonlyMap<string, ActorRuntime>;
21
+ }
22
+ export type PlanNodeKind = 'step' | 'detach' | 'join';
23
+ export interface PlanNode {
24
+ readonly id: string;
25
+ readonly actorId: string;
26
+ readonly kind: PlanNodeKind;
27
+ /** Human-readable step text (Gherkin text or programmatic title). */
28
+ readonly title: string;
29
+ /** Default compilation is a chain (screenplay total order — ADR-0001 D6). */
30
+ readonly dependsOn: readonly string[];
31
+ /** detach/join: the named background-task handle. */
32
+ readonly taskHandle?: string;
33
+ /** join: how long to wait for the task. */
34
+ readonly joinTimeoutMs?: number;
35
+ run(ctx: StepRunContext): Promise<void>;
36
+ }
37
+ export interface ScenarioPlan {
38
+ readonly scenarioId: string;
39
+ readonly name: string;
40
+ readonly featureUri?: string;
41
+ readonly actors: readonly ResolvedActor[];
42
+ readonly nodes: readonly PlanNode[];
43
+ }
44
+ /**
45
+ * Named background tasks (the detach/join escape hatch — ADR-0001 §5.9).
46
+ * An unjoined handle at scenario end fails the scenario (leak detection).
47
+ */
48
+ export declare class TaskRegistry {
49
+ #private;
50
+ /**
51
+ * Check-then-start: the duplicate-handle check runs BEFORE the task body is
52
+ * invoked, so a rejected registration never leaves an untracked task running
53
+ * (and never produces an unhandled rejection).
54
+ */
55
+ register(handle: string, start: () => Promise<void>, startedBy: string): void;
56
+ join(handle: string, timeoutMs: number): Promise<void>;
57
+ unjoined(): readonly {
58
+ handle: string;
59
+ startedBy: string;
60
+ }[];
61
+ /**
62
+ * Settle everything (ignoring outcomes) so nothing dangles past the
63
+ * scenario — with a budget: a detached task that ignores the AbortSignal
64
+ * must never hang the run forever. Returns the handles still pending after
65
+ * the budget so the caller can fail the scenario explicitly.
66
+ */
67
+ drain(timeoutMs: number): Promise<readonly string[]>;
68
+ }
69
+ export interface PlanExecutionResult {
70
+ readonly status: 'passed' | 'failed';
71
+ readonly error?: unknown;
72
+ readonly failedNodeId?: string;
73
+ }
74
+ /**
75
+ * Executes a ScenarioPlan (ADR-0002 D7). The default shape is a chain, so
76
+ * execution is sequential in text order; `detach` nodes spawn tracked tasks;
77
+ * `join` nodes await them. Failure policy is failFast: the first failure
78
+ * aborts in-flight sibling work via the shared AbortSignal.
79
+ */
80
+ export declare function executePlan(plan: ScenarioPlan, deps: {
81
+ readonly actors: ReadonlyMap<string, ActorRuntime>;
82
+ readonly events: EventSink;
83
+ readonly abortController: AbortController;
84
+ /** Grace period for abort-ignoring detached tasks. */
85
+ readonly drainTimeoutMs?: number;
86
+ }): Promise<PlanExecutionResult>;
87
+ //# sourceMappingURL=scheduler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../src/scheduler.ts"],"names":[],"mappings":"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"}