@ocpp-debugkit/toolkit 0.2.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/NOTICE +14 -0
- package/README.md +35 -0
- package/dist/cli/commands/inspect.d.ts +8 -0
- package/dist/cli/commands/inspect.d.ts.map +1 -0
- package/dist/cli/commands/inspect.js +63 -0
- package/dist/cli/commands/inspect.js.map +1 -0
- package/dist/cli/commands/report.d.ts +9 -0
- package/dist/cli/commands/report.d.ts.map +1 -0
- package/dist/cli/commands/report.js +54 -0
- package/dist/cli/commands/report.js.map +1 -0
- package/dist/cli/commands/scenario.d.ts +10 -0
- package/dist/cli/commands/scenario.d.ts.map +1 -0
- package/dist/cli/commands/scenario.js +104 -0
- package/dist/cli/commands/scenario.js.map +1 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +47 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/utils.d.ts +13 -0
- package/dist/cli/utils.d.ts.map +1 -0
- package/dist/cli/utils.js +43 -0
- package/dist/cli/utils.js.map +1 -0
- package/dist/core/__fixtures__/connector-fault.d.ts +116 -0
- package/dist/core/__fixtures__/connector-fault.d.ts.map +1 -0
- package/dist/core/__fixtures__/connector-fault.js +231 -0
- package/dist/core/__fixtures__/connector-fault.js.map +1 -0
- package/dist/core/__fixtures__/failed-auth.d.ts +64 -0
- package/dist/core/__fixtures__/failed-auth.d.ts.map +1 -0
- package/dist/core/__fixtures__/failed-auth.js +173 -0
- package/dist/core/__fixtures__/failed-auth.js.map +1 -0
- package/dist/core/__fixtures__/normal-session.d.ts +108 -0
- package/dist/core/__fixtures__/normal-session.d.ts.map +1 -0
- package/dist/core/__fixtures__/normal-session.js +261 -0
- package/dist/core/__fixtures__/normal-session.js.map +1 -0
- package/dist/core/detection.d.ts +23 -0
- package/dist/core/detection.d.ts.map +1 -0
- package/dist/core/detection.js +226 -0
- package/dist/core/detection.js.map +1 -0
- package/dist/core/fixtures/index.d.ts +298 -0
- package/dist/core/fixtures/index.d.ts.map +1 -0
- package/dist/core/fixtures/index.js +19 -0
- package/dist/core/fixtures/index.js.map +1 -0
- package/dist/core/index.d.ts +14 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +22 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/normalizer.d.ts +83 -0
- package/dist/core/normalizer.d.ts.map +1 -0
- package/dist/core/normalizer.js +289 -0
- package/dist/core/normalizer.js.map +1 -0
- package/dist/core/parser.d.ts +53 -0
- package/dist/core/parser.d.ts.map +1 -0
- package/dist/core/parser.js +282 -0
- package/dist/core/parser.js.map +1 -0
- package/dist/core/schemas.d.ts +68 -0
- package/dist/core/schemas.d.ts.map +1 -0
- package/dist/core/schemas.js +81 -0
- package/dist/core/schemas.js.map +1 -0
- package/dist/core/summarizer.d.ts +25 -0
- package/dist/core/summarizer.d.ts.map +1 -0
- package/dist/core/summarizer.js +47 -0
- package/dist/core/summarizer.js.map +1 -0
- package/dist/core/timeline.d.ts +26 -0
- package/dist/core/timeline.d.ts.map +1 -0
- package/dist/core/timeline.js +318 -0
- package/dist/core/timeline.js.map +1 -0
- package/dist/core/types.d.ts +160 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +9 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/validator.d.ts +33 -0
- package/dist/core/validator.d.ts.map +1 -0
- package/dist/core/validator.js +122 -0
- package/dist/core/validator.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/react/components.d.ts +50 -0
- package/dist/react/components.d.ts.map +1 -0
- package/dist/react/components.js +49 -0
- package/dist/react/components.js.map +1 -0
- package/dist/react/index.d.ts +11 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +10 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/types.d.ts +33 -0
- package/dist/react/types.d.ts.map +1 -0
- package/dist/react/types.js +5 -0
- package/dist/react/types.js.map +1 -0
- package/dist/replay/engine.d.ts +55 -0
- package/dist/replay/engine.d.ts.map +1 -0
- package/dist/replay/engine.js +86 -0
- package/dist/replay/engine.js.map +1 -0
- package/dist/replay/index.d.ts +11 -0
- package/dist/replay/index.d.ts.map +1 -0
- package/dist/replay/index.js +11 -0
- package/dist/replay/index.js.map +1 -0
- package/dist/reporter/index.d.ts +6 -0
- package/dist/reporter/index.d.ts.map +1 -0
- package/dist/reporter/index.js +5 -0
- package/dist/reporter/index.js.map +1 -0
- package/dist/reporter/markdown.d.ts +20 -0
- package/dist/reporter/markdown.d.ts.map +1 -0
- package/dist/reporter/markdown.js +181 -0
- package/dist/reporter/markdown.js.map +1 -0
- package/dist/reporter/types.d.ts +34 -0
- package/dist/reporter/types.d.ts.map +1 -0
- package/dist/reporter/types.js +9 -0
- package/dist/reporter/types.js.map +1 -0
- package/dist/scenarios/__scenarios__/station-offline.d.ts +94 -0
- package/dist/scenarios/__scenarios__/station-offline.d.ts.map +1 -0
- package/dist/scenarios/__scenarios__/station-offline.js +168 -0
- package/dist/scenarios/__scenarios__/station-offline.js.map +1 -0
- package/dist/scenarios/__scenarios__/unexpected-stop-reason.d.ts +112 -0
- package/dist/scenarios/__scenarios__/unexpected-stop-reason.d.ts.map +1 -0
- package/dist/scenarios/__scenarios__/unexpected-stop-reason.js +216 -0
- package/dist/scenarios/__scenarios__/unexpected-stop-reason.js.map +1 -0
- package/dist/scenarios/index.d.ts +21 -0
- package/dist/scenarios/index.d.ts.map +1 -0
- package/dist/scenarios/index.js +61 -0
- package/dist/scenarios/index.js.map +1 -0
- package/package.json +110 -0
|
@@ -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/core/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"}
|
|
@@ -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/core/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/core/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/core/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/core/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"}
|
|
@@ -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/core/timeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E;;;;;GAKG;AACH,SAAS,oBAAoB,CAAC,KAAY;IACxC,IAAI,KAAK,CAAC,WAAW,KAAK,MAAM,EAAE,CAAC;QACjC,IAAI,KAAK,CAAC,MAAM,KAAK,kBAAkB,EAAE,CAAC;YACxC,2EAA2E;YAC3E,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,KAAK,CAAC,MAAM,KAAK,iBAAiB,IAAI,KAAK,CAAC,MAAM,KAAK,aAAa,EAAE,CAAC;YACzE,MAAM,OAAO,GAAG,KAAK,CAAC,OAAsC,CAAC;YAC7D,IAAI,OAAO,OAAO,EAAE,aAAa,KAAK,QAAQ,EAAE,CAAC;gBAC/C,OAAO,OAAO,CAAC,aAAa,CAAC;YAC/B,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,KAAK,CAAC,WAAW,KAAK,YAAY,EAAE,CAAC;QACvC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAsC,CAAC;QAC7D,IAAI,OAAO,OAAO,EAAE,aAAa,KAAK,QAAQ,EAAE,CAAC;YAC/C,OAAO,OAAO,CAAC,aAAa,CAAC;QAC/B,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,SAAS,kBAAkB,CAAC,KAAY;IACtC,IAAI,KAAK,CAAC,WAAW,KAAK,MAAM,IAAI,KAAK,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;QAC3D,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAoC,CAAC;IAC3D,IAAI,OAAO,OAAO,EAAE,WAAW,KAAK,QAAQ,EAAE,CAAC;QAC7C,OAAO,OAAO,CAAC,WAAW,CAAC;IAC7B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,SAAS,gBAAgB,CAAC,MAAe;IACvC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,WAAW,KAAK,MAAM,IAAI,KAAK,CAAC,MAAM,KAAK,kBAAkB,EAAE,CAAC;YACxE,MAAM,OAAO,GAAG,KAAK,CAAC,OAAgD,CAAC;YACvE,IAAI,OAAO,OAAO,EAAE,uBAAuB,KAAK,QAAQ,EAAE,CAAC;gBACzD,OAAO,OAAO,CAAC,uBAAuB,CAAC;YACzC,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;GAEG;AACH,SAAS,eAAe,CAAC,KAAY;IACnC,IAAI,KAAK,CAAC,WAAW,KAAK,MAAM,IAAI,KAAK,CAAC,MAAM,KAAK,oBAAoB,EAAE,CAAC;QAC1E,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,OAAO,GAAG,KAAK,CAAC,OAA+B,CAAC;IACtD,OAAO,OAAO,EAAE,MAAM,KAAK,SAAS,CAAC;AACvC,CAAC;AAED;;GAEG;AACH,SAAS,mBAAmB,CAAC,KAAY;IACvC,IAAI,KAAK,CAAC,WAAW,KAAK,MAAM,IAAI,KAAK,CAAC,MAAM,KAAK,oBAAoB,EAAE,CAAC;QAC1E,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,OAAO,GAAG,KAAK,CAAC,OAA+B,CAAC;IACtD,OAAO,OAAO,EAAE,MAAM,KAAK,aAAa,IAAI,OAAO,EAAE,MAAM,KAAK,SAAS,CAAC;AAC5E,CAAC;AAED,8EAA8E;AAC9E,yBAAyB;AACzB,8EAA8E;AAE9E;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAe;IAClD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,SAAS,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAE3C,oFAAoF;IACpF,uFAAuF;IACvF,yBAAyB;IACzB,MAAM,wBAAwB,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE3D,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,WAAW,KAAK,YAAY,EAAE,CAAC;YACvC,MAAM,IAAI,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;YACzC,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;gBAClB,wBAAwB,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;YACtD,CAAC;QACH,CAAC;IACH,CAAC;IAED,0FAA0F;IAC1F,mEAAmE;IACnE,MAAM,4BAA4B,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC/D,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,WAAW,KAAK,MAAM,EAAE,CAAC;YACjC,MAAM,IAAI,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;YACzC,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;gBAClB,4BAA4B,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;YAC1D,CAAC;QACH,CAAC;IACH,CAAC;IAED,sCAAsC;IACtC,8CAA8C;IAC9C,uCAAuC;IACvC,oFAAoF;IACpF,yEAAyE;IACzE,qFAAqF;IAErF,iGAAiG;IACjG,MAAM,YAAY,GAIZ,EAAE,CAAC;IAET,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,WAAW,KAAK,MAAM,IAAI,KAAK,CAAC,MAAM,KAAK,kBAAkB,EAAE,CAAC;YACxE,MAAM,IAAI,GAAG,wBAAwB,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC;YACnE,MAAM,aAAa,GACjB,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,KAAK,CAAC,SAAS,IAAI,CAAC,CAAC,WAAW,KAAK,YAAY,CAAC;gBACrF,IAAI,CAAC;YACP,YAAY,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9E,CAAC;IACH,CAAC;IAED,oEAAoE;IACpE,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,CAAC,aAAa,CAAC,WAAW,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;IACzD,CAAC;IAED,2EAA2E;IAC3E,6EAA6E;IAC7E,8BAA8B;IAC9B,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAAkB,CAAC;IACtD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,WAAW,KAAK,MAAM,EAAE,CAAC;YACjC,MAAM,IAAI,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;YACzC,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;gBAClB,mBAAmB,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;YACjD,CAAC;YACD,qDAAqD;YACrD,IAAI,KAAK,CAAC,MAAM,KAAK,kBAAkB,EAAE,CAAC;gBACxC,MAAM,QAAQ,GAAG,wBAAwB,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;gBAC/D,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;oBAC3B,mBAAmB,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;gBACrD,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,gCAAgC;IAChC,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAA0B,CAAC;IAE3D,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,IAAI,GAAkB,IAAI,CAAC;QAE/B,IAAI,KAAK,CAAC,WAAW,KAAK,MAAM,EAAE,CAAC;YACjC,0DAA0D;YAC1D,IAAI,GAAG,mBAAmB,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC;YACxD,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;gBAClB,uCAAuC;gBACvC,IAAI,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;YACrC,CAAC;QACH,CAAC;aAAM,IAAI,KAAK,CAAC,WAAW,KAAK,YAAY,EAAE,CAAC;YAC9C,6DAA6D;YAC7D,IAAI,GAAG,mBAAmB,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC;YACxD,kEAAkE;YAClE,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;gBAClB,IAAI,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;YACrC,CAAC;QACH,CAAC;QAED,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAClB,MAAM,KAAK,GAAG,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YAC/C,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAClB,gBAAgB,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACpC,CAAC;aAAM,CAAC;YACN,4EAA4E;YAC5E,MAAM,KAAK,GAAG,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YAC/C,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAClB,gBAAgB,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IAED,6EAA6E;IAC7E,MAAM,UAAU,GAAG,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IACpD,gBAAgB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC9B,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAU,CAAC;IAE3C,iBAAiB;IACjB,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,IAAI,YAAY,GAAG,CAAC,CAAC;IAErB,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,gBAAgB,EAAE,CAAC;QAChD,kDAAkD;QAClD,MAAM,WAAW,GAAG,QAAQ,CAAC,IAAI,CAC/B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,MAAM,IAAI,CAAC,CAAC,MAAM,KAAK,kBAAkB,CACnE,CAAC;QACF,MAAM,WAAW,GAAG,WAAW,CAAC,CAAC,CAAC,kBAAkB,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAEzE,oEAAoE;QACpE,yEAAyE;QACzE,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;QAC/D,MAAM,YAAY,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,SAAS,IAAI,IAAI,CAAC;QACpD,MAAM,UAAU,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,SAAS,IAAI,IAAI,CAAC;QAEpE,MAAM,iBAAiB,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;YAChD,2BAA2B;YAC3B,IAAI,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;gBAAE,OAAO,KAAK,CAAC;YAE7C,wEAAwE;YACxE,IAAI,CAAC,CAAC,WAAW,KAAK,MAAM,IAAI,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC9D,OAAO,IAAI,CAAC;YACd,CAAC;YAED,MAAM,gBAAgB,GAAG,kBAAkB,CAAC,CAAC,CAAC,CAAC;YAC/C,6DAA6D;YAC7D,IAAI,gBAAgB,KAAK,IAAI,IAAI,WAAW,KAAK,IAAI,IAAI,gBAAgB,KAAK,WAAW,EAAE,CAAC;gBAC1F,IAAI,YAAY,KAAK,IAAI,IAAI,UAAU,KAAK,IAAI,IAAI,CAAC,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;oBACzE,OAAO,CAAC,CAAC,SAAS,IAAI,YAAY,IAAI,CAAC,CAAC,SAAS,IAAI,UAAU,GAAG,KAAK,CAAC,CAAC,cAAc;gBACzF,CAAC;gBACD,OAAO,IAAI,CAAC,CAAC,0BAA0B;YACzC,CAAC;YAED,+CAA+C;YAC/C,IAAI,CAAC,CAAC,MAAM,KAAK,kBAAkB,IAAI,CAAC,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;gBAChE,4CAA4C;gBAC5C,OAAO,YAAY,KAAK,CAAC,CAAC;YAC5B,CAAC;YAED,wEAAwE;YACxE,gFAAgF;YAChF,IACE,gBAAgB,KAAK,IAAI;gBACzB,CAAC,CAAC,WAAW,KAAK,MAAM;gBACxB,CAAC,CAAC,MAAM,KAAK,kBAAkB;gBAC/B,CAAC,CAAC,MAAM,KAAK,WAAW,EACxB,CAAC;gBACD,IAAI,YAAY,KAAK,IAAI,IAAI,UAAU,KAAK,IAAI,IAAI,CAAC,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;oBACzE,2EAA2E;oBAC3E,OAAO,CAAC,CAAC,SAAS,IAAI,YAAY,GAAG,MAAM,IAAI,CAAC,CAAC,SAAS,IAAI,UAAU,GAAG,KAAK,CAAC;gBACnF,CAAC;YACH,CAAC;YAED,OAAO,KAAK,CAAC;QACf,CAAC,CAAC,CAAC;QAEH,2EAA2E;QAC3E,KAAK,MAAM,CAAC,IAAI,iBAAiB,EAAE,CAAC;YAClC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC7B,CAAC;QAED,MAAM,SAAS,GAAG,CAAC,GAAG,QAAQ,EAAE,GAAG,iBAAiB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YAClE,kDAAkD;YAClD,OAAO,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;QAEH,QAAQ,CAAC,IAAI,CACX,aAAa,CAAC,WAAW,YAAY,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,IAAI,CAAC,CAClF,CAAC;QACF,YAAY,EAAE,CAAC;IACjB,CAAC;IAED,8BAA8B;IAC9B,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACrB,IAAI,CAAC,CAAC,SAAS,KAAK,IAAI;YAAE,OAAO,CAAC,CAAC;QACnC,IAAI,CAAC,CAAC,SAAS,KAAK,IAAI;YAAE,OAAO,CAAC,CAAC,CAAC;QACpC,OAAO,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,oBAAoB;IACpB,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACxB,CAAC,CAAC,SAAS,GAAG,WAAW,CAAC,EAAE,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,8EAA8E;AAC9E,uBAAuB;AACvB,8EAA8E;AAE9E,SAAS,aAAa,CACpB,SAAiB,EACjB,SAAiB,EACjB,MAAe,EACf,cAA6B,IAAI,EACjC,gBAA+B,IAAI;IAEnC,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;IAEzF,MAAM,SAAS,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACzE,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAEvE,2BAA2B;IAC3B,IAAI,MAAM,GAAsB,QAAQ,CAAC;IACzC,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,MAAM,IAAI,CAAC,CAAC,MAAM,KAAK,iBAAiB,CAAC,CAAC;IAC/F,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAChD,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IAExD,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,GAAG,WAAW,CAAC;IACvB,CAAC;SAAM,IAAI,UAAU,IAAI,cAAc,EAAE,CAAC;QACxC,MAAM,GAAG,SAAS,CAAC;IACrB,CAAC;IAED,qEAAqE;IACrE,IAAI,aAAa,KAAK,IAAI,EAAE,CAAC;QAC3B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,MAAM,IAAI,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;YACzC,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;gBAClB,aAAa,GAAG,IAAI,CAAC;gBACrB,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO;QACL,SAAS;QACT,SAAS;QACT,WAAW;QACX,aAAa;QACb,SAAS;QACT,OAAO;QACP,MAAM;QACN,MAAM;KACP,CAAC;AACJ,CAAC"}
|