@ocpp-debugkit/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/dist/__fixtures__/connector-fault.d.ts +116 -0
- package/dist/__fixtures__/connector-fault.d.ts.map +1 -0
- package/dist/__fixtures__/connector-fault.js +231 -0
- package/dist/__fixtures__/connector-fault.js.map +1 -0
- package/dist/__fixtures__/failed-auth.d.ts +64 -0
- package/dist/__fixtures__/failed-auth.d.ts.map +1 -0
- package/dist/__fixtures__/failed-auth.js +173 -0
- package/dist/__fixtures__/failed-auth.js.map +1 -0
- package/dist/__fixtures__/normal-session.d.ts +108 -0
- package/dist/__fixtures__/normal-session.d.ts.map +1 -0
- package/dist/__fixtures__/normal-session.js +261 -0
- package/dist/__fixtures__/normal-session.js.map +1 -0
- package/dist/detection.d.ts +23 -0
- package/dist/detection.d.ts.map +1 -0
- package/dist/detection.js +226 -0
- package/dist/detection.js.map +1 -0
- package/dist/fixtures/index.d.ts +298 -0
- package/dist/fixtures/index.d.ts.map +1 -0
- package/dist/fixtures/index.js +19 -0
- package/dist/fixtures/index.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/normalizer.d.ts +83 -0
- package/dist/normalizer.d.ts.map +1 -0
- package/dist/normalizer.js +289 -0
- package/dist/normalizer.js.map +1 -0
- package/dist/parser.d.ts +53 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +282 -0
- package/dist/parser.js.map +1 -0
- package/dist/schemas.d.ts +68 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +81 -0
- package/dist/schemas.js.map +1 -0
- package/dist/summarizer.d.ts +25 -0
- package/dist/summarizer.d.ts.map +1 -0
- package/dist/summarizer.js +47 -0
- package/dist/summarizer.js.map +1 -0
- package/dist/timeline.d.ts +26 -0
- package/dist/timeline.d.ts.map +1 -0
- package/dist/timeline.js +318 -0
- package/dist/timeline.js.map +1 -0
- package/dist/types.d.ts +160 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/validator.d.ts +33 -0
- package/dist/validator.d.ts.map +1 -0
- package/dist/validator.js +122 -0
- package/dist/validator.js.map +1 -0
- package/package.json +61 -0
package/dist/parser.js
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trace parser — accepts untrusted input in JSON Object, JSONL, or bare array
|
|
3
|
+
* format and produces normalized `Event` objects with warnings for malformed
|
|
4
|
+
* entries.
|
|
5
|
+
*
|
|
6
|
+
* Security:
|
|
7
|
+
* - Safe JSON parsing with try/catch
|
|
8
|
+
* - Input size limit (10 MB)
|
|
9
|
+
* - Event count limit (10,000)
|
|
10
|
+
* - Zod schema validation prevents prototype pollution
|
|
11
|
+
* - Malformed individual events are skipped with a `ParseWarning`
|
|
12
|
+
* - Structural errors (invalid JSON, missing events array) fail fast
|
|
13
|
+
*
|
|
14
|
+
* @see ADR-0002 (input trace formats)
|
|
15
|
+
* @see ADR-0007 (malformed trace handling)
|
|
16
|
+
* @see docs/trace-format-spec.md
|
|
17
|
+
*/
|
|
18
|
+
import { bareArraySchema, rawOcppMessageSchema, traceEventInputSchema, traceSchema, } from './schemas.js';
|
|
19
|
+
import { normalizeEvents } from './normalizer.js';
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Limits (ADR-0007, docs/trace-format-spec.md)
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
/** Maximum input size in bytes (10 MB). */
|
|
24
|
+
export const MAX_INPUT_SIZE_BYTES = 10 * 1024 * 1024;
|
|
25
|
+
/** Maximum number of events after parsing. */
|
|
26
|
+
export const MAX_EVENT_COUNT = 10_000;
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// Errors
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
/** Error thrown when input exceeds size limits or has structural problems. */
|
|
31
|
+
export class ParseError extends Error {
|
|
32
|
+
constructor(message) {
|
|
33
|
+
super(message);
|
|
34
|
+
this.name = 'ParseError';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// Input size validation
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
/**
|
|
41
|
+
* Validate the input size before any parsing.
|
|
42
|
+
* @throws {ParseError} if the input exceeds the maximum size.
|
|
43
|
+
*/
|
|
44
|
+
function validateInputSize(input) {
|
|
45
|
+
const byteLength = Buffer.byteLength(input, 'utf8');
|
|
46
|
+
if (byteLength > MAX_INPUT_SIZE_BYTES) {
|
|
47
|
+
throw new ParseError(`Input size (${byteLength} bytes) exceeds the maximum allowed size ` +
|
|
48
|
+
`(${MAX_INPUT_SIZE_BYTES} bytes).`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Validate the event count after parsing.
|
|
53
|
+
* @throws {ParseError} if the event count exceeds the maximum.
|
|
54
|
+
*/
|
|
55
|
+
function validateEventCount(events) {
|
|
56
|
+
if (events.length > MAX_EVENT_COUNT) {
|
|
57
|
+
throw new ParseError(`Event count (${events.length}) exceeds the maximum allowed count ` + `(${MAX_EVENT_COUNT}).`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function detectFormat(input) {
|
|
61
|
+
const trimmed = input.trim();
|
|
62
|
+
if (trimmed.startsWith('{')) {
|
|
63
|
+
// Could be JSON Object format or JSONL starting with an event object.
|
|
64
|
+
// JSONL has multiple lines; a JSON Object is a single JSON value.
|
|
65
|
+
// We check if the trimmed input contains a newline followed by another `{`
|
|
66
|
+
// that is NOT inside a string (simplified check: if the last non-whitespace
|
|
67
|
+
// character is `}`, and there's content after the first `}`... but that's
|
|
68
|
+
// complex. Simpler: try parsing as a single JSON value first, and if that
|
|
69
|
+
// fails, try JSONL.
|
|
70
|
+
return 'json-object';
|
|
71
|
+
}
|
|
72
|
+
if (trimmed.startsWith('[')) {
|
|
73
|
+
return 'bare-array';
|
|
74
|
+
}
|
|
75
|
+
// Default to JSONL for other cases (each line is a JSON object)
|
|
76
|
+
return 'jsonl';
|
|
77
|
+
}
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
// JSONL parsing
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
/**
|
|
82
|
+
* Parse JSONL input: one event per line.
|
|
83
|
+
* Blank lines are ignored. Malformed lines produce warnings.
|
|
84
|
+
*
|
|
85
|
+
* @returns array of valid TraceEventInput and warnings for malformed lines.
|
|
86
|
+
*/
|
|
87
|
+
function parseJsonl(input) {
|
|
88
|
+
const lines = input.split('\n');
|
|
89
|
+
const events = [];
|
|
90
|
+
const warnings = [];
|
|
91
|
+
for (let i = 0; i < lines.length; i++) {
|
|
92
|
+
const line = lines[i];
|
|
93
|
+
if (line === undefined)
|
|
94
|
+
continue;
|
|
95
|
+
const trimmed = line.trim();
|
|
96
|
+
if (trimmed === '') {
|
|
97
|
+
continue; // Skip blank lines
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
const parsed = JSON.parse(trimmed);
|
|
101
|
+
const result = traceEventInputSchema.safeParse(parsed);
|
|
102
|
+
if (result.success) {
|
|
103
|
+
events.push(result.data);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
warnings.push({
|
|
107
|
+
index: i,
|
|
108
|
+
message: `Line ${i + 1}: invalid event structure — ${result.error.issues[0]?.message ?? 'unknown error'}`,
|
|
109
|
+
rawInput: trimmed.slice(0, 200),
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
catch (e) {
|
|
114
|
+
warnings.push({
|
|
115
|
+
index: i,
|
|
116
|
+
message: `Line ${i + 1}: invalid JSON — ${e instanceof Error ? e.message : 'unknown error'}`,
|
|
117
|
+
rawInput: trimmed.slice(0, 200),
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return { events, warnings };
|
|
122
|
+
}
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
// Bare array parsing
|
|
125
|
+
// ---------------------------------------------------------------------------
|
|
126
|
+
/**
|
|
127
|
+
* Parse bare array format: `[ [2, "id", "Action", {...}], [3, "id", {...}], ... ]`
|
|
128
|
+
*
|
|
129
|
+
* Each element is a raw OCPP message. Direction is inferred; timestamp is null.
|
|
130
|
+
*/
|
|
131
|
+
function parseBareArray(input) {
|
|
132
|
+
const warnings = [];
|
|
133
|
+
try {
|
|
134
|
+
const parsed = JSON.parse(input);
|
|
135
|
+
const result = bareArraySchema.safeParse(parsed);
|
|
136
|
+
if (!result.success) {
|
|
137
|
+
throw new ParseError(`Invalid bare array format — ${result.error.issues[0]?.message ?? 'unknown error'}`);
|
|
138
|
+
}
|
|
139
|
+
// Convert raw messages to TraceEventInput (with null timestamp, no direction)
|
|
140
|
+
const events = result.data.map((message) => ({
|
|
141
|
+
timestamp: null,
|
|
142
|
+
direction: undefined,
|
|
143
|
+
message,
|
|
144
|
+
}));
|
|
145
|
+
return { events, warnings };
|
|
146
|
+
}
|
|
147
|
+
catch (e) {
|
|
148
|
+
if (e instanceof ParseError)
|
|
149
|
+
throw e;
|
|
150
|
+
throw new ParseError(`Invalid JSON in bare array format — ${e instanceof Error ? e.message : 'unknown error'}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
// JSON Object parsing
|
|
155
|
+
// ---------------------------------------------------------------------------
|
|
156
|
+
/**
|
|
157
|
+
* Parse JSON Object format: `{ "traceId": ..., "metadata": ..., "events": [...] }`
|
|
158
|
+
*
|
|
159
|
+
* Also handles the case where the JSON Object contains an array of raw messages
|
|
160
|
+
* in the `events` field (degenerate — treated as bare messages without timestamp/direction).
|
|
161
|
+
*/
|
|
162
|
+
function parseJsonObject(input) {
|
|
163
|
+
let parsed;
|
|
164
|
+
try {
|
|
165
|
+
parsed = JSON.parse(input);
|
|
166
|
+
}
|
|
167
|
+
catch (e) {
|
|
168
|
+
throw new ParseError(`Invalid JSON — ${e instanceof Error ? e.message : 'unknown error'}`);
|
|
169
|
+
}
|
|
170
|
+
// If the parsed value is an array, treat it as a bare array (degenerate JSON Object)
|
|
171
|
+
if (Array.isArray(parsed)) {
|
|
172
|
+
return parseBareArray(input);
|
|
173
|
+
}
|
|
174
|
+
const result = traceSchema.safeParse(parsed);
|
|
175
|
+
if (!result.success) {
|
|
176
|
+
throw new ParseError(`Invalid trace structure — ${result.error.issues[0]?.message ?? 'unknown error'}`);
|
|
177
|
+
}
|
|
178
|
+
const trace = result.data;
|
|
179
|
+
const warnings = [];
|
|
180
|
+
const events = [];
|
|
181
|
+
// Validate each event individually — malformed events are skipped with warnings
|
|
182
|
+
for (let i = 0; i < trace.events.length; i++) {
|
|
183
|
+
const eventInput = trace.events[i];
|
|
184
|
+
if (eventInput === undefined)
|
|
185
|
+
continue;
|
|
186
|
+
// Re-validate the individual message (the schema already validated the
|
|
187
|
+
// overall structure, but we want to catch edge cases in the message array)
|
|
188
|
+
const msgResult = rawOcppMessageSchema.safeParse(eventInput.message);
|
|
189
|
+
if (!msgResult.success) {
|
|
190
|
+
warnings.push({
|
|
191
|
+
index: i,
|
|
192
|
+
message: `Event ${i + 1}: invalid OCPP message — ${msgResult.error.issues[0]?.message ?? 'unknown error'}`,
|
|
193
|
+
});
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
events.push({
|
|
197
|
+
timestamp: eventInput.timestamp ?? null,
|
|
198
|
+
direction: eventInput.direction,
|
|
199
|
+
message: msgResult.data,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
if (events.length === 0) {
|
|
203
|
+
throw new ParseError('Trace contains no valid events after parsing.');
|
|
204
|
+
}
|
|
205
|
+
return { events, warnings };
|
|
206
|
+
}
|
|
207
|
+
// ---------------------------------------------------------------------------
|
|
208
|
+
// parseTrace()
|
|
209
|
+
// ---------------------------------------------------------------------------
|
|
210
|
+
/**
|
|
211
|
+
* Parse a trace from a raw string input.
|
|
212
|
+
*
|
|
213
|
+
* Accepts:
|
|
214
|
+
* - JSON Object format (metadata + events array)
|
|
215
|
+
* - JSONL format (one event per line)
|
|
216
|
+
* - Bare array format (array of raw OCPP message arrays)
|
|
217
|
+
*
|
|
218
|
+
* @param input - Raw trace string (untrusted)
|
|
219
|
+
* @returns ParseResult with normalized events and warnings for malformed entries
|
|
220
|
+
* @throws {ParseError} for structural errors (invalid JSON, missing events, size/count limits exceeded)
|
|
221
|
+
*
|
|
222
|
+
* @see ADR-0002, ADR-0007
|
|
223
|
+
*/
|
|
224
|
+
export function parseTrace(input) {
|
|
225
|
+
// Validate input size
|
|
226
|
+
validateInputSize(input);
|
|
227
|
+
const trimmed = input.trim();
|
|
228
|
+
if (trimmed === '') {
|
|
229
|
+
throw new ParseError('Input is empty.');
|
|
230
|
+
}
|
|
231
|
+
// Detect format and parse accordingly
|
|
232
|
+
const format = detectFormat(trimmed);
|
|
233
|
+
let events;
|
|
234
|
+
let warnings;
|
|
235
|
+
if (format === 'bare-array') {
|
|
236
|
+
const result = parseBareArray(trimmed);
|
|
237
|
+
events = result.events;
|
|
238
|
+
warnings = result.warnings;
|
|
239
|
+
}
|
|
240
|
+
else if (format === 'jsonl') {
|
|
241
|
+
const result = parseJsonl(trimmed);
|
|
242
|
+
events = result.events;
|
|
243
|
+
warnings = result.warnings;
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
// json-object — but it might actually be JSONL if JSON.parse fails
|
|
247
|
+
try {
|
|
248
|
+
const result = parseJsonObject(trimmed);
|
|
249
|
+
events = result.events;
|
|
250
|
+
warnings = result.warnings;
|
|
251
|
+
}
|
|
252
|
+
catch (e) {
|
|
253
|
+
if (e instanceof ParseError) {
|
|
254
|
+
// If JSON Object parsing fails, try JSONL as a fallback
|
|
255
|
+
// (input starts with `{` but might be JSONL with event objects)
|
|
256
|
+
const jsonlResult = parseJsonl(trimmed);
|
|
257
|
+
if (jsonlResult.events.length > 0) {
|
|
258
|
+
events = jsonlResult.events;
|
|
259
|
+
warnings = jsonlResult.warnings;
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
throw e;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
throw e;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
// Validate event count
|
|
271
|
+
validateEventCount(events);
|
|
272
|
+
if (events.length === 0) {
|
|
273
|
+
throw new ParseError('Trace contains no valid events.');
|
|
274
|
+
}
|
|
275
|
+
// Normalize events into canonical Event objects
|
|
276
|
+
const normalizedEvents = normalizeEvents(events);
|
|
277
|
+
return {
|
|
278
|
+
events: normalizedEvents,
|
|
279
|
+
warnings,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
//# sourceMappingURL=parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod schemas for OCPP DebugKit trace input validation.
|
|
3
|
+
*
|
|
4
|
+
* These schemas validate untrusted input at the boundary (ADR-0007).
|
|
5
|
+
* They prevent prototype pollution and ensure structural correctness
|
|
6
|
+
* before any processing occurs.
|
|
7
|
+
*
|
|
8
|
+
* @see docs/trace-format-spec.md
|
|
9
|
+
* @see ADR-0002 (input trace formats)
|
|
10
|
+
* @see ADR-0003 (canonical event model)
|
|
11
|
+
* @see ADR-0007 (malformed trace handling)
|
|
12
|
+
*/
|
|
13
|
+
import { z } from 'zod';
|
|
14
|
+
export declare const directionSchema: z.ZodEnum<{
|
|
15
|
+
CS_TO_CSMS: "CS_TO_CSMS";
|
|
16
|
+
CSMS_TO_CS: "CSMS_TO_CS";
|
|
17
|
+
UNKNOWN: "UNKNOWN";
|
|
18
|
+
}>;
|
|
19
|
+
export declare const messageTypeSchema: z.ZodEnum<{
|
|
20
|
+
Call: "Call";
|
|
21
|
+
CallResult: "CallResult";
|
|
22
|
+
CallError: "CallError";
|
|
23
|
+
}>;
|
|
24
|
+
/**
|
|
25
|
+
* Raw OCPP 1.6 JSON message array.
|
|
26
|
+
* - Call (2): [2, UniqueId, Action, Payload]
|
|
27
|
+
* - CallResult (3): [3, UniqueId, Payload]
|
|
28
|
+
* - CallError (4): [4, UniqueId, ErrorCode, ErrorDescription, ErrorDetails]
|
|
29
|
+
*
|
|
30
|
+
* We validate the first two elements strictly (MessageTypeId + UniqueId)
|
|
31
|
+
* and accept arbitrary payload data.
|
|
32
|
+
*/
|
|
33
|
+
export declare const rawOcppMessageSchema: z.ZodTuple<[z.ZodNumber, z.ZodString], z.ZodUnknown>;
|
|
34
|
+
export declare const traceEventInputSchema: z.ZodObject<{
|
|
35
|
+
timestamp: z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>>;
|
|
36
|
+
direction: z.ZodOptional<z.ZodEnum<{
|
|
37
|
+
CS_TO_CSMS: "CS_TO_CSMS";
|
|
38
|
+
CSMS_TO_CS: "CSMS_TO_CS";
|
|
39
|
+
UNKNOWN: "UNKNOWN";
|
|
40
|
+
}>>;
|
|
41
|
+
message: z.ZodTuple<[z.ZodNumber, z.ZodString], z.ZodUnknown>;
|
|
42
|
+
}, z.core.$strip>;
|
|
43
|
+
export declare const traceMetadataSchema: z.ZodObject<{
|
|
44
|
+
stationId: z.ZodOptional<z.ZodString>;
|
|
45
|
+
ocppVersion: z.ZodOptional<z.ZodString>;
|
|
46
|
+
source: z.ZodOptional<z.ZodString>;
|
|
47
|
+
description: z.ZodOptional<z.ZodString>;
|
|
48
|
+
}, z.core.$strip>;
|
|
49
|
+
export declare const traceSchema: z.ZodObject<{
|
|
50
|
+
traceId: z.ZodOptional<z.ZodString>;
|
|
51
|
+
metadata: z.ZodOptional<z.ZodObject<{
|
|
52
|
+
stationId: z.ZodOptional<z.ZodString>;
|
|
53
|
+
ocppVersion: z.ZodOptional<z.ZodString>;
|
|
54
|
+
source: z.ZodOptional<z.ZodString>;
|
|
55
|
+
description: z.ZodOptional<z.ZodString>;
|
|
56
|
+
}, z.core.$strip>>;
|
|
57
|
+
events: z.ZodArray<z.ZodObject<{
|
|
58
|
+
timestamp: z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>>;
|
|
59
|
+
direction: z.ZodOptional<z.ZodEnum<{
|
|
60
|
+
CS_TO_CSMS: "CS_TO_CSMS";
|
|
61
|
+
CSMS_TO_CS: "CSMS_TO_CS";
|
|
62
|
+
UNKNOWN: "UNKNOWN";
|
|
63
|
+
}>>;
|
|
64
|
+
message: z.ZodTuple<[z.ZodNumber, z.ZodString], z.ZodUnknown>;
|
|
65
|
+
}, z.core.$strip>>;
|
|
66
|
+
}, z.core.$strip>;
|
|
67
|
+
export declare const bareArraySchema: z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodString], z.ZodUnknown>>;
|
|
68
|
+
//# sourceMappingURL=schemas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,eAAO,MAAM,eAAe;;;;EAAkD,CAAC;AAE/E,eAAO,MAAM,iBAAiB;;;;EAA8C,CAAC;AAE7E;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,sDAwB7B,CAAC;AAML,eAAO,MAAM,qBAAqB;;;;;;;;iBAIhC,CAAC;AAMH,eAAO,MAAM,mBAAmB;;;;;iBAK9B,CAAC;AAMH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;iBAItB,CAAC;AAMH,eAAO,MAAM,eAAe,kEAAuC,CAAC"}
|
package/dist/schemas.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod schemas for OCPP DebugKit trace input validation.
|
|
3
|
+
*
|
|
4
|
+
* These schemas validate untrusted input at the boundary (ADR-0007).
|
|
5
|
+
* They prevent prototype pollution and ensure structural correctness
|
|
6
|
+
* before any processing occurs.
|
|
7
|
+
*
|
|
8
|
+
* @see docs/trace-format-spec.md
|
|
9
|
+
* @see ADR-0002 (input trace formats)
|
|
10
|
+
* @see ADR-0003 (canonical event model)
|
|
11
|
+
* @see ADR-0007 (malformed trace handling)
|
|
12
|
+
*/
|
|
13
|
+
import { z } from 'zod';
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Primitives
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
export const directionSchema = z.enum(['CS_TO_CSMS', 'CSMS_TO_CS', 'UNKNOWN']);
|
|
18
|
+
export const messageTypeSchema = z.enum(['Call', 'CallResult', 'CallError']);
|
|
19
|
+
/**
|
|
20
|
+
* Raw OCPP 1.6 JSON message array.
|
|
21
|
+
* - Call (2): [2, UniqueId, Action, Payload]
|
|
22
|
+
* - CallResult (3): [3, UniqueId, Payload]
|
|
23
|
+
* - CallError (4): [4, UniqueId, ErrorCode, ErrorDescription, ErrorDetails]
|
|
24
|
+
*
|
|
25
|
+
* We validate the first two elements strictly (MessageTypeId + UniqueId)
|
|
26
|
+
* and accept arbitrary payload data.
|
|
27
|
+
*/
|
|
28
|
+
export const rawOcppMessageSchema = z
|
|
29
|
+
.tuple([z.number(), z.string()])
|
|
30
|
+
.rest(z.unknown())
|
|
31
|
+
.refine((msg) => msg[0] === 2 || msg[0] === 3 || msg[0] === 4, {
|
|
32
|
+
message: 'MessageTypeId must be 2 (Call), 3 (CallResult), or 4 (CallError)',
|
|
33
|
+
})
|
|
34
|
+
.refine((msg) => (msg[0] === 2 ? msg.length >= 4 : true), {
|
|
35
|
+
message: 'Call message must have at least 4 elements: [2, UniqueId, Action, Payload]',
|
|
36
|
+
})
|
|
37
|
+
.refine((msg) => (msg[0] === 3 ? msg.length >= 3 : true), {
|
|
38
|
+
message: 'CallResult message must have at least 3 elements: [3, UniqueId, Payload]',
|
|
39
|
+
})
|
|
40
|
+
.refine((msg) => (msg[0] === 4 ? msg.length >= 5 : true), {
|
|
41
|
+
message: 'CallError message must have at least 5 elements: [4, UniqueId, ErrorCode, ErrorDescription, ErrorDetails]',
|
|
42
|
+
})
|
|
43
|
+
.refine((msg) => (msg[0] === 2 ? typeof msg[2] === 'string' : true), {
|
|
44
|
+
message: 'Call message Action (index 2) must be a string',
|
|
45
|
+
})
|
|
46
|
+
.refine((msg) => (msg[0] === 4 ? typeof msg[2] === 'string' : true), {
|
|
47
|
+
message: 'CallError ErrorCode (index 2) must be a string',
|
|
48
|
+
})
|
|
49
|
+
.refine((msg) => (msg[0] === 4 ? typeof msg[3] === 'string' : true), {
|
|
50
|
+
message: 'CallError ErrorDescription (index 3) must be a string',
|
|
51
|
+
});
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// Trace Event Input (ADR-0002, ADR-0003)
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
export const traceEventInputSchema = z.object({
|
|
56
|
+
timestamp: z.union([z.string(), z.number()]).nullable().optional(),
|
|
57
|
+
direction: directionSchema.optional(),
|
|
58
|
+
message: rawOcppMessageSchema,
|
|
59
|
+
});
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
// Trace Metadata (ADR-0002)
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
export const traceMetadataSchema = z.object({
|
|
64
|
+
stationId: z.string().optional(),
|
|
65
|
+
ocppVersion: z.string().optional(),
|
|
66
|
+
source: z.string().optional(),
|
|
67
|
+
description: z.string().optional(),
|
|
68
|
+
});
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
// JSON Object Trace Format (ADR-0002)
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
export const traceSchema = z.object({
|
|
73
|
+
traceId: z.string().optional(),
|
|
74
|
+
metadata: traceMetadataSchema.optional(),
|
|
75
|
+
events: z.array(traceEventInputSchema).min(1, { message: 'events array must not be empty' }),
|
|
76
|
+
});
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
// Bare Array Format (degenerate — array of raw OCPP messages)
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
export const bareArraySchema = z.array(rawOcppMessageSchema).min(1);
|
|
81
|
+
//# sourceMappingURL=schemas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemas.js","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session summarizer — produces overview statistics for a charging session.
|
|
3
|
+
*
|
|
4
|
+
* @see ADR-0003
|
|
5
|
+
*/
|
|
6
|
+
import type { Session, SessionSummary } from './types.js';
|
|
7
|
+
/**
|
|
8
|
+
* Summarize a charging session with overview statistics.
|
|
9
|
+
*
|
|
10
|
+
* @param session - The session to summarize
|
|
11
|
+
* @param failureCount - Number of failures detected in this session
|
|
12
|
+
* @returns Summary statistics
|
|
13
|
+
*/
|
|
14
|
+
export declare function summarizeSession(session: Session, failureCount?: number): SessionSummary;
|
|
15
|
+
/**
|
|
16
|
+
* Summarize all sessions in a trace.
|
|
17
|
+
*
|
|
18
|
+
* @param sessions - All sessions in the trace
|
|
19
|
+
* @param failures - All detected failures
|
|
20
|
+
* @returns Array of session summaries
|
|
21
|
+
*/
|
|
22
|
+
export declare function summarizeSessions(sessions: Session[], failures: {
|
|
23
|
+
eventIds: string[];
|
|
24
|
+
}[]): SessionSummary[];
|
|
25
|
+
//# sourceMappingURL=summarizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summarizer.d.ts","sourceRoot":"","sources":["../src/summarizer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE1D;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,SAAI,GAAG,cAAc,CAqBnF;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,OAAO,EAAE,EACnB,QAAQ,EAAE;IAAE,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,EAAE,GACjC,cAAc,EAAE,CAUlB"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session summarizer — produces overview statistics for a charging session.
|
|
3
|
+
*
|
|
4
|
+
* @see ADR-0003
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Summarize a charging session with overview statistics.
|
|
8
|
+
*
|
|
9
|
+
* @param session - The session to summarize
|
|
10
|
+
* @param failureCount - Number of failures detected in this session
|
|
11
|
+
* @returns Summary statistics
|
|
12
|
+
*/
|
|
13
|
+
export function summarizeSession(session, failureCount = 0) {
|
|
14
|
+
const actionSequence = session.events
|
|
15
|
+
.filter((e) => e.messageType === 'Call' && e.action !== null)
|
|
16
|
+
.map((e) => e.action);
|
|
17
|
+
const durationMs = session.startTime !== null && session.endTime !== null
|
|
18
|
+
? session.endTime - session.startTime
|
|
19
|
+
: null;
|
|
20
|
+
return {
|
|
21
|
+
sessionId: session.sessionId,
|
|
22
|
+
stationId: session.stationId,
|
|
23
|
+
connectorId: session.connectorId,
|
|
24
|
+
transactionId: session.transactionId,
|
|
25
|
+
status: session.status,
|
|
26
|
+
eventCount: session.events.length,
|
|
27
|
+
durationMs,
|
|
28
|
+
failureCount,
|
|
29
|
+
actionSequence,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Summarize all sessions in a trace.
|
|
34
|
+
*
|
|
35
|
+
* @param sessions - All sessions in the trace
|
|
36
|
+
* @param failures - All detected failures
|
|
37
|
+
* @returns Array of session summaries
|
|
38
|
+
*/
|
|
39
|
+
export function summarizeSessions(sessions, failures) {
|
|
40
|
+
return sessions.map((session) => {
|
|
41
|
+
// Count failures that belong to this session
|
|
42
|
+
const sessionEventIds = new Set(session.events.map((e) => e.id));
|
|
43
|
+
const failureCount = failures.filter((f) => f.eventIds.some((id) => sessionEventIds.has(id))).length;
|
|
44
|
+
return summarizeSession(session, failureCount);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=summarizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summarizer.js","sourceRoot":"","sources":["../src/summarizer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAgB,EAAE,YAAY,GAAG,CAAC;IACjE,MAAM,cAAc,GAAG,OAAO,CAAC,MAAM;SAClC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,MAAM,IAAI,CAAC,CAAC,MAAM,KAAK,IAAI,CAAC;SAC5D,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAgB,CAAC,CAAC;IAElC,MAAM,UAAU,GACd,OAAO,CAAC,SAAS,KAAK,IAAI,IAAI,OAAO,CAAC,OAAO,KAAK,IAAI;QACpD,CAAC,CAAC,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,SAAS;QACrC,CAAC,CAAC,IAAI,CAAC;IAEX,OAAO;QACL,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM;QACjC,UAAU;QACV,YAAY;QACZ,cAAc;KACf,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAC/B,QAAmB,EACnB,QAAkC;IAElC,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QAC9B,6CAA6C;QAC7C,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACjE,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CACzC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CACjD,CAAC,MAAM,CAAC;QAET,OAAO,gBAAgB,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session timeline builder — correlates events into logical charging sessions.
|
|
3
|
+
*
|
|
4
|
+
* Session correlation strategy (ADR-0006):
|
|
5
|
+
* 1. Primary: transactionId (from StartTransaction response / StopTransaction)
|
|
6
|
+
* 2. Secondary: connectorId + stationId grouping
|
|
7
|
+
* 3. Events without a transaction are grouped by stationId + connectorId
|
|
8
|
+
*
|
|
9
|
+
* @see ADR-0006
|
|
10
|
+
*/
|
|
11
|
+
import type { Event, Session } from './types.js';
|
|
12
|
+
/**
|
|
13
|
+
* Build session timelines from normalized events.
|
|
14
|
+
*
|
|
15
|
+
* Correlates events into logical charging sessions by:
|
|
16
|
+
* 1. Matching StartTransaction responses to find transactionIds
|
|
17
|
+
* 2. Grouping StopTransaction and MeterValues events by transactionId
|
|
18
|
+
* 3. Grouping StatusNotification events by connectorId + stationId
|
|
19
|
+
* 4. Events without a transaction are assigned to a "no-session" group
|
|
20
|
+
*
|
|
21
|
+
* Sessions are ordered by their first event's timestamp.
|
|
22
|
+
*
|
|
23
|
+
* @see ADR-0006
|
|
24
|
+
*/
|
|
25
|
+
export declare function buildSessionTimeline(events: Event[]): Session[];
|
|
26
|
+
//# sourceMappingURL=timeline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timeline.d.ts","sourceRoot":"","sources":["../src/timeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AA4FjD;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,EAAE,CAgN/D"}
|