@ocpp-debugkit/toolkit 0.2.1 → 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.
- package/README.md +234 -12
- package/dist/cli/commands/anonymize.d.ts +15 -0
- package/dist/cli/commands/anonymize.d.ts.map +1 -0
- package/dist/cli/commands/anonymize.js +104 -0
- package/dist/cli/commands/anonymize.js.map +1 -0
- package/dist/cli/commands/ci.d.ts +10 -0
- package/dist/cli/commands/ci.d.ts.map +1 -0
- package/dist/cli/commands/ci.js +96 -0
- package/dist/cli/commands/ci.js.map +1 -0
- package/dist/cli/commands/diff.d.ts +10 -0
- package/dist/cli/commands/diff.d.ts.map +1 -0
- package/dist/cli/commands/diff.js +71 -0
- package/dist/cli/commands/diff.js.map +1 -0
- package/dist/cli/index.js +27 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/core/assertions.d.ts +36 -0
- package/dist/core/assertions.d.ts.map +1 -0
- package/dist/core/assertions.js +335 -0
- package/dist/core/assertions.js.map +1 -0
- package/dist/core/detection.d.ts +8 -1
- package/dist/core/detection.d.ts.map +1 -1
- package/dist/core/detection.js +287 -1
- package/dist/core/detection.js.map +1 -1
- package/dist/core/diff.d.ts +68 -0
- package/dist/core/diff.d.ts.map +1 -0
- package/dist/core/diff.js +199 -0
- package/dist/core/diff.js.map +1 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +4 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/types.d.ts +77 -2
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/scenarios/__scenarios__/heartbeat-irregular.d.ts +51 -0
- package/dist/scenarios/__scenarios__/heartbeat-irregular.d.ts.map +1 -0
- package/dist/scenarios/__scenarios__/heartbeat-irregular.js +81 -0
- package/dist/scenarios/__scenarios__/heartbeat-irregular.js.map +1 -0
- package/dist/scenarios/__scenarios__/meter-anomaly.d.ts +115 -0
- package/dist/scenarios/__scenarios__/meter-anomaly.d.ts.map +1 -0
- package/dist/scenarios/__scenarios__/meter-anomaly.js +152 -0
- package/dist/scenarios/__scenarios__/meter-anomaly.js.map +1 -0
- package/dist/scenarios/__scenarios__/short-session.d.ts +111 -0
- package/dist/scenarios/__scenarios__/short-session.d.ts.map +1 -0
- package/dist/scenarios/__scenarios__/short-session.js +129 -0
- package/dist/scenarios/__scenarios__/short-session.js.map +1 -0
- package/dist/scenarios/__scenarios__/slow-csms-response.d.ts +52 -0
- package/dist/scenarios/__scenarios__/slow-csms-response.d.ts.map +1 -0
- package/dist/scenarios/__scenarios__/slow-csms-response.js +61 -0
- package/dist/scenarios/__scenarios__/slow-csms-response.js.map +1 -0
- package/dist/scenarios/__scenarios__/unexpected-start.js +3 -3
- package/dist/scenarios/__scenarios__/unresponsive-csms.d.ts +53 -0
- package/dist/scenarios/__scenarios__/unresponsive-csms.d.ts.map +1 -0
- package/dist/scenarios/__scenarios__/unresponsive-csms.js +53 -0
- package/dist/scenarios/__scenarios__/unresponsive-csms.js.map +1 -0
- package/dist/scenarios/compare.d.ts +39 -0
- package/dist/scenarios/compare.d.ts.map +1 -0
- package/dist/scenarios/compare.js +77 -0
- package/dist/scenarios/compare.js.map +1 -0
- package/dist/scenarios/index.d.ts +10 -3
- package/dist/scenarios/index.d.ts.map +1 -1
- package/dist/scenarios/index.js +22 -1
- package/dist/scenarios/index.js.map +1 -1
- 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
|
|
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
|
-
|
|
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
|
-
###
|
|
132
|
+
### Replay — Deterministic Event Replay
|
|
14
133
|
|
|
15
|
-
```
|
|
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
|
-
|
|
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
|
-
|
|
210
|
+
## CLI
|
|
24
211
|
|
|
25
212
|
```bash
|
|
26
|
-
|
|
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
|
-
|
|
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":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAM3D,6BAA6B;AAC7B,MAAM,QAAQ,GAAG,2BAA2B,CAAC;AAC7C,MAAM,QAAQ,GAAG,yCAAyC,CAAC;AAC3D,MAAM,KAAK,GAAG,yCAAyC,CAAC;AAExD,SAAS,cAAc,CAAC,KAAc,EAAE,SAA2B;IACjE,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,IAAI,MAAM,GAAG,KAAK,CAAC;QACnB,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC;QACtD,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC;QACtD,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC;QAChD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC;IAC9D,CAAC;IAED,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,MAAM,GAAG,GAAG,KAAgC,CAAC;QAC7C,MAAM,MAAM,GAA4B,EAAE,CAAC;QAE3C,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YAC7C,mCAAmC;YACnC,IAAI,GAAG,KAAK,OAAO,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;gBAC/C,MAAM,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC;YAC7B,CAAC;iBAAM,IAAI,GAAG,KAAK,yBAAyB,IAAI,GAAG,KAAK,uBAAuB,EAAE,CAAC;gBAChF,MAAM,CAAC,GAAG,CAAC,GAAG,cAAc,CAAC;YAC/B,CAAC;iBAAM,IAAI,GAAG,KAAK,WAAW,EAAE,CAAC;gBAC/B,MAAM,CAAC,GAAG,CAAC,GAAG,cAAc,CAAC;YAC/B,CAAC;iBAAM,IAAI,GAAG,KAAK,eAAe,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;gBAC9D,MAAM,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC;YACjC,CAAC;iBAAM,IAAI,GAAG,KAAK,YAAY,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;gBAC3D,MAAM,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC;YAC7B,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,GAAG,CAAC,GAAG,cAAc,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;YAC/C,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAAY,EAAE,OAAyB;IAC5E,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE/B,aAAa;IACb,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;IAC7C,IAAI,KAAK,CAAC;IACV,IAAI,CAAC;QACH,KAAK,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,QAAQ,CAAC,mBAAmB,IAAI,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,IAAI,KAAK,CAAC,IAAI,GAAG,oBAAoB,EAAE,CAAC;QACtC,MAAM,IAAI,QAAQ,CAChB,cAAc,KAAK,CAAC,IAAI,oCAAoC,oBAAoB,UAAU,CAC3F,CAAC;IACJ,CAAC;IAED,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,QAAQ,CAAC,wBAAwB,IAAI,EAAE,CAAC,CAAC;IACrD,CAAC;IAED,aAAa;IACb,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,QAAQ,CAAC,yBAAyB,CAAC,CAAC;IAChD,CAAC;IAED,YAAY;IACZ,MAAM,SAAS,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IAC9B,MAAM,UAAU,GAAG,cAAc,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAErD,SAAS;IACT,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAEnD,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC3C,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QAC1C,OAAO,CAAC,GAAG,CAAC,gCAAgC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAChE,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACtB,CAAC;AACH,CAAC"}
|
|
@@ -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":"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"}
|
|
@@ -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":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACzE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAMtD,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,KAAa,EACb,KAAa,EACb,OAAoB;IAEpB,MAAM,QAAQ,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IACtC,MAAM,QAAQ,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAEtC,IAAI,OAAO,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC;QACH,OAAO,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;QAC/B,OAAO,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IACjC,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,MAAM,IAAI,QAAQ,CAAC,8BAA8B,CAAC,CAAC;IACrD,CAAC;IAED,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAE1C,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC3C,OAAO;IACT,CAAC;IAED,wBAAwB;IACxB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5B,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;IAC5C,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC1D,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QACjC,OAAO,CAAC,GAAG,CAAC,OAAO,KAAK,CAAC,MAAM,IAAI,SAAS,gBAAgB,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC;IAClF,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC1D,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QACjC,OAAO,CAAC,GAAG,CAAC,OAAO,KAAK,CAAC,MAAM,IAAI,SAAS,gBAAgB,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC;IAClF,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAC1D,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChC,OAAO,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC,KAAK,gBAAgB,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC;QAC9D,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACtD,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACxD,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,CAAC,CAAC;IACpE,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;QACrC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;IACjE,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,CAAC,CAAC;IACpE,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;QACrC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;IACjE,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;IACtC,IAAI,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5C,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC;YAC7C,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAClB,CAAC"}
|
package/dist/cli/index.js
CHANGED
|
@@ -7,6 +7,9 @@ import { Command } from 'commander';
|
|
|
7
7
|
import { inspectCommand } from './commands/inspect.js';
|
|
8
8
|
import { reportCommand } from './commands/report.js';
|
|
9
9
|
import { scenarioListCommand, scenarioRunCommand, scenarioRunFileCommand, } from './commands/scenario.js';
|
|
10
|
+
import { ciCommand } from './commands/ci.js';
|
|
11
|
+
import { anonymizeCommand } from './commands/anonymize.js';
|
|
12
|
+
import { diffCommand } from './commands/diff.js';
|
|
10
13
|
const program = new Command();
|
|
11
14
|
program
|
|
12
15
|
.name('ocpp-debugkit')
|
|
@@ -52,5 +55,29 @@ scenarioCmd
|
|
|
52
55
|
throw new Error('Either a scenario name or --file <path> is required.');
|
|
53
56
|
}
|
|
54
57
|
});
|
|
58
|
+
// ci
|
|
59
|
+
program
|
|
60
|
+
.command('ci [dir]')
|
|
61
|
+
.description('Run all scenarios and exit 0 (all pass) or 1 (any fail)')
|
|
62
|
+
.option('--format <format>', 'Output format (text, json)', 'text')
|
|
63
|
+
.action(async (dir, options) => {
|
|
64
|
+
await ciCommand(dir, options);
|
|
65
|
+
});
|
|
66
|
+
// anonymize
|
|
67
|
+
program
|
|
68
|
+
.command('anonymize <file>')
|
|
69
|
+
.description('Strip sensitive fields from a trace file')
|
|
70
|
+
.option('-o, --output <file>', 'Write anonymized trace to file (default: stdout)')
|
|
71
|
+
.action(async (file, options) => {
|
|
72
|
+
await anonymizeCommand(file, options);
|
|
73
|
+
});
|
|
74
|
+
// diff
|
|
75
|
+
program
|
|
76
|
+
.command('diff <a> <b>')
|
|
77
|
+
.description('Compare two trace files and show differences')
|
|
78
|
+
.option('--format <format>', 'Output format (text, json)', 'text')
|
|
79
|
+
.action(async (fileA, fileB, options) => {
|
|
80
|
+
await diffCommand(fileA, fileB, options);
|
|
81
|
+
});
|
|
55
82
|
program.parse();
|
|
56
83
|
//# sourceMappingURL=index.js.map
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,sBAAsB,GACvB,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|