Thank you for your interest in contributing to OCPP DebugKit! This document covers setup, conventions, and the contribution workflow.
- Node.js >= 20.0.0
- pnpm 10.x (
npm install -g pnpm) - Git
# Clone the repository
git clone https://github.com/ocpp-debugkit/toolkit.git
cd toolkit
# Install dependencies
pnpm install
# Verify everything works (same checks CI runs, in the same order)
pnpm lint
pnpm format:check
pnpm typecheck
pnpm build
pnpm testocpp-debugkit/
├── packages/
│ └── toolkit/ # Single npm package @ocpp-debugkit/toolkit
│ └── src/
│ ├── core/ # Data model, parser, normalizer, timeline, failure detection
│ ├── scenarios/ # Predefined trace scenarios for testing
│ ├── reporter/ # Report generators (Markdown, HTML)
│ ├── replay/ # Replay engine
│ ├── react/ # Reusable React components
│ └── cli/ # Command-line interface (bin: ocpp-debugkit)
├── apps/
│ └── web/ # Single Next.js app (landing, inspector, docs, blog)
└── ...
All modules ship in a single package, @ocpp-debugkit/toolkit, exposed via
subpath exports (@ocpp-debugkit/toolkit/core, /scenarios, /reporter,
/replay, /react, /cli, /fixtures). See
ADR-0010 for the rationale.
See AGENTS.md for a detailed overview of the architecture,
build commands, and package dependency graph.
OCPP DebugKit is a pnpm monorepo with a single published package
(@ocpp-debugkit/toolkit) and a single Next.js web app.
Trace Input (JSON/JSONL)
→ parseTrace() → Events[]
→ buildSessionTimeline() → Sessions[]
→ detectFailures() → Failures[]
→ summarizeSessions() → SessionSummary[]
Each step is a pure function — no side effects, no I/O. The CLI and web app compose these functions to provide the full analysis pipeline.
| Module | Location | Description |
|---|---|---|
| Types | src/core/types.ts |
All TypeScript interfaces and type definitions |
| Schemas | src/core/schemas.ts |
Zod validation schemas for input |
| Parser | src/core/parser.ts |
parseTrace() — JSON, JSONL, bare array |
| Normalizer | src/core/normalizer.ts |
Event normalization (timestamps, directions) |
| Timeline | src/core/timeline.ts |
buildSessionTimeline() — session correlation |
| Detection | src/core/detection.ts |
detectFailures() — failure detection rules |
| Diff | src/core/diff.ts |
diffTraces() — compare two traces |
| Assertions | src/core/assertions.ts |
evaluateScenario() — scenario assertions |
| Summarizer | src/core/summarizer.ts |
Session summary statistics |
| Validator | src/core/validator.ts |
OCPP 1.6 structural validation |
| Scenarios | src/scenarios/ |
Predefined scenarios + registry |
| Reporter | src/reporter/ |
Markdown + HTML report generators |
| Replay | src/replay/ |
Deterministic replay engine |
| React | src/react/ |
Reusable UI components |
| CLI | src/cli/ |
Command-line interface |
core,scenarios,reporter,replay,react: browser-safe (no Node built-ins)cli: Node-only (usesfs,path,process). Never imported by browser code.
All work should be tracked via GitHub Issues. Check existing issues or create a new one using the appropriate issue template (bug report, feature request, or scenario request).
git checkout -b feat/<scope>-<description>- Follow the code style (TypeScript strict, Prettier, ESLint).
- Write tests for the behavior you introduce.
- Update documentation as needed.
pnpm lint
pnpm format:check
pnpm typecheck
pnpm build
pnpm testThese are the checks CI runs, in the same order, so a green run here means a
green run there. CI stops at the first failure, which means a formatting problem
masks every result after it. pnpm format:check only reports; run pnpm format
to apply the fixes.
If your PR changes publishable package behavior, add a changeset:
pnpm changesetThis creates a file in .changeset/ describing the change and which packages
are affected.
- Use the PR template.
- Link the issue with
Closes #N. - Ensure CI passes (lint, typecheck, test, build).
This project uses Conventional Commits:
feat(core): implement trace parser for JSON input
fix(cli): handle missing trace file gracefully
docs: add architecture overview to docs
chore: add vitest configuration
test(core): add parser edge case tests
feat/<scope>-<description> # e.g. feat/core-parse-trace
fix/<scope>-<description> # e.g. fix/cli-stdout-encoding
chore/<description> # e.g. chore/ci-workflow
docs/<description> # e.g. docs/readme
test/<description> # e.g. test/core-coverage
- TypeScript strict mode — no
anywithout justification. - Prettier for formatting (single quotes, trailing commas, 100 char width).
- ESLint with
typescript-eslintstrict rules. - Use
import typefor type-only imports.
- Tests use Vitest.
- Test files:
*.test.ts/*.spec.ts. - Tests live next to the code they test.
- Coverage target: 70%+ for core package.
pnpm test # Run all unit tests
pnpm test:external-fixture # External fixture test (installs from tarball)- Test the behavior, not the implementation.
- Cover both positive (expected result) and negative (error/edge case) paths.
- Use the
makeEvent()helper pattern fromdetection.test.tsfor creating test events. - For CLI tests, use the
execa-based pattern fromcli.test.ts.
Scenarios are the most common first contribution. A scenario is a synthetic trace with expected failure outcomes and optional assertions.
-
Create a file in
packages/toolkit/src/scenarios/__scenarios__/:export default { name: 'my-scenario', description: 'Description of what the scenario tests.', trace: { /* synthetic trace data */ }, expectedFailures: ['FAILED_AUTHORIZATION'], assertions: [ { type: 'event_order', params: { actions: ['BootNotification', 'Authorize'] } } ], };
-
Import and register it in
packages/toolkit/src/scenarios/index.ts. -
Update the scenario count in every place that states it:
packages/toolkit/src/scenarios/index.test.tstests/external-fixture/test.mjsREADME.mdpackages/toolkit/README.md
The first two fail the test suite when they drift. The two READMEs do not, so they are easy to miss.
-
Run
ocpp-debugkit cito verify all scenarios pass, then run the full local check from Verify Locally.ocpp-debugkit cidoes not cover formatting, lint, or types. -
Add a changeset with
pnpm changeset, and pick patch. A new scenario does add a public export, but scenarios are test corpus rather than consumer API surface: nothing is built against an individual scenario constant, and the registry is consumed as a whole throughscenariosandgetScenario(). Minor and major releases are reserved for changes to the analysis engine, so version numbers keep lining up with the milestones in ROADMAP.md.
- All data must be synthetic — no real station IDs, transaction IDs, idTags, or personal data.
- Use
SYNTHETIC-TAG-NNNfor idTags,CS-SYNTHETIC-NNNfor station IDs. - Each scenario takes its own station ID. Check the existing files in
__scenarios__/for the next free number rather than reusing one. expectedFailuresmust align with detection rules available in the current version.- Test your scenario with
ocpp-debugkit scenario run my-scenario.
Detection rules identify failure patterns in traces.
Every detection rule ships with a scenario that exercises it, added in the same pull request as the rule. A rule without a scenario has no regression guard, so nothing in the corpus catches it breaking. This is the invariant that keeps the scenario corpus honest as rules are added, and it means the scenario count in the READMEs tracks real coverage rather than volume. See How to Add a Scenario.
-
Add the failure code to
FailureCodeinpackages/toolkit/src/core/types.ts. -
Add suggested steps and severity in
packages/toolkit/src/core/detection.ts:SUGGESTED_STEPS.MY_NEW_RULE = [ 'Step 1 to resolve the issue', 'Step 2 to resolve the issue', ]; SEVERITY.MY_NEW_RULE = 'warning';
-
Implement the detection function:
function detectMyNewRule(events: Event[]): Failure[] { const failures: Failure[] = []; // Detection logic return failures; }
-
Wire it into
detectFailures(). -
Add unit tests in
detection.test.ts(positive + negative cases). -
Audit all existing scenarios — new rules may trigger on existing fixtures. Fix false positives or add the new code to
expectedFailures. -
Add a scenario that exercises the new rule, in this same pull request, following How to Add a Scenario. This is the positive side of step 6: step 6 confirms the rule stays quiet where it should, and this confirms it fires where it should. A rule shipped without one is incomplete.
Good-first-issues are labeled with good-first-issue and are designed for
new contributors. They should:
- Have clear instructions and acceptance criteria
- Point to the relevant files and code
- Be scoped to a single concern
- Not require deep knowledge of the codebase
If you're a new contributor, look for issues with the good-first-issue label
on the issues page.
Comment on the issue saying you would like to work on it and it will be assigned to you.
Please hold one open claim at a time. Once your pull request for it is merged, say which issue you want next and it will be assigned. This keeps issues from sitting claimed while another is still in review, so other newcomers can see what is genuinely free.
OCPP DebugKit processes untrusted input (trace files, pasted content). When working on parsing, CLI, or UI code:
- Validate all external input — CLI args, file content, paste input.
- Safe JSON parsing — always use try/catch and enforce size limits.
- No dynamic code execution — no
eval(),Function()on untrusted input. - No prototype pollution — validate object shapes, use safe parsing.
- Path safety — validate file paths, prevent path traversal.
- Safe rendering — no
dangerouslySetInnerHTML, no unsafe HTML injection. - Browser-local processing — no automatic uploading of user data.
- No secrets in committed files — no credentials, API keys, or tokens.
- No sensitive data in committed artifacts — use synthetic data in trace fixtures, sample data, and test data. Real station IDs, transaction IDs, IPs, or personal information must not appear in committed files. User-loaded traces and runtime-generated reports are not subject to this rule — they contain the user's own data and must not be redacted.
If you discover a security vulnerability, please see the Security Policy for responsible disclosure.
Maintainers may use AI-assisted development tools, but all contributions must be reviewed, tested, documented, and scoped like normal engineering work. AI-generated code is held to the same standards as any other contribution: it must pass CI, include tests, be security-reviewed, and be understandable by a human reviewer.
Contributors using AI agents can point them at AGENTS.md for
a structured overview of this repository's architecture, conventions, and build
system. CURRENT_STATE.md reflects what has been built
so far and what is in progress — use it to orient your agent before starting
work.
No AI tool preference is assumed or required. The project does not endorse any specific AI tool.
The following documents are updated as part of the work, inside the PR:
| Document | When updated |
|---|---|
CURRENT_STATE.md |
Inside every PR, before merge |
AGENTS.md |
When architecture, packages, or build commands change |
CONTRIBUTING.md |
When contribution process or conventions change |
ROADMAP.md |
At each milestone boundary |
README.md |
When description, badges, quickstart, or links change |
- Open a GitHub Issue
- Read the documentation
- Check the roadmap