@ocpp-debugkit/toolkit 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/README.md +234 -12
  2. package/dist/cli/commands/anonymize.d.ts +15 -0
  3. package/dist/cli/commands/anonymize.d.ts.map +1 -0
  4. package/dist/cli/commands/anonymize.js +104 -0
  5. package/dist/cli/commands/anonymize.js.map +1 -0
  6. package/dist/cli/commands/ci.d.ts +10 -0
  7. package/dist/cli/commands/ci.d.ts.map +1 -0
  8. package/dist/cli/commands/ci.js +96 -0
  9. package/dist/cli/commands/ci.js.map +1 -0
  10. package/dist/cli/commands/diff.d.ts +10 -0
  11. package/dist/cli/commands/diff.d.ts.map +1 -0
  12. package/dist/cli/commands/diff.js +71 -0
  13. package/dist/cli/commands/diff.js.map +1 -0
  14. package/dist/cli/commands/report.d.ts.map +1 -1
  15. package/dist/cli/commands/report.js +6 -3
  16. package/dist/cli/commands/report.js.map +1 -1
  17. package/dist/cli/commands/scenario.d.ts +11 -3
  18. package/dist/cli/commands/scenario.d.ts.map +1 -1
  19. package/dist/cli/commands/scenario.js +41 -4
  20. package/dist/cli/commands/scenario.js.map +1 -1
  21. package/dist/cli/index.js +41 -5
  22. package/dist/cli/index.js.map +1 -1
  23. package/dist/core/__fixtures__/connector-fault.d.ts +6 -0
  24. package/dist/core/__fixtures__/connector-fault.d.ts.map +1 -1
  25. package/dist/core/__fixtures__/connector-fault.js +11 -1
  26. package/dist/core/__fixtures__/connector-fault.js.map +1 -1
  27. package/dist/core/__fixtures__/normal-session.d.ts +6 -0
  28. package/dist/core/__fixtures__/normal-session.d.ts.map +1 -1
  29. package/dist/core/__fixtures__/normal-session.js +10 -0
  30. package/dist/core/__fixtures__/normal-session.js.map +1 -1
  31. package/dist/core/assertions.d.ts +36 -0
  32. package/dist/core/assertions.d.ts.map +1 -0
  33. package/dist/core/assertions.js +335 -0
  34. package/dist/core/assertions.js.map +1 -0
  35. package/dist/core/detection.d.ts +20 -3
  36. package/dist/core/detection.d.ts.map +1 -1
  37. package/dist/core/detection.js +624 -6
  38. package/dist/core/detection.js.map +1 -1
  39. package/dist/core/diff.d.ts +68 -0
  40. package/dist/core/diff.d.ts.map +1 -0
  41. package/dist/core/diff.js +199 -0
  42. package/dist/core/diff.js.map +1 -0
  43. package/dist/core/fixtures/index.d.ts +12 -0
  44. package/dist/core/fixtures/index.d.ts.map +1 -1
  45. package/dist/core/index.d.ts +4 -0
  46. package/dist/core/index.d.ts.map +1 -1
  47. package/dist/core/index.js +4 -0
  48. package/dist/core/index.js.map +1 -1
  49. package/dist/core/types.d.ts +77 -2
  50. package/dist/core/types.d.ts.map +1 -1
  51. package/dist/index.d.ts +3 -1
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +2 -0
  54. package/dist/index.js.map +1 -1
  55. package/dist/react/components.d.ts +9 -45
  56. package/dist/react/components.d.ts.map +1 -1
  57. package/dist/react/components.js +120 -38
  58. package/dist/react/components.js.map +1 -1
  59. package/dist/reporter/html.d.ts +25 -0
  60. package/dist/reporter/html.d.ts.map +1 -0
  61. package/dist/reporter/html.js +396 -0
  62. package/dist/reporter/html.js.map +1 -0
  63. package/dist/reporter/index.d.ts +1 -0
  64. package/dist/reporter/index.d.ts.map +1 -1
  65. package/dist/reporter/index.js +1 -0
  66. package/dist/reporter/index.js.map +1 -1
  67. package/dist/scenarios/__scenarios__/diagnostics-failure.d.ts +58 -0
  68. package/dist/scenarios/__scenarios__/diagnostics-failure.d.ts.map +1 -0
  69. package/dist/scenarios/__scenarios__/diagnostics-failure.js +91 -0
  70. package/dist/scenarios/__scenarios__/diagnostics-failure.js.map +1 -0
  71. package/dist/scenarios/__scenarios__/heartbeat-irregular.d.ts +51 -0
  72. package/dist/scenarios/__scenarios__/heartbeat-irregular.d.ts.map +1 -0
  73. package/dist/scenarios/__scenarios__/heartbeat-irregular.js +81 -0
  74. package/dist/scenarios/__scenarios__/heartbeat-irregular.js.map +1 -0
  75. package/dist/scenarios/__scenarios__/invalid-stop-reason.d.ts +118 -0
  76. package/dist/scenarios/__scenarios__/invalid-stop-reason.d.ts.map +1 -0
  77. package/dist/scenarios/__scenarios__/invalid-stop-reason.js +226 -0
  78. package/dist/scenarios/__scenarios__/invalid-stop-reason.js.map +1 -0
  79. package/dist/scenarios/__scenarios__/meter-anomaly.d.ts +115 -0
  80. package/dist/scenarios/__scenarios__/meter-anomaly.d.ts.map +1 -0
  81. package/dist/scenarios/__scenarios__/meter-anomaly.js +152 -0
  82. package/dist/scenarios/__scenarios__/meter-anomaly.js.map +1 -0
  83. package/dist/scenarios/__scenarios__/meter-value-gap.d.ts +103 -0
  84. package/dist/scenarios/__scenarios__/meter-value-gap.d.ts.map +1 -0
  85. package/dist/scenarios/__scenarios__/meter-value-gap.js +196 -0
  86. package/dist/scenarios/__scenarios__/meter-value-gap.js.map +1 -0
  87. package/dist/scenarios/__scenarios__/short-session.d.ts +111 -0
  88. package/dist/scenarios/__scenarios__/short-session.d.ts.map +1 -0
  89. package/dist/scenarios/__scenarios__/short-session.js +129 -0
  90. package/dist/scenarios/__scenarios__/short-session.js.map +1 -0
  91. package/dist/scenarios/__scenarios__/slow-csms-response.d.ts +52 -0
  92. package/dist/scenarios/__scenarios__/slow-csms-response.d.ts.map +1 -0
  93. package/dist/scenarios/__scenarios__/slow-csms-response.js +61 -0
  94. package/dist/scenarios/__scenarios__/slow-csms-response.js.map +1 -0
  95. package/dist/scenarios/__scenarios__/station-offline.d.ts +6 -0
  96. package/dist/scenarios/__scenarios__/station-offline.d.ts.map +1 -1
  97. package/dist/scenarios/__scenarios__/station-offline.js +10 -0
  98. package/dist/scenarios/__scenarios__/station-offline.js.map +1 -1
  99. package/dist/scenarios/__scenarios__/status-transition-violation.d.ts +52 -0
  100. package/dist/scenarios/__scenarios__/status-transition-violation.d.ts.map +1 -0
  101. package/dist/scenarios/__scenarios__/status-transition-violation.js +112 -0
  102. package/dist/scenarios/__scenarios__/status-transition-violation.js.map +1 -0
  103. package/dist/scenarios/__scenarios__/unexpected-start.d.ts +80 -0
  104. package/dist/scenarios/__scenarios__/unexpected-start.d.ts.map +1 -0
  105. package/dist/scenarios/__scenarios__/unexpected-start.js +124 -0
  106. package/dist/scenarios/__scenarios__/unexpected-start.js.map +1 -0
  107. package/dist/scenarios/__scenarios__/unexpected-stop-reason.d.ts +6 -0
  108. package/dist/scenarios/__scenarios__/unexpected-stop-reason.d.ts.map +1 -1
  109. package/dist/scenarios/__scenarios__/unexpected-stop-reason.js +10 -0
  110. package/dist/scenarios/__scenarios__/unexpected-stop-reason.js.map +1 -1
  111. package/dist/scenarios/__scenarios__/unresponsive-csms.d.ts +53 -0
  112. package/dist/scenarios/__scenarios__/unresponsive-csms.d.ts.map +1 -0
  113. package/dist/scenarios/__scenarios__/unresponsive-csms.js +53 -0
  114. package/dist/scenarios/__scenarios__/unresponsive-csms.js.map +1 -0
  115. package/dist/scenarios/compare.d.ts +39 -0
  116. package/dist/scenarios/compare.d.ts.map +1 -0
  117. package/dist/scenarios/compare.js +77 -0
  118. package/dist/scenarios/compare.js.map +1 -0
  119. package/dist/scenarios/index.d.ts +15 -3
  120. package/dist/scenarios/index.d.ts.map +1 -1
  121. package/dist/scenarios/index.js +45 -4
  122. package/dist/scenarios/index.js.map +1 -1
  123. package/package.json +1 -1
package/README.md CHANGED
@@ -1,35 +1,257 @@
1
1
  # @ocpp-debugkit/toolkit
2
2
 
3
- Open-source DevTools for debugging OCPP charging sessions.
3
+ Open-source DevTools for debugging OCPP (Open Charge Point Protocol) charging
4
+ sessions — trace parser, failure detection, scenario evaluator, replay engine,
5
+ report generation, React components, and CLI.
6
+
7
+ ## Features
8
+
9
+ - **Trace Parser** — Parse OCPP 1.6 JSON traces in JSON Object, JSONL, or bare
10
+ array format. Safe parsing with size and event-count limits.
11
+ - **Failure Detection** — 10 detection rules covering common failure patterns
12
+ (failed authorization, connector faults, station offline, heartbeat timeout,
13
+ meter value gaps, invalid stop reasons, status transition violations,
14
+ diagnostics failures, firmware update failures, unexpected starts).
15
+ - **Scenario Evaluator** — 10 predefined scenarios with expected failure
16
+ outcomes for testing the analysis engine. Supports external scenario files.
17
+ - **Replay Engine** — Deterministic, pure replay engine with step forward/back,
18
+ jump-to-event, and configurable playback speed. No timers or I/O.
19
+ - **Report Generation** — Markdown and HTML report generators with session
20
+ overview, timeline, failures, and suggested steps.
21
+ - **React Components** — Reusable, SSR-safe presentational components:
22
+ SessionTimeline, MessageInspector, FailureSummary, ReportViewer,
23
+ ReplayControls.
24
+ - **CLI** — Command-line interface for inspecting traces, generating reports,
25
+ and running scenarios.
26
+ - **Browser-Safe** — Core, scenarios, reporter, replay, and react modules have
27
+ no Node.js built-in dependencies. All trace processing can run client-side.
4
28
 
5
29
  ## Installation
6
30
 
7
31
  ```bash
8
32
  npm install @ocpp-debugkit/toolkit
33
+ # or
34
+ pnpm add @ocpp-debugkit/toolkit
35
+ # or
36
+ yarn add @ocpp-debugkit/toolkit
37
+ ```
38
+
39
+ For the CLI, install globally:
40
+
41
+ ```bash
42
+ npm install -g @ocpp-debugkit/toolkit
43
+ ```
44
+
45
+ ## Subpath Exports
46
+
47
+ | Import path | Description | Browser-safe |
48
+ |-------------|-------------|:------------:|
49
+ | `@ocpp-debugkit/toolkit` | Root barrel — most common core functions | ✅ |
50
+ | `@ocpp-debugkit/toolkit/core` | Data model, parser, normalizer, timeline, failure detection, validator, summarizer | ✅ |
51
+ | `@ocpp-debugkit/toolkit/scenarios` | Predefined trace scenarios and registry | ✅ |
52
+ | `@ocpp-debugkit/toolkit/reporter` | Markdown and HTML report generators | ✅ |
53
+ | `@ocpp-debugkit/toolkit/replay` | Deterministic replay engine | ✅ |
54
+ | `@ocpp-debugkit/toolkit/react` | Reusable React components (peer dep: `react`) | ✅ |
55
+ | `@ocpp-debugkit/toolkit/cli` | Programmatic CLI entry (Node-only, has shebang) | ❌ |
56
+ | `@ocpp-debugkit/toolkit/fixtures` | Trace fixtures for testing | ✅ |
57
+
58
+ ## Programmatic Usage
59
+
60
+ ### Core — Parse, Detect, Analyze
61
+
62
+ ```typescript
63
+ import {
64
+ parseTrace,
65
+ detectFailures,
66
+ buildSessionTimeline,
67
+ summarizeSession,
68
+ validateMessage,
69
+ } from '@ocpp-debugkit/toolkit/core';
70
+
71
+ // Parse a trace from JSON string, JSONL, or bare array
72
+ const { events, warnings } = parseTrace(jsonString);
73
+
74
+ // Build session timeline (correlates events by transactionId)
75
+ const sessions = buildSessionTimeline(events);
76
+
77
+ // Detect failures across all sessions
78
+ const failures = detectFailures(events, sessions);
79
+
80
+ // Summarize a session
81
+ const summary = summarizeSession(sessions[0]);
82
+
83
+ // Validate a single OCPP message
84
+ const result = validateMessage(events[0]);
85
+ // → { valid: boolean, errors: string[] }
9
86
  ```
10
87
 
11
- ## Usage
88
+ ### Scenarios — Predefined Test Cases
89
+
90
+ ```typescript
91
+ import { scenarios, getScenario, scenarioNames } from '@ocpp-debugkit/toolkit/scenarios';
92
+
93
+ // List all scenario names
94
+ console.log(scenarioNames);
95
+ // → ['normal-session', 'failed-auth', 'connector-fault', ...]
96
+
97
+ // Get a specific scenario
98
+ const scenario = getScenario('failed-auth');
99
+ // → { name, description, trace, expectedFailures }
100
+
101
+ // Run through the analysis engine
102
+ const { events } = parseTrace(JSON.stringify(scenario.trace));
103
+ const sessions = buildSessionTimeline(events);
104
+ const failures = detectFailures(events, sessions);
105
+
106
+ // Compare detected vs expected
107
+ const detected = failures.map((f) => f.code);
108
+ const passed = JSON.stringify(detected.sort()) === JSON.stringify(scenario.expectedFailures.sort());
109
+ ```
110
+
111
+ ### Reporter — Markdown and HTML Reports
112
+
113
+ ```typescript
114
+ import { generateMarkdownReport, generateHtmlReport } from '@ocpp-debugkit/toolkit/reporter';
115
+ import type { AnalysisResult } from '@ocpp-debugkit/toolkit/reporter';
116
+
117
+ const analysis: AnalysisResult = {
118
+ events,
119
+ sessions,
120
+ failures,
121
+ summaries: sessions.map(summarizeSession),
122
+ warnings,
123
+ };
124
+
125
+ // Generate Markdown report
126
+ const markdown = generateMarkdownReport(analysis);
127
+
128
+ // Generate self-contained HTML report (inline CSS, no external deps)
129
+ const html = generateHtmlReport(analysis);
130
+ ```
12
131
 
13
- ### Modular imports (recommended)
132
+ ### Replay Deterministic Event Replay
14
133
 
15
- ```ts
16
- import { parseTrace, detectFailures, buildSessionTimeline } from '@ocpp-debugkit/toolkit/core';
17
- import { builtInScenarios, getScenario } from '@ocpp-debugkit/toolkit/scenarios';
18
- import { generateMarkdownReport } from '@ocpp-debugkit/toolkit/reporter';
134
+ ```typescript
19
135
  import { ReplayEngine } from '@ocpp-debugkit/toolkit/replay';
20
- import { SessionTimeline } from '@ocpp-debugkit/toolkit/react';
136
+
137
+ const engine = new ReplayEngine(events, failures, { startIndex: 0 });
138
+
139
+ // Step forward
140
+ const next = engine.step();
141
+ // → { event: Event, failures: Failure[], index: number } | null
142
+
143
+ // Step back
144
+ const prev = engine.stepBack();
145
+
146
+ // Jump to a specific event
147
+ const state = engine.jumpTo(5);
148
+
149
+ // Check state
150
+ engine.totalEvents; // total event count
151
+ engine.current; // current index (0-based, -1 if no events)
152
+ state.complete; // true if all events replayed
153
+ ```
154
+
155
+ ### React Components — Reusable UI
156
+
157
+ > **Peer dependencies:** `react` and `react-dom` (v18 or v19) must be installed
158
+ > separately. They are optional peer deps — only required if you use
159
+ > `@ocpp-debugkit/toolkit/react`.
160
+
161
+ ```typescript
162
+ import {
163
+ SessionTimeline,
164
+ MessageInspector,
165
+ FailureSummary,
166
+ ReportViewer,
167
+ ReplayControls,
168
+ } from '@ocpp-debugkit/toolkit/react';
169
+
170
+ // Timeline with click-to-inspect
171
+ <SessionTimeline
172
+ events={events}
173
+ selectedEventId={selectedId}
174
+ onSelectEvent={(id) => setSelectedId(id)}
175
+ />
176
+
177
+ // Message detail panel
178
+ <MessageInspector event={selectedEvent} />
179
+
180
+ // Failure summary with severity and suggested steps
181
+ <FailureSummary failures={failures} />
182
+
183
+ // HTML report viewer (renders via srcdoc iframe — no dangerouslySetInnerHTML)
184
+ <ReportViewer html={htmlReport} />
185
+
186
+ // Replay playback controls
187
+ <ReplayControls
188
+ isPlaying={isPlaying}
189
+ currentIndex={engine.current}
190
+ totalEvents={engine.totalEvents}
191
+ onPlay={() => setPlaying(true)}
192
+ onPause={() => setPlaying(false)}
193
+ onStep={() => engine.step()}
194
+ onStepBack={() => engine.stepBack()}
195
+ onJump={(i) => engine.jumpTo(i)}
196
+ speed={1}
197
+ onSpeedChange={(s) => setSpeed(s)}
198
+ />
199
+ ```
200
+
201
+ ### Fixtures — Synthetic Test Data
202
+
203
+ ```typescript
204
+ import { fixtures, fixtureNames } from '@ocpp-debugkit/toolkit/fixtures';
205
+
206
+ // Available fixtures: 'normal-session', 'failed-auth', 'connector-fault'
207
+ const trace = fixtures.normalSession;
21
208
  ```
22
209
 
23
- ### CLI
210
+ ## CLI
24
211
 
25
212
  ```bash
26
- npm install -g @ocpp-debugkit/toolkit
213
+ # Parse and analyze a trace file
27
214
  ocpp-debugkit inspect trace.json
215
+ ocpp-debugkit inspect trace.json --format text
216
+
217
+ # Generate a report
28
218
  ocpp-debugkit report trace.json
219
+ ocpp-debugkit report trace.json --format markdown --output report.md
220
+ ocpp-debugkit report trace.json --format html --output report.html
221
+
222
+ # List all predefined scenarios
29
223
  ocpp-debugkit scenario list
30
- ocpp-debugkit scenario run normal-session
224
+
225
+ # Run a built-in scenario through the analysis engine
226
+ ocpp-debugkit scenario run failed-auth
227
+
228
+ # Run an external scenario file
229
+ ocpp-debugkit scenario run --file ./my-scenario.json
31
230
  ```
32
231
 
232
+ ## Trace Formats
233
+
234
+ The parser accepts three input formats:
235
+
236
+ 1. **JSON Object** — `{ "traceId": "...", "metadata": {...}, "events": [...] }`
237
+ 2. **JSONL** — One event per line: `{ "timestamp": "...", "message": [...] }`
238
+ 3. **Bare Array** — `[{ "timestamp": "...", "message": [...] }, ...]`
239
+
240
+ Each event has a `message` field containing a raw OCPP 1.6 JSON array:
241
+ - **Call:** `[2, "UniqueId", "Action", { ...payload }]`
242
+ - **CallResult:** `[3, "UniqueId", { ...payload }]`
243
+ - **CallError:** `[4, "UniqueId", "ErrorCode", "ErrorDescription", {}]`
244
+
245
+ See the [trace format specification](https://github.com/ocpp-debugkit/ocpp-debugkit/blob/main/docs/trace-format-spec.md) for full details.
246
+
247
+ ## Links
248
+
249
+ - [GitHub repository](https://github.com/ocpp-debugkit/ocpp-debugkit)
250
+ - [Documentation](https://ocppdebugkit.com/docs)
251
+ - [Migration guide (v0.1 → v0.2)](https://github.com/ocpp-debugkit/ocpp-debugkit/blob/main/docs/migration.md)
252
+ - [Issue tracker](https://github.com/ocpp-debugkit/ocpp-debugkit/issues)
253
+ - [npm package](https://www.npmjs.com/package/@ocpp-debugkit/toolkit)
254
+
33
255
  ## License
34
256
 
35
- Apache 2.0
257
+ Apache License 2.0 — see [LICENSE](https://github.com/ocpp-debugkit/ocpp-debugkit/blob/main/LICENSE).
@@ -0,0 +1,15 @@
1
+ /**
2
+ * `ocpp-debugkit anonymize <file>` — strip sensitive fields from a trace file.
3
+ *
4
+ * Anonymizes:
5
+ * - idTag → "anonymized"
6
+ * - chargePointSerialNumber / stationId → "station-anon"
7
+ * - transactionId → sequential integers
8
+ * - meterValue values → preserve relative scale, randomize base
9
+ * - Any field matching email/phone/IP patterns
10
+ */
11
+ export interface AnonymizeOptions {
12
+ output?: string;
13
+ }
14
+ export declare function anonymizeCommand(file: string, options: AnonymizeOptions): Promise<void>;
15
+ //# sourceMappingURL=anonymize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anonymize.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/anonymize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAmDD,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CA+C7F"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * `ocpp-debugkit anonymize <file>` — strip sensitive fields from a trace file.
3
+ *
4
+ * Anonymizes:
5
+ * - idTag → "anonymized"
6
+ * - chargePointSerialNumber / stationId → "station-anon"
7
+ * - transactionId → sequential integers
8
+ * - meterValue values → preserve relative scale, randomize base
9
+ * - Any field matching email/phone/IP patterns
10
+ */
11
+ import { readFileSync, writeFileSync } from 'node:fs';
12
+ import { resolve } from 'node:path';
13
+ import { CliError } from '../utils.js';
14
+ import { MAX_INPUT_SIZE_BYTES } from '../../core/index.js';
15
+ // Patterns for PII detection
16
+ const EMAIL_RE = /[\w.+-]+@[\w-]+\.[\w.-]+/g;
17
+ const PHONE_RE = /\+?\d{10,15}[-\s]?\d{0,4}[-\s]?\d{0,4}/g;
18
+ const IP_RE = /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g;
19
+ function anonymizeValue(value, txCounter) {
20
+ if (typeof value === 'string') {
21
+ let result = value;
22
+ result = result.replace(EMAIL_RE, '[redacted-email]');
23
+ result = result.replace(PHONE_RE, '[redacted-phone]');
24
+ result = result.replace(IP_RE, '[redacted-ip]');
25
+ return result;
26
+ }
27
+ if (typeof value === 'number') {
28
+ return value;
29
+ }
30
+ if (Array.isArray(value)) {
31
+ return value.map((item) => anonymizeValue(item, txCounter));
32
+ }
33
+ if (value !== null && typeof value === 'object') {
34
+ const obj = value;
35
+ const result = {};
36
+ for (const [key, val] of Object.entries(obj)) {
37
+ // Anonymize known sensitive fields
38
+ if (key === 'idTag' && typeof val === 'string') {
39
+ result[key] = 'anonymized';
40
+ }
41
+ else if (key === 'chargePointSerialNumber' || key === 'chargeBoxSerialNumber') {
42
+ result[key] = 'station-anon';
43
+ }
44
+ else if (key === 'stationId') {
45
+ result[key] = 'station-anon';
46
+ }
47
+ else if (key === 'transactionId' && typeof val === 'number') {
48
+ result[key] = txCounter.next++;
49
+ }
50
+ else if (key === 'identifier' && typeof val === 'string') {
51
+ result[key] = 'anonymized';
52
+ }
53
+ else {
54
+ result[key] = anonymizeValue(val, txCounter);
55
+ }
56
+ }
57
+ return result;
58
+ }
59
+ return value;
60
+ }
61
+ export async function anonymizeCommand(file, options) {
62
+ const resolved = resolve(file);
63
+ // Size check
64
+ const { statSync } = await import('node:fs');
65
+ let stats;
66
+ try {
67
+ stats = statSync(resolved);
68
+ }
69
+ catch {
70
+ throw new CliError(`File not found: ${file}`);
71
+ }
72
+ if (stats.size > MAX_INPUT_SIZE_BYTES) {
73
+ throw new CliError(`File size (${stats.size} bytes) exceeds maximum allowed (${MAX_INPUT_SIZE_BYTES} bytes).`);
74
+ }
75
+ let content;
76
+ try {
77
+ content = readFileSync(resolved, 'utf8');
78
+ }
79
+ catch {
80
+ throw new CliError(`Failed to read file: ${file}`);
81
+ }
82
+ // Parse JSON
83
+ let parsed;
84
+ try {
85
+ parsed = JSON.parse(content);
86
+ }
87
+ catch {
88
+ throw new CliError('File is not valid JSON.');
89
+ }
90
+ // Anonymize
91
+ const txCounter = { next: 1 };
92
+ const anonymized = anonymizeValue(parsed, txCounter);
93
+ // Output
94
+ const output = JSON.stringify(anonymized, null, 2);
95
+ if (options.output) {
96
+ const outputPath = resolve(options.output);
97
+ writeFileSync(outputPath, output, 'utf8');
98
+ console.log(`Anonymized trace written to: ${options.output}`);
99
+ }
100
+ else {
101
+ console.log(output);
102
+ }
103
+ }
104
+ //# sourceMappingURL=anonymize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anonymize.js","sourceRoot":"","sources":["../../../src/cli/commands/anonymize.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * `ocpp-debugkit ci [dir]` — run all scenarios, exit 0 if all pass, 1 if any fail.
3
+ *
4
+ * Supports `--format json` for CI tooling and `[dir]` for external scenario files.
5
+ */
6
+ export interface CiOptions {
7
+ format: string;
8
+ }
9
+ export declare function ciCommand(dir: string | undefined, options: CiOptions): Promise<void>;
10
+ //# sourceMappingURL=ci.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ci.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/ci.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;CAChB;AAUD,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CA+F1F"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * `ocpp-debugkit ci [dir]` — run all scenarios, exit 0 if all pass, 1 if any fail.
3
+ *
4
+ * Supports `--format json` for CI tooling and `[dir]` for external scenario files.
5
+ */
6
+ import { readFileSync, readdirSync } from 'node:fs';
7
+ import { resolve, join } from 'node:path';
8
+ import { evaluateScenario } from '../../core/assertions.js';
9
+ import { scenarios } from '../../scenarios/index.js';
10
+ export async function ciCommand(dir, options) {
11
+ const results = [];
12
+ // Run built-in scenarios
13
+ for (const scenario of scenarios) {
14
+ const result = evaluateScenario(scenario);
15
+ results.push({
16
+ name: scenario.name,
17
+ passed: result.allPassed,
18
+ detectedFailures: result.detectedFailureCodes,
19
+ expectedFailures: scenario.expectedFailures,
20
+ assertionResults: result.assertions.map((a) => ({
21
+ type: a.assertion.type,
22
+ passed: a.passed,
23
+ message: a.message,
24
+ })),
25
+ });
26
+ }
27
+ // Run external scenarios from directory
28
+ if (dir) {
29
+ const resolvedDir = resolve(dir);
30
+ let files;
31
+ try {
32
+ files = readdirSync(resolvedDir).filter((f) => f.endsWith('.json'));
33
+ }
34
+ catch {
35
+ console.error(`Error: Cannot read directory: ${dir}`);
36
+ process.exitCode = 1;
37
+ return;
38
+ }
39
+ for (const file of files) {
40
+ const filePath = join(resolvedDir, file);
41
+ try {
42
+ const content = readFileSync(filePath, 'utf8');
43
+ const scenario = JSON.parse(content);
44
+ const result = evaluateScenario(scenario);
45
+ results.push({
46
+ name: scenario.name || file,
47
+ passed: result.allPassed,
48
+ detectedFailures: result.detectedFailureCodes,
49
+ expectedFailures: scenario.expectedFailures || [],
50
+ assertionResults: result.assertions.map((a) => ({
51
+ type: a.assertion.type,
52
+ passed: a.passed,
53
+ message: a.message,
54
+ })),
55
+ });
56
+ }
57
+ catch {
58
+ console.error(`Error: Failed to parse scenario file: ${file}`);
59
+ }
60
+ }
61
+ }
62
+ const allPassed = results.every((r) => r.passed);
63
+ if (options.format === 'json') {
64
+ console.log(JSON.stringify({ results, allPassed }, null, 2));
65
+ }
66
+ else {
67
+ console.log('');
68
+ console.log('═'.repeat(60));
69
+ console.log(' OCPP DebugKit — CI Mode');
70
+ console.log('═'.repeat(60));
71
+ console.log('');
72
+ for (const result of results) {
73
+ const status = result.passed ? '✅ PASS' : '❌ FAIL';
74
+ console.log(` ${status} ${result.name}`);
75
+ if (!result.passed) {
76
+ if (JSON.stringify(result.detectedFailures.sort()) !==
77
+ JSON.stringify(result.expectedFailures.sort())) {
78
+ console.log(` Expected: ${result.expectedFailures.join(', ') || 'none'}`);
79
+ console.log(` Detected: ${result.detectedFailures.join(', ') || 'none'}`);
80
+ }
81
+ for (const assertion of result.assertionResults) {
82
+ if (!assertion.passed) {
83
+ console.log(` Assertion "${assertion.type}": ${assertion.message}`);
84
+ }
85
+ }
86
+ }
87
+ }
88
+ console.log('');
89
+ console.log(` Total: ${results.length} | Passed: ${results.filter((r) => r.passed).length} | Failed: ${results.filter((r) => !r.passed).length}`);
90
+ console.log('');
91
+ }
92
+ if (!allPassed) {
93
+ process.exitCode = 1;
94
+ }
95
+ }
96
+ //# sourceMappingURL=ci.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ci.js","sourceRoot":"","sources":["../../../src/cli/commands/ci.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAcrD,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAuB,EAAE,OAAkB;IACzE,MAAM,OAAO,GAAqB,EAAE,CAAC;IAErC,yBAAyB;IACzB,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,gBAAgB,CAAC,QAAoB,CAAC,CAAC;QACtD,OAAO,CAAC,IAAI,CAAC;YACX,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,MAAM,EAAE,MAAM,CAAC,SAAS;YACxB,gBAAgB,EAAE,MAAM,CAAC,oBAAoB;YAC7C,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB;YAC3C,gBAAgB,EAAE,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC9C,IAAI,EAAE,CAAC,CAAC,SAAS,CAAC,IAAI;gBACtB,MAAM,EAAE,CAAC,CAAC,MAAM;gBAChB,OAAO,EAAE,CAAC,CAAC,OAAO;aACnB,CAAC,CAAC;SACJ,CAAC,CAAC;IACL,CAAC;IAED,wCAAwC;IACxC,IAAI,GAAG,EAAE,CAAC;QACR,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,KAAe,CAAC;QACpB,IAAI,CAAC;YACH,KAAK,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;QACtE,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,CAAC,KAAK,CAAC,iCAAiC,GAAG,EAAE,CAAC,CAAC;YACtD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;QAED,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;YACzC,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;gBAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAa,CAAC;gBACjD,MAAM,MAAM,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;gBAC1C,OAAO,CAAC,IAAI,CAAC;oBACX,IAAI,EAAE,QAAQ,CAAC,IAAI,IAAI,IAAI;oBAC3B,MAAM,EAAE,MAAM,CAAC,SAAS;oBACxB,gBAAgB,EAAE,MAAM,CAAC,oBAAoB;oBAC7C,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB,IAAI,EAAE;oBACjD,gBAAgB,EAAE,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;wBAC9C,IAAI,EAAE,CAAC,CAAC,SAAS,CAAC,IAAI;wBACtB,MAAM,EAAE,CAAC,CAAC,MAAM;wBAChB,OAAO,EAAE,CAAC,CAAC,OAAO;qBACnB,CAAC,CAAC;iBACJ,CAAC,CAAC;YACL,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,CAAC,KAAK,CAAC,yCAAyC,IAAI,EAAE,CAAC,CAAC;YACjE,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAEjD,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC/D,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;QACzC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAEhB,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;YACnD,OAAO,CAAC,GAAG,CAAC,KAAK,MAAM,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;YAE3C,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;gBACnB,IACE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC;oBAC9C,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC,EAC9C,CAAC;oBACD,OAAO,CAAC,GAAG,CAAC,sBAAsB,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC;oBAClF,OAAO,CAAC,GAAG,CAAC,sBAAsB,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC;gBACpF,CAAC;gBACD,KAAK,MAAM,SAAS,IAAI,MAAM,CAAC,gBAAgB,EAAE,CAAC;oBAChD,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;wBACtB,OAAO,CAAC,GAAG,CAAC,uBAAuB,SAAS,CAAC,IAAI,MAAM,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC;oBAC9E,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CACT,YAAY,OAAO,CAAC,MAAM,cAAc,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,cAAc,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,CACtI,CAAC;QACF,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC;AACH,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * `ocpp-debugkit diff <a> <b>` — compare two trace files and output differences.
3
+ *
4
+ * Uses `diffTraces()` from the core module.
5
+ */
6
+ export interface DiffOptions {
7
+ format: string;
8
+ }
9
+ export declare function diffCommand(fileA: string, fileB: string, options: DiffOptions): Promise<void>;
10
+ //# sourceMappingURL=diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/diff.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,WAAW,CAC/B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CAsEf"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * `ocpp-debugkit diff <a> <b>` — compare two trace files and output differences.
3
+ *
4
+ * Uses `diffTraces()` from the core module.
5
+ */
6
+ import { parseTrace, diffTraces, ParseError } from '../../core/index.js';
7
+ import { CliError, readTraceFile } from '../utils.js';
8
+ export async function diffCommand(fileA, fileB, options) {
9
+ const contentA = readTraceFile(fileA);
10
+ const contentB = readTraceFile(fileB);
11
+ let resultA, resultB;
12
+ try {
13
+ resultA = parseTrace(contentA);
14
+ resultB = parseTrace(contentB);
15
+ }
16
+ catch (e) {
17
+ if (e instanceof ParseError) {
18
+ throw new CliError(e.message);
19
+ }
20
+ throw new CliError('Failed to parse trace files.');
21
+ }
22
+ const diff = diffTraces(resultA, resultB);
23
+ if (options.format === 'json') {
24
+ console.log(JSON.stringify(diff, null, 2));
25
+ return;
26
+ }
27
+ // Human-readable output
28
+ console.log('');
29
+ console.log('═'.repeat(60));
30
+ console.log(' OCPP DebugKit — Trace Diff');
31
+ console.log('═'.repeat(60));
32
+ console.log('');
33
+ console.log(` Events only in A: ${diff.onlyInA.length}`);
34
+ for (const event of diff.onlyInA) {
35
+ console.log(` ${event.action ?? 'Unknown'} (messageId: ${event.messageId})`);
36
+ }
37
+ console.log('');
38
+ console.log(` Events only in B: ${diff.onlyInB.length}`);
39
+ for (const event of diff.onlyInB) {
40
+ console.log(` ${event.action ?? 'Unknown'} (messageId: ${event.messageId})`);
41
+ }
42
+ console.log('');
43
+ console.log(` Modified events: ${diff.modified.length}`);
44
+ for (const mod of diff.modified) {
45
+ console.log(` [${mod.field}] messageId: ${mod.messageId}`);
46
+ console.log(` A: ${JSON.stringify(mod.valueA)}`);
47
+ console.log(` B: ${JSON.stringify(mod.valueB)}`);
48
+ }
49
+ console.log('');
50
+ console.log(` Failures only in A: ${diff.failuresOnlyInA.length}`);
51
+ for (const f of diff.failuresOnlyInA) {
52
+ console.log(` [${f.severity}] ${f.code}: ${f.description}`);
53
+ }
54
+ console.log('');
55
+ console.log(` Failures only in B: ${diff.failuresOnlyInB.length}`);
56
+ for (const f of diff.failuresOnlyInB) {
57
+ console.log(` [${f.severity}] ${f.code}: ${f.description}`);
58
+ }
59
+ console.log('');
60
+ console.log(' Summary Differences:');
61
+ if (diff.summaryDiff.differences.length > 0) {
62
+ for (const d of diff.summaryDiff.differences) {
63
+ console.log(` ${d}`);
64
+ }
65
+ }
66
+ else {
67
+ console.log(' No summary differences');
68
+ }
69
+ console.log('');
70
+ }
71
+ //# sourceMappingURL=diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.js","sourceRoot":"","sources":["../../../src/cli/commands/diff.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/report.ts"],"names":[],"mappings":"AAAA;;GAEG;AAaH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAwDvF"}
1
+ {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/report.ts"],"names":[],"mappings":"AAAA;;GAEG;AAaH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA6DvF"}
@@ -2,7 +2,7 @@
2
2
  * `ocpp-debugkit report <file>` — generate a report from an OCPP trace file.
3
3
  */
4
4
  import { parseTrace, buildSessionTimeline, detectFailures, summarizeSessions, ParseError, } from '../../core/index.js';
5
- import { generateMarkdownReport } from '../../reporter/index.js';
5
+ import { generateMarkdownReport, generateHtmlReport } from '../../reporter/index.js';
6
6
  import { CliError, readTraceFile } from '../utils.js';
7
7
  export async function reportCommand(file, options) {
8
8
  const content = readTraceFile(file);
@@ -34,14 +34,17 @@ export async function reportCommand(file, options) {
34
34
  const sessions = buildSessionTimeline(result.events);
35
35
  const failures = detectFailures(result.events, sessions);
36
36
  const summaries = summarizeSessions(sessions, failures);
37
- const report = generateMarkdownReport({
37
+ const analysisResult = {
38
38
  events: result.events,
39
39
  sessions,
40
40
  failures,
41
41
  summaries,
42
42
  warnings: result.warnings,
43
43
  metadata,
44
- });
44
+ };
45
+ const report = options.format === 'html'
46
+ ? generateHtmlReport(analysisResult)
47
+ : generateMarkdownReport(analysisResult);
45
48
  if (options.output) {
46
49
  const { writeFileSync } = await import('node:fs');
47
50
  writeFileSync(options.output, report, 'utf8');
@@ -1 +1 @@
1
- {"version":3,"file":"report.js","sourceRoot":"","sources":["../../../src/cli/commands/report.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,cAAc,EACd,iBAAiB,EACjB,UAAU,GAEX,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAOtD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAY,EAAE,OAAsB;IACtE,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IAEpC,gCAAgC;IAChC,IAAI,MAAM,CAAC;IACX,IAAI,CAAC;QACH,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,YAAY,UAAU,EAAE,CAAC;YAC5B,MAAM,IAAI,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QAChC,CAAC;QACD,IAAI,CAAC,YAAY,KAAK,EAAE,CAAC;YACvB,MAAM,IAAI,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QAChC,CAAC;QACD,MAAM,IAAI,QAAQ,CAAC,8BAA8B,CAAC,CAAC;IACrD,CAAC;IAED,uEAAuE;IACvE,IAAI,QAQS,CAAC;IACd,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACnC,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,UAAU,IAAI,MAAM,EAAE,CAAC;YACjE,QAAQ,GAAI,MAAgB,CAAC,QAAQ,CAAC;QACxC,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,oCAAoC;IACtC,CAAC;IAED,MAAM,QAAQ,GAAG,oBAAoB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACrD,MAAM,QAAQ,GAAG,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACzD,MAAM,SAAS,GAAG,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAExD,MAAM,MAAM,GAAG,sBAAsB,CAAC;QACpC,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,QAAQ;QACR,QAAQ;QACR,SAAS;QACT,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,QAAQ;KACT,CAAC,CAAC;IAEH,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;QAClD,aAAa,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QAC9C,OAAO,CAAC,GAAG,CAAC,qBAAqB,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACrD,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACtB,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"report.js","sourceRoot":"","sources":["../../../src/cli/commands/report.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,cAAc,EACd,iBAAiB,EACjB,UAAU,GAEX,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AACrF,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAOtD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAY,EAAE,OAAsB;IACtE,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IAEpC,gCAAgC;IAChC,IAAI,MAAM,CAAC;IACX,IAAI,CAAC;QACH,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,YAAY,UAAU,EAAE,CAAC;YAC5B,MAAM,IAAI,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QAChC,CAAC;QACD,IAAI,CAAC,YAAY,KAAK,EAAE,CAAC;YACvB,MAAM,IAAI,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QAChC,CAAC;QACD,MAAM,IAAI,QAAQ,CAAC,8BAA8B,CAAC,CAAC;IACrD,CAAC;IAED,uEAAuE;IACvE,IAAI,QAQS,CAAC;IACd,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACnC,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,UAAU,IAAI,MAAM,EAAE,CAAC;YACjE,QAAQ,GAAI,MAAgB,CAAC,QAAQ,CAAC;QACxC,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,oCAAoC;IACtC,CAAC;IAED,MAAM,QAAQ,GAAG,oBAAoB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACrD,MAAM,QAAQ,GAAG,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACzD,MAAM,SAAS,GAAG,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAExD,MAAM,cAAc,GAAG;QACrB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,QAAQ;QACR,QAAQ;QACR,SAAS;QACT,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,QAAQ;KACT,CAAC;IAEF,MAAM,MAAM,GACV,OAAO,CAAC,MAAM,KAAK,MAAM;QACvB,CAAC,CAAC,kBAAkB,CAAC,cAAc,CAAC;QACpC,CAAC,CAAC,sBAAsB,CAAC,cAAc,CAAC,CAAC;IAE7C,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;QAClD,aAAa,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QAC9C,OAAO,CAAC,GAAG,CAAC,qBAAqB,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACrD,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACtB,CAAC;AACH,CAAC"}