Skip to content

feat(engine): trace parser (JSON object / JSONL / bare array)#17

Merged
sepehr-safari merged 1 commit into
mainfrom
feat/engine-parser
Jul 11, 2026
Merged

feat(engine): trace parser (JSON object / JSONL / bare array)#17
sepehr-safari merged 1 commit into
mainfrom
feat/engine-parser

Conversation

@sepehr-safari

Copy link
Copy Markdown
Member

What & why

Third slice of S1 — Engine core: src/ocpp/parser.zig, which turns an
untrusted trace string into normalized Events plus warnings. Mirrors the
toolkit's core/parser.ts + schemas.ts — the shared conformance contract —
so both implementations accept the same inputs and reject the same malformations.

Changes

  • Three formats + detection — JSON object ({ traceId?, metadata?, events[] }),
    JSONL (one event per line, blank lines skipped), and bare array of raw
    messages. A {-leading input that isn't a single JSON value falls back to
    JSONL (kept only if it yields events).
  • Structural validationvalidateRawMessage / classifyEventInput mirror
    rawOcppMessageSchema / traceEventInputSchema (MessageTypeId ∈ {2,3,4},
    string UniqueId, per-type arity, string action / error positions; optional
    string|number|null timestamp; optional enum direction).
  • Warnings vs errors — malformed individual JSONL events warn (1-based
    line number + bounded excerpt) and are skipped; JSON object and bare array are
    all-or-nothing at the structural level, matching the toolkit's Zod schemas.

Security (untrusted input)

  • Size limit MAX_INPUT_SIZE_BYTES = 10 MB, checked before parsing.
  • Event-count limit MAX_EVENT_COUNT = 10 000, checked after.
  • Parsing uses a caller-owned arena with allocate = .alloc_always, so every
    string is copied in and the ParseResult borrows only the arena — never the
    caller's input buffer.
  • __proto__ and friends are inert: Zig objects have no prototype, so key
    "pollution" is a non-issue (covered by a test).

Testing

  • native test -Dplatform=null25/25 pass (8 new parser tests ported from
    the toolkit: each format, blank-line skipping, per-line + structural warnings,
    normalization through the parser, no-reorder, size/count limits, pollution).
  • native check --strict and zig fmt --check clean.

Closes #13

Add `src/ocpp/parser.zig` — the third S1 engine module — parsing untrusted
trace input into normalized `Event`s. Mirrors the toolkit's `core/parser.ts`
and `schemas.ts` (the shared conformance contract), not its source.

- Format detection and per-format parsers: JSON object
  (`{ traceId?, metadata?, events[] }`), JSONL (one event per line), and bare
  array of raw messages, plus the object→JSONL fallback.
- Structural validation of raw messages and event inputs; malformed individual
  events warn (with 1-based line/index and a bounded excerpt) while structural
  failures abort with a typed error.
- Untrusted-input limits: 10 MB input (checked first) and 10 000 events
  (checked after), and parsing into a caller-owned arena with `alloc_always`
  so the result borrows only the arena, never the input buffer.

The toolkit's parser test cases are ported.

Closes #13
@sepehr-safari sepehr-safari added this to the S1 — Engine core milestone Jul 11, 2026
@sepehr-safari sepehr-safari added type:feature New capability area:engine Pure Zig OCPP engine priority:high labels Jul 11, 2026
@sepehr-safari
sepehr-safari merged commit 35526a4 into main Jul 11, 2026
3 checks passed
@sepehr-safari
sepehr-safari deleted the feat/engine-parser branch July 11, 2026 10:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:engine Pure Zig OCPP engine priority:high type:feature New capability

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(engine): trace parser (JSON object / JSONL / bare array)

1 participant