@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/timeline.js
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
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
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Helpers
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
/**
|
|
15
|
+
* Extract transactionId from an event's payload.
|
|
16
|
+
* - StartTransaction response (CallResult): { transactionId, idTagInfo }
|
|
17
|
+
* - StopTransaction request (Call): { transactionId, ... }
|
|
18
|
+
* - MeterValues request (Call): { transactionId, ... }
|
|
19
|
+
*/
|
|
20
|
+
function extractTransactionId(event) {
|
|
21
|
+
if (event.messageType === 'Call') {
|
|
22
|
+
if (event.action === 'StartTransaction') {
|
|
23
|
+
// StartTransaction request doesn't have transactionId — the response does.
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
if (event.action === 'StopTransaction' || event.action === 'MeterValues') {
|
|
27
|
+
const payload = event.payload;
|
|
28
|
+
if (typeof payload?.transactionId === 'number') {
|
|
29
|
+
return payload.transactionId;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (event.messageType === 'CallResult') {
|
|
34
|
+
const payload = event.payload;
|
|
35
|
+
if (typeof payload?.transactionId === 'number') {
|
|
36
|
+
return payload.transactionId;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Extract connectorId from an event's payload.
|
|
43
|
+
* Present in StatusNotification, StartTransaction, MeterValues.
|
|
44
|
+
*/
|
|
45
|
+
function extractConnectorId(event) {
|
|
46
|
+
if (event.messageType !== 'Call' || event.payload === null) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
const payload = event.payload;
|
|
50
|
+
if (typeof payload?.connectorId === 'number') {
|
|
51
|
+
return payload.connectorId;
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Extract stationId from BootNotification payload.
|
|
57
|
+
*/
|
|
58
|
+
function extractStationId(events) {
|
|
59
|
+
for (const event of events) {
|
|
60
|
+
if (event.messageType === 'Call' && event.action === 'BootNotification') {
|
|
61
|
+
const payload = event.payload;
|
|
62
|
+
if (typeof payload?.chargePointSerialNumber === 'string') {
|
|
63
|
+
return payload.chargePointSerialNumber;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return 'unknown';
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Determine if an event indicates a connector fault status.
|
|
71
|
+
*/
|
|
72
|
+
function isFaultedStatus(event) {
|
|
73
|
+
if (event.messageType !== 'Call' || event.action !== 'StatusNotification') {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
const payload = event.payload;
|
|
77
|
+
return payload?.status === 'Faulted';
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Determine if an event indicates the connector is unavailable/offline.
|
|
81
|
+
*/
|
|
82
|
+
function isUnavailableStatus(event) {
|
|
83
|
+
if (event.messageType !== 'Call' || event.action !== 'StatusNotification') {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
const payload = event.payload;
|
|
87
|
+
return payload?.status === 'Unavailable' || payload?.status === 'Offline';
|
|
88
|
+
}
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
// buildSessionTimeline()
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
/**
|
|
93
|
+
* Build session timelines from normalized events.
|
|
94
|
+
*
|
|
95
|
+
* Correlates events into logical charging sessions by:
|
|
96
|
+
* 1. Matching StartTransaction responses to find transactionIds
|
|
97
|
+
* 2. Grouping StopTransaction and MeterValues events by transactionId
|
|
98
|
+
* 3. Grouping StatusNotification events by connectorId + stationId
|
|
99
|
+
* 4. Events without a transaction are assigned to a "no-session" group
|
|
100
|
+
*
|
|
101
|
+
* Sessions are ordered by their first event's timestamp.
|
|
102
|
+
*
|
|
103
|
+
* @see ADR-0006
|
|
104
|
+
*/
|
|
105
|
+
export function buildSessionTimeline(events) {
|
|
106
|
+
if (events.length === 0) {
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
const stationId = extractStationId(events);
|
|
110
|
+
// Step 1: Build a map of messageId → transactionId from StartTransaction responses.
|
|
111
|
+
// The StartTransaction Call (msg-005) gets a response (CallResult) with transactionId.
|
|
112
|
+
// We match by messageId.
|
|
113
|
+
const messageIdToTransactionId = new Map();
|
|
114
|
+
for (const event of events) {
|
|
115
|
+
if (event.messageType === 'CallResult') {
|
|
116
|
+
const txId = extractTransactionId(event);
|
|
117
|
+
if (txId !== null) {
|
|
118
|
+
messageIdToTransactionId.set(event.messageId, txId);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// Step 2: Build a map of messageId → transactionId for StopTransaction/MeterValues Calls.
|
|
123
|
+
// These Calls contain the transactionId in their payload directly.
|
|
124
|
+
const callMessageIdToTransactionId = new Map();
|
|
125
|
+
for (const event of events) {
|
|
126
|
+
if (event.messageType === 'Call') {
|
|
127
|
+
const txId = extractTransactionId(event);
|
|
128
|
+
if (txId !== null) {
|
|
129
|
+
callMessageIdToTransactionId.set(event.messageId, txId);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// Step 3: Group events into sessions.
|
|
134
|
+
// A session is identified by a transactionId.
|
|
135
|
+
// Events are assigned to a session if:
|
|
136
|
+
// - They contain that transactionId in their payload (StopTransaction, MeterValues)
|
|
137
|
+
// - They are a StartTransaction Call/CallResult with a matched messageId
|
|
138
|
+
// - They are StatusNotification events for the same connectorId around the same time
|
|
139
|
+
// First, find all StartTransaction Call messages and their corresponding response transactionIds
|
|
140
|
+
const startTxCalls = [];
|
|
141
|
+
for (const event of events) {
|
|
142
|
+
if (event.messageType === 'Call' && event.action === 'StartTransaction') {
|
|
143
|
+
const txId = messageIdToTransactionId.get(event.messageId) ?? null;
|
|
144
|
+
const responseEvent = events.find((e) => e.messageId === event.messageId && e.messageType === 'CallResult') ??
|
|
145
|
+
null;
|
|
146
|
+
startTxCalls.push({ callEvent: event, responseEvent, transactionId: txId });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// If no transactions found, return a single session with all events
|
|
150
|
+
if (startTxCalls.length === 0) {
|
|
151
|
+
return [createSession('session-0', stationId, events)];
|
|
152
|
+
}
|
|
153
|
+
// Build a map of messageId → transactionId for ALL Call messages that have
|
|
154
|
+
// a transactionId (StopTransaction, MeterValues). Their CallResult responses
|
|
155
|
+
// inherit this transactionId.
|
|
156
|
+
const callMessageIdToTxId = new Map();
|
|
157
|
+
for (const event of events) {
|
|
158
|
+
if (event.messageType === 'Call') {
|
|
159
|
+
const txId = extractTransactionId(event);
|
|
160
|
+
if (txId !== null) {
|
|
161
|
+
callMessageIdToTxId.set(event.messageId, txId);
|
|
162
|
+
}
|
|
163
|
+
// StartTransaction Call — get txId from the response
|
|
164
|
+
if (event.action === 'StartTransaction') {
|
|
165
|
+
const respTxId = messageIdToTransactionId.get(event.messageId);
|
|
166
|
+
if (respTxId !== undefined) {
|
|
167
|
+
callMessageIdToTxId.set(event.messageId, respTxId);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// Group events by transactionId
|
|
173
|
+
const sessionEventsMap = new Map();
|
|
174
|
+
for (const event of events) {
|
|
175
|
+
let txId = null;
|
|
176
|
+
if (event.messageType === 'Call') {
|
|
177
|
+
// Call: check if this messageId has a known transactionId
|
|
178
|
+
txId = callMessageIdToTxId.get(event.messageId) ?? null;
|
|
179
|
+
if (txId === null) {
|
|
180
|
+
// Try extracting from payload directly
|
|
181
|
+
txId = extractTransactionId(event);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
else if (event.messageType === 'CallResult') {
|
|
185
|
+
// CallResult: check if the matching Call has a transactionId
|
|
186
|
+
txId = callMessageIdToTxId.get(event.messageId) ?? null;
|
|
187
|
+
// Also check if transactionId is directly in the response payload
|
|
188
|
+
if (txId === null) {
|
|
189
|
+
txId = extractTransactionId(event);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (txId !== null) {
|
|
193
|
+
const group = sessionEventsMap.get(txId) ?? [];
|
|
194
|
+
group.push(event);
|
|
195
|
+
sessionEventsMap.set(txId, group);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
// Events without a transaction — assign to null group (will be distributed)
|
|
199
|
+
const group = sessionEventsMap.get(null) ?? [];
|
|
200
|
+
group.push(event);
|
|
201
|
+
sessionEventsMap.set(null, group);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// Distribute null-group events to sessions by connectorId and time proximity
|
|
205
|
+
const nullEvents = sessionEventsMap.get(null) ?? [];
|
|
206
|
+
sessionEventsMap.delete(null);
|
|
207
|
+
const usedNullEventIds = new Set();
|
|
208
|
+
// Build sessions
|
|
209
|
+
const sessions = [];
|
|
210
|
+
let sessionIndex = 0;
|
|
211
|
+
for (const [txId, txEvents] of sessionEventsMap) {
|
|
212
|
+
// Find connectorId from the StartTransaction Call
|
|
213
|
+
const startTxCall = txEvents.find((e) => e.messageType === 'Call' && e.action === 'StartTransaction');
|
|
214
|
+
const connectorId = startTxCall ? extractConnectorId(startTxCall) : null;
|
|
215
|
+
// Include null-group events that belong to this session's connector
|
|
216
|
+
// and time range, or that are responses to Calls already in the session.
|
|
217
|
+
const txMessageIds = new Set(txEvents.map((e) => e.messageId));
|
|
218
|
+
const sessionStart = txEvents[0]?.timestamp ?? null;
|
|
219
|
+
const sessionEnd = txEvents[txEvents.length - 1]?.timestamp ?? null;
|
|
220
|
+
const relatedNullEvents = nullEvents.filter((e) => {
|
|
221
|
+
// Skip already-used events
|
|
222
|
+
if (usedNullEventIds.has(e.id))
|
|
223
|
+
return false;
|
|
224
|
+
// Include CallResult/CallError that match a Call already in the session
|
|
225
|
+
if (e.messageType !== 'Call' && txMessageIds.has(e.messageId)) {
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
228
|
+
const eventConnectorId = extractConnectorId(e);
|
|
229
|
+
// Include events with matching connectorId within time range
|
|
230
|
+
if (eventConnectorId !== null && connectorId !== null && eventConnectorId === connectorId) {
|
|
231
|
+
if (sessionStart !== null && sessionEnd !== null && e.timestamp !== null) {
|
|
232
|
+
return e.timestamp >= sessionStart && e.timestamp <= sessionEnd + 60000; // 1 min grace
|
|
233
|
+
}
|
|
234
|
+
return true; // No timestamps — include
|
|
235
|
+
}
|
|
236
|
+
// Include BootNotification events at the start
|
|
237
|
+
if (e.action === 'BootNotification' || e.action === 'Heartbeat') {
|
|
238
|
+
// Only include if this is the first session
|
|
239
|
+
return sessionIndex === 0;
|
|
240
|
+
}
|
|
241
|
+
// Include events without connectorId (like Authorize) within time range
|
|
242
|
+
// (extending before session start to include pre-session events like Authorize)
|
|
243
|
+
if (eventConnectorId === null &&
|
|
244
|
+
e.messageType === 'Call' &&
|
|
245
|
+
e.action !== 'BootNotification' &&
|
|
246
|
+
e.action !== 'Heartbeat') {
|
|
247
|
+
if (sessionStart !== null && sessionEnd !== null && e.timestamp !== null) {
|
|
248
|
+
// Include if within 5 minutes before session start through 1 min after end
|
|
249
|
+
return e.timestamp >= sessionStart - 300000 && e.timestamp <= sessionEnd + 60000;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return false;
|
|
253
|
+
});
|
|
254
|
+
// Mark these null events as used so they aren't included in other sessions
|
|
255
|
+
for (const e of relatedNullEvents) {
|
|
256
|
+
usedNullEventIds.add(e.id);
|
|
257
|
+
}
|
|
258
|
+
const allEvents = [...txEvents, ...relatedNullEvents].sort((a, b) => {
|
|
259
|
+
// Sort by original event order (ID is sequential)
|
|
260
|
+
return a.id.localeCompare(b.id);
|
|
261
|
+
});
|
|
262
|
+
sessions.push(createSession(`session-${sessionIndex}`, stationId, allEvents, connectorId, txId));
|
|
263
|
+
sessionIndex++;
|
|
264
|
+
}
|
|
265
|
+
// Sort sessions by start time
|
|
266
|
+
sessions.sort((a, b) => {
|
|
267
|
+
if (a.startTime === null)
|
|
268
|
+
return 1;
|
|
269
|
+
if (b.startTime === null)
|
|
270
|
+
return -1;
|
|
271
|
+
return a.startTime - b.startTime;
|
|
272
|
+
});
|
|
273
|
+
// Renumber sessions
|
|
274
|
+
sessions.forEach((s, i) => {
|
|
275
|
+
s.sessionId = `session-${i}`;
|
|
276
|
+
});
|
|
277
|
+
return sessions;
|
|
278
|
+
}
|
|
279
|
+
// ---------------------------------------------------------------------------
|
|
280
|
+
// createSession helper
|
|
281
|
+
// ---------------------------------------------------------------------------
|
|
282
|
+
function createSession(sessionId, stationId, events, connectorId = null, transactionId = null) {
|
|
283
|
+
const timestamps = events.map((e) => e.timestamp).filter((t) => t !== null);
|
|
284
|
+
const startTime = timestamps.length > 0 ? Math.min(...timestamps) : null;
|
|
285
|
+
const endTime = timestamps.length > 0 ? Math.max(...timestamps) : null;
|
|
286
|
+
// Determine session status
|
|
287
|
+
let status = 'active';
|
|
288
|
+
const hasStop = events.some((e) => e.messageType === 'Call' && e.action === 'StopTransaction');
|
|
289
|
+
const hasFaulted = events.some(isFaultedStatus);
|
|
290
|
+
const hasUnavailable = events.some(isUnavailableStatus);
|
|
291
|
+
if (hasStop) {
|
|
292
|
+
status = 'completed';
|
|
293
|
+
}
|
|
294
|
+
else if (hasFaulted || hasUnavailable) {
|
|
295
|
+
status = 'aborted';
|
|
296
|
+
}
|
|
297
|
+
// If we didn't get a transactionId from parameter, try to extract it
|
|
298
|
+
if (transactionId === null) {
|
|
299
|
+
for (const event of events) {
|
|
300
|
+
const txId = extractTransactionId(event);
|
|
301
|
+
if (txId !== null) {
|
|
302
|
+
transactionId = txId;
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return {
|
|
308
|
+
sessionId,
|
|
309
|
+
stationId,
|
|
310
|
+
connectorId,
|
|
311
|
+
transactionId,
|
|
312
|
+
startTime,
|
|
313
|
+
endTime,
|
|
314
|
+
events,
|
|
315
|
+
status,
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
//# sourceMappingURL=timeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timeline.js","sourceRoot":"","sources":["../src/timeline.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core type definitions for OCPP DebugKit.
|
|
3
|
+
*
|
|
4
|
+
* These are the proposed canonical types from the M0.5 design phase
|
|
5
|
+
* (ADR-0003 through ADR-0006). They will be fully implemented with Zod
|
|
6
|
+
* schemas in v0.1.0 (Issue #13).
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Direction of an OCPP message.
|
|
10
|
+
* @see ADR-0004
|
|
11
|
+
*/
|
|
12
|
+
export type Direction = 'CS_TO_CSMS' | 'CSMS_TO_CS' | 'UNKNOWN';
|
|
13
|
+
/**
|
|
14
|
+
* OCPP 1.6 JSON message type.
|
|
15
|
+
* - Call (2): request from one side to the other.
|
|
16
|
+
* - CallResult (3): successful response.
|
|
17
|
+
* - CallError (4): error response.
|
|
18
|
+
*/
|
|
19
|
+
export type MessageType = 'Call' | 'CallResult' | 'CallError';
|
|
20
|
+
/**
|
|
21
|
+
* A raw OCPP 1.6 JSON message as it appears on the wire.
|
|
22
|
+
* The shape depends on the message type:
|
|
23
|
+
* - Call: [2, UniqueId, Action, Payload]
|
|
24
|
+
* - CallResult: [3, UniqueId, Payload]
|
|
25
|
+
* - CallError: [4, UniqueId, ErrorCode, ErrorDescription, ErrorDetails]
|
|
26
|
+
*/
|
|
27
|
+
export type RawOcppMessage = [number, string, ...unknown[]];
|
|
28
|
+
/**
|
|
29
|
+
* A trace event entry as it appears in a trace file (JSON Object or JSONL).
|
|
30
|
+
* This is the input shape before normalization.
|
|
31
|
+
*/
|
|
32
|
+
export interface TraceEventInput {
|
|
33
|
+
/** ISO 8601 string or Unix epoch (ms or s). Optional. */
|
|
34
|
+
timestamp?: string | number | null;
|
|
35
|
+
/** Direction of the message. Inferred if absent. */
|
|
36
|
+
direction?: Direction;
|
|
37
|
+
/** Raw OCPP 1.6 JSON message array. */
|
|
38
|
+
message: RawOcppMessage;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The canonical normalized event used internally by DebugKit.
|
|
42
|
+
* @see ADR-0003
|
|
43
|
+
*/
|
|
44
|
+
export interface Event {
|
|
45
|
+
/** Generated unique event ID (sequential, stable within a parse). */
|
|
46
|
+
id: string;
|
|
47
|
+
/** OCPP UniqueId from the message array. */
|
|
48
|
+
messageId: string;
|
|
49
|
+
/** Normalized timestamp in epoch milliseconds. null if missing. */
|
|
50
|
+
timestamp: number | null;
|
|
51
|
+
/** Direction of the message. */
|
|
52
|
+
direction: Direction;
|
|
53
|
+
/** OCPP message type. */
|
|
54
|
+
messageType: MessageType;
|
|
55
|
+
/** OCPP action name (e.g., "BootNotification"). Present only for Call messages. */
|
|
56
|
+
action: string | null;
|
|
57
|
+
/** OCPP payload object. */
|
|
58
|
+
payload: unknown;
|
|
59
|
+
/** Error code, present only for CallError messages. */
|
|
60
|
+
errorCode: string | null;
|
|
61
|
+
/** Error description, present only for CallError messages. */
|
|
62
|
+
errorDescription: string | null;
|
|
63
|
+
/** The original raw OCPP message array, unmodified. */
|
|
64
|
+
rawMessage: RawOcppMessage;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Metadata for a trace file.
|
|
68
|
+
*/
|
|
69
|
+
export interface TraceMetadata {
|
|
70
|
+
stationId?: string;
|
|
71
|
+
ocppVersion?: string;
|
|
72
|
+
source?: string;
|
|
73
|
+
description?: string;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The JSON Object trace format.
|
|
77
|
+
* @see ADR-0002, docs/trace-format-spec.md
|
|
78
|
+
*/
|
|
79
|
+
export interface Trace {
|
|
80
|
+
traceId?: string;
|
|
81
|
+
metadata?: TraceMetadata;
|
|
82
|
+
events: TraceEventInput[];
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* A logical charging session derived from trace events.
|
|
86
|
+
* @see ADR-0006
|
|
87
|
+
*/
|
|
88
|
+
export interface Session {
|
|
89
|
+
sessionId: string;
|
|
90
|
+
stationId: string;
|
|
91
|
+
connectorId: number | null;
|
|
92
|
+
transactionId: number | null;
|
|
93
|
+
startTime: number | null;
|
|
94
|
+
endTime: number | null;
|
|
95
|
+
events: Event[];
|
|
96
|
+
status: 'active' | 'completed' | 'aborted';
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* A warning produced during parsing when an individual event is malformed.
|
|
100
|
+
* @see ADR-0007
|
|
101
|
+
*/
|
|
102
|
+
export interface ParseWarning {
|
|
103
|
+
index: number;
|
|
104
|
+
message: string;
|
|
105
|
+
rawInput?: string;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Result of parsing a trace.
|
|
109
|
+
*/
|
|
110
|
+
export interface ParseResult {
|
|
111
|
+
events: Event[];
|
|
112
|
+
warnings: ParseWarning[];
|
|
113
|
+
}
|
|
114
|
+
/** Severity of a detected failure. */
|
|
115
|
+
export type FailureSeverity = 'critical' | 'warning' | 'info';
|
|
116
|
+
/** Failure rule codes implemented in v0.1. */
|
|
117
|
+
export type FailureCode = 'FAILED_AUTHORIZATION' | 'CONNECTOR_FAULT' | 'STATION_OFFLINE_DURING_SESSION';
|
|
118
|
+
/**
|
|
119
|
+
* A detected failure in a trace.
|
|
120
|
+
*/
|
|
121
|
+
export interface Failure {
|
|
122
|
+
/** Failure rule code. */
|
|
123
|
+
code: FailureCode;
|
|
124
|
+
/** Human-readable description. */
|
|
125
|
+
description: string;
|
|
126
|
+
/** Severity level. */
|
|
127
|
+
severity: FailureSeverity;
|
|
128
|
+
/** Event IDs associated with the failure. */
|
|
129
|
+
eventIds: string[];
|
|
130
|
+
/** Suggested next steps for resolution. */
|
|
131
|
+
suggestedSteps: string[];
|
|
132
|
+
}
|
|
133
|
+
/** Summary statistics for a charging session. */
|
|
134
|
+
export interface SessionSummary {
|
|
135
|
+
sessionId: string;
|
|
136
|
+
stationId: string;
|
|
137
|
+
connectorId: number | null;
|
|
138
|
+
transactionId: number | null;
|
|
139
|
+
status: 'active' | 'completed' | 'aborted';
|
|
140
|
+
eventCount: number;
|
|
141
|
+
durationMs: number | null;
|
|
142
|
+
failureCount: number;
|
|
143
|
+
/** Ordered list of actions in the session. */
|
|
144
|
+
actionSequence: string[];
|
|
145
|
+
}
|
|
146
|
+
/** Result of validating a single OCPP message. */
|
|
147
|
+
export interface ValidationResult {
|
|
148
|
+
valid: boolean;
|
|
149
|
+
errors: string[];
|
|
150
|
+
}
|
|
151
|
+
/** A scenario fixture for testing the analysis engine. */
|
|
152
|
+
export interface Scenario {
|
|
153
|
+
name: string;
|
|
154
|
+
description: string;
|
|
155
|
+
/** The trace data to analyze. */
|
|
156
|
+
trace: Trace;
|
|
157
|
+
/** Failure codes expected to be detected. */
|
|
158
|
+
expectedFailures: FailureCode[];
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core type definitions for OCPP DebugKit.
|
|
3
|
+
*
|
|
4
|
+
* These are the proposed canonical types from the M0.5 design phase
|
|
5
|
+
* (ADR-0003 through ADR-0006). They will be fully implemented with Zod
|
|
6
|
+
* schemas in v0.1.0 (Issue #13).
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OCPP message validator — checks structural compliance of individual messages.
|
|
3
|
+
*
|
|
4
|
+
* Validates that an OCPP 1.6 JSON message conforms to the protocol's
|
|
5
|
+
* structural requirements.
|
|
6
|
+
*
|
|
7
|
+
* @see docs/trace-format-spec.md
|
|
8
|
+
*/
|
|
9
|
+
import type { Event, ValidationResult } from './types.js';
|
|
10
|
+
/**
|
|
11
|
+
* Validate a single OCPP message for structural compliance.
|
|
12
|
+
*
|
|
13
|
+
* Checks:
|
|
14
|
+
* - Message array has correct minimum length for its type
|
|
15
|
+
* - MessageTypeId is 2, 3, or 4
|
|
16
|
+
* - UniqueId is a non-empty string
|
|
17
|
+
* - Call messages have a string Action
|
|
18
|
+
* - CallError messages have ErrorCode and ErrorDescription
|
|
19
|
+
* - MessageType field is consistent with raw message's MessageTypeId
|
|
20
|
+
*
|
|
21
|
+
* @param event - The event to validate
|
|
22
|
+
* @param allEvents - Optional: all events in the trace (for Call/Response matching)
|
|
23
|
+
* @returns Validation result with errors array
|
|
24
|
+
*/
|
|
25
|
+
export declare function validateMessage(event: Event, allEvents?: Event[]): ValidationResult;
|
|
26
|
+
/**
|
|
27
|
+
* Validate all events in a trace.
|
|
28
|
+
*
|
|
29
|
+
* @param events - All events in the trace
|
|
30
|
+
* @returns Map of event ID to validation result
|
|
31
|
+
*/
|
|
32
|
+
export declare function validateMessages(events: Event[]): Map<string, ValidationResult>;
|
|
33
|
+
//# sourceMappingURL=validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../src/validator.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAe,gBAAgB,EAAE,MAAM,YAAY,CAAC;AA+FvE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,KAAK,EAAE,GAAG,gBAAgB,CAcnF;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAQ/E"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OCPP message validator — checks structural compliance of individual messages.
|
|
3
|
+
*
|
|
4
|
+
* Validates that an OCPP 1.6 JSON message conforms to the protocol's
|
|
5
|
+
* structural requirements.
|
|
6
|
+
*
|
|
7
|
+
* @see docs/trace-format-spec.md
|
|
8
|
+
*/
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Validation rules
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
/**
|
|
13
|
+
* Validate the message structure based on its type.
|
|
14
|
+
*
|
|
15
|
+
* Call (2): [2, UniqueId, Action, Payload] — 4+ elements, Action is string
|
|
16
|
+
* CallResult (3): [3, UniqueId, Payload] — 3+ elements
|
|
17
|
+
* CallError (4): [4, UniqueId, ErrorCode, ErrorDescription, ErrorDetails] — 5+ elements
|
|
18
|
+
*/
|
|
19
|
+
function validateMessageStructure(event) {
|
|
20
|
+
const errors = [];
|
|
21
|
+
const msg = event.rawMessage;
|
|
22
|
+
const msgType = event.messageType;
|
|
23
|
+
// Check minimum length
|
|
24
|
+
const minLength = msgType === 'Call' ? 4 : msgType === 'CallResult' ? 3 : 5;
|
|
25
|
+
if (msg.length < minLength) {
|
|
26
|
+
errors.push(`${msgType} message must have at least ${minLength} elements (has ${msg.length})`);
|
|
27
|
+
}
|
|
28
|
+
// Check MessageTypeId (index 0)
|
|
29
|
+
if (msg[0] !== 2 && msg[0] !== 3 && msg[0] !== 4) {
|
|
30
|
+
errors.push(`Invalid MessageTypeId: ${msg[0]} (expected 2, 3, or 4)`);
|
|
31
|
+
}
|
|
32
|
+
// Check UniqueId (index 1)
|
|
33
|
+
if (typeof msg[1] !== 'string' || msg[1] === '') {
|
|
34
|
+
errors.push('UniqueId (index 1) must be a non-empty string');
|
|
35
|
+
}
|
|
36
|
+
// Call-specific: Action (index 2) must be a string
|
|
37
|
+
if (msgType === 'Call') {
|
|
38
|
+
if (typeof msg[2] !== 'string' || msg[2] === '') {
|
|
39
|
+
errors.push('Call Action (index 2) must be a non-empty string');
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// CallError-specific: ErrorCode and ErrorDescription
|
|
43
|
+
if (msgType === 'CallError') {
|
|
44
|
+
if (typeof msg[2] !== 'string' || msg[2] === '') {
|
|
45
|
+
errors.push('CallError ErrorCode (index 2) must be a non-empty string');
|
|
46
|
+
}
|
|
47
|
+
if (typeof msg[3] !== 'string') {
|
|
48
|
+
errors.push('CallError ErrorDescription (index 3) must be a string');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return errors;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Validate that the event's messageType matches the raw message's MessageTypeId.
|
|
55
|
+
*/
|
|
56
|
+
function validateTypeConsistency(event) {
|
|
57
|
+
const errors = [];
|
|
58
|
+
const expectedType = event.rawMessage[0] === 2 ? 'Call' : event.rawMessage[0] === 3 ? 'CallResult' : 'CallError';
|
|
59
|
+
if (event.messageType !== expectedType) {
|
|
60
|
+
errors.push(`MessageType mismatch: event says "${event.messageType}" but raw message has MessageTypeId ${event.rawMessage[0]} ("${expectedType}")`);
|
|
61
|
+
}
|
|
62
|
+
return errors;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Validate that CallResult/CallError have a matching Call.
|
|
66
|
+
* This is a soft validation — the function checks if there's a Call with
|
|
67
|
+
* the same messageId in the provided event list.
|
|
68
|
+
*/
|
|
69
|
+
function validateResponseHasCall(event, allEvents) {
|
|
70
|
+
const errors = [];
|
|
71
|
+
if (event.messageType === 'CallResult' || event.messageType === 'CallError') {
|
|
72
|
+
const hasCall = allEvents.some((e) => e.messageType === 'Call' && e.messageId === event.messageId);
|
|
73
|
+
if (!hasCall) {
|
|
74
|
+
errors.push(`${event.messageType} with messageId "${event.messageId}" has no matching Call`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return errors;
|
|
78
|
+
}
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
// validateMessage()
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
/**
|
|
83
|
+
* Validate a single OCPP message for structural compliance.
|
|
84
|
+
*
|
|
85
|
+
* Checks:
|
|
86
|
+
* - Message array has correct minimum length for its type
|
|
87
|
+
* - MessageTypeId is 2, 3, or 4
|
|
88
|
+
* - UniqueId is a non-empty string
|
|
89
|
+
* - Call messages have a string Action
|
|
90
|
+
* - CallError messages have ErrorCode and ErrorDescription
|
|
91
|
+
* - MessageType field is consistent with raw message's MessageTypeId
|
|
92
|
+
*
|
|
93
|
+
* @param event - The event to validate
|
|
94
|
+
* @param allEvents - Optional: all events in the trace (for Call/Response matching)
|
|
95
|
+
* @returns Validation result with errors array
|
|
96
|
+
*/
|
|
97
|
+
export function validateMessage(event, allEvents) {
|
|
98
|
+
const errors = [];
|
|
99
|
+
errors.push(...validateMessageStructure(event));
|
|
100
|
+
errors.push(...validateTypeConsistency(event));
|
|
101
|
+
if (allEvents) {
|
|
102
|
+
errors.push(...validateResponseHasCall(event, allEvents));
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
valid: errors.length === 0,
|
|
106
|
+
errors,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Validate all events in a trace.
|
|
111
|
+
*
|
|
112
|
+
* @param events - All events in the trace
|
|
113
|
+
* @returns Map of event ID to validation result
|
|
114
|
+
*/
|
|
115
|
+
export function validateMessages(events) {
|
|
116
|
+
const results = new Map();
|
|
117
|
+
for (const event of events) {
|
|
118
|
+
results.set(event.id, validateMessage(event, events));
|
|
119
|
+
}
|
|
120
|
+
return results;
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=validator.js.map
|