@sciflow/schema-prosemirror
v0.1.1
Published
The canonical manuscript ProseMirror schema for the [SciFlow](https://docs.sciflow.org) editor, plus the generators that derive other formats from it: JSON Schema, the snapshot schema, and a JATS 1.4 `<body>`.
Readme
@sciflow/schema-prosemirror
The canonical manuscript ProseMirror schema for the SciFlow editor,
plus the generators that derive other formats from it: JSON Schema, the snapshot schema, and a
JATS 1.4 <body>.
Install
npm install @sciflow/schema-prosemirrorprosemirror-model and the other prosemirror-* packages it builds on are ordinary dependencies
of this package. If you also install @sciflow/editor-core or @sciflow/editor-start, keep every
prosemirror-* version identical across your install — two copies of prosemirror-model produce
schema instances that fail instanceof checks against each other.
Public API
| Export | Description |
| --- | --- |
| manuscript | The ProseMirror Schema instance the editor runs on. There is no separate manuscriptSchema alias. |
| figure | The figure NodeSpec on its own, for hosts that inspect or extend that one node. |
| generateJsonSchema(schema): JsonSchema | Derives a JSON Schema for the ProseMirror document tree from a live Schema. |
| generateSnapshotSchema(schema): JsonSchema | Derives a JSON Schema for the whole snapshot wrapper (doc + files + references + selection state). |
| generateJatsBody(doc, options?): string | Renders a JATS 1.4 <body> XML string from document JSON. |
| JsonSchema, PMNode, PMMark, JatsBodyOptions | Types for the generators above. |
| Node attribute types | BlockquoteNodeAttrs, BookmarkNodeAttrs, CitationNodeAttrs, CodeBlockNodeAttrs, DocNodeAttrs, FigureNodeAttrs, FootnoteNodeAttrs, HeadingNodeAttrs, ImageNodeAttrs, LinkNodeAttrs, MathNodeAttrs, OrderedListNodeAttrs, ParagraphNodeAttrs, PartNodeAttrs, PlaceholderNodeAttrs, PoetryNodeAttrs, Table*NodeAttrs, VerbatimNodeAttrs, and the shared ManuscriptNodeAttrMap, HasIdAttr, NodeId, PartType, Placement, NumberingStyle, TextDirection and related unions. |
Usage
Loading a stored document
Instantiating nodes is ProseMirror's own API — this package exports no nodeFromJSON or
marksFromJSON helpers:
import { Node } from 'prosemirror-model';
import { manuscript } from '@sciflow/schema-prosemirror';
const doc = Node.fromJSON(manuscript, snapshot.doc);Node.fromJSON does not validate content against the schema. A document that violates a content
expression loads silently and fails later, at the first transform that re-validates it.
JATS export
generateJatsBody takes document JSON — PMNode, where type is a string and content is
an array — not a live ProseMirror node. Passing a live node fails with nodes.map is not a
function:
import { generateJatsBody } from '@sciflow/schema-prosemirror';
const xml = generateJatsBody({
type: 'doc',
content: [
{ type: 'paragraph', content: [{ type: 'text', text: 'Review sample' }] },
],
});
// → '<body><p>Review sample</p></body>'Pass snapshot.doc directly, or pmDoc.toJSON() if you parsed the document first. Output is a
single line unless you set options.pretty; when it is on, line breaks are inserted only
between block-level elements, so mixed content is never broken across lines.
Scope of the output. This is a <body> fragment, not a complete article: a caller assembles
<front>, <back> and the <article> wrapper around it. Which constructs are DTD-valid and
which are known limitations is documented in the
Content Import & Export reference.
Identifier rewriting, and a collision you must guard against. JATS types id as ID and
rid as IDREFS, so both must be XML Names — they cannot start with a digit or contain arbitrary
punctuation. Document identifiers routinely are plain numbers. The generator repairs them:
characters an XML Name may not contain become -, and a result that still does not start legally
is prefixed with id-. So a reference keyed "299" is written as id="id-299".
Two consequences for anyone assembling the surrounding article:
- Your back matter must apply the same mapping, or
<ref id="299">will no longer match therid="id-299"the body emits. - The mapping is pure but not injective.
299andid-299both map toid-299, as doref 1,ref:1andref-1. A document carrying two such source identifiers produces duplicateIDvalues, which a validating parser rejects. Real manuscripts rarely carry both forms, but the generator does not detect or resolve the collision — ensure your source identifiers are distinct after the mapping.
JSON Schema
import { manuscript, generateSnapshotSchema } from '@sciflow/schema-prosemirror';
const snapshotSchema = generateSnapshotSchema(manuscript); // feed to Ajv, etc.Descriptions in the generated schema are read from the description fields on each NodeSpec,
MarkSpec and AttributeSpec.
Where this sits
@sciflow/schema-prosemirror and @sciflow/schema-core are siblings, not a stack: this
package does not depend on @sciflow/schema-core. That package holds the helpers that must work
without ProseMirror; everything here needs a live Schema.
This package is consumed by @sciflow/editor-core, @sciflow/pandoc-ast, and — as a peer
dependency — @sciflow/editor-start.
Building
From the workspace root:
npx nx build @sciflow/schema-prosemirrorRunning unit tests
npx nx test @sciflow/schema-prosemirrorTests use Vitest.
License
MIT. See the repository LICENSE.
