@tracelane/report 0.1.0-alpha.2 → 0.1.0-alpha.20

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 (67) hide show
  1. package/NOTICE +8 -0
  2. package/README.md +33 -5
  3. package/dist/_security/detectors/insecure-cookies.d.ts +4 -0
  4. package/dist/_security/detectors/insecure-cookies.d.ts.map +1 -0
  5. package/dist/_security/detectors/insecure-cookies.js +32 -0
  6. package/dist/_security/detectors/insecure-cookies.js.map +1 -0
  7. package/dist/_security/detectors/missing-headers.d.ts +4 -0
  8. package/dist/_security/detectors/missing-headers.d.ts.map +1 -0
  9. package/dist/_security/detectors/missing-headers.js +36 -0
  10. package/dist/_security/detectors/missing-headers.js.map +1 -0
  11. package/dist/_security/detectors/mixed-content.d.ts +5 -0
  12. package/dist/_security/detectors/mixed-content.d.ts.map +1 -0
  13. package/dist/_security/detectors/mixed-content.js +74 -0
  14. package/dist/_security/detectors/mixed-content.js.map +1 -0
  15. package/dist/_security/detectors/reverse-tabnabbing.d.ts +10 -0
  16. package/dist/_security/detectors/reverse-tabnabbing.d.ts.map +1 -0
  17. package/dist/_security/detectors/reverse-tabnabbing.js +44 -0
  18. package/dist/_security/detectors/reverse-tabnabbing.js.map +1 -0
  19. package/dist/_security/index.d.ts +29 -0
  20. package/dist/_security/index.d.ts.map +1 -0
  21. package/dist/_security/index.js +36 -0
  22. package/dist/_security/index.js.map +1 -0
  23. package/dist/_security/response-meta.d.ts +28 -0
  24. package/dist/_security/response-meta.d.ts.map +1 -0
  25. package/dist/_security/response-meta.js +50 -0
  26. package/dist/_security/response-meta.js.map +1 -0
  27. package/dist/_security/serialized-dom.d.ts +20 -0
  28. package/dist/_security/serialized-dom.d.ts.map +1 -0
  29. package/dist/_security/serialized-dom.js +32 -0
  30. package/dist/_security/serialized-dom.js.map +1 -0
  31. package/dist/_security/suppress.d.ts +7 -0
  32. package/dist/_security/suppress.d.ts.map +1 -0
  33. package/dist/_security/suppress.js +8 -0
  34. package/dist/_security/suppress.js.map +1 -0
  35. package/dist/assets.d.ts +20 -0
  36. package/dist/assets.d.ts.map +1 -1
  37. package/dist/assets.js +60 -6
  38. package/dist/assets.js.map +1 -1
  39. package/dist/build-report.d.ts +19 -0
  40. package/dist/build-report.d.ts.map +1 -1
  41. package/dist/build-report.js +20 -2
  42. package/dist/build-report.js.map +1 -1
  43. package/dist/index.d.ts +3 -0
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +3 -0
  46. package/dist/index.js.map +1 -1
  47. package/dist/markdown.d.ts +2 -1
  48. package/dist/markdown.d.ts.map +1 -1
  49. package/dist/markdown.js +11 -1
  50. package/dist/markdown.js.map +1 -1
  51. package/dist/metadata.d.ts +21 -2
  52. package/dist/metadata.d.ts.map +1 -1
  53. package/dist/metadata.js +98 -27
  54. package/dist/metadata.js.map +1 -1
  55. package/dist/panels.d.ts +40 -5
  56. package/dist/panels.d.ts.map +1 -1
  57. package/dist/panels.js +175 -23
  58. package/dist/panels.js.map +1 -1
  59. package/dist/report-writer.d.ts +43 -0
  60. package/dist/report-writer.d.ts.map +1 -0
  61. package/dist/report-writer.js +65 -0
  62. package/dist/report-writer.js.map +1 -0
  63. package/dist/template.d.ts +19 -0
  64. package/dist/template.d.ts.map +1 -1
  65. package/dist/template.js +987 -72
  66. package/dist/template.js.map +1 -1
  67. package/package.json +28 -7
package/dist/panels.js CHANGED
@@ -5,18 +5,39 @@
5
5
  // renders them — no event filtering happens in the browser). Extracting at
6
6
  // build time keeps all the parsing logic unit-testable and the runtime thin.
7
7
  //
8
- // Sources (P1 PRD §F.3 / §E.2):
8
+ // Sources (P1 PRD §F.3 / §E.2 + Phase 5 network-plugin integration):
9
9
  // • Console — EventType.Plugin (6) with data.plugin === 'rrweb/console@1'.
10
- // Network EventType.Custom (5) with data.tag === 'tracelane.test.network-error'
11
- // (the v1.1 rich path); falls back to scraping console.error messages
12
- // prefixed '[tracelane.net]' (the v1 path) when no custom events exist.
13
- import { EventType } from '@cubenest/rrweb-core';
10
+ // The CDP network-scrape lines ('[tracelane.net] …') are filtered OUT of
11
+ // the console panel (they're a network signal, surfaced as Network rows).
12
+ // Network UNION of the framework-agnostic in-page plugin
13
+ // (EventType.Plugin (6), data.plugin === 'rrweb/network@1'; ships in
14
+ // `@cubenest/rrweb-core`) and the v1 CDP console-scrape
15
+ // ('[tracelane.net]'), where the CDP row's AUTHORITATIVE status wins for
16
+ // an overlapping request. Falls back to the v1.1 EventType.Custom (5)
17
+ // path (tag 'tracelane.test.network-error') for pre-Phase-5 recorders
18
+ // that emitted neither source.
19
+ import { EventType, NETWORK_PLUGIN_NAME } from '@cubenest/rrweb-core';
14
20
  /** rrweb console plugin tag (P1 PRD §F.3). */
15
21
  export const CONSOLE_PLUGIN = 'rrweb/console@1';
22
+ /**
23
+ * rrweb network plugin tag — the framework-agnostic in-page capture
24
+ * (`rrweb/network@1`). Re-exported from `@cubenest/rrweb-core` so it stays
25
+ * in sync with the substrate's wire-format contract.
26
+ */
27
+ export const NETWORK_PLUGIN = NETWORK_PLUGIN_NAME;
16
28
  /** Custom-event tag for the v1.1 rich network path (P1 PRD §E.3). */
17
29
  export const NETWORK_EVENT_TAG = 'tracelane.test.network-error';
18
30
  /** console.error prefix for the v1 network fallback (P1 PRD §E.2). */
19
31
  export const NETWORK_CONSOLE_PREFIX = '[tracelane.net]';
32
+ /**
33
+ * console.error prefix the capture layer uses for privacy-safe response
34
+ * metadata consumed by `@tracelane/security`. Like the network-scrape lines,
35
+ * these are a non-console signal and are filtered OUT of the console panel —
36
+ * the security analyzer surfaces them as advisory findings instead. Defined
37
+ * locally (mirroring {@link NETWORK_CONSOLE_PREFIX}) rather than imported from
38
+ * `@tracelane/security` just for a string constant.
39
+ */
40
+ export const SEC_CONSOLE_PREFIX = '[tracelane.sec]';
20
41
  function isPlugin(e) {
21
42
  return e.type === EventType.Plugin;
22
43
  }
@@ -56,10 +77,13 @@ function safeStringify(value) {
56
77
  }
57
78
  }
58
79
  /**
59
- * Extract console rows: EventType.Plugin events emitted by the rrweb console
60
- * plugin. The plugin nests the level + serialized args under `data.payload`.
80
+ * All console-plugin rows, UNFILTERED — EventType.Plugin events emitted by the
81
+ * rrweb console plugin. The plugin nests the level + serialized args under
82
+ * `data.payload`. This includes the CDP network-scrape lines
83
+ * ('[tracelane.net] …'); `extractNetwork` reads those here to recover the
84
+ * authoritative status, while `extractConsole` filters them out for its panel.
61
85
  */
62
- export function extractConsole(events) {
86
+ function extractConsoleRows(events) {
63
87
  const rows = [];
64
88
  for (const e of events) {
65
89
  if (!isPlugin(e))
@@ -67,12 +91,25 @@ export function extractConsole(events) {
67
91
  const data = e.data;
68
92
  if (data.plugin !== CONSOLE_PLUGIN)
69
93
  continue;
70
- const level = typeof data.payload?.level === 'string' ? data.payload.level : 'log';
71
- const message = stringifyArgs(data.payload?.payload);
94
+ const payload = data.payload;
95
+ const level = typeof payload?.level === 'string' ? payload.level : 'log';
96
+ const message = stringifyArgs(payload?.payload);
72
97
  rows.push({ level, message, timestamp: e.timestamp });
73
98
  }
74
99
  return rows;
75
100
  }
101
+ /**
102
+ * Extract console panel rows. Drops the CDP network-scrape lines
103
+ * ('[tracelane.net] …'): they are a *network* signal that `extractNetwork`
104
+ * already surfaces as structured rows in the Network panel. Letting them
105
+ * through here double-renders the same failure as a raw console string,
106
+ * inconsistently with the structured row (audit A-4). Also drops the
107
+ * '[tracelane.sec] …' response-metadata lines: those are a *security* signal
108
+ * the analyzer turns into advisory findings, not console output (Task 9).
109
+ */
110
+ export function extractConsole(events) {
111
+ return extractConsoleRows(events).filter((row) => !row.message.includes(NETWORK_CONSOLE_PREFIX) && !row.message.includes(SEC_CONSOLE_PREFIX));
112
+ }
76
113
  /** Parse a '[tracelane.net] <METHOD> <STATUS> <URL>' console line, if it is one. */
77
114
  function parseNetConsoleLine(message, timestamp) {
78
115
  const idx = message.indexOf(NETWORK_CONSOLE_PREFIX);
@@ -91,11 +128,135 @@ function parseNetConsoleLine(message, timestamp) {
91
128
  };
92
129
  }
93
130
  /**
94
- * Extract network-error rows. Prefers the v1.1 custom-event path
95
- * (EventType.Custom, tag 'tracelane.test.network-error'); when none are present,
96
- * falls back to scraping console.error lines prefixed '[tracelane.net]' (v1).
131
+ * initiatorTypes that come from a true fetch/XHR error path. The plugin tags
132
+ * fetch/XHR wrapper rows with exactly these; everything else (img/script/link/
133
+ * css/font/navigation/…) is a PerformanceObserver timing entry.
134
+ */
135
+ const ERROR_PATH_INITIATORS = new Set(['fetch', 'xmlhttprequest']);
136
+ /**
137
+ * Whether a `status === 0` request came from a *true* error path rather than
138
+ * a PerformanceObserver timing entry (audit A-1).
139
+ *
140
+ * The in-page `rrweb/network@1` plugin's default mode is PerformanceObserver:
141
+ * successful cross-origin SUB-resources (img/script/link/css/font — analytics,
142
+ * CDN, Google Fonts) report `responseStatus: 0` per the Resource Timing spec
143
+ * **even though they loaded with HTTP 200**. Treating those as failures
144
+ * surfaces PHANTOM network errors. Only the fetch/XHR wrappers (and CDP rows)
145
+ * report a real status, so a status-0 there is a genuine network error.
146
+ *
147
+ * Discriminator: an error-path row has `initiatorType` of `'fetch'` /
148
+ * `'xmlhttprequest'` (and typically a `method`); a PerformanceObserver
149
+ * sub-resource has a resource-type `initiatorType` (img/script/css/…) and no
150
+ * `method`. We require either an explicit fetch/XHR initiatorType OR (no
151
+ * initiatorType at all AND a `method`) — the latter covers older substrates
152
+ * that omitted initiatorType on the wrapper rows.
153
+ */
154
+ function isTrueErrorPath(req) {
155
+ const initiator = typeof req.initiatorType === 'string' ? req.initiatorType : undefined;
156
+ if (initiator !== undefined)
157
+ return ERROR_PATH_INITIATORS.has(initiator);
158
+ // No initiatorType: a `method` indicates a fetch/XHR wrapper row (a true
159
+ // request), whereas a PerformanceObserver entry never carries a method.
160
+ return typeof req.method === 'string';
161
+ }
162
+ /**
163
+ * Coerce a `CapturedNetworkRequest` from the network plugin into a
164
+ * panel-visible `NetworkEntry`. Treats genuine network errors (`status === 0`
165
+ * on a true fetch/XHR error path) and 4xx/5xx responses as "failed" — matches
166
+ * the report's existing "Network errors" panel semantics. Returns `undefined`
167
+ * for non-failed requests so the caller can skip them.
168
+ *
169
+ * A `status === 0` PerformanceObserver sub-resource is NOT a failure (see
170
+ * {@link isTrueErrorPath}) — those are cross-origin resources that loaded fine
171
+ * but report 0 per the Resource Timing spec.
172
+ *
173
+ * `fallbackTs` is the wrapping rrweb event's `timestamp` (wall-clock ms);
174
+ * we prefer the captured request's `timestamp` field when present (also
175
+ * wall-clock ms — `timeOrigin + requestMadeAt` in the plugin), and fall
176
+ * back to the wrapping event's timestamp so panel ordering still works
177
+ * when the plugin omits per-request `timestamp` (e.g. older substrates).
178
+ */
179
+ function networkEntryFromCapturedRequest(req, fallbackTs) {
180
+ const status = typeof req.status === 'number' ? req.status : undefined;
181
+ // Filter to FAILED requests only — the renderer's panel is "Network errors"
182
+ // and the v1/v1.1 paths only ever surfaced failures. Successful 2xx/3xx
183
+ // responses captured by the plugin's broader PerformanceObserver path are
184
+ // intentionally dropped here. A status of 0 only counts as a failure when it
185
+ // came from a true error path (fetch/XHR), never a PerformanceObserver
186
+ // sub-resource that reports 0 per the Resource Timing spec (audit A-1).
187
+ const isFailed = (status === 0 && isTrueErrorPath(req)) || (status !== undefined && status >= 400);
188
+ if (!isFailed)
189
+ return undefined;
190
+ const ts = typeof req.timestamp === 'number' ? req.timestamp : fallbackTs;
191
+ const url = typeof req.name === 'string' ? req.name : '';
192
+ return {
193
+ ...(typeof req.method === 'string' ? { method: req.method } : {}),
194
+ url,
195
+ status: status ?? 0,
196
+ timestamp: ts,
197
+ };
198
+ }
199
+ /** Normalize a URL for cross-source dedup (drop trailing slash + fragment). */
200
+ function networkKey(url) {
201
+ return url.replace(/#.*$/, '').replace(/\/$/, '');
202
+ }
203
+ /**
204
+ * Extract network-error rows.
205
+ *
206
+ * The in-page rrweb network plugin (`rrweb/network@1`, the Phase 5 default)
207
+ * captures via PerformanceObserver, so its rows are status-poor for
208
+ * cross-origin requests. The legacy CDP `[tracelane.net]` console-scrape, when
209
+ * present, carries the AUTHORITATIVE HTTP status. So rather than "first
210
+ * non-empty source wins" — which would shadow the authoritative CDP rows
211
+ * whenever the plugin emitted anything (audit A-4) — we UNION the plugin + CDP
212
+ * sources, with CDP rows replacing an overlapping plugin row for the same URL.
213
+ *
214
+ * Resolution:
215
+ * 1. Failed rows from the in-page network plugin (EventType.Plugin) merged
216
+ * with the v1 CDP console-scrape ('[tracelane.net]'), where a CDP row
217
+ * REPLACES an overlapping plugin row for the same request (real status
218
+ * wins over a PerformanceObserver 0). Output ordered by timestamp.
219
+ * 2. If neither produced any rows, fall back to the v1.1 custom-event path
220
+ * (EventType.Custom) — pre-Phase-5 recorders that used neither the plugin
221
+ * nor the CDP console-scrape.
97
222
  */
98
223
  export function extractNetwork(events) {
224
+ // 1a. The in-page network plugin (framework-agnostic; PerformanceObserver).
225
+ const fromPlugin = [];
226
+ for (const e of events) {
227
+ if (!isPlugin(e))
228
+ continue;
229
+ const data = e.data;
230
+ if (data.plugin !== NETWORK_PLUGIN)
231
+ continue;
232
+ const payload = data.payload;
233
+ if (!payload || !Array.isArray(payload.requests))
234
+ continue;
235
+ for (const req of payload.requests) {
236
+ const entry = networkEntryFromCapturedRequest(req, e.timestamp);
237
+ if (entry)
238
+ fromPlugin.push(entry);
239
+ }
240
+ }
241
+ // 1b. v1 CDP console-scrape — AUTHORITATIVE status. Read the unfiltered
242
+ // console rows here (extractConsole() drops these lines for its panel).
243
+ const scraped = [];
244
+ for (const row of extractConsoleRows(events)) {
245
+ const parsed = parseNetConsoleLine(row.message, row.timestamp);
246
+ if (parsed)
247
+ scraped.push(parsed);
248
+ }
249
+ if (fromPlugin.length > 0 || scraped.length > 0) {
250
+ // Merge: CDP rows are authoritative and replace an overlapping plugin row
251
+ // for the same URL; everything non-overlapping from both is retained.
252
+ const byKey = new Map();
253
+ for (const entry of fromPlugin)
254
+ byKey.set(networkKey(entry.url), entry);
255
+ for (const entry of scraped)
256
+ byKey.set(networkKey(entry.url), entry);
257
+ return [...byKey.values()].sort((a, b) => a.timestamp - b.timestamp);
258
+ }
259
+ // 2. v1.1 fallback: rich custom events (pre-Phase-5 recorders).
99
260
  const rich = [];
100
261
  for (const e of events) {
101
262
  if (!isCustom(e))
@@ -111,15 +272,6 @@ export function extractNetwork(events) {
111
272
  timestamp: e.timestamp,
112
273
  });
113
274
  }
114
- if (rich.length > 0)
115
- return rich;
116
- // v1 fallback: scrape the console.
117
- const scraped = [];
118
- for (const row of extractConsole(events)) {
119
- const parsed = parseNetConsoleLine(row.message, row.timestamp);
120
- if (parsed)
121
- scraped.push(parsed);
122
- }
123
- return scraped;
275
+ return rich;
124
276
  }
125
277
  //# sourceMappingURL=panels.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"panels.js","sourceRoot":"","sources":["../src/panels.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"panels.js","sourceRoot":"","sources":["../src/panels.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import type { eventWithTime } from '@cubenest/rrweb-core';
2
+ import type { Suppression } from './_security/index.js';
3
+ import type { ReportMeta } from './types.js';
4
+ /** Inputs for {@link writeReport}. */
5
+ export interface WriteReportInput {
6
+ /** Output directory (created if missing). */
7
+ outDir: string;
8
+ /** Worker/project id (e.g. WDIO `0-0` or a Playwright project name); namespaces the filename for parallel safety. */
9
+ cid?: string | undefined;
10
+ /** Captured rrweb event stream. */
11
+ events: eventWithTime[];
12
+ /** Report metadata (spec, title, status, error, browser, ...). */
13
+ meta: ReportMeta;
14
+ /** Render the report's "Generated by tracelane" footer. Default true. */
15
+ footer?: boolean | undefined;
16
+ /** Run the advisory security analyzer + render its findings. Default true. */
17
+ security?: boolean | undefined;
18
+ /** Suppressions forwarded to the analyzer. Ignored when `security` is false. */
19
+ securitySuppress?: Suppression[] | undefined;
20
+ }
21
+ /**
22
+ * Turn an arbitrary spec path / test title into a filesystem-safe slug:
23
+ * lowercase, non-alphanumerics collapsed to single dashes, trimmed, capped.
24
+ */
25
+ export declare function slugify(input: string): string;
26
+ /**
27
+ * Build a unique-ish report filename: `<spec>--<title>--<cid>-<ts>.html`. The
28
+ * timestamp + cid keep retries and parallel workers from overwriting each other.
29
+ */
30
+ export declare function reportFileName(meta: ReportMeta, cid?: string): string;
31
+ /**
32
+ * Slug for the spec segment: take the file's basename (so an absolute path like
33
+ * `/repo/test/login.spec.ts` doesn't bloat the filename), then strip the common
34
+ * test-file suffixes (`.spec.ts`, `.test.mts`, `.e2e.js`, …) so it slugifies to
35
+ * a clean `login`, not `login-spec`.
36
+ */
37
+ export declare function specSlug(spec: string): string;
38
+ /**
39
+ * Build the report HTML and write it under `outDir`, namespaced by `cid`.
40
+ * Returns the absolute-or-relative path written (as `join` produced it).
41
+ */
42
+ export declare function writeReport(input: WriteReportInput): string;
43
+ //# sourceMappingURL=report-writer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report-writer.d.ts","sourceRoot":"","sources":["../src/report-writer.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,sCAAsC;AACtC,MAAM,WAAW,gBAAgB;IAC/B,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,qHAAqH;IACrH,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,mCAAmC;IACnC,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,kEAAkE;IAClE,IAAI,EAAE,UAAU,CAAC;IACjB,yEAAyE;IACzE,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC/B,gFAAgF;IAChF,gBAAgB,CAAC,EAAE,WAAW,EAAE,GAAG,SAAS,CAAC;CAC9C;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAO7C;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAKrE;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG7C;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAa3D"}
@@ -0,0 +1,65 @@
1
+ // Write a self-contained HTML report to disk (ADR-0005 / ADR-0006).
2
+ //
3
+ // Given the captured event stream + metadata, call buildReport to produce the
4
+ // offline HTML string, then write it under `outDir`. Files are namespaced by
5
+ // `cid` (the adapter's worker/project id — e.g. the WDIO capability id or the
6
+ // Playwright project name) so parallel workers never collide (ADR-0006 action
7
+ // item #6). Lives in @tracelane/report so every adapter (WDIO, Playwright)
8
+ // shares one write path.
9
+ import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
10
+ import { join } from 'node:path';
11
+ import { buildReport } from './build-report.js';
12
+ /**
13
+ * Turn an arbitrary spec path / test title into a filesystem-safe slug:
14
+ * lowercase, non-alphanumerics collapsed to single dashes, trimmed, capped.
15
+ */
16
+ export function slugify(input) {
17
+ const slug = input
18
+ .toLowerCase()
19
+ .replace(/[^a-z0-9]+/g, '-')
20
+ .replace(/^-+|-+$/g, '');
21
+ // Cap length so deeply-nested titles don't blow past filesystem name limits.
22
+ return (slug || 'report').slice(0, 120);
23
+ }
24
+ /**
25
+ * Build a unique-ish report filename: `<spec>--<title>--<cid>-<ts>.html`. The
26
+ * timestamp + cid keep retries and parallel workers from overwriting each other.
27
+ */
28
+ export function reportFileName(meta, cid) {
29
+ const specPart = meta.spec ? specSlug(meta.spec) : 'spec';
30
+ const titlePart = slugify(meta.title);
31
+ const cidPart = cid ? `${slugify(cid)}-` : '';
32
+ return `${specPart}--${titlePart}--${cidPart}${Date.now()}.html`;
33
+ }
34
+ /**
35
+ * Slug for the spec segment: take the file's basename (so an absolute path like
36
+ * `/repo/test/login.spec.ts` doesn't bloat the filename), then strip the common
37
+ * test-file suffixes (`.spec.ts`, `.test.mts`, `.e2e.js`, …) so it slugifies to
38
+ * a clean `login`, not `login-spec`.
39
+ */
40
+ export function specSlug(spec) {
41
+ const base = spec.replace(/^.*[/\\]/, '');
42
+ return slugify(base.replace(/(\.(spec|test|e2e|cy))?\.[cm]?[jt]sx?$/, ''));
43
+ }
44
+ /**
45
+ * Build the report HTML and write it under `outDir`, namespaced by `cid`.
46
+ * Returns the absolute-or-relative path written (as `join` produced it).
47
+ */
48
+ export function writeReport(input) {
49
+ const { outDir, cid, events, meta, footer, security, securitySuppress } = input;
50
+ const buildOptions = {};
51
+ if (footer === false)
52
+ buildOptions.footer = false;
53
+ if (security !== undefined)
54
+ buildOptions.security = security;
55
+ if (securitySuppress !== undefined)
56
+ buildOptions.securitySuppress = securitySuppress;
57
+ const html = buildReport(events, meta, buildOptions);
58
+ if (!existsSync(outDir)) {
59
+ mkdirSync(outDir, { recursive: true });
60
+ }
61
+ const filePath = join(outDir, reportFileName(meta, cid));
62
+ writeFileSync(filePath, html);
63
+ return filePath;
64
+ }
65
+ //# sourceMappingURL=report-writer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report-writer.js","sourceRoot":"","sources":["../src/report-writer.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,EAAE;AACF,8EAA8E;AAC9E,6EAA6E;AAC7E,8EAA8E;AAC9E,8EAA8E;AAC9E,2EAA2E;AAC3E,yBAAyB;AAEzB,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,OAAO,EAA2B,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAqBzE;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,KAAa;IACnC,MAAM,IAAI,GAAG,KAAK;SACf,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC3B,6EAA6E;IAC7E,OAAO,CAAC,IAAI,IAAI,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAC1C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,IAAgB,EAAE,GAAY;IAC3D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAC1D,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtC,MAAM,OAAO,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9C,OAAO,GAAG,QAAQ,KAAK,SAAS,KAAK,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC;AACnE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC1C,OAAO,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,wCAAwC,EAAE,EAAE,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,KAAuB;IACjD,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,EAAE,GAAG,KAAK,CAAC;IAChF,MAAM,YAAY,GAAuB,EAAE,CAAC;IAC5C,IAAI,MAAM,KAAK,KAAK;QAAE,YAAY,CAAC,MAAM,GAAG,KAAK,CAAC;IAClD,IAAI,QAAQ,KAAK,SAAS;QAAE,YAAY,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC7D,IAAI,gBAAgB,KAAK,SAAS;QAAE,YAAY,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;IACrF,MAAM,IAAI,GAAG,WAAW,CAAC,MAAM,EAAE,IAAI,EAAE,YAAY,CAAC,CAAC;IACrD,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACxB,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,CAAC;IACD,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;IACzD,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC9B,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -1,3 +1,4 @@
1
+ import type { SecurityFinding } from './_security/index.js';
1
2
  import type { ConsoleEntry, NetworkEntry } from './panels.js';
2
3
  import type { ReportMeta } from './types.js';
3
4
  /** Everything `renderReportHtml` needs; build-report.ts prepares it. */
@@ -9,10 +10,28 @@ export interface ReportTemplateData {
9
10
  console: ConsoleEntry[];
10
11
  /** Extracted network panel rows (Task 2.10). */
11
12
  network: NetworkEntry[];
13
+ /**
14
+ * Advisory security-hygiene findings (Task 12). Rendered as a collapsed
15
+ * panel; the panel + its tab are OMITTED entirely when this is empty (no
16
+ * zero-state). Advisory only — NOT a security audit.
17
+ */
18
+ security: SecurityFinding[];
12
19
  /** Pre-rendered "Copy as Markdown for AI paste" payload (Task 2.12). */
13
20
  markdown: string;
14
21
  /** Whether the events were pruned to fit the size budget (ADR-0005 banner). */
15
22
  pruned: boolean;
23
+ /** Total recorded event count (Phase 6 meta-strip). */
24
+ eventCount: number;
25
+ /** Earliest event timestamp (wall-clock ms) or 0 if no events. */
26
+ firstTs: number;
27
+ /** Latest event timestamp (wall-clock ms) or 0 if no events. Treated as the
28
+ * "failure" moment for the timeline marker when the test failed. */
29
+ lastTs: number;
30
+ /**
31
+ * Render the self-marketing footer. Default true. Pass false to suppress it
32
+ * (`report: { footer: false }` opt-out — audit A-8).
33
+ */
34
+ footer?: boolean;
16
35
  }
17
36
  /** Compose the full self-contained HTML document. */
18
37
  export declare function renderReportHtml(data: ReportTemplateData): string;
@@ -1 +1 @@
1
- {"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,wEAAwE;AACxE,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,UAAU,CAAC;IACjB,kEAAkE;IAClE,WAAW,EAAE,MAAM,CAAC;IACpB,gDAAgD;IAChD,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,gDAAgD;IAChD,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,MAAM,EAAE,OAAO,CAAC;CACjB;AA6GD,qDAAqD;AACrD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,kBAAkB,GAAG,MAAM,CA4CjE"}
1
+ {"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAW5D,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,wEAAwE;AACxE,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,UAAU,CAAC;IACjB,kEAAkE;IAClE,WAAW,EAAE,MAAM,CAAC;IACpB,gDAAgD;IAChD,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,gDAAgD;IAChD,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB;;;;OAIG;IACH,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,MAAM,EAAE,OAAO,CAAC;IAChB,uDAAuD;IACvD,UAAU,EAAE,MAAM,CAAC;IACnB,kEAAkE;IAClE,OAAO,EAAE,MAAM,CAAC;IAChB;yEACqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AA86BD,qDAAqD;AACrD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,kBAAkB,GAAG,MAAM,CAqJjE"}