Skip to content

Export editor ProseMirror schemas for backend consumption - #1040

Open
koutst wants to merge 5 commits into
mainfrom
claude/tiptap-prosemirror-overview-25ba23
Open

Export editor ProseMirror schemas for backend consumption#1040
koutst wants to merge 5 commits into
mainfrom
claude/tiptap-prosemirror-overview-25ba23

Conversation

@koutst

@koutst koutst commented Aug 19, 2026

Copy link
Copy Markdown
Member

What

Adds npm run schema:export, which derives ProseMirror schema JSON from the same TipTap extension sets the editors actually run and writes them to schemas/prosemirror/:

File Source Covers
block-editor.json (35 nodes, 10 marks) ExtensionKit + AI nodes Notebook notes, posts
comment-editor.json (15 nodes, 6 marks) comment editor extensions, review superset Comments + reviews

The backend loads these with prosemirror-py to validate, read, and write editor documents against the exact schema the frontend enforces:

from prosemirror.model import Node, Schema
schema = Schema(json.load(open("comment-editor.json")))
doc = Node.from_json(schema, comment_json)  # raises on unknown nodes/attrs
doc.check()                                 # raises on invalid nesting

How

  • 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 block editor's ExtensionKit was already importable).
  • The export calls TipTap's getSchema() and serializes each NodeSpec/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's id, textAlign), and schema order.
  • Output is deterministic — rerunning with unchanged extensions is byte-identical, so a future CI drift check is a straight diff.
  • Design choices (documented in schemas/prosemirror/README.md): default: undefineddefault: null to keep attrs optional; permissive doc (block+) rather than the editable notebook's heading block+; sectionHeader/aiWriter/aiImage included as supersets of what can be persisted.

Verification

  • Export runs clean and is deterministic (identical SHAs across runs).
  • Loaded both schemas in prosemirror-py 0.5.x and verified: a realistic comment doc (mention + richLink + 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

  • CI enforcement: regenerate on PRs and fail on drift (schema changed without re-export).
  • Getting the artifacts into the backend repo + loading them there.

🤖 Generated with Claude Code

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.
@koutst
koutst requested a review from a team as a code owner August 19, 2026 00:04
@vercel

vercel Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
web Ready Ready Preview Aug 19, 2026 3:50pm

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread scripts/export-prosemirror-schema.ts Outdated
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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread schemas/prosemirror/README.md Outdated
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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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.
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant