Week 14 of my Google Summer of Code journey with CircuitVerse (August 24th to August 30th) started on a really good note.
Aboo had merged both of my stacked PRs late on Sunday, so I came into Monday with the Editor's Picks round-trip fixes and ELK Auto Layout both finally in! 🎉
After spending the last few weeks testing and strengthening the pipeline, it felt really good to see those pieces land. I wasted no time and moved straight to one of the final major parts of my project — JSON Schema and validation.
📜 Building the Canonical JSON Schema
The goal of the schema is to define exactly what a valid Canonical v1 project is allowed to look like.
Since I already had the TypeScript types in canonical.types.ts, I used ts-json-schema-generator to generate an initial schema from them.
To be completely honest, this gave me a really useful starting point. But the generated output was definitely not something I could just drop into the project and call finished.
I spent the rest of the time reshaping it to match how Canonical v1 actually works. I split common structures into reusable definitions with $ref and added stricter rules for things such as:
- canonical scope, component and net IDs
- port names and port references
- supported component and annotation types
- directions and bit widths
- routing connections and intermediate nodes
- required project/circuit metadata
- numeric ranges, allowed enum values and extra properties
In simple terms, TypeScript protects us while writing the code, while the JSON Schema gives us a contract we can use when an actual .cv file arrives at runtime.
Once I was happy with it, I raised PR #1236 containing the Canonical v1 schema.
🛡️ Adding Runtime Validation with AJV
As soon as the schema PR was up, I started the validation layer on top of it.
For this I used Ajv. The first validation pass checks the incoming JSON against the schema — whether the expected fields exist, the values have the right types, IDs match the required formats, unsupported properties are rejected, and so on.
But I realised there is another class of errors that a JSON Schema alone cannot fully catch.
For example, a net can contain a perfectly valid-looking component ID while that component doesn't actually exist in the same circuit. The JSON shape is valid, but the project itself is inconsistent.
So after the AJV schema check, I added a second cross-reference validation pass. It checks things like:
- the focused circuit and tab order only reference real circuits
- SubCircuit components point to existing circuits
- nets and layouts reference real components
- routing data refers to valid nets and intermediate nodes
- routing endpoints actually belong to the net they claim to be part of
- duplicate component or net IDs are rejected
I also made the validation errors include useful JSON paths, so instead of simply saying that a project is invalid, the importer can point much closer to where the problem actually is.
Most importantly, importCanonical() now accepts the incoming data as unknown and does not start modifying the project until validation succeeds. Once AJV validates the structure, its typed validator safely narrows the data to CanonicalProject for the rest of the importer.
I kept layout optional as well. That is important because a project without layout data is still valid — the ELK Auto Layout feature from last week can generate one during import.
There were also a couple of compatibility details, like normalising older timePeriod values into valid numbers before canonical export.
By the end of the week, I raised PR #1242 for the runtime validation work, stacked directly on top of the schema PR.
So once again the stack kept the changes clean:
main→ #1236 JSON Schema → #1242 AJV validation
It was nice getting to use the stacked PR workflow again almost immediately after learning it last week. 😄
🔮 The Final Week
Next week will be the last week of my GSoC journey.
I'll focus on getting the remaining reviews addressed, finishing anything my mentors want polished, and writing my final project report to properly wrap up the summer.
One last week. Let's finish strong! 🚀🔥
Top comments (0)