@tracelane/wdio 0.1.0-alpha.1

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 (48) hide show
  1. package/NOTICE +19 -0
  2. package/README.md +138 -0
  3. package/dist/framework-result.d.ts +21 -0
  4. package/dist/framework-result.d.ts.map +1 -0
  5. package/dist/framework-result.js +87 -0
  6. package/dist/framework-result.js.map +1 -0
  7. package/dist/hooks.d.ts +34 -0
  8. package/dist/hooks.d.ts.map +1 -0
  9. package/dist/hooks.js +56 -0
  10. package/dist/hooks.js.map +1 -0
  11. package/dist/index.d.ts +10 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +11 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/inpage-bundle.d.ts +9 -0
  16. package/dist/inpage-bundle.d.ts.map +1 -0
  17. package/dist/inpage-bundle.js +47 -0
  18. package/dist/inpage-bundle.js.map +1 -0
  19. package/dist/network-capture.d.ts +25 -0
  20. package/dist/network-capture.d.ts.map +1 -0
  21. package/dist/network-capture.js +56 -0
  22. package/dist/network-capture.js.map +1 -0
  23. package/dist/options.d.ts +36 -0
  24. package/dist/options.d.ts.map +1 -0
  25. package/dist/options.js +4 -0
  26. package/dist/options.js.map +1 -0
  27. package/dist/report-writer.d.ts +36 -0
  28. package/dist/report-writer.d.ts.map +1 -0
  29. package/dist/report-writer.js +56 -0
  30. package/dist/report-writer.js.map +1 -0
  31. package/dist/rrweb-bundle.js +141 -0
  32. package/dist/service.d.ts +44 -0
  33. package/dist/service.d.ts.map +1 -0
  34. package/dist/service.js +73 -0
  35. package/dist/service.js.map +1 -0
  36. package/dist/test-identity.d.ts +23 -0
  37. package/dist/test-identity.d.ts.map +1 -0
  38. package/dist/test-identity.js +15 -0
  39. package/dist/test-identity.js.map +1 -0
  40. package/dist/tracelane-session.d.ts +79 -0
  41. package/dist/tracelane-session.d.ts.map +1 -0
  42. package/dist/tracelane-session.js +189 -0
  43. package/dist/tracelane-session.js.map +1 -0
  44. package/dist/wdio-executor.d.ts +27 -0
  45. package/dist/wdio-executor.d.ts.map +1 -0
  46. package/dist/wdio-executor.js +39 -0
  47. package/dist/wdio-executor.js.map +1 -0
  48. package/package.json +56 -0
package/NOTICE ADDED
@@ -0,0 +1,19 @@
1
+ rrweb-stack
2
+ Copyright 2026 Cubenest
3
+
4
+ This product includes software developed by:
5
+ - PostHog (https://posthog.com) — vendored rrweb fork, bundled (via esbuild) into
6
+ the in-page recorder injected by this Service:
7
+ @posthog/rrweb 0.0.34 (MIT License)
8
+ @posthog/rrweb-types 0.0.24 (MIT License)
9
+ Forked from upstream rrweb-io/rrweb 2.0.0-alpha.17. Re-exported through
10
+ @cubenest/rrweb-core.
11
+ - rrweb contributors (https://github.com/rrweb-io/rrweb) — console-record plugin:
12
+ @rrweb/rrweb-plugin-console-record 2.0.0-alpha.20 (MIT License) — bundled into
13
+ the in-page recorder.
14
+
15
+ The rrweb player + report assets are inlined by @tracelane/report; see that
16
+ package's NOTICE.
17
+
18
+ This product is licensed under the Apache License, Version 2.0.
19
+ See the LICENSE file for details.
package/README.md ADDED
@@ -0,0 +1,138 @@
1
+ # @tracelane/wdio
2
+
3
+ A [WebdriverIO](https://webdriver.io) **Service** that records an [rrweb](https://www.rrweb.io)-grade session of your test run and, on a failed test, writes a **single self-contained `.html` report** to `./tracelane-reports/` — replay, console panel, and failed-network panel, all offline in one file.
4
+
5
+ ```ts
6
+ // wdio.conf.ts
7
+ import type { Options } from '@wdio/types';
8
+ import TraceLaneService from '@tracelane/wdio';
9
+
10
+ export const config: Options.Testrunner = {
11
+ runner: 'local',
12
+ framework: 'mocha',
13
+ specs: ['./test/specs/**/*.ts'],
14
+ capabilities: [
15
+ {
16
+ browserName: 'chrome',
17
+ 'goog:chromeOptions': { args: ['--remote-debugging-port=9222', '--no-sandbox'] },
18
+ },
19
+ ],
20
+ services: [
21
+ ['devtools', {}], // enables browser.cdp(...) for network capture — see "Network capture" below
22
+ [
23
+ TraceLaneService,
24
+ {
25
+ mode: process.env.TRACELANE_MODE ?? 'failed', // 'failed' | 'all'
26
+ outDir: './tracelane-reports',
27
+ capture: { rrweb: true, network: true, console: true },
28
+ },
29
+ ],
30
+ ],
31
+ reporters: ['spec'],
32
+ };
33
+ ```
34
+
35
+ Run your suite. On a failing Chrome+Mocha test you get `./tracelane-reports/<spec>--<title>--<cid>-<ts>.html` — open it in any browser, fully offline.
36
+
37
+ ## Install
38
+
39
+ ```sh
40
+ pnpm add -D @tracelane/wdio webdriverio @wdio/cli @wdio/local-runner @wdio/mocha-framework
41
+ ```
42
+
43
+ `webdriverio` and `@wdio/types` are **peer dependencies** (`^9.0.0`); you already have them in a WDIO project.
44
+
45
+ ## How it works
46
+
47
+ - On the first test, the Service injects an rrweb recorder bundle into the page and installs an in-page event buffer (`window.__tracelane__events`).
48
+ - The Node side drains that buffer on a poll (default every 5 s) and on every `afterTest`, re-injecting on navigation ([ADR-0006](https://github.com/Cubenest/rrweb-stack/blob/main/prds/adrs/0006-p1-in-page-buffer-node-polled.md)).
49
+ - In `failed` mode (default) a passing test discards its buffer; a failing test's buffer is handed to [`@tracelane/report`](https://github.com/Cubenest/rrweb-stack/tree/main/packages/tracelane-report), which builds the single offline HTML (≤ 25 MB, [ADR-0005](https://github.com/Cubenest/rrweb-stack/blob/main/prds/adrs/0005-p1-failed-only-self-contained-html.md)).
50
+
51
+ ## Options
52
+
53
+ | Option | Type | Default | Notes |
54
+ |---|---|---|---|
55
+ | `mode` | `'failed' \| 'all'` | `'failed'` | `failed` writes a report only on failure; `all` on every test. `TRACELANE_MODE` env var overrides this. |
56
+ | `outDir` | `string` | `'./tracelane-reports'` | Report output directory (created if missing). |
57
+ | `capture.rrweb` | `boolean` | `true` | Record the rrweb session. |
58
+ | `capture.network` | `boolean` | `true` | Route failed responses (`status >= 400`) into the console timeline via CDP. |
59
+ | `capture.console` | `boolean` | `true` | Capture `console.*` via the rrweb console plugin. Setting this `false` also drops the `[tracelane.net]` network-error lines, since `capture.network` surfaces them through `console.error`. |
60
+ | `drainIntervalMs` | `number` | `5000` | Node-side drain poll interval. |
61
+ | `cooldownMs` | `number` | `250` | Re-injection cooldown guard (suppresses double-init on hash/HMR navigation). |
62
+ | `allure` | `boolean` | `false` | Reserved for the v1.1 Allure shim. No-op in v1. |
63
+ | `visualDiff` | `boolean` | `false` | Reserved for the post-MVP visual-diff add-on. No-op in v1. |
64
+
65
+ The options type is published — `import type { TraceLaneOptions } from '@tracelane/wdio'`.
66
+
67
+ ## Hook-factory alternative (no Service)
68
+
69
+ For setups that can't register a Service, the **same logic** is available as plain `wdio.conf.ts` hook functions ([ADR-0004](https://github.com/Cubenest/rrweb-stack/blob/main/prds/adrs/0004-p1-wdio-service-not-reporter.md)):
70
+
71
+ ```ts
72
+ import { traceLaneHooks } from '@tracelane/wdio/hooks';
73
+
74
+ const tracelane = traceLaneHooks({ mode: 'failed', outDir: './tracelane-reports' });
75
+
76
+ export const config: Options.Testrunner = {
77
+ // ...
78
+ beforeSession: tracelane.beforeSession,
79
+ before: tracelane.before,
80
+ beforeSuite: tracelane.beforeSuite,
81
+ beforeTest: tracelane.beforeTest,
82
+ beforeCommand: tracelane.beforeCommand, // re-injection on navigation
83
+ afterTest: tracelane.afterTest,
84
+ afterSuite: tracelane.afterSuite,
85
+ after: tracelane.after,
86
+ onComplete: tracelane.onComplete,
87
+ };
88
+ ```
89
+
90
+ ## FAQ — Why is this a Service and not a Reporter?
91
+
92
+ Because only a **Service** can do what `tracelane` needs ([ADR-0004](https://github.com/Cubenest/rrweb-stack/blob/main/prds/adrs/0004-p1-wdio-service-not-reporter.md)):
93
+
94
+ | | Service (`Services.ServiceInstance`) | Reporter (`@wdio/reporter`) |
95
+ |---|---|---|
96
+ | Access to live `browser` in worker hooks | **Yes** | No — reporters run in the launcher process |
97
+ | `browser.execute(...)` to inject rrweb / drain the buffer | **Yes** | No |
98
+ | `browser.cdp(...)` for network capture | **Yes** | No |
99
+
100
+ A Reporter receives only serialized lifecycle events (`testFail`, `testEnd`, …) in the launcher process — it has no page handle, so it can't inject rrweb, drain the in-page buffer, or attach CDP. (`wdio-allure-reporter` hit exactly this wall and had to add a runtime-side `addAttachment` helper to bridge browser access.) A paired Allure **Reporter** shim is planned for v1.1 to push `tracelane` artifacts into Allure's `addAttachment` API — that's a Reporter-shaped problem; capture is not.
101
+
102
+ ## Network capture
103
+
104
+ Failed responses (`status >= 400`) are routed into the report's console timeline (prefixed `[tracelane.net]`, [P1 PRD §E.2](https://github.com/Cubenest/rrweb-stack/blob/main/prds/compass_artifact_wf-d53d32da-17e9-41b5-bb70-21dd1bf648c6_text_markdown.md)). This needs `browser.cdp(...)`, which a separate WDIO service provides:
105
+
106
+ - **WDIO 8:** add `['devtools', {}]` via `@wdio/devtools-service@8`.
107
+ - **WDIO 9:** `@wdio/devtools-service` has no stable v9 line (it stabilized at v10); use the v10 service or a CDP-capable session. **If `browser.cdp` is unavailable, `tracelane` degrades gracefully to rrweb + console capture** — the report is still produced, just without the network panel.
108
+
109
+ Cloud Selenium vendors typically don't expose CDP; rrweb + console capture still work there.
110
+
111
+ ## Supported runners / browsers
112
+
113
+ | Runner | Status |
114
+ |---|---|
115
+ | Mocha | **Supported** (primary) |
116
+ | Jasmine | Supported (same `afterTest` result shape) |
117
+ | Cucumber | Supported via `beforeScenario`/`afterScenario` |
118
+
119
+ | Browser | rrweb + console | Network (CDP) |
120
+ |---|---|---|
121
+ | Chrome / Chromium ≥ 116 | **Yes** | Yes (with a CDP-capable session) |
122
+ | Edge (Chromium) | Yes | Yes |
123
+ | Firefox | Yes | No (CDP is Chromium-only) |
124
+ | Safari | Yes | No |
125
+
126
+ ## Versioning
127
+
128
+ Semantic Versioning. Currently `0.1.0-alpha.0` (pre-release; the API may shift before `1.0.0`).
129
+
130
+ ## Telemetry
131
+
132
+ None. `tracelane` collects and sends nothing; reports are written to your local `outDir` only.
133
+
134
+ ## License
135
+
136
+ Apache 2.0. The bundled rrweb engine + console plugin remain MIT-licensed; see `NOTICE`.
137
+
138
+ Contributions are accepted under the [Developer Certificate of Origin (DCO)](https://developercertificate.org/) — sign your commits with `git commit -s`.
@@ -0,0 +1,21 @@
1
+ import type { ReportStatus } from '@tracelane/report';
2
+ /** The WDIO test frameworks tracelane recognizes (P1 PRD §A.2). */
3
+ export type Framework = 'mocha' | 'jasmine' | 'cucumber';
4
+ /** Framework-neutral outcome derived from a per-framework result object. */
5
+ export interface NormalizedResult {
6
+ /** Whether the test/scenario passed. */
7
+ passed: boolean;
8
+ /** Report status string for the metadata header. */
9
+ status: ReportStatus;
10
+ /** Failure message, when failed/broken. */
11
+ error?: string;
12
+ /** Duration in milliseconds, when the framework reported one. */
13
+ durationMs?: number;
14
+ }
15
+ /**
16
+ * Normalize a test outcome across frameworks (P1 PRD §A.2). Switch on the
17
+ * `framework` from `wdio.conf`. The two positional args map to the hook
18
+ * signatures: Mocha/Jasmine call with `(result)`; Cucumber with `(world, result)`.
19
+ */
20
+ export declare function normalizeResult(framework: Framework | string | undefined, a: unknown, b?: unknown): NormalizedResult;
21
+ //# sourceMappingURL=framework-result.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"framework-result.d.ts","sourceRoot":"","sources":["../src/framework-result.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD,mEAAmE;AACnE,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,UAAU,CAAC;AAEzD,4EAA4E;AAC5E,MAAM,WAAW,gBAAgB;IAC/B,wCAAwC;IACxC,MAAM,EAAE,OAAO,CAAC;IAChB,oDAAoD;IACpD,MAAM,EAAE,YAAY,CAAC;IACrB,2CAA2C;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AA+FD;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,SAAS,GAAG,MAAM,GAAG,SAAS,EACzC,CAAC,EAAE,OAAO,EACV,CAAC,CAAC,EAAE,OAAO,GACV,gBAAgB,CAMlB"}
@@ -0,0 +1,87 @@
1
+ // Normalize the per-framework test-result shape (P1 PRD §A.2).
2
+ //
3
+ // Mocha & Jasmine deliver `afterTest(test, context, result)` where
4
+ // `result = { error?, result?, duration, passed, retries }`. Cucumber has no
5
+ // `afterTest`; it delivers `afterScenario(world, result)` where the outcome
6
+ // lives on a `World` object (`world.result.status` / `.message`) and/or a
7
+ // `PickleResult` (`{ passed, error, duration }`). We switch on the configured
8
+ // framework and collapse all of them to one neutral shape the report builder
9
+ // consumes (ReportStatus + error string + duration).
10
+ /** Coerce an unknown error-ish value into a single-line message string. */
11
+ function errorMessage(error) {
12
+ if (error === undefined || error === null)
13
+ return undefined;
14
+ if (typeof error === 'string')
15
+ return error;
16
+ if (error instanceof Error)
17
+ return error.stack ?? error.message;
18
+ const message = error.message;
19
+ if (typeof message === 'string')
20
+ return message;
21
+ const stack = error.stack;
22
+ if (typeof stack === 'string')
23
+ return stack;
24
+ return String(error);
25
+ }
26
+ /** Build a NormalizedResult, mapping `passed`/`skipped` to a ReportStatus. */
27
+ function fromPassed(passed, opts) {
28
+ const status = opts.skipped
29
+ ? 'skipped'
30
+ : passed
31
+ ? 'passed'
32
+ : // A failure with an assertion message is 'failed'; one without (e.g. a
33
+ // thrown non-assertion error) is 'broken' in the Allure-style taxonomy.
34
+ opts.error
35
+ ? 'failed'
36
+ : 'broken';
37
+ const result = { passed, status };
38
+ if (opts.error !== undefined)
39
+ result.error = opts.error;
40
+ if (opts.durationMs !== undefined)
41
+ result.durationMs = opts.durationMs;
42
+ return result;
43
+ }
44
+ /** Normalize a Mocha/Jasmine `afterTest` result. */
45
+ function normalizeMochaJasmine(result) {
46
+ const passed = result.passed === true;
47
+ return fromPassed(passed, {
48
+ error: errorMessage(result.error),
49
+ durationMs: typeof result.duration === 'number' ? result.duration : undefined,
50
+ skipped: result.skipped === true,
51
+ });
52
+ }
53
+ /**
54
+ * Normalize a Cucumber outcome. Cucumber's `afterScenario(world, result)` passes
55
+ * both a `World` (rich `result.status`/`message`) and a `PickleResult`
56
+ * (`{ passed, error }`); we read whichever is populated, preferring the explicit
57
+ * `PickleResult.passed` and falling back to `World.result.status`.
58
+ */
59
+ function normalizeCucumber(world, result) {
60
+ const cukeStatus = world.result?.status?.toUpperCase();
61
+ const skipped = cukeStatus === 'SKIPPED' || cukeStatus === 'PENDING';
62
+ // Prefer the PickleResult.passed flag; otherwise derive from the World status.
63
+ const passed = result?.passed !== undefined ? result.passed === true : cukeStatus === 'PASSED';
64
+ const durationMs = world.result?.duration
65
+ ? world.result.duration.seconds * 1000 + world.result.duration.nanos / 1e6
66
+ : typeof result?.duration === 'number'
67
+ ? result.duration
68
+ : undefined;
69
+ return fromPassed(passed, {
70
+ error: result?.error ?? world.result?.message,
71
+ durationMs,
72
+ skipped,
73
+ });
74
+ }
75
+ /**
76
+ * Normalize a test outcome across frameworks (P1 PRD §A.2). Switch on the
77
+ * `framework` from `wdio.conf`. The two positional args map to the hook
78
+ * signatures: Mocha/Jasmine call with `(result)`; Cucumber with `(world, result)`.
79
+ */
80
+ export function normalizeResult(framework, a, b) {
81
+ if (framework === 'cucumber') {
82
+ return normalizeCucumber((a ?? {}), b);
83
+ }
84
+ // mocha, jasmine, and any unknown framework all use the afterTest result shape.
85
+ return normalizeMochaJasmine((a ?? {}));
86
+ }
87
+ //# sourceMappingURL=framework-result.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"framework-result.js","sourceRoot":"","sources":["../src/framework-result.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import type { Frameworks } from '@wdio/types';
2
+ import type { TraceLaneOptions } from './options.js';
3
+ /** Options for {@link traceLaneHooks}. Same shape as the Service options. */
4
+ export interface TraceLaneHookOptions extends TraceLaneOptions {
5
+ /**
6
+ * Test framework, so the result-shape switch (P1 PRD §A.2) is correct. The
7
+ * Service reads this from `config.framework`; the hook factory has no config
8
+ * arg, so pass it here (default `'mocha'`).
9
+ */
10
+ framework?: string;
11
+ }
12
+ /** The bound hook functions returned by {@link traceLaneHooks}. */
13
+ export interface TraceLaneHooks {
14
+ beforeSession(config: unknown, capabilities: unknown, specs: string[], cid?: string): void;
15
+ before(capabilities: unknown, specs: string[], browser: WebdriverIO.Browser): Promise<void>;
16
+ beforeSuite(suite: Frameworks.Suite): void;
17
+ beforeTest(test: Frameworks.Test, context?: unknown): Promise<void>;
18
+ beforeCommand(commandName: string, args: unknown[]): Promise<void>;
19
+ afterTest(test: Frameworks.Test, context: unknown, result: Frameworks.TestResult): Promise<void>;
20
+ afterSuite(suite: Frameworks.Suite): void;
21
+ after(result: number, capabilities: unknown, specs: string[]): Promise<void>;
22
+ onComplete(exitCode: number, config: unknown, capabilities: unknown, results: unknown): void;
23
+ /** Cucumber scenario start (mirrors the Service; #3). */
24
+ beforeScenario(world: unknown, context?: unknown): Promise<void>;
25
+ /** Cucumber scenario end (mirrors the Service; #3). */
26
+ afterScenario(world: unknown, result?: unknown): Promise<void>;
27
+ }
28
+ /**
29
+ * Build a set of WDIO hook functions wired to one TraceLaneSession (P1 PRD §M.2).
30
+ * Returns the same logic the Service runs — just as standalone hooks, including
31
+ * the Cucumber `beforeScenario`/`afterScenario` pair (#3).
32
+ */
33
+ export declare function traceLaneHooks(options?: TraceLaneHookOptions): TraceLaneHooks;
34
+ //# sourceMappingURL=hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAKrD,6EAA6E;AAC7E,MAAM,WAAW,oBAAqB,SAAQ,gBAAgB;IAC5D;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,mEAAmE;AACnE,MAAM,WAAW,cAAc;IAC7B,aAAa,CAAC,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3F,MAAM,CAAC,YAAY,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,WAAW,CAAC,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5F,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,KAAK,GAAG,IAAI,CAAC;IAC3C,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnE,SAAS,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjG,UAAU,CAAC,KAAK,EAAE,UAAU,CAAC,KAAK,GAAG,IAAI,CAAC;IAC1C,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IAC7F,yDAAyD;IACzD,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,uDAAuD;IACvD,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,GAAE,oBAAyB,GAAG,cAAc,CAoCjF"}
package/dist/hooks.js ADDED
@@ -0,0 +1,56 @@
1
+ // traceLaneHooks — the hook-factory alternative to the Service (Task 2.15 /
2
+ // ADR-0004 / P1 PRD §M.2).
3
+ //
4
+ // For users who can't (or don't want to) register a Service, this returns the
5
+ // same capture logic as plain `wdio.conf.ts` hook functions wired to one shared
6
+ // TraceLaneSession:
7
+ //
8
+ // const tracelane = traceLaneHooks({ mode: 'failed', outDir: './tracelane-reports' })
9
+ // export const config = { before: tracelane.before, afterTest: tracelane.afterTest, ... }
10
+ //
11
+ // It is published at the `@tracelane/wdio/hooks` subpath so the Service stays the
12
+ // default, discoverable surface.
13
+ import { scenarioIdentity, testIdentity } from './test-identity.js';
14
+ import { TraceLaneSession } from './tracelane-session.js';
15
+ /**
16
+ * Build a set of WDIO hook functions wired to one TraceLaneSession (P1 PRD §M.2).
17
+ * Returns the same logic the Service runs — just as standalone hooks, including
18
+ * the Cucumber `beforeScenario`/`afterScenario` pair (#3).
19
+ */
20
+ export function traceLaneHooks(options = {}) {
21
+ const session = new TraceLaneSession(options, options.framework ?? 'mocha');
22
+ return {
23
+ beforeSession(_config, _capabilities, _specs, cid) {
24
+ session.setCid(cid);
25
+ },
26
+ async before(_capabilities, _specs, browser) {
27
+ await session.onBefore(browser);
28
+ },
29
+ beforeSuite(_suite) { },
30
+ async beforeTest(test, _context) {
31
+ const { title, spec } = testIdentity(test);
32
+ await session.onBeforeTest(title, spec);
33
+ },
34
+ async beforeCommand(commandName, args) {
35
+ if (commandName === 'url' && typeof args[0] === 'string') {
36
+ await session.onUrl(args[0]);
37
+ }
38
+ },
39
+ async afterTest(_test, _context, result) {
40
+ await session.onAfterTest(result);
41
+ },
42
+ afterSuite(_suite) { },
43
+ async after(_result, _capabilities, _specs) {
44
+ await session.onAfter();
45
+ },
46
+ onComplete(_exitCode, _config, _capabilities, _results) { },
47
+ async beforeScenario(world, _context) {
48
+ const { title, spec } = scenarioIdentity(world);
49
+ await session.onBeforeTest(title, spec);
50
+ },
51
+ async afterScenario(world, result) {
52
+ await session.onAfterTest(world, result);
53
+ },
54
+ };
55
+ }
56
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,2BAA2B;AAC3B,EAAE;AACF,8EAA8E;AAC9E,gFAAgF;AAChF,oBAAoB;AACpB,EAAE;AACF,wFAAwF;AACxF,4FAA4F;AAC5F,EAAE;AACF,kFAAkF;AAClF,iCAAiC;AAIjC,OAAO,EAAiB,gBAAgB,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACnF,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AA8B1D;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,UAAgC,EAAE;IAC/D,MAAM,OAAO,GAAG,IAAI,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,CAAC;IAE5E,OAAO;QACL,aAAa,CAAC,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG;YAC/C,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACtB,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,EAAE,OAAO;YACzC,MAAM,OAAO,CAAC,QAAQ,CAAC,OAAiC,CAAC,CAAC;QAC5D,CAAC;QACD,WAAW,CAAC,MAAM,IAAG,CAAC;QACtB,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,QAAQ;YAC7B,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,YAAY,CAAC,IAAgB,CAAC,CAAC;YACvD,MAAM,OAAO,CAAC,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAC1C,CAAC;QACD,KAAK,CAAC,aAAa,CAAC,WAAW,EAAE,IAAI;YACnC,IAAI,WAAW,KAAK,KAAK,IAAI,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;gBACzD,MAAM,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YAC/B,CAAC;QACH,CAAC;QACD,KAAK,CAAC,SAAS,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM;YACrC,MAAM,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QACpC,CAAC;QACD,UAAU,CAAC,MAAM,IAAG,CAAC;QACrB,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,aAAa,EAAE,MAAM;YACxC,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;QAC1B,CAAC;QACD,UAAU,CAAC,SAAS,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,IAAG,CAAC;QAC1D,KAAK,CAAC,cAAc,CAAC,KAAK,EAAE,QAAQ;YAClC,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;YAChD,MAAM,OAAO,CAAC,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAC1C,CAAC;QACD,KAAK,CAAC,aAAa,CAAC,KAAK,EAAE,MAAM;YAC/B,MAAM,OAAO,CAAC,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAC3C,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,10 @@
1
+ export { default, default as TraceLaneService } from './service.js';
2
+ export { traceLaneHooks } from './hooks.js';
3
+ export type { TraceLaneHookOptions, TraceLaneHooks } from './hooks.js';
4
+ export type { CaptureOptions, TraceLaneOptions } from './options.js';
5
+ export { DEFAULT_OUT_DIR } from './options.js';
6
+ export { createWdioExecutor } from './wdio-executor.js';
7
+ export type { WdioBrowser } from './wdio-executor.js';
8
+ export { normalizeResult } from './framework-result.js';
9
+ export type { Framework, NormalizedResult } from './framework-result.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGpE,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,YAAY,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGvE,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAG/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAGtD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,YAAY,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ // Public API surface for @tracelane/wdio.
2
+ // The WebdriverIO Service — the default, recommended surface (ADR-0004, P1 PRD §M.1).
3
+ export { default, default as TraceLaneService } from './service.js';
4
+ // Hook-factory alternative (also published at `@tracelane/wdio/hooks`).
5
+ export { traceLaneHooks } from './hooks.js';
6
+ export { DEFAULT_OUT_DIR } from './options.js';
7
+ // The BrowserExecutor adapter (advanced use / custom integrations).
8
+ export { createWdioExecutor } from './wdio-executor.js';
9
+ // Re-export the framework result-shape switch + the recognized framework union.
10
+ export { normalizeResult } from './framework-result.js';
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAE1C,sFAAsF;AACtF,OAAO,EAAE,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEpE,wEAAwE;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAK5C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,oEAAoE;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAGxD,gFAAgF;AAChF,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The rrweb in-page bundle source (defines `window.rrweb`). Read from
3
+ * `dist/rrweb-bundle.js`, cached after first read.
4
+ *
5
+ * @throws if the bundle is missing — that means the package was used without its
6
+ * build step (`pnpm --filter @tracelane/wdio build`) having run.
7
+ */
8
+ export declare function loadRrwebBundle(): string;
9
+ //# sourceMappingURL=inpage-bundle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inpage-bundle.d.ts","sourceRoot":"","sources":["../src/inpage-bundle.ts"],"names":[],"mappings":"AAgCA;;;;;;GAMG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAWxC"}
@@ -0,0 +1,47 @@
1
+ // Loads the in-page rrweb bundle source string.
2
+ //
3
+ // The bundle (`dist/rrweb-bundle.js`) is produced at build time by
4
+ // `scripts/build-rrweb-bundle.mjs` (esbuild → IIFE that defines `window.rrweb`
5
+ // with `record` + `getRecordConsolePlugin`). @tracelane/core's recorder is
6
+ // bundle-source-agnostic (ADR-0006) and expects this source as a plain string,
7
+ // which it `window.eval`s in the page on every (re-)injection.
8
+ //
9
+ // We read it once and cache it. In the published package this module compiles
10
+ // to `dist/inpage-bundle.js`, sitting next to `dist/rrweb-bundle.js`; under
11
+ // vitest the source lives in `src/`, so we also probe a sibling `../dist/`. We
12
+ // derive the module directory from `fileURLToPath(import.meta.url)` directly
13
+ // (not via `new URL(rel, import.meta.url)`) because under the jsdom test env the
14
+ // global `URL` resolves relative inputs against the page origin, not the file.
15
+ import { existsSync, readFileSync } from 'node:fs';
16
+ import { dirname, join } from 'node:path';
17
+ import { fileURLToPath } from 'node:url';
18
+ let cached;
19
+ /** Candidate locations for the built bundle, in priority order. */
20
+ function candidatePaths() {
21
+ const moduleDir = dirname(fileURLToPath(import.meta.url));
22
+ return [
23
+ // Published layout: dist/inpage-bundle.js → dist/rrweb-bundle.js.
24
+ join(moduleDir, 'rrweb-bundle.js'),
25
+ // Source/test layout: src/inpage-bundle.ts → ../dist/rrweb-bundle.js.
26
+ join(moduleDir, '..', 'dist', 'rrweb-bundle.js'),
27
+ ];
28
+ }
29
+ /**
30
+ * The rrweb in-page bundle source (defines `window.rrweb`). Read from
31
+ * `dist/rrweb-bundle.js`, cached after first read.
32
+ *
33
+ * @throws if the bundle is missing — that means the package was used without its
34
+ * build step (`pnpm --filter @tracelane/wdio build`) having run.
35
+ */
36
+ export function loadRrwebBundle() {
37
+ if (cached !== undefined)
38
+ return cached;
39
+ const candidates = candidatePaths();
40
+ const found = candidates.find((p) => existsSync(p));
41
+ if (found === undefined) {
42
+ throw new Error(`@tracelane/wdio: in-page rrweb bundle not found (looked in ${candidates.join(', ')}). Run the package build (\`pnpm --filter @tracelane/wdio build\`) to generate it.`);
43
+ }
44
+ cached = readFileSync(found, 'utf8');
45
+ return cached;
46
+ }
47
+ //# sourceMappingURL=inpage-bundle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inpage-bundle.js","sourceRoot":"","sources":["../src/inpage-bundle.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,EAAE;AACF,mEAAmE;AACnE,+EAA+E;AAC/E,2EAA2E;AAC3E,+EAA+E;AAC/E,+DAA+D;AAC/D,EAAE;AACF,8EAA8E;AAC9E,4EAA4E;AAC5E,+EAA+E;AAC/E,6EAA6E;AAC7E,iFAAiF;AACjF,+EAA+E;AAE/E,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,IAAI,MAA0B,CAAC;AAE/B,mEAAmE;AACnE,SAAS,cAAc;IACrB,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1D,OAAO;QACL,kEAAkE;QAClE,IAAI,CAAC,SAAS,EAAE,iBAAiB,CAAC;QAClC,sEAAsE;QACtE,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,iBAAiB,CAAC;KACjD,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe;IAC7B,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IACxC,MAAM,UAAU,GAAG,cAAc,EAAE,CAAC;IACpC,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IACpD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,8DAA8D,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,oFAAoF,CACxK,CAAC;IACJ,CAAC;IACD,MAAM,GAAG,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACrC,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,25 @@
1
+ import type { BrowserExecutor } from '@tracelane/core';
2
+ /**
3
+ * Page-side logger. Self-contained (no Node closures) so it can be
4
+ * `.toString()`-serialized by `execute` (PRD §A.4). The console plugin captures
5
+ * this `console.error`; the `[tracelane.net]` prefix lets the report's network
6
+ * panel scrape it back out (PRD §E.2).
7
+ */
8
+ declare function logNetworkErrorInPage(url: string, status: number, method: string): void;
9
+ /** Pull the request method out of CDP request headers, defaulting to GET. */
10
+ declare function methodOf(headers: Record<string, string> | undefined): string;
11
+ /**
12
+ * Attach CDP network capture to a BrowserExecutor (P1 PRD §E.2).
13
+ *
14
+ * Enables the Network domain and registers a `Network.responseReceived`
15
+ * subscriber that forwards 4xx/5xx responses into `console.error`. Resolves once
16
+ * `Network.enable` has been sent. The subscriber's own `execute` calls are
17
+ * fire-and-forget (their failures must not break the test).
18
+ */
19
+ export declare function attachNetworkCapture(executor: BrowserExecutor): Promise<void>;
20
+ export declare const __internal: {
21
+ logNetworkErrorInPage: typeof logNetworkErrorInPage;
22
+ methodOf: typeof methodOf;
23
+ };
24
+ export {};
25
+ //# sourceMappingURL=network-capture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network-capture.d.ts","sourceRoot":"","sources":["../src/network-capture.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAWvD;;;;;GAKG;AACH,iBAAS,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAEhF;AAED,6EAA6E;AAC7E,iBAAS,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,GAAG,MAAM,CAKrE;AAED;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBnF;AAGD,eAAO,MAAM,UAAU;;;CAAsC,CAAC"}
@@ -0,0 +1,56 @@
1
+ // CDP network capture wiring (Task 2.16 / P1 PRD §E.2).
2
+ //
3
+ // Enable the CDP Network domain, subscribe to `Network.responseReceived`, and
4
+ // route any response with `status >= 400` into the page's `console.error` via
5
+ // `executor.execute`. The rrweb console plugin (installed by @tracelane/core's
6
+ // recorder) then captures that console line, so failed responses show up in the
7
+ // report's console panel "for free" — no dedicated network transport in v1.
8
+ //
9
+ // The console line is prefixed `[tracelane.net]` so @tracelane/report's network
10
+ // panel can scrape it back out (NETWORK_CONSOLE_PREFIX in panels.ts).
11
+ /**
12
+ * Page-side logger. Self-contained (no Node closures) so it can be
13
+ * `.toString()`-serialized by `execute` (PRD §A.4). The console plugin captures
14
+ * this `console.error`; the `[tracelane.net]` prefix lets the report's network
15
+ * panel scrape it back out (PRD §E.2).
16
+ */
17
+ function logNetworkErrorInPage(url, status, method) {
18
+ console.error(`[tracelane.net] ${method} ${status} ${url}`);
19
+ }
20
+ /** Pull the request method out of CDP request headers, defaulting to GET. */
21
+ function methodOf(headers) {
22
+ if (!headers)
23
+ return 'GET';
24
+ // CDP exposes the pseudo-header `:method` for HTTP/2/3; fall back to a plain
25
+ // `method` header, then GET.
26
+ return headers[':method'] ?? headers.method ?? 'GET';
27
+ }
28
+ /**
29
+ * Attach CDP network capture to a BrowserExecutor (P1 PRD §E.2).
30
+ *
31
+ * Enables the Network domain and registers a `Network.responseReceived`
32
+ * subscriber that forwards 4xx/5xx responses into `console.error`. Resolves once
33
+ * `Network.enable` has been sent. The subscriber's own `execute` calls are
34
+ * fire-and-forget (their failures must not break the test).
35
+ */
36
+ export async function attachNetworkCapture(executor) {
37
+ await executor.cdp('Network', 'enable');
38
+ executor.on('Network.responseReceived', (params) => {
39
+ const response = params?.response;
40
+ const status = response?.status;
41
+ if (typeof status !== 'number' || status < 400)
42
+ return;
43
+ const url = response?.url ?? '';
44
+ const method = methodOf(response?.requestHeaders);
45
+ // Fire-and-forget: a logging failure (e.g. page mid-navigation) must not
46
+ // surface as a test error.
47
+ void executor
48
+ .execute(logNetworkErrorInPage, url, status, method)
49
+ .catch(() => {
50
+ /* page may be navigating; drop this one line */
51
+ });
52
+ });
53
+ }
54
+ // Exposed for unit tests: the page-side logger + method resolver are pure.
55
+ export const __internal = { logNetworkErrorInPage, methodOf };
56
+ //# sourceMappingURL=network-capture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network-capture.js","sourceRoot":"","sources":["../src/network-capture.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,EAAE;AACF,8EAA8E;AAC9E,8EAA8E;AAC9E,+EAA+E;AAC/E,gFAAgF;AAChF,4EAA4E;AAC5E,EAAE;AACF,gFAAgF;AAChF,sEAAsE;AAatE;;;;;GAKG;AACH,SAAS,qBAAqB,CAAC,GAAW,EAAE,MAAc,EAAE,MAAc;IACxE,OAAO,CAAC,KAAK,CAAC,mBAAmB,MAAM,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC,CAAC;AAC9D,CAAC;AAED,6EAA6E;AAC7E,SAAS,QAAQ,CAAC,OAA2C;IAC3D,IAAI,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IAC3B,6EAA6E;IAC7E,6BAA6B;IAC7B,OAAO,OAAO,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,MAAM,IAAI,KAAK,CAAC;AACvD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,QAAyB;IAClE,MAAM,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACxC,QAAQ,CAAC,EAAE,CAAC,0BAA0B,EAAE,CAAC,MAAe,EAAE,EAAE;QAC1D,MAAM,QAAQ,GAAI,MAAgC,EAAE,QAAQ,CAAC;QAC7D,MAAM,MAAM,GAAG,QAAQ,EAAE,MAAM,CAAC;QAChC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,GAAG,GAAG;YAAE,OAAO;QACvD,MAAM,GAAG,GAAG,QAAQ,EAAE,GAAG,IAAI,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;QAClD,yEAAyE;QACzE,2BAA2B;QAC3B,KAAK,QAAQ;aACV,OAAO,CAAC,qBAAqD,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC;aACnF,KAAK,CAAC,GAAG,EAAE;YACV,gDAAgD;QAClD,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;AACL,CAAC;AAED,2EAA2E;AAC3E,MAAM,CAAC,MAAM,UAAU,GAAG,EAAE,qBAAqB,EAAE,QAAQ,EAAE,CAAC"}
@@ -0,0 +1,36 @@
1
+ import type { ConsolePluginOptions, Mode } from '@tracelane/core';
2
+ /** Which capture channels are enabled (P1 PRD §M.1). */
3
+ export interface CaptureOptions {
4
+ /** Record the rrweb session. Default true. */
5
+ rrweb?: boolean;
6
+ /** Attach CDP and route failed responses into the console timeline. Default true. */
7
+ network?: boolean;
8
+ /** Capture `console.*` via the rrweb console plugin. Default true. */
9
+ console?: boolean;
10
+ }
11
+ /** Options for {@link TraceLaneService} and {@link traceLaneHooks} (P1 PRD §M.1). */
12
+ export interface TraceLaneOptions {
13
+ /**
14
+ * Capture mode (ADR-0005). `'failed'` (default) writes a report only on test
15
+ * failure; `'all'` writes one for every test. The `TRACELANE_MODE` env var
16
+ * overrides this at report-decision time.
17
+ */
18
+ mode?: Mode;
19
+ /** Directory to write reports into. Default `'./tracelane-reports'`. */
20
+ outDir?: string;
21
+ /** Reserved for the v1.1 Allure shim (ADR-0004). No-op in v1. Default false. */
22
+ allure?: boolean;
23
+ /** Per-channel capture toggles. */
24
+ capture?: CaptureOptions;
25
+ /** Reserved for the post-MVP visual-diff add-on (P1 PRD §H). No-op in v1. */
26
+ visualDiff?: boolean;
27
+ /** Node-side drain poll interval in ms (ADR-0006). Default 5000. */
28
+ drainIntervalMs?: number;
29
+ /** Re-injection cooldown guard in ms (ADR-0006). Default 250. */
30
+ cooldownMs?: number;
31
+ /** Options forwarded to the in-page rrweb console plugin. */
32
+ consolePluginOptions?: ConsolePluginOptions;
33
+ }
34
+ /** The default output directory for reports. */
35
+ export declare const DEFAULT_OUT_DIR = "./tracelane-reports";
36
+ //# sourceMappingURL=options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAElE,wDAAwD;AACxD,MAAM,WAAW,cAAc;IAC7B,8CAA8C;IAC9C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,qFAAqF;IACrF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,sEAAsE;IACtE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,qFAAqF;AACrF,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,wEAAwE;IACxE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,mCAAmC;IACnC,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,6EAA6E;IAC7E,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,oEAAoE;IACpE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6DAA6D;IAC7D,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;CAC7C;AAED,gDAAgD;AAChD,eAAO,MAAM,eAAe,wBAAwB,CAAC"}
@@ -0,0 +1,4 @@
1
+ // User-facing options for the WDIO Service / hook factory (P1 PRD §M.1).
2
+ /** The default output directory for reports. */
3
+ export const DEFAULT_OUT_DIR = './tracelane-reports';
4
+ //# sourceMappingURL=options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA,yEAAyE;AAsCzE,gDAAgD;AAChD,MAAM,CAAC,MAAM,eAAe,GAAG,qBAAqB,CAAC"}