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 +403 -0
- package/dist/index.d.mts +254 -0
- package/dist/index.d.ts +254 -0
- package/dist/index.js +668 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +638 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/application/BrunoLifecycleAdapter.ts","../src/infrastructure/BrunoJsonReportParser.ts","../src/shared/utils.ts","../src/infrastructure/BruFileScanner.ts","../src/infrastructure/TypedEventBus.ts"],"sourcesContent":["export { BrunoLifecycleAdapter } from './application/BrunoLifecycleAdapter.js';\nexport { BrunoJsonReportParser } from './infrastructure/BrunoJsonReportParser.js';\nexport { BruFileScanner } from './infrastructure/BruFileScanner.js';\nexport { TypedEventBus } from './infrastructure/TypedEventBus.js';\nexport type { EventHandler } from './infrastructure/TypedEventBus.js';\nexport type { DiscoveredRequest } from './infrastructure/BruFileScanner.js';\nexport type {\n AdapterRunConfig,\n ReportParserContract,\n} from './domain/models.js';\nexport type {\n AssertionResult,\n AssertionResultEvent,\n ErrorDetail,\n EventMetadata,\n EventPayload,\n EventReliability,\n ExecutionSummary,\n LifecycleEvent,\n LifecycleEventName,\n ReportJsonReadyEvent,\n RequestDiscoveredEvent,\n RequestFinishedEvent,\n RequestResult,\n RequestSkippedEvent,\n RequestStartedEvent,\n RequestStatus,\n RunFailedEvent,\n RunFinishedEvent,\n RunStartedEvent,\n RunStartingEvent,\n RunStatus,\n StderrEvent,\n StdoutEvent,\n TestFinishedEvent,\n TestResult,\n TestStartedEvent,\n TestStatus,\n} from './domain/events.js';\n","import { spawn } from 'node:child_process';\nimport { resolve, dirname } from 'node:path';\nimport type {\n AssertionResultEvent,\n ErrorDetail,\n EventPayload,\n ExecutionSummary,\n LifecycleEventName,\n RequestFinishedEvent,\n RequestResult,\n RequestSkippedEvent,\n RequestStartedEvent,\n RunStatus,\n TestFinishedEvent,\n TestStartedEvent,\n} from '../domain/events.js';\nimport type { AdapterRunConfig, ReportParserContract } from '../domain/models.js';\nimport { BrunoJsonReportParser } from '../infrastructure/BrunoJsonReportParser.js';\nimport { BruFileScanner } from '../infrastructure/BruFileScanner.js';\nimport type { DiscoveredRequest } from '../infrastructure/BruFileScanner.js';\nimport type { EventHandler } from '../infrastructure/TypedEventBus.js';\nimport { TypedEventBus } from '../infrastructure/TypedEventBus.js';\nimport { elapsedMs, generateRunId, nowIso } from '../shared/utils.js';\n\nexport class BrunoLifecycleAdapter {\n private readonly bus = new TypedEventBus();\n private readonly parser: ReportParserContract;\n private readonly scanner: BruFileScanner;\n\n constructor(parser?: ReportParserContract, scanner?: BruFileScanner) {\n this.parser = parser ?? new BrunoJsonReportParser();\n this.scanner = scanner ?? new BruFileScanner();\n }\n\n on<T extends LifecycleEventName>(event: T, handler: EventHandler<T>): () => void {\n return this.bus.on(event, handler);\n }\n\n once<T extends LifecycleEventName>(event: T, handler: EventHandler<T>): () => void {\n return this.bus.once(event, handler);\n }\n\n off<T extends LifecycleEventName>(event: T, handler: EventHandler<T>): void {\n this.bus.off(event, handler);\n }\n\n async run(config: AdapterRunConfig): Promise<ExecutionSummary> {\n const runId = generateRunId();\n const startedAt = nowIso();\n\n this.emit('run:starting', {\n event: 'run:starting',\n runId,\n timestamp: startedAt,\n reliability: 'native',\n cwd: config.cwd,\n collectionPath: config.collectionPath,\n });\n\n // Start collection discovery concurrently with the process — the scan runs\n // while bru executes and the result is awaited inside the close handler.\n // On error (e.g. collection path does not exist) the promise settles to []\n // so that discovery is always non-fatal.\n // When collectionPath is a single .bru file, scan the parent directory so\n // that discovery still returns the one request being executed.\n const resolvedCollection = resolve(config.cwd, config.collectionPath);\n const collectionRoot = resolvedCollection.endsWith('.bru')\n ? dirname(resolvedCollection)\n : resolvedCollection;\n const discoveryPromise: Promise<DiscoveredRequest[]> =\n this.scanner.scan(collectionRoot, config.recursive ?? false).catch(() => []);\n\n const args = this.buildArgs(config);\n const bin = config.bruBin ?? 'bru';\n\n return new Promise<ExecutionSummary>((resolveRun, rejectRun) => {\n let timedOut = false;\n let timeoutHandle: ReturnType<typeof setTimeout> | undefined;\n\n const child = spawn(bin, args, {\n cwd: config.cwd,\n stdio: ['ignore', 'pipe', 'pipe'],\n shell: false,\n });\n\n if (child.pid === undefined) {\n const err = new Error(`Failed to spawn \"${bin}\". Is Bruno CLI installed?`);\n const finishedAt = nowIso();\n const durationMs = elapsedMs(startedAt);\n const detail: ErrorDetail = { message: err.message };\n const summary = this.buildEmptySummary({\n runId,\n collectionPath: config.collectionPath,\n startedAt,\n finishedAt,\n durationMs,\n exitCode: -1,\n status: 'failed',\n error: detail,\n });\n this.emit('run:failed', {\n event: 'run:failed',\n runId,\n timestamp: finishedAt,\n reliability: 'native',\n exitCode: -1,\n durationMs,\n error: detail,\n summary,\n });\n rejectRun(err);\n return;\n }\n\n this.emit('run:started', {\n event: 'run:started',\n runId,\n timestamp: nowIso(),\n reliability: 'native',\n cwd: config.cwd,\n collectionPath: config.collectionPath,\n pid: child.pid,\n });\n\n if (config.timeoutMs && config.timeoutMs > 0) {\n timeoutHandle = setTimeout(() => {\n timedOut = true;\n child.kill('SIGTERM');\n }, config.timeoutMs);\n }\n\n child.stdout.setEncoding('utf-8');\n child.stderr.setEncoding('utf-8');\n\n child.stdout.on('data', (chunk: string) => {\n this.emit('stdout', {\n event: 'stdout',\n runId,\n timestamp: nowIso(),\n reliability: 'native',\n chunk,\n });\n });\n\n child.stderr.on('data', (chunk: string) => {\n this.emit('stderr', {\n event: 'stderr',\n runId,\n timestamp: nowIso(),\n reliability: 'native',\n chunk,\n });\n });\n\n child.on('error', (err) => {\n clearTimeout(timeoutHandle);\n const detail: ErrorDetail = { message: err.message, stack: err.stack };\n const finishedAt = nowIso();\n const durationMs = elapsedMs(startedAt);\n const summary = this.buildEmptySummary({\n runId,\n collectionPath: config.collectionPath,\n startedAt,\n finishedAt,\n durationMs,\n exitCode: -1,\n status: 'failed',\n error: detail,\n });\n this.emit('run:failed', {\n event: 'run:failed',\n runId,\n timestamp: finishedAt,\n reliability: 'native',\n exitCode: -1,\n durationMs,\n error: detail,\n summary,\n });\n rejectRun(err);\n });\n\n child.on('close', (code) => {\n clearTimeout(timeoutHandle);\n\n // Use an async IIFE so we can await the discovery promise and the\n // report parser without exposing an unhandled-promise error to callers.\n void (async () => {\n const exitCode = code ?? -1;\n const finishedAt = nowIso();\n const durationMs = elapsedMs(startedAt);\n const runStatus: RunStatus = timedOut || exitCode !== 0 ? 'failed' : 'finished';\n\n // Await discovery (started concurrently with spawn — usually already settled).\n const discovered = await discoveryPromise;\n for (const req of discovered) {\n this.emit('request:discovered', {\n event: 'request:discovered',\n runId,\n timestamp: finishedAt,\n reliability: 'inferred',\n requestName: req.requestName,\n requestFile: req.requestFile,\n });\n }\n\n // Parse the JSON report if one was requested.\n let summary: ExecutionSummary;\n let reportParsedSuccessfully = false;\n\n if (config.reporterJsonPath) {\n try {\n summary = await this.parser.parse(config.reporterJsonPath);\n reportParsedSuccessfully = true;\n } catch {\n summary = this.buildEmptySummary({\n runId,\n collectionPath: config.collectionPath,\n startedAt,\n finishedAt,\n durationMs,\n exitCode,\n status: runStatus,\n });\n }\n } else {\n summary = this.buildEmptySummary({\n runId,\n collectionPath: config.collectionPath,\n startedAt,\n finishedAt,\n durationMs,\n exitCode,\n status: runStatus,\n });\n }\n\n const enriched: ExecutionSummary = {\n ...summary,\n runId,\n collectionPath: config.collectionPath,\n startedAt,\n finishedAt,\n durationMs,\n exitCode,\n status: runStatus,\n };\n\n this.emitReportEvents(enriched, runId, finishedAt);\n\n if (config.reporterJsonPath && reportParsedSuccessfully) {\n this.emit('report:json:ready', {\n event: 'report:json:ready',\n runId,\n timestamp: finishedAt,\n reliability: 'derived',\n path: config.reporterJsonPath,\n summary: enriched,\n });\n }\n\n if (runStatus === 'finished') {\n this.emit('run:finished', {\n event: 'run:finished',\n runId,\n timestamp: finishedAt,\n reliability: 'native',\n exitCode,\n durationMs,\n summary: enriched,\n });\n } else {\n const error: ErrorDetail = timedOut\n ? { message: `Run timed out after ${config.timeoutMs ?? 0}ms` }\n : { message: `Run exited with code ${exitCode}` };\n this.emit('run:failed', {\n event: 'run:failed',\n runId,\n timestamp: finishedAt,\n reliability: 'native',\n exitCode,\n durationMs,\n error,\n summary: enriched,\n });\n }\n\n resolveRun(enriched);\n })().catch((err: unknown) => {\n rejectRun(err instanceof Error ? err : new Error(String(err)));\n });\n });\n });\n }\n\n private emitReportEvents(summary: ExecutionSummary, runId: string, timestamp: string): void {\n for (const req of summary.requests) {\n // Emit request:started (derived) before the finished/skipped event so that\n // consumers that track per-request lifecycle see a consistent start/finish pair.\n const reqStarted: RequestStartedEvent = {\n event: 'request:started',\n runId,\n timestamp,\n reliability: 'derived',\n requestName: req.requestName,\n requestFile: req.requestFile,\n };\n this.emit('request:started', reqStarted);\n\n if (req.status === 'skipped') {\n const skipped: RequestSkippedEvent = {\n event: 'request:skipped',\n runId,\n timestamp,\n reliability: 'derived',\n requestName: req.requestName,\n requestFile: req.requestFile,\n };\n this.emit('request:skipped', skipped);\n } else {\n const finished: RequestFinishedEvent = {\n event: 'request:finished',\n runId,\n timestamp,\n reliability: 'derived',\n requestName: req.requestName,\n requestFile: req.requestFile,\n status: req.status,\n ...(req.responseStatus !== undefined ? { responseStatus: req.responseStatus } : {}),\n ...(req.durationMs !== undefined ? { durationMs: req.durationMs } : {}),\n ...(req.error !== undefined ? { error: req.error } : {}),\n };\n this.emit('request:finished', finished);\n }\n\n for (const test of req.tests) {\n // Emit test:started (derived) immediately before test:finished so\n // consumers see a consistent start/finish pair per test.\n const testStarted: TestStartedEvent = {\n event: 'test:started',\n runId,\n timestamp,\n reliability: 'derived',\n requestName: req.requestName,\n testName: test.testName,\n };\n this.emit('test:started', testStarted);\n\n const testFinished: TestFinishedEvent = {\n event: 'test:finished',\n runId,\n timestamp,\n reliability: 'derived',\n requestName: req.requestName,\n testName: test.testName,\n status: test.status,\n ...(test.error !== undefined ? { error: test.error } : {}),\n };\n this.emit('test:finished', testFinished);\n }\n\n for (const a of req.assertions) {\n const assertionResult: AssertionResultEvent = {\n event: 'assertion:result',\n runId,\n timestamp,\n reliability: 'derived',\n requestName: req.requestName,\n assertion: a.assertion,\n passed: a.passed,\n ...(a.actual !== undefined ? { actual: a.actual } : {}),\n ...(a.expected !== undefined ? { expected: a.expected } : {}),\n ...(a.error !== undefined ? { error: a.error } : {}),\n };\n this.emit('assertion:result', assertionResult);\n }\n }\n }\n\n private buildArgs(config: AdapterRunConfig): string[] {\n const args: string[] = ['run', config.collectionPath];\n\n if (config.recursive) {\n args.push('-r');\n }\n if (config.env) {\n args.push('--env', config.env);\n }\n if (config.reporterJsonPath) {\n args.push('--reporter-json', config.reporterJsonPath);\n }\n if (config.extraArgs) {\n args.push(...config.extraArgs);\n }\n\n return args;\n }\n\n private buildEmptySummary(opts: {\n runId: string;\n collectionPath: string;\n startedAt: string;\n finishedAt: string;\n durationMs: number;\n exitCode: number;\n status: RunStatus;\n error?: ErrorDetail | undefined;\n requests?: RequestResult[] | undefined;\n }): ExecutionSummary {\n const requests = opts.requests ?? [];\n return {\n runId: opts.runId,\n collectionPath: opts.collectionPath,\n startedAt: opts.startedAt,\n finishedAt: opts.finishedAt,\n durationMs: opts.durationMs,\n exitCode: opts.exitCode,\n status: opts.status,\n totalRequests: requests.length,\n passedRequests: requests.filter((r) => r.status === 'finished').length,\n failedRequests: requests.filter((r) => r.status === 'failed').length,\n skippedRequests: requests.filter((r) => r.status === 'skipped').length,\n totalTests: 0,\n passedTests: 0,\n failedTests: 0,\n totalAssertions: 0,\n passedAssertions: 0,\n failedAssertions: 0,\n requests,\n ...(opts.error !== undefined ? { error: opts.error } : {}),\n };\n }\n\n private emit<T extends LifecycleEventName>(event: T, payload: EventPayload<T>): void {\n this.bus.emit(event, payload);\n }\n}\n","import { readFile } from 'node:fs/promises';\nimport type {\n AssertionResult,\n ErrorDetail,\n ExecutionSummary,\n RequestResult,\n RequestStatus,\n TestResult,\n TestStatus,\n} from '../domain/events.js';\nimport type { ReportParserContract } from '../domain/models.js';\nimport { nowIso } from '../shared/utils.js';\n\n// ---------------------------------------------------------------------------\n// Raw Bruno JSON report schema (best-effort, resilient to partial changes)\n//\n// The Bruno CLI writes a JSON file that is an *array* of iteration objects,\n// one per CSV/JSON iteration (typically just one for a standard run):\n//\n// [{ iterationIndex, iterationData, results, summary }, ...]\n//\n// Each element in `results` is the outcome of a single .bru request file.\n// ---------------------------------------------------------------------------\n\ninterface RawAssertionResult {\n lhsExpr?: string;\n rhsExpr?: string;\n status?: string;\n error?: string | null;\n}\n\ninterface RawTestResult {\n description?: string;\n status?: string;\n error?: string | null;\n}\n\ninterface RawResponse {\n status?: number | string;\n responseTime?: number;\n}\n\ninterface RawRequest {\n name?: string;\n test?: { filename?: string };\n status?: string;\n response?: RawResponse;\n error?: string | null;\n testResults?: RawTestResult[];\n assertionResults?: RawAssertionResult[];\n}\n\ninterface RawSummary {\n totalRequests?: number;\n passedRequests?: number;\n failedRequests?: number;\n skippedRequests?: number;\n totalTests?: number;\n passedTests?: number;\n failedTests?: number;\n totalAssertions?: number;\n passedAssertions?: number;\n failedAssertions?: number;\n}\n\ninterface RawIteration {\n iterationIndex?: number;\n results?: RawRequest[];\n summary?: RawSummary;\n}\n\ntype RawBrunoReport = RawIteration[];\n\n// ---------------------------------------------------------------------------\n// Parser implementation\n// ---------------------------------------------------------------------------\n\nexport class BrunoJsonReportParser implements ReportParserContract {\n async parse(reportPath: string): Promise<ExecutionSummary> {\n const raw = await this.readRaw(reportPath);\n return this.mapToSummary(raw);\n }\n\n private async readRaw(reportPath: string): Promise<RawBrunoReport> {\n let content: string;\n try {\n content = await readFile(reportPath, 'utf-8');\n } catch (err) {\n throw new Error(\n `BrunoJsonReportParser: cannot read report at \"${reportPath}\": ${String(err)}`,\n { cause: err },\n );\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(content);\n } catch (err) {\n throw new Error(\n `BrunoJsonReportParser: invalid JSON in report at \"${reportPath}\": ${String(err)}`,\n { cause: err },\n );\n }\n // Normalise report shape:\n // - Array of iterations → use as-is (canonical Bruno CLI v3.x shape).\n // - Plain object (e.g. older `{ summary, results }` format) → wrap into a\n // single-iteration array so downstream code always sees RawIteration[].\n // - Any other shape → fall back to [] so parsing never hard-crashes.\n if (Array.isArray(parsed)) {\n return parsed as RawBrunoReport;\n }\n\n if (parsed !== null && typeof parsed === 'object') {\n const obj = parsed as { results?: unknown; summary?: unknown; iterationIndex?: unknown };\n const results = Array.isArray(obj.results) ? (obj.results as RawRequest[]) : [];\n const summary =\n obj.summary !== null && typeof obj.summary === 'object'\n ? (obj.summary as RawSummary)\n : undefined;\n const iteration: RawIteration = {\n iterationIndex: typeof obj.iterationIndex === 'number' ? obj.iterationIndex : 0,\n results,\n ...(summary !== undefined ? { summary } : {}),\n };\n return [iteration];\n }\n\n return [];\n }\n\n private mapToSummary(raw: RawBrunoReport): ExecutionSummary {\n // Aggregate requests from every iteration (usually just one).\n const requests = raw.flatMap((iter) => (iter.results ?? []).map((r) => this.mapRequest(r)));\n // Use the first iteration's summary block when available.\n const summary = raw[0]?.summary ?? {};\n\n const totalRequests = summary.totalRequests ?? requests.length;\n const passedRequests =\n summary.passedRequests ?? requests.filter((r) => r.status === 'finished').length;\n const failedRequests =\n summary.failedRequests ?? requests.filter((r) => r.status === 'failed').length;\n const skippedRequests =\n summary.skippedRequests ?? requests.filter((r) => r.status === 'skipped').length;\n\n const allTests = requests.flatMap((r) => r.tests);\n const totalTests = summary.totalTests ?? allTests.length;\n const passedTests = summary.passedTests ?? allTests.filter((t) => t.status === 'passed').length;\n const failedTests = summary.failedTests ?? allTests.filter((t) => t.status === 'failed').length;\n\n const allAssertions = requests.flatMap((r) => r.assertions);\n const totalAssertions = summary.totalAssertions ?? allAssertions.length;\n const passedAssertions =\n summary.passedAssertions ?? allAssertions.filter((a) => a.passed).length;\n const failedAssertions =\n summary.failedAssertions ?? allAssertions.filter((a) => !a.passed).length;\n\n const now = nowIso();\n return {\n runId: '',\n collectionPath: '',\n startedAt: now,\n finishedAt: now,\n durationMs: 0,\n exitCode: 0,\n status: failedRequests > 0 ? 'failed' : 'finished',\n totalRequests,\n passedRequests,\n failedRequests,\n skippedRequests,\n totalTests,\n passedTests,\n failedTests,\n totalAssertions,\n passedAssertions,\n failedAssertions,\n requests,\n };\n }\n\n private mapRequest(raw: RawRequest): RequestResult {\n const tests = (raw.testResults ?? []).map((t) => this.mapTest(t));\n const assertions = (raw.assertionResults ?? []).map((a) => this.mapAssertion(a));\n const responseStatus =\n typeof raw.response?.status === 'number' ? raw.response.status : undefined;\n return {\n requestName: raw.name ?? raw.test?.filename ?? 'unknown',\n requestFile: raw.test?.filename ?? '',\n status: this.mapRequestStatus(raw.status),\n ...(responseStatus !== undefined ? { responseStatus } : {}),\n ...(raw.response?.responseTime !== undefined ? { durationMs: raw.response.responseTime } : {}),\n ...(raw.error ? { error: this.makeError(String(raw.error)) } : {}),\n tests,\n assertions,\n };\n }\n\n private mapTest(raw: RawTestResult): TestResult {\n return {\n testName: raw.description ?? 'unknown',\n status: this.mapTestStatus(raw.status),\n ...(raw.error ? { error: this.makeError(String(raw.error)) } : {}),\n };\n }\n\n private mapAssertion(raw: RawAssertionResult): AssertionResult {\n const passed = raw.status === 'pass' || raw.status === 'passed';\n const assertion =\n raw.lhsExpr !== undefined && raw.rhsExpr !== undefined\n ? `${raw.lhsExpr} ${raw.rhsExpr}`\n : 'unknown';\n return {\n assertion,\n passed,\n ...(raw.error ? { error: this.makeError(String(raw.error)) } : {}),\n };\n }\n\n private mapRequestStatus(status: string | undefined): RequestStatus {\n switch (status?.toLowerCase()) {\n case 'pass':\n case 'passed':\n case 'success':\n return 'finished';\n case 'fail':\n case 'failed':\n case 'error':\n return 'failed';\n case 'skip':\n case 'skipped':\n return 'skipped';\n default:\n return 'finished';\n }\n }\n\n private mapTestStatus(status: string | undefined): TestStatus {\n switch (status?.toLowerCase()) {\n case 'pass':\n case 'passed':\n return 'passed';\n case 'fail':\n case 'failed':\n return 'failed';\n case 'skip':\n case 'skipped':\n return 'skipped';\n default:\n return 'failed';\n }\n }\n\n private makeError(message: string): ErrorDetail {\n return { message };\n }\n}\n","import { randomUUID } from 'node:crypto';\n\nexport function generateRunId(): string {\n return randomUUID();\n}\n\nexport function nowIso(): string {\n return new Date().toISOString();\n}\n\nexport function elapsedMs(startedAt: string): number {\n return Date.now() - new Date(startedAt).getTime();\n}\n","import { readdir, readFile } from 'node:fs/promises';\nimport { join, relative } from 'node:path';\nimport type { Dirent } from 'node:fs';\n\n/**\n * A request file discovered inside a Bruno collection directory.\n */\nexport interface DiscoveredRequest {\n /** The human-readable request name, taken from the `meta { name: ... }` block. */\n requestName: string;\n /**\n * The path to the `.bru` file, relative to the collection root.\n * Uses forward slashes on all platforms.\n */\n requestFile: string;\n}\n\n/**\n * Scans a Bruno collection directory for HTTP request files (`.bru` files\n * whose `meta` block contains `type: http`).\n *\n * - The `environments/` directory is always excluded because it contains\n * variable definition files, not request files.\n * - Non-request `.bru` files (e.g. `type: collection`, `type: folder`)\n * are silently skipped.\n * - Any I/O error (missing directory, unreadable file, etc.) is caught\n * internally and results in an empty list — scanning is non-fatal.\n */\nexport class BruFileScanner {\n /**\n * Returns all HTTP request files found under `rootDir`.\n *\n * @param rootDir Absolute path to the collection root (the directory\n * passed to `bru run`).\n * @param recursive When `true`, descends into sub-directories. Mirrors\n * the `-r` flag behaviour of `bru run`.\n */\n async scan(rootDir: string, recursive: boolean): Promise<DiscoveredRequest[]> {\n const found = await this.collectBruFiles(rootDir, rootDir, recursive);\n // Sort by relative path so the order is stable and predictable.\n found.sort((a, b) => a.requestFile.localeCompare(b.requestFile));\n return found;\n }\n\n // ---------------------------------------------------------------------------\n // Private helpers\n // ---------------------------------------------------------------------------\n\n private async collectBruFiles(\n rootDir: string,\n dir: string,\n recursive: boolean,\n ): Promise<DiscoveredRequest[]> {\n let entries: Dirent[];\n try {\n entries = await readdir(dir, { withFileTypes: true });\n } catch {\n return [];\n }\n\n const results: DiscoveredRequest[] = [];\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n\n if (entry.isDirectory()) {\n // The `environments/` directory holds variable definition files — skip it.\n if (entry.name === 'environments') continue;\n if (recursive) {\n const nested = await this.collectBruFiles(rootDir, fullPath, true);\n results.push(...nested);\n }\n } else if (entry.isFile() && entry.name.endsWith('.bru')) {\n const discovered = await this.parseRequestFile(rootDir, fullPath);\n if (discovered !== null) {\n results.push(discovered);\n }\n }\n }\n\n return results;\n }\n\n private async parseRequestFile(\n rootDir: string,\n filePath: string,\n ): Promise<DiscoveredRequest | null> {\n let content: string;\n try {\n content = await readFile(filePath, 'utf-8');\n } catch {\n return null;\n }\n\n const meta = this.parseMetaBlock(content);\n\n // Exclude non-HTTP files (e.g. environment files use `type: collection`).\n // If there is no meta block at all, we conservatively skip the file.\n if (!meta.type || meta.type !== 'http') {\n return null;\n }\n\n const requestName = meta.name ?? this.nameFromPath(filePath);\n // Normalise to forward-slash separators regardless of OS.\n const requestFile = relative(rootDir, filePath).replace(/\\\\/g, '/');\n\n return { requestName, requestFile };\n }\n\n private parseMetaBlock(content: string): { name: string | undefined; type: string | undefined } {\n // Match `meta { ... }` — the `s` flag makes `.` match newlines.\n const metaMatch = content.match(/meta\\s*\\{([^}]*)\\}/s);\n if (!metaMatch) return { name: undefined, type: undefined };\n\n const block = metaMatch[1];\n const nameMatch = block.match(/\\bname:\\s*([^\\n]+)/);\n const typeMatch = block.match(/\\btype:\\s*([^\\n]+)/);\n\n return {\n name: nameMatch?.[1]?.trim(),\n type: typeMatch?.[1]?.trim(),\n };\n }\n\n private nameFromPath(filePath: string): string {\n const parts = filePath.replace(/\\\\/g, '/').split('/');\n return parts.at(-1)?.replace(/\\.bru$/, '') ?? 'unknown';\n }\n}\n","import type { EventPayload, LifecycleEventName } from '../domain/events.js';\n\nexport type EventHandler<T extends LifecycleEventName> = (payload: EventPayload<T>) => void;\n\ntype HandlerSet = Set<EventHandler<LifecycleEventName>>;\n\n/**\n * Strongly-typed event bus wrapping a plain Map of handler sets.\n * Does not extend Node's EventEmitter to remain platform-neutral.\n */\nexport class TypedEventBus {\n private readonly handlers = new Map<LifecycleEventName, HandlerSet>();\n\n on<T extends LifecycleEventName>(event: T, handler: EventHandler<T>): () => void {\n this.getOrCreate(event).add(handler as unknown as EventHandler<LifecycleEventName>);\n return () => this.off(event, handler);\n }\n\n once<T extends LifecycleEventName>(event: T, handler: EventHandler<T>): () => void {\n const wrapper: EventHandler<T> = (payload) => {\n this.off(event, wrapper);\n handler(payload);\n };\n return this.on(event, wrapper);\n }\n\n off<T extends LifecycleEventName>(event: T, handler: EventHandler<T>): void {\n this.handlers.get(event)?.delete(handler as unknown as EventHandler<LifecycleEventName>);\n }\n\n emit<T extends LifecycleEventName>(event: T, payload: EventPayload<T>): void {\n const set = this.handlers.get(event);\n if (!set) return;\n for (const handler of set) {\n handler(payload as EventPayload<LifecycleEventName>);\n }\n }\n\n removeAllListeners(event?: LifecycleEventName): void {\n if (event !== undefined) {\n this.handlers.delete(event);\n } else {\n this.handlers.clear();\n }\n }\n\n private getOrCreate(event: LifecycleEventName): HandlerSet {\n let set = this.handlers.get(event);\n if (!set) {\n set = new Set();\n this.handlers.set(event, set);\n }\n return set;\n }\n}\n"],"mappings":"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","names":["import_node_path","import_promises"]}
|