Export editor ProseMirror schemas for backend consumption - #1040
Conversation
Adds npm run schema:export, which derives ProseMirror schema JSON from the same TipTap extension sets the editors run and writes them to schemas/prosemirror/ (block editor + comment editor). The backend can load these with prosemirror-py to validate, read, and write editor documents against the exact schema the frontend enforces. The comment editor's extension list moves out of useCommentEditor into commentEditorExtensions.ts so the hook and the export script share one source of truth.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c01732e0d2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Lean on JSON.stringify's native function-dropping and OrderedMap.toObject() instead of a hand-rolled deep copy. The semantic guards stay: undefined attribute defaults map to null (JSON would silently drop the key, flipping the attr to required), non-serializable defaults fail loudly, and non-finite numbers throw rather than becoming null. Output is byte-identical.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cdd2744543
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| with open("comment-editor.json") as f: | ||
| schema = Schema(json.load(f)) | ||
|
|
||
| doc = Node.from_json(schema, comment_json) # raises on unknown nodes/marks/attrs |
There was a problem hiding this comment.
Validate raw attributes before accepting documents
When the backend follows this example, Node.from_json constructs nodes using the schema's declared attributes, so unrecognized input attributes are ignored rather than rejected; the subsequent doc.check() therefore cannot see them. This makes the documented validation boundary accept payloads it claims to reject, so compare the raw JSON attributes against each node/mark spec before parsing or remove the attrs guarantee.
Useful? React with 👍 / 👎.
The tsx addition was installed with npm 11, which strips dev flags and optional-peer entries that npm 10 (bundled with the .nvmrc Node 22) writes. Re-ran the install with npm 10 so lockfile metadata matches what the project's toolchain produces; all resolved versions are unchanged.
Even npm 10 minors differ: 10.9 records license fields on re-resolved entries that 10.7 does not. Resolved versions are unchanged.
prosemirror-py's from_json only reads schema-declared attributes, so unrecognized input attrs are silently stripped, not rejected. Unknown node/mark types and missing required attributes still raise.
|



What
Adds
npm run schema:export, which derives ProseMirror schema JSON from the same TipTap extension sets the editors actually run and writes them toschemas/prosemirror/:block-editor.json(35 nodes, 10 marks)ExtensionKit+ AI nodescomment-editor.json(15 nodes, 6 marks)The backend loads these with prosemirror-py to validate, read, and write editor documents against the exact schema the frontend enforces:
How
useCommentEditorintocommentEditorExtensions.ts, so the hook and the export script share one source of truth (the block editor'sExtensionKitwas already importable).getSchema()and serializes eachNodeSpec/MarkSpec, stripping DOM-only fields (toDOM/parseDOM). This captures things a hand-written schema would miss: config-derived defaults (codeBlock.language: "javascript"), global attributes (UniqueID'sid,textAlign), and schema order.schemas/prosemirror/README.md):default: undefined→default: nullto keep attrs optional; permissivedoc(block+) rather than the editable notebook'sheading block+;sectionHeader/aiWriter/aiImageincluded as supersets of what can be persisted.Verification
sectionHeader+ marks + codeBlock) round-trips with attr defaults applied; programmatic doc construction works against the block schema; unknown node types and invalid nesting are rejected.npm run type-check, eslint, prettier all pass.Follow-up
🤖 Generated with Claude Code