bruno-lifecycle-adapter 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,403 @@
1
+ # bruno-lifecycle-adapter
2
+
3
+ A TypeScript package that wraps [Bruno CLI](https://www.usebruno.com/) and exposes an observable execution lifecycle for external integrations.
4
+
5
+ It runs Bruno collections through `bru run`, captures process output, parses generated reports, and emits typed lifecycle events such as run start/end, request execution, test results, assertions, stdout, and stderr.
6
+
7
+ The adapter is designed to be **independent from Bruno's core**, with a clean architecture, strong typing, automated tests, and a stable internal contract that can evolve even if Bruno's report format changes over time.
8
+
9
+ ---
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npm install bruno-lifecycle-adapter
15
+ ```
16
+
17
+ Bruno CLI must be installed separately (the adapter calls `bru run`):
18
+
19
+ ```bash
20
+ npm install -g @usebruno/cli
21
+ ```
22
+
23
+ ---
24
+
25
+ ## Usage
26
+
27
+ ```ts
28
+ import { BrunoLifecycleAdapter } from 'bruno-lifecycle-adapter';
29
+
30
+ const adapter = new BrunoLifecycleAdapter();
31
+
32
+ adapter.on('run:started', (event) => {
33
+ console.log(`Run started (pid ${event.pid}), runId: ${event.runId}`);
34
+ });
35
+
36
+ adapter.on('request:finished', (event) => {
37
+ console.log(`${event.requestName} → ${event.status} (${event.responseStatus ?? '?'})`);
38
+ });
39
+
40
+ adapter.on('test:finished', (event) => {
41
+ console.log(` test "${event.testName}": ${event.status}`);
42
+ });
43
+
44
+ adapter.on('assertion:result', (event) => {
45
+ console.log(` assertion "${event.assertion}": ${event.passed ? 'pass' : 'fail'}`);
46
+ });
47
+
48
+ const summary = await adapter.run({
49
+ cwd: process.cwd(),
50
+ collectionPath: './my-collection',
51
+ env: 'local',
52
+ reporterJsonPath: './tmp/bruno-report.json',
53
+ });
54
+
55
+ console.log('Exit code:', summary.exitCode);
56
+ console.log('Passed requests:', summary.passedRequests, '/', summary.totalRequests);
57
+ ```
58
+
59
+ ---
60
+
61
+ ## Configuration
62
+
63
+ ```ts
64
+ interface AdapterRunConfig {
65
+ /** Working directory where `bru run` is executed. */
66
+ cwd: string;
67
+
68
+ /** Path to the Bruno collection directory or specific `.bru` file. */
69
+ collectionPath: string;
70
+
71
+ /** Environment name passed to `--env`. */
72
+ env?: string;
73
+
74
+ /** Path where Bruno writes its JSON report (`--reporter-json`). */
75
+ reporterJsonPath?: string;
76
+
77
+ /** When true, passes `-r` to `bru run` to recurse into sub-folders. */
78
+ recursive?: boolean;
79
+
80
+ /** Extra CLI arguments appended verbatim after the collection path. */
81
+ extraArgs?: string[];
82
+
83
+ /** Path to the `bru` binary. Defaults to `bru` (resolved from PATH). */
84
+ bruBin?: string;
85
+
86
+ /** Timeout in milliseconds for the entire run. `0` means no timeout. */
87
+ timeoutMs?: number;
88
+ }
89
+ ```
90
+
91
+ ---
92
+
93
+ ## Event Subscription
94
+
95
+ ```ts
96
+ // Subscribe and get an unsubscribe handle
97
+ const unsubscribe = adapter.on('stdout', (event) => {
98
+ process.stdout.write(event.chunk);
99
+ });
100
+
101
+ // Subscribe once – fires only on the first emission
102
+ adapter.once('run:finished', (event) => {
103
+ console.log('Total duration:', event.durationMs, 'ms');
104
+ });
105
+
106
+ // Unsubscribe manually
107
+ adapter.off('stderr', myStderrHandler);
108
+
109
+ // Or use the returned unsubscribe function
110
+ unsubscribe();
111
+ ```
112
+
113
+ ---
114
+
115
+ ## Lifecycle Events
116
+
117
+ | Event | Reliability | Description |
118
+ |---|---|---|
119
+ | `run:starting` | native | Emitted before spawning the process |
120
+ | `run:started` | native | Emitted once the child process has a PID |
121
+ | `run:finished` | native | Emitted when the process exits with code 0 |
122
+ | `run:failed` | native | Emitted when the process exits with non-zero code, times out, or fails to spawn |
123
+ | `request:discovered` | inferred | Emitted per `.bru` request file found in the collection directory, fired after the process exits and before report-derived events |
124
+ | `request:started` | derived | Emitted immediately before each `request:finished` or `request:skipped` (derived from the JSON report; fires in batch after the run) |
125
+ | `request:finished` | derived | Emitted per non-skipped request after successful report parsing |
126
+ | `request:skipped` | derived | Emitted per skipped request after successful report parsing |
127
+ | `test:started` | derived | Emitted immediately before each `test:finished` (derived from the JSON report; fires in batch after the run) |
128
+ | `test:finished` | derived | Emitted per test result after successful report parsing |
129
+ | `assertion:result` | derived | Emitted per assertion after successful report parsing |
130
+ | `stdout` | native | Raw stdout chunk from the `bru run` process |
131
+ | `stderr` | native | Raw stderr chunk from the `bru run` process |
132
+ | `report:json:ready` | derived | Emitted only after the JSON report is parsed successfully |
133
+
134
+ ### Event Reliability
135
+
136
+ Each event payload includes a `reliability` field:
137
+
138
+ - **`native`** – directly observed from process stdout/stderr or exit code
139
+ - **`derived`** – computed from one or more native signals (e.g. parsed report data)
140
+ - **`inferred`** – best-effort; could not be confirmed from available signals
141
+
142
+ ---
143
+
144
+ ## Event Payloads
145
+
146
+ All event payloads extend `EventMetadata`:
147
+
148
+ ```ts
149
+ interface EventMetadata {
150
+ runId: string; // UUID identifying this run
151
+ timestamp: string; // ISO 8601 timestamp
152
+ reliability: 'native' | 'derived' | 'inferred';
153
+ }
154
+ ```
155
+
156
+ Example payloads:
157
+
158
+ ```ts
159
+ interface RunStartedEvent extends EventMetadata {
160
+ event: 'run:started';
161
+ cwd: string;
162
+ collectionPath: string;
163
+ pid: number;
164
+ }
165
+
166
+ interface RequestFinishedEvent extends EventMetadata {
167
+ event: 'request:finished';
168
+ requestName: string;
169
+ requestFile: string;
170
+ status: 'discovered' | 'started' | 'finished' | 'skipped' | 'failed';
171
+ responseStatus?: number;
172
+ durationMs?: number;
173
+ error?: { message: string; stack?: string };
174
+ }
175
+
176
+ interface TestFinishedEvent extends EventMetadata {
177
+ event: 'test:finished';
178
+ requestName: string;
179
+ testName: string;
180
+ status: 'started' | 'passed' | 'failed' | 'skipped';
181
+ error?: { message: string; stack?: string };
182
+ }
183
+ ```
184
+
185
+ ---
186
+
187
+ ## Execution Summary
188
+
189
+ `adapter.run()` resolves with an `ExecutionSummary`:
190
+
191
+ ```ts
192
+ interface ExecutionSummary {
193
+ runId: string;
194
+ collectionPath: string;
195
+ startedAt: string;
196
+ finishedAt: string;
197
+ durationMs: number;
198
+ exitCode: number;
199
+ status: 'starting' | 'running' | 'finished' | 'failed' | 'cancelled';
200
+ totalRequests: number;
201
+ passedRequests: number;
202
+ failedRequests: number;
203
+ skippedRequests: number;
204
+ totalTests: number;
205
+ passedTests: number;
206
+ failedTests: number;
207
+ totalAssertions: number;
208
+ passedAssertions: number;
209
+ failedAssertions: number;
210
+ requests: RequestResult[];
211
+ error?: ErrorDetail;
212
+ }
213
+ ```
214
+
215
+ ---
216
+
217
+ ## Custom Report Parser
218
+
219
+ You can supply your own report parser to handle custom Bruno report formats:
220
+
221
+ ```ts
222
+ import { BrunoLifecycleAdapter } from 'bruno-lifecycle-adapter';
223
+ import type { ReportParserContract } from 'bruno-lifecycle-adapter';
224
+
225
+ class MyCustomParser implements ReportParserContract {
226
+ async parse(reportPath: string): Promise<ExecutionSummary> {
227
+ // read and map your format
228
+ }
229
+ }
230
+
231
+ const adapter = new BrunoLifecycleAdapter(new MyCustomParser());
232
+ ```
233
+
234
+ ---
235
+
236
+ ## Limitations
237
+
238
+ - **No native per-request events from process stdout.** Bruno CLI does not emit structured events to stdout; per-request and per-test events are derived from the JSON report. Subscribe to `stdout` for raw output.
239
+ - **Report events require `reporterJsonPath`.** Without a JSON report path, `request:finished`, `test:finished`, `assertion:result`, and `report:json:ready` are not emitted.
240
+ - **Bruno JSON schema may change.** The parser maps known fields and falls back gracefully, but new Bruno versions may change the report schema. Inspect the raw `stdout` events if you need forward compatibility.
241
+ - **Timeout kills the process with `SIGTERM`.** The process is sent `SIGTERM` on timeout; clean shutdown depends on the OS and Bruno's signal handling.
242
+
243
+ ---
244
+
245
+ ## Project Structure
246
+
247
+ ```
248
+ src/
249
+ domain/
250
+ events.ts # All lifecycle event types and execution models
251
+ models.ts # AdapterRunConfig and ReportParserContract interfaces
252
+ application/
253
+ BrunoLifecycleAdapter.ts # Main adapter (spawns bru, emits events)
254
+ infrastructure/
255
+ TypedEventBus.ts # Strongly-typed event bus
256
+ BrunoJsonReportParser.ts # Bruno JSON report → internal model
257
+ shared/
258
+ utils.ts # generateRunId, nowIso, elapsedMs
259
+ index.ts # Public exports
260
+ tests/
261
+ unit/
262
+ TypedEventBus.test.ts
263
+ BrunoJsonReportParser.test.ts
264
+ BrunoLifecycleAdapter.test.ts
265
+ BruFileScanner.test.ts
266
+ integration/
267
+ adapter.integration.test.ts # End-to-end integration tests
268
+ fixtures/
269
+ bruno-project/ # Sample Bruno collection
270
+ bruno.json
271
+ environments/local.bru
272
+ health-check.bru
273
+ echo-post.bru
274
+ multi-request-flow/
275
+ 01-list-items.bru
276
+ 02-get-item-by-id.bru
277
+ external-listener/ # External JS consumer package
278
+ package.json
279
+ index.js
280
+ e2e/
281
+ app-server.js # Standalone HTTP server for Docker E2E
282
+ run.js # Docker E2E test runner entry-point
283
+ ```
284
+
285
+ ---
286
+
287
+ ## Development
288
+
289
+ ```bash
290
+ npm install
291
+ npm run build # compile with tsup
292
+ npm test # run unit tests with vitest
293
+ npm run test:unit # explicit alias for unit tests
294
+ npm run test:e2e # run full E2E suite inside Docker Compose
295
+ npm run typecheck # tsc --noEmit
296
+ npm run lint # eslint
297
+ npm run format # prettier --write
298
+ ```
299
+
300
+ ---
301
+
302
+ ## Integration Tests
303
+
304
+ Integration tests verify the adapter against a real Bruno installation.
305
+
306
+ ### Prerequisites
307
+
308
+ 1. Build the adapter:
309
+ ```bash
310
+ npm run build
311
+ ```
312
+ 2. Install Bruno CLI:
313
+ ```bash
314
+ npm install -g @usebruno/cli
315
+ ```
316
+ 3. Install external listener dependencies:
317
+ ```bash
318
+ npm run test:integration:setup
319
+ ```
320
+
321
+ ### Running integration tests
322
+
323
+ ```bash
324
+ npm run test:integration
325
+ ```
326
+
327
+ ### Full CI-equivalent local run
328
+
329
+ This command reproduces the exact CI execution sequence:
330
+
331
+ ```bash
332
+ npm run ci:local
333
+ ```
334
+
335
+ It runs: build → lint → typecheck → unit tests → integration setup → integration tests.
336
+
337
+ > **Note:** Integration tests **fail** (they do not skip) when `bru` is not
338
+ > found on PATH. This is intentional — a passing run without Bruno would give
339
+ > false confidence. Unit tests (`npm test`) run without Bruno.
340
+
341
+ ### How integration tests work
342
+
343
+ 1. A minimal HTTP server is started on port `47891` to serve the Bruno
344
+ collection's requests locally — no external network calls required.
345
+ 2. The **external listener** (`tests/integration/fixtures/external-listener/`)
346
+ is spawned as a child process. It imports the adapter, subscribes to all
347
+ lifecycle events, and writes structured JSON-lines to stdout for each event.
348
+ 3. The integration test captures the listener's stdout and asserts that
349
+ expected events were logged — confirming that the adapter correctly surfaces
350
+ lifecycle information to external consumers.
351
+
352
+ ### CI
353
+
354
+ Integration tests run automatically on **pull requests targeting `main`** via
355
+ the `.github/workflows/integration.yml` workflow. They do not run on feature
356
+ branch pushes to keep CI fast.
357
+
358
+ ---
359
+
360
+ ## E2E Tests
361
+
362
+ E2E tests verify the adapter end-to-end inside isolated Docker containers —
363
+ no host-level Bruno installation is required.
364
+
365
+ ### Running E2E tests
366
+
367
+ ```bash
368
+ docker compose up --build --exit-code-from e2e --abort-on-container-exit
369
+ ```
370
+
371
+ Or via the npm script:
372
+
373
+ ```bash
374
+ npm run test:e2e
375
+ ```
376
+
377
+ ### How E2E tests work
378
+
379
+ The `docker-compose.yml` starts two services:
380
+
381
+ 1. **`app`** (`node:20-alpine`): a minimal HTTP server exposing the same routes
382
+ as the integration test server (`/health`, `/echo`, `/items`, `/items/1`).
383
+ Reachable within the Compose network as `http://app:47891`.
384
+ 2. **`e2e`** (built from `Dockerfile`): installs Bruno CLI globally, resolves
385
+ the adapter from the local `dist/`, installs the external listener, and
386
+ runs `tests/e2e/run.js`.
387
+
388
+ The `e2e` container exits 0 on success, 1 on failure.
389
+ `docker compose up --exit-code-from e2e` propagates that exit code to the host.
390
+
391
+ ### CI
392
+
393
+ E2E tests run automatically on **pull requests targeting `main`** via the
394
+ `.github/workflows/e2e.yml` workflow. Two jobs run in sequence:
395
+
396
+ 1. `docker-build` — verifies the `Dockerfile` builds successfully.
397
+ 2. `e2e` — runs `docker compose up` with the full stack.
398
+
399
+ ---
400
+
401
+ ## License
402
+
403
+ MIT
@@ -0,0 +1,254 @@
1
+ /**
2
+ * Indicates how the event was produced relative to Bruno's native output.
3
+ * - `native` – directly observed from process stdout/stderr or exit code
4
+ * - `derived` – computed from one or more native signals
5
+ * - `inferred`– best-effort, could not be confirmed from available signals
6
+ */
7
+ type EventReliability = 'native' | 'derived' | 'inferred';
8
+ type RunStatus = 'starting' | 'running' | 'finished' | 'failed' | 'cancelled';
9
+ type RequestStatus = 'discovered' | 'started' | 'finished' | 'skipped' | 'failed';
10
+ type TestStatus = 'started' | 'passed' | 'failed' | 'skipped';
11
+ interface ErrorDetail {
12
+ message: string;
13
+ stack?: string | undefined;
14
+ }
15
+ interface EventMetadata {
16
+ runId: string;
17
+ timestamp: string;
18
+ reliability: EventReliability;
19
+ }
20
+ interface RunStartingEvent extends EventMetadata {
21
+ event: 'run:starting';
22
+ cwd: string;
23
+ collectionPath: string;
24
+ }
25
+ interface RunStartedEvent extends EventMetadata {
26
+ event: 'run:started';
27
+ cwd: string;
28
+ collectionPath: string;
29
+ pid: number;
30
+ }
31
+ interface RunFinishedEvent extends EventMetadata {
32
+ event: 'run:finished';
33
+ exitCode: number;
34
+ durationMs: number;
35
+ summary: ExecutionSummary;
36
+ }
37
+ interface RunFailedEvent extends EventMetadata {
38
+ event: 'run:failed';
39
+ exitCode: number;
40
+ durationMs: number;
41
+ error: ErrorDetail;
42
+ summary: ExecutionSummary;
43
+ }
44
+ interface RequestDiscoveredEvent extends EventMetadata {
45
+ event: 'request:discovered';
46
+ requestName: string;
47
+ requestFile: string;
48
+ }
49
+ interface RequestStartedEvent extends EventMetadata {
50
+ event: 'request:started';
51
+ requestName: string;
52
+ requestFile: string;
53
+ }
54
+ interface RequestFinishedEvent extends EventMetadata {
55
+ event: 'request:finished';
56
+ requestName: string;
57
+ requestFile: string;
58
+ status: RequestStatus;
59
+ responseStatus?: number | undefined;
60
+ durationMs?: number | undefined;
61
+ error?: ErrorDetail | undefined;
62
+ }
63
+ interface RequestSkippedEvent extends EventMetadata {
64
+ event: 'request:skipped';
65
+ requestName: string;
66
+ requestFile: string;
67
+ }
68
+ interface TestStartedEvent extends EventMetadata {
69
+ event: 'test:started';
70
+ requestName: string;
71
+ testName: string;
72
+ }
73
+ interface TestFinishedEvent extends EventMetadata {
74
+ event: 'test:finished';
75
+ requestName: string;
76
+ testName: string;
77
+ status: TestStatus;
78
+ error?: ErrorDetail | undefined;
79
+ }
80
+ interface AssertionResultEvent extends EventMetadata {
81
+ event: 'assertion:result';
82
+ requestName: string;
83
+ assertion: string;
84
+ passed: boolean;
85
+ actual?: unknown;
86
+ expected?: unknown;
87
+ error?: ErrorDetail | undefined;
88
+ }
89
+ interface StdoutEvent extends EventMetadata {
90
+ event: 'stdout';
91
+ chunk: string;
92
+ }
93
+ interface StderrEvent extends EventMetadata {
94
+ event: 'stderr';
95
+ chunk: string;
96
+ }
97
+ interface ReportJsonReadyEvent extends EventMetadata {
98
+ event: 'report:json:ready';
99
+ path: string;
100
+ summary: ExecutionSummary;
101
+ }
102
+ type LifecycleEvent = RunStartingEvent | RunStartedEvent | RunFinishedEvent | RunFailedEvent | RequestDiscoveredEvent | RequestStartedEvent | RequestFinishedEvent | RequestSkippedEvent | TestStartedEvent | TestFinishedEvent | AssertionResultEvent | StdoutEvent | StderrEvent | ReportJsonReadyEvent;
103
+ type LifecycleEventName = LifecycleEvent['event'];
104
+ type EventPayload<T extends LifecycleEventName> = Extract<LifecycleEvent, {
105
+ event: T;
106
+ }>;
107
+ interface AssertionResult {
108
+ assertion: string;
109
+ passed: boolean;
110
+ actual?: unknown;
111
+ expected?: unknown;
112
+ error?: ErrorDetail | undefined;
113
+ }
114
+ interface TestResult {
115
+ testName: string;
116
+ status: TestStatus;
117
+ error?: ErrorDetail | undefined;
118
+ }
119
+ interface RequestResult {
120
+ requestName: string;
121
+ requestFile: string;
122
+ status: RequestStatus;
123
+ responseStatus?: number | undefined;
124
+ durationMs?: number | undefined;
125
+ tests: TestResult[];
126
+ assertions: AssertionResult[];
127
+ error?: ErrorDetail | undefined;
128
+ }
129
+ interface ExecutionSummary {
130
+ runId: string;
131
+ collectionPath: string;
132
+ startedAt: string;
133
+ finishedAt: string;
134
+ durationMs: number;
135
+ exitCode: number;
136
+ status: RunStatus;
137
+ totalRequests: number;
138
+ passedRequests: number;
139
+ failedRequests: number;
140
+ skippedRequests: number;
141
+ totalTests: number;
142
+ passedTests: number;
143
+ failedTests: number;
144
+ totalAssertions: number;
145
+ passedAssertions: number;
146
+ failedAssertions: number;
147
+ requests: RequestResult[];
148
+ error?: ErrorDetail | undefined;
149
+ }
150
+
151
+ interface AdapterRunConfig {
152
+ /** Working directory where `bru run` is executed. */
153
+ cwd: string;
154
+ /** Path to the Bruno collection directory or specific `.bru` file. */
155
+ collectionPath: string;
156
+ /** When true, passes `-r` to `bru run` to recurse into sub-folders. */
157
+ recursive?: boolean | undefined;
158
+ /** Environment name to pass to `--env`. */
159
+ env?: string | undefined;
160
+ /** Path where Bruno should write its JSON report (`--reporter-json`). */
161
+ reporterJsonPath?: string | undefined;
162
+ /** Extra CLI arguments appended verbatim after the collection path. */
163
+ extraArgs?: string[] | undefined;
164
+ /** Path to the `bru` binary. Defaults to `bru` (resolved from PATH). */
165
+ bruBin?: string | undefined;
166
+ /** Timeout in milliseconds for the entire run. `0` means no timeout. */
167
+ timeoutMs?: number | undefined;
168
+ }
169
+ interface ReportParserContract {
170
+ parse(reportPath: string): Promise<ExecutionSummary>;
171
+ }
172
+
173
+ /**
174
+ * A request file discovered inside a Bruno collection directory.
175
+ */
176
+ interface DiscoveredRequest {
177
+ /** The human-readable request name, taken from the `meta { name: ... }` block. */
178
+ requestName: string;
179
+ /**
180
+ * The path to the `.bru` file, relative to the collection root.
181
+ * Uses forward slashes on all platforms.
182
+ */
183
+ requestFile: string;
184
+ }
185
+ /**
186
+ * Scans a Bruno collection directory for HTTP request files (`.bru` files
187
+ * whose `meta` block contains `type: http`).
188
+ *
189
+ * - The `environments/` directory is always excluded because it contains
190
+ * variable definition files, not request files.
191
+ * - Non-request `.bru` files (e.g. `type: collection`, `type: folder`)
192
+ * are silently skipped.
193
+ * - Any I/O error (missing directory, unreadable file, etc.) is caught
194
+ * internally and results in an empty list — scanning is non-fatal.
195
+ */
196
+ declare class BruFileScanner {
197
+ /**
198
+ * Returns all HTTP request files found under `rootDir`.
199
+ *
200
+ * @param rootDir Absolute path to the collection root (the directory
201
+ * passed to `bru run`).
202
+ * @param recursive When `true`, descends into sub-directories. Mirrors
203
+ * the `-r` flag behaviour of `bru run`.
204
+ */
205
+ scan(rootDir: string, recursive: boolean): Promise<DiscoveredRequest[]>;
206
+ private collectBruFiles;
207
+ private parseRequestFile;
208
+ private parseMetaBlock;
209
+ private nameFromPath;
210
+ }
211
+
212
+ type EventHandler<T extends LifecycleEventName> = (payload: EventPayload<T>) => void;
213
+ /**
214
+ * Strongly-typed event bus wrapping a plain Map of handler sets.
215
+ * Does not extend Node's EventEmitter to remain platform-neutral.
216
+ */
217
+ declare class TypedEventBus {
218
+ private readonly handlers;
219
+ on<T extends LifecycleEventName>(event: T, handler: EventHandler<T>): () => void;
220
+ once<T extends LifecycleEventName>(event: T, handler: EventHandler<T>): () => void;
221
+ off<T extends LifecycleEventName>(event: T, handler: EventHandler<T>): void;
222
+ emit<T extends LifecycleEventName>(event: T, payload: EventPayload<T>): void;
223
+ removeAllListeners(event?: LifecycleEventName): void;
224
+ private getOrCreate;
225
+ }
226
+
227
+ declare class BrunoLifecycleAdapter {
228
+ private readonly bus;
229
+ private readonly parser;
230
+ private readonly scanner;
231
+ constructor(parser?: ReportParserContract, scanner?: BruFileScanner);
232
+ on<T extends LifecycleEventName>(event: T, handler: EventHandler<T>): () => void;
233
+ once<T extends LifecycleEventName>(event: T, handler: EventHandler<T>): () => void;
234
+ off<T extends LifecycleEventName>(event: T, handler: EventHandler<T>): void;
235
+ run(config: AdapterRunConfig): Promise<ExecutionSummary>;
236
+ private emitReportEvents;
237
+ private buildArgs;
238
+ private buildEmptySummary;
239
+ private emit;
240
+ }
241
+
242
+ declare class BrunoJsonReportParser implements ReportParserContract {
243
+ parse(reportPath: string): Promise<ExecutionSummary>;
244
+ private readRaw;
245
+ private mapToSummary;
246
+ private mapRequest;
247
+ private mapTest;
248
+ private mapAssertion;
249
+ private mapRequestStatus;
250
+ private mapTestStatus;
251
+ private makeError;
252
+ }
253
+
254
+ export { type AdapterRunConfig, type AssertionResult, type AssertionResultEvent, BruFileScanner, BrunoJsonReportParser, BrunoLifecycleAdapter, type DiscoveredRequest, type ErrorDetail, type EventHandler, type EventMetadata, type EventPayload, type EventReliability, type ExecutionSummary, type LifecycleEvent, type LifecycleEventName, type ReportJsonReadyEvent, type ReportParserContract, type RequestDiscoveredEvent, type RequestFinishedEvent, type RequestResult, type RequestSkippedEvent, type RequestStartedEvent, type RequestStatus, type RunFailedEvent, type RunFinishedEvent, type RunStartedEvent, type RunStartingEvent, type RunStatus, type StderrEvent, type StdoutEvent, type TestFinishedEvent, type TestResult, type TestStartedEvent, type TestStatus, TypedEventBus };