document-content-model 3.3.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +82 -31
- package/dist/codec.d.cts +2 -7
- package/dist/codec.d.ts +2 -7
- package/dist/content-BN0PWqpt.d.cts +2393 -0
- package/dist/content-CiZf6Fqn.d.ts +2393 -0
- package/dist/content-json-schema-defs.cjs +1005 -0
- package/dist/content-json-schema-defs.js +1005 -0
- package/dist/content.cjs +1 -7
- package/dist/content.d.cts +2 -2354
- package/dist/content.d.ts +2 -2354
- package/dist/content.js +1 -7
- package/dist/definitions.cjs +109 -0
- package/dist/definitions.d.cts +115 -0
- package/dist/definitions.d.ts +115 -0
- package/dist/definitions.js +99 -0
- package/dist/index.cjs +42 -18
- package/dist/index.d.cts +7 -6
- package/dist/index.d.ts +7 -6
- package/dist/index.js +6 -5
- package/dist/package-node-Bt4_XS3d.d.ts +441 -0
- package/dist/package-node-Bz55gXHd.d.cts +441 -0
- package/dist/package-node.cjs +120 -0
- package/dist/package-node.d.cts +2 -0
- package/dist/package-node.d.ts +2 -0
- package/dist/package-node.js +93 -0
- package/dist/package.cjs +37 -7
- package/dist/package.d.cts +452 -755
- package/dist/package.d.ts +452 -755
- package/dist/package.js +39 -8
- package/dist/schema-io.cjs +55 -25
- package/dist/schema-io.d.cts +13 -11
- package/dist/schema-io.d.ts +13 -11
- package/dist/schema-io.js +54 -25
- package/package.json +2 -2
- package/schemas/content-document.schema.json +1476 -68
- package/schemas/document-package.schema.json +3056 -30
- package/dist/layout.cjs +0 -150
- package/dist/layout.d.cts +0 -684
- package/dist/layout.d.ts +0 -684
- package/dist/layout.js +0 -136
- package/schemas/layout-document.schema.json +0 -760
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/ExaDev/document-schema.js) [](https://www.npmjs.com/package/document-schema.js) [](https://github.com/ExaDev/document-schema.js/releases/latest) [](https://github.com/ExaDev/document-schema.js/actions)
|
|
4
4
|
|
|
5
|
-
> The canonical, format-agnostic content and
|
|
5
|
+
> The canonical, format-agnostic content and document-package schema pivot shared by [ooxml.js](https://github.com/ExaDev/ooxml.js), [odf.js](https://github.com/ExaDev/odf.js), [documents.js](https://github.com/ExaDev/documents.js), [pdf-codec](https://github.com/ExaDev/pdf-codec), and [markdown-codec](https://github.com/ExaDev/markdown-codec).
|
|
6
6
|
|
|
7
7
|
Both `ooxml.js` and `documents.js` independently arrived at the same content vocabulary, producing two field-identical copies in two places. This package is the fix: one schema, imported by every format package instead of redefined by each. It also sidesteps a circular dependency (`documents.js` depends on both `ooxml.js` and `odf.js`).
|
|
8
8
|
|
|
@@ -48,30 +48,70 @@ graph TD
|
|
|
48
48
|
style schema fill:#f9a825,stroke:#333,stroke-width:3px
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
-
`ContentDocument` (the semantic pivot) is a discriminated union of five kinds: `wordprocessing` (docx/odt sections of paragraphs/runs/tables/images), `presentation` (pptx/odp slides of shapes), `spreadsheet` (xlsx/ods sheets of cells, columns, rows, print settings), `drawing` (odg pages of shapes plus vector primitives — rect/ellipse/line/path), and `formula` (an equation carrying its own MathML node tree plus StarMath source when the producing format had one, extended with the two-layer math model: an optional verbatim-LaTeX `presentation` authoritative for rendering, an optional semantic `content: MathExpression` tree authoritative for computation, and provenance — neither layer stored derived from the other, so editing one never silently mutates the other). `ContentEmbeddedObjectSchema` lets any of the five embed another whole `ContentDocument`. Every paragraph/run/image/table/shape/vector/spreadsheet-cell leaf also carries its own canonical `headingLevel`-or-position fields directly: a `ContentParagraph`'s optional `headingLevel` (1 = the outermost heading, independent of the round-trip-only `styleId`), and every such leaf's optional `frames: LayoutFrame[]` — that node's own rendered page position(s) (`pageIndex` plus PDF user-space `xPt`/`yPt`/`widthPt`/`heightPt`), fused directly onto the content tree once a layout pass has run. `
|
|
51
|
+
`ContentDocument` (the semantic pivot) is a discriminated union of five kinds: `wordprocessing` (docx/odt sections of paragraphs/runs/tables/images), `presentation` (pptx/odp slides of shapes), `spreadsheet` (xlsx/ods sheets of cells, columns, rows, print settings), `drawing` (odg pages of shapes plus vector primitives — rect/ellipse/line/path), and `formula` (an equation carrying its own MathML node tree plus StarMath source when the producing format had one, extended with the two-layer math model: an optional verbatim-LaTeX `presentation` authoritative for rendering, an optional semantic `content: MathExpression` tree authoritative for computation, and provenance — neither layer stored derived from the other, so editing one never silently mutates the other). `ContentEmbeddedObjectSchema` lets any of the five embed another whole `ContentDocument`. Every paragraph/run/image/table/shape/vector/spreadsheet-cell leaf also carries its own canonical `headingLevel`-or-position fields directly: a `ContentParagraph`'s optional `headingLevel` (1 = the outermost heading, independent of the round-trip-only `styleId`), and every such leaf's optional `frames: LayoutFrame[]` — that node's own rendered page position(s) (`pageIndex` plus PDF user-space `xPt`/`yPt`/`widthPt`/`heightPt`), fused directly onto the content tree once a layout pass has run. `DocumentPackage` is the single hierarchical artefact — structure, layout, and content fused in one tree (see [The package tree](#the-package-tree)): the root carries `kind`, `metadata`, the optional document-level `symbolTable` and rendered `pages`, the optional package-level `styles`/`definitions` tables (see [Definitions tables and styles](#definitions-tables-and-styles)), and `children` — one group per top-level container with the content tree grouped inside it; the schema does not keep populated `frames` fields and `pages` in sync or detect staleness, and does not check that a tree's `style` refs name table entries (both are producer responsibilities, exactly as the frames/pages pairing always was). Every one of the five kinds also accepts an optional document-level `symbolTable` — the math curation layer mapping each written symbol glyph (within a scope) to its id, quantity kind, preferred unit, and definition source, alongside the unit registry (SI dimension-exponent vectors, exact rational conversions, per-unit-system normalisation contexts) that the `qty` nodes of lowered formulas resolve against.
|
|
52
52
|
|
|
53
|
-
The
|
|
53
|
+
The `LayoutDocument` family (pages of positioned `LayoutItem`s — `text`/`image`/`rect`/`line`/`ellipse`/`path`/`link` in PDF user-space coordinates) no longer lives here: 4.0.0 demoted it to a pdf-codec-private model ([pdf-codec#65](https://github.com/ExaDev/pdf-codec/issues/65)), where the only codec that ever read or wrote it owns it outright. `documentFromJson` recognises old layout-document `$schema` URIs and throws a tombstone pointing at pdf-codec rather than failing as if the value were unrelated. Dependents stay on document-schema.js 3.x via semver until their own majors, so the demotion is not a cascade-breaker.
|
|
54
|
+
|
|
55
|
+
The package contains only [Zod](https://zod.dev) schemas, their inferred types, trivial schema-attached helpers (hex-colour conversion, recursive structural type guards, the style-resolution helpers of `src/definitions.ts`), and one small structural interface (`ContentCodec`, see [Codecs](#codecs)). No XML, ZIP, PDF, or binary handling; the sole dependency is `zod`.
|
|
54
56
|
|
|
55
57
|
Two format-agnostic helpers live here because they operate on the content model itself: cell-addressing utilities in `src/a1.ts` (0-based row/column indices, row-first order matching `ContentSheetCell`'s `{row, column}`) and the `FontFace` interface in `src/font-port.ts` (`{family, bold, italic}`).
|
|
56
58
|
|
|
57
59
|
## Usage
|
|
58
60
|
|
|
59
61
|
```ts
|
|
60
|
-
import { ContentDocumentSchema, DocumentPackageSchema
|
|
62
|
+
import { ContentDocumentSchema, DocumentPackageSchema } from 'document-schema.js';
|
|
61
63
|
|
|
64
|
+
// The codec-exchange form: what every format's reader produces and every writer consumes -- always flat,
|
|
65
|
+
// always fully materialised (no styles table, no refs), never versioned (that lives on the serialised artefact).
|
|
62
66
|
const content = ContentDocumentSchema.parse(someWordprocessingOrPresentationValue);
|
|
63
|
-
// A content-only package -- no layout pass has run yet, so no node carries `frames` and `pages` stays absent.
|
|
64
|
-
const pkg = DocumentPackageSchema.parse({ formatVersion: 2, content });
|
|
65
67
|
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
const
|
|
68
|
+
// The package tree: what a serialised dump carries. `children` holds one group per top-level container,
|
|
69
|
+
// with the content grouped inside it (see "The package tree" below).
|
|
70
|
+
const pkg = DocumentPackageSchema.parse({
|
|
71
|
+
kind: 'wordprocessing',
|
|
72
|
+
metadata: { title: 'Example' },
|
|
73
|
+
children: [
|
|
74
|
+
{
|
|
75
|
+
node: { kind: 'section', pageSize: { widthPt: 612, heightPt: 792 }, margins: { topPt: 72, rightPt: 72, bottomPt: 72, leftPt: 72 } },
|
|
76
|
+
children: [
|
|
77
|
+
{ node: { kind: 'paragraph', headingLevel: 1, runs: [{ text: 'Heading' }] }, children: [] },
|
|
78
|
+
{ kind: 'paragraph', runs: [{ text: 'Body.' }] },
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
});
|
|
69
83
|
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
const
|
|
84
|
+
// Once a layout pass has fused rendered positions onto the tree's own nodes (each via its own `frames` array)
|
|
85
|
+
// and reported each page's own size, `pages` is populated to match:
|
|
86
|
+
const laidOut = DocumentPackageSchema.parse({ ...pkg, pages: [{ widthPt: 612, heightPt: 792 }] });
|
|
73
87
|
```
|
|
74
88
|
|
|
89
|
+
## The package tree
|
|
90
|
+
|
|
91
|
+
`DocumentPackage` ([#20](https://github.com/ExaDev/document-schema.js/issues/20)) is the promoted single hierarchical artefact — one tree where 3.x carried `{ formatVersion, content, pages }` with the content flat. The tree's vocabulary is defined in `src/package-node.ts` and was proven first as [document-outline.js](https://github.com/ExaDev/document-outline.js)'s phase-1 `decompose`/`flatten` implementation ([document-outline.js#2](https://github.com/ExaDev/document-outline.js/issues/2)); this package's schemas are that shape's schema-home port, matching it node for node:
|
|
92
|
+
|
|
93
|
+
- **Groups** are `{ node, children }` where `node` embeds either an anchor paragraph (heading groups and list-item groups carry the full `ContentParagraph` — runs, formatting, frames — never a projected text label) or a container descriptor: `{ kind: 'section', pageSize, margins }`, `{ kind: 'slide', size, notes }`, `{ kind: 'sheet', name, cells, columns, rows, printSettings }`, `{ kind: 'drawPage', size }`, each tagged with a `kind` the flat container type does not carry, or a shape group's untagged frame descriptor.
|
|
94
|
+
- **Bare leaves** carry their own `kind` and never `children`. Discrimination is structural on `node`+`children`, not on the presence of a `kind`.
|
|
95
|
+
- **Section groups are mandatory** — one per `ContentSection` — because a section carries pre-layout page geometry (`pageSize`/`margins`) that a rendered `pages` array cannot hold.
|
|
96
|
+
- **Grouping never crosses container boundaries**: a shape is its own group with its inner blocks grouped inside it (never a slide's paragraphs flattened across its shapes — that is a TOC projection, not a decomposition); a sheet's grid rides on the sheet node with images and embedded documents as children; embedded documents stay intact as one leaf.
|
|
97
|
+
- **Style refs ride on group wrappers only** — a group may carry `style: string` naming a `styles` table entry; `ContentDocument` nodes carry no ref field, so the flat codec-exchange form is always fully materialised.
|
|
98
|
+
|
|
99
|
+
The flat `ContentDocument` and the tree are **one format, two encodings**, related by three laws — the contract with document-outline.js (which property-tests them over real corpus documents) and, at the package boundary, with documents.js ([documents.js#623](https://github.com/ExaDev/documents.js/issues/623)):
|
|
100
|
+
|
|
101
|
+
1. **Strict structural equality, both directions, for table-free packages** — `decompose(flatten(pkg))` and `flatten(decompose(pkg))` reproduce their input exactly.
|
|
102
|
+
2. **Effective-property equality, universally** — resolve styles first, then compare: a factored and an unfactored serialisation of one document are equal (this is also why content hashing and structural diffing resolve first).
|
|
103
|
+
3. **Minting idempotence** — factoring a package a second time mints the identical styles table: `decompose(flatten(decompose(x))) === decompose(x)`.
|
|
104
|
+
|
|
105
|
+
The codecs do not change: they keep producing flat `ContentDocument`s (their natural reading shape); decomposition runs once at the package boundary in documents.js and flatten runs once where a builder consumes a package.
|
|
106
|
+
|
|
107
|
+
## Definitions tables and styles
|
|
108
|
+
|
|
109
|
+
The package root carries a generic definitions-table facility ([#21](https://github.com/ExaDev/document-schema.js/issues/21)): named tables whose entries tree nodes reference by string id. **Styles are the first tenant**; link and footnote definitions are future tenants of the same mechanism ([markdown-codec#63](https://github.com/ExaDev/markdown-codec/issues/63), [#22](https://github.com/ExaDev/document-schema.js/issues/22)) — which is why the tenant-generic `definitions` table (entries tagged with a `kind` discriminator and an open body, `src/definitions.ts`) sits alongside the styles-specific `styles` table rather than the facility being shaped around styles.
|
|
110
|
+
|
|
111
|
+
A styles entry carries `{ paragraph?, run? }` sub-objects of **resolved canonical properties only**: paragraph `alignment`/`list`/`spacingBeforePt`/`spacingAfterPt`/`lineSpacing`/`indentLeftPt`/`indentFirstLinePt`, run `bold`/`italic`/`underline`/`strike`/`fontFamily`/`sizePt`/`color`. Never `frames`, never `sourcePath`, never `styleId` (per-node facts — a position is a fact about a node, not a style), never a `basedOn` graph (the table is a dictionary, not a program) — and the ban list is **enforced by schema shape** (strict objects that reject those keys outright), not merely documented.
|
|
112
|
+
|
|
113
|
+
Resolution is one overlay chain — outermost ancestor group's style, each nearer group's style, the node's own direct properties; innermost wins, with the resolved run half applying one level further down as run defaults under each run's own properties. `src/definitions.ts` exports the pure helpers that implement it (`overlayStyleEntries`, `resolveStyleChain`, `applyParagraphStyleProperties`, `applyRunStyleProperties`); minting entries (the deterministic frequency pass that factors repeated property tuples into `s1`, `s2`, … refs) is documents.js's boundary behaviour, not this package's.
|
|
114
|
+
|
|
75
115
|
Every module is also importable directly — `tsdown` builds one file per source module, and `package.json`'s `"./*"` export makes each individually resolvable:
|
|
76
116
|
|
|
77
117
|
```ts
|
|
@@ -81,24 +121,23 @@ import { ColorSchema } from 'document-schema.js/color';
|
|
|
81
121
|
|
|
82
122
|
## Codecs
|
|
83
123
|
|
|
84
|
-
`ContentCodec
|
|
124
|
+
`ContentCodec` (`src/codec.ts`) is the format-agnostic *interface* a sibling package's docx/pptx/odt/odp/ods/odg/xlsx/markdown codec can implement, so a caller working across formats holds one of these instead of a format-specific function pair:
|
|
85
125
|
|
|
86
126
|
```ts
|
|
87
|
-
import type { ContentCodec
|
|
127
|
+
import type { ContentCodec } from 'document-schema.js';
|
|
88
128
|
|
|
89
129
|
declare const docxCodec: ContentCodec; // read(bytes) -> ContentDocument; write(content) -> bytes -- write is optional
|
|
90
|
-
declare const pdfCodec: LayoutCodec; // read(bytes) -> LayoutDocument; write(layout) -> bytes -- write is required
|
|
91
130
|
```
|
|
92
131
|
|
|
93
|
-
|
|
132
|
+
`ContentCodec.write` is optional (`odf` has a reader but no builder — recovering MathML from glyphs is OCR-adjacent), and the interface is generic over its own `TOptions`. There is no `LayoutCodec` any more: it modelled the one format that produces layout cheaply on read — PDF — and the whole `LayoutDocument` family it described moved to pdf-codec in 4.0.0 (see [pdf-codec#65](https://github.com/ExaDev/pdf-codec/issues/65)).
|
|
94
133
|
|
|
95
|
-
|
|
134
|
+
The interface constructs no `DocumentPackage`; composing one (decomposing a codec's flat `ContentDocument` into the tree) is the caller's job (`documents.js`'s `DOCUMENT_FORMAT_CODECS` registry is the concrete example).
|
|
96
135
|
|
|
97
136
|
This package also hosts the **port contracts** a layout engine consumes: `TextMeasurer`/`StyledRun`/`WrappedLine` (`src/text-layout.ts`), `ProvidedFont`/`FontSubstitution` (`src/font-port.ts`), `MathBox`/`MathFontMetrics`/`PositionedFormula` (`src/math-layout.ts`), and `Point` (`src/geometry.ts`).
|
|
98
137
|
|
|
99
138
|
## JSON Schema
|
|
100
139
|
|
|
101
|
-
|
|
140
|
+
Two plain [JSON Schema](https://json-schema.org) files are published — generated from the Zod definitions via [`z.toJSONSchema()`](https://zod.dev/json-schema) at build time (`scripts/generate-json-schemas.mjs`) — for non-TypeScript consumers:
|
|
102
141
|
|
|
103
142
|
```ts
|
|
104
143
|
const documentPackageSchema = require('document-schema.js/schemas/document-package.schema.json');
|
|
@@ -111,14 +150,13 @@ or from any language/tool that can read a file out of `node_modules`:
|
|
|
111
150
|
```
|
|
112
151
|
node_modules/document-schema.js/schemas/document-package.schema.json
|
|
113
152
|
node_modules/document-schema.js/schemas/content-document.schema.json
|
|
114
|
-
node_modules/document-schema.js/schemas/layout-document.schema.json
|
|
115
153
|
```
|
|
116
154
|
|
|
117
|
-
Each file's `$id` is a jsdelivr URL pinned to the exact npm version — immutable and live on publish
|
|
155
|
+
Each file's `$id` is a jsdelivr URL pinned to the exact npm version — immutable and live on publish, and (see [Versioning by `$schema`](#versioning-by-schema)) the version of anything stamped with it. Both files carry the same hand-authored `$defs` block (the same object emitted twice in one generator run, so the copies cannot drift), covering the recursive paragraph/table/embedded-object, MathML, and package-tree node models that Zod's converter cannot express directly; `content-json-schema-defs.ts` holds those fragments, and a regression test compares each fragment that has a real Zod counterpart against a live `z.toJSONSchema()` of that schema so a field changed without updating its fragment fails a test. The one deliberate cross-file `$ref` is the embedded-object cycle back to a whole `ContentDocument`. Fragments downstream of a `z.custom()` node (`ContentBlock`, `ContentTable`/`Cell`/`Row`, `ContentEmbeddedObject(Block)`, the seven package-tree group wrappers, `MathMlNode`/`Element`/`Attribute`, `ContentFormula`, `MathExpression` and its recursive variants) still need hand re-verification against `src/content.ts`/`src/package-node.ts`/`src/mathml.ts`/`src/math.ts` — see below.
|
|
118
156
|
|
|
119
157
|
### `z.custom()` vs `z.lazy()` for recursive schemas
|
|
120
158
|
|
|
121
|
-
`ContentBlockSchema`, `ContentEmbeddedObjectSchema`, `MathMlNodeSchema`, and `MathExpressionSchema` are `z.custom()` type-guard predicates rather than real Zod schemas, because `z.lazy()` was believed to collapse to `unknown` for recursive children. A throwaway spike (reverted) re-tested `MathMlNodeSchema` (the simplest case) against `zod@4.4.3`.
|
|
159
|
+
`ContentBlockSchema`, `ContentEmbeddedObjectSchema`, the package tree's per-kind group schemas (`src/package-node.ts`), `MathMlNodeSchema`, and `MathExpressionSchema` are `z.custom()` type-guard predicates rather than real Zod schemas, because `z.lazy()` was believed to collapse to `unknown` for recursive children. A throwaway spike (reverted) re-tested `MathMlNodeSchema` (the simplest case) against `zod@4.4.3`.
|
|
122
160
|
|
|
123
161
|
**Finding: `z.lazy()` works now, with one constructional gotcha.** The naive rewrite —
|
|
124
162
|
|
|
@@ -138,41 +176,54 @@ export const MathMlNodeSchema: z.ZodType<MathMlNode> = z.discriminatedUnion('typ
|
|
|
138
176
|
|
|
139
177
|
**This is a tracked follow-up, not carried out here.** Converting `MathMlNodeSchema` for real would let the JSON-schema generator drop its hand-authored `$defs` entries; `ContentBlockSchema`/`ContentEmbeddedObjectSchema` are harder (mutual recursion across table/cell/row, plus the full `ContentDocument` cycle) and were not spiked.
|
|
140
178
|
|
|
179
|
+
### Versioning by `$schema`
|
|
180
|
+
|
|
181
|
+
There is no `formatVersion` field anywhere in a 4.0.0 dump. A serialised value states its version through the release-pinned `$schema` URI its dumper stamped, and that URI **is** the version — one source of truth instead of a hand-kept integer beside URIs that already named the release. `documentFromJson` is the enforcement point for untrusted input:
|
|
182
|
+
|
|
183
|
+
- a URI from the **same major** as the installed release parses (patch and minor releases are semver-compatible with their major's schema generation);
|
|
184
|
+
- an **older major's** URI throws `SchemaVersionMismatchError` naming the change — pre-4.0.0 dumps carry the retired `formatVersion` field and the flat `{ formatVersion, content, pages }` package shape, replaced by the tree form ([#20](https://github.com/ExaDev/document-schema.js/issues/20));
|
|
185
|
+
- a **newer major's** URI throws the same error with the upgrade pointer;
|
|
186
|
+
- a **layout-document** URI (any release) throws `LayoutSchemaDemotedError` pointing at pdf-codec — the demotion tombstone.
|
|
187
|
+
|
|
188
|
+
A bare `DocumentPackageSchema.parse(value)` does **not** version-discriminate — it structurally validates whatever it is handed against the installed schema, full stop — so a caller ingesting a dump it did not itself produce must go through `documentFromJson`, not a direct parse. `documentSchemaKindOf(value)` still answers "which kind does this URI name" version-agnostically without parsing. Content hashes and structural comparisons over serialised dumps must exclude `$schema` — it is envelope metadata that names the dumper, not content; two dumps of one document by two releases hash equal once it is excluded.
|
|
189
|
+
|
|
141
190
|
### Self-describing JSON
|
|
142
191
|
|
|
143
|
-
`documentPackageWithSchema`/`contentDocumentWithSchema
|
|
192
|
+
`documentPackageWithSchema`/`contentDocumentWithSchema` each stamp a `$schema` property pointing at the `.schema.json` file for the currently installed version:
|
|
144
193
|
|
|
145
194
|
```ts
|
|
146
195
|
import { documentPackageWithSchema } from 'document-schema.js';
|
|
147
196
|
|
|
148
197
|
const tagged = documentPackageWithSchema(pkg);
|
|
149
|
-
// { $schema: 'https://cdn.jsdelivr.net/npm/document-schema.js@
|
|
198
|
+
// { $schema: 'https://cdn.jsdelivr.net/npm/document-schema.js@4.0.0/schemas/document-package.schema.json', kind: 'wordprocessing', metadata: {...}, children: [...] }
|
|
150
199
|
writeFileSync('package.json.doc', JSON.stringify(tagged, null, 2));
|
|
151
200
|
```
|
|
152
201
|
|
|
153
|
-
A caller who already knows the kind can keep using the schemas directly — `DocumentPackageSchema.parse(value)` tolerates and strips an incoming `$schema` (none are `.strict()`). `documentFromJson` is for the "don't yet know the kind" case, reading `$schema` to decide which schema to run:
|
|
202
|
+
A caller who already knows the kind can keep using the schemas directly — `DocumentPackageSchema.parse(value)` tolerates and strips an incoming `$schema` (none are `.strict()`). `documentFromJson` is for the "don't yet know the kind or provenance" case, reading `$schema` to decide which schema to run and whether this release may run it:
|
|
154
203
|
|
|
155
204
|
```ts
|
|
156
|
-
import { documentFromJson, UnrecognizedDocumentSchemaError } from 'document-schema.js';
|
|
205
|
+
import { documentFromJson, SchemaVersionMismatchError, UnrecognizedDocumentSchemaError } from 'document-schema.js';
|
|
157
206
|
|
|
158
207
|
try {
|
|
159
208
|
const { kind, value } = documentFromJson(JSON.parse(readFileSync('some-file.json', 'utf8')));
|
|
160
|
-
// kind: 'DocumentPackage' | 'ContentDocument'
|
|
209
|
+
// kind: 'DocumentPackage' | 'ContentDocument'
|
|
161
210
|
} catch (error) {
|
|
162
211
|
if (error instanceof UnrecognizedDocumentSchemaError) {
|
|
163
212
|
console.error('not a document-schema.js value:', error.schema);
|
|
213
|
+
} else if (error instanceof SchemaVersionMismatchError) {
|
|
214
|
+
console.error(`dump is @${error.dumpVersion}, installed is @${error.installedVersion}`);
|
|
164
215
|
}
|
|
165
216
|
}
|
|
166
217
|
```
|
|
167
218
|
|
|
168
|
-
`
|
|
219
|
+
`schemaUriFor(kind)` is the URL builder. No JSON-Schema-validator dependency (e.g. `ajv`) for ingest — the `.schema.json` files are a weaker approximation of the real Zod schemas, so re-validating against them would be a fidelity regression.
|
|
169
220
|
|
|
170
221
|
## Used by
|
|
171
222
|
|
|
172
223
|
- [ooxml.js](https://github.com/ExaDev/ooxml.js) — `readDocx`/`readPptx`/`readXlsxContent` return types are typed against this package's schemas, not a local lookalike.
|
|
173
224
|
- [odf.js](https://github.com/ExaDev/odf.js) — ODF typed readers return the same shared types, so ODF and OOXML speak the identical pivot.
|
|
174
|
-
- [documents.js](https://github.com/ExaDev/documents.js) — primary consumer of `ContentDocument
|
|
175
|
-
- [pdf-codec](https://github.com/ExaDev/pdf-codec) — `readPdf`/`writePdf` operate on this package's `
|
|
225
|
+
- [documents.js](https://github.com/ExaDev/documents.js) — primary consumer of `ContentDocument` and `DocumentPackage`; its `DOCUMENT_FORMAT_CODECS` registry implements `ContentCodec` per format, and its package boundary runs decompose/flatten against the tree form.
|
|
226
|
+
- [pdf-codec](https://github.com/ExaDev/pdf-codec) — owns its layout item model outright since 4.0.0; `readPdf`/`writePdf` operate on pdf-codec's own `LayoutDocument`, and this package's `ContentDocument` remains its content pivot.
|
|
176
227
|
- [markdown-codec](https://github.com/ExaDev/markdown-codec) — `readMarkdown`/`writeMarkdown` read and write this package's `ContentDocument` directly.
|
|
177
228
|
|
|
178
229
|
None depend on each other for this vocabulary — each depends on `document-schema.js` directly.
|
|
@@ -183,13 +234,13 @@ Requires Node.js `>=20` and pnpm `11.6.0` (pinned via `packageManager` in `packa
|
|
|
183
234
|
|
|
184
235
|
```sh
|
|
185
236
|
pnpm install
|
|
186
|
-
pnpm build # turbo run _build -> tsdown && node scripts/generate-json-schemas.mjs (ESM + CJS + .d.ts in dist/, plus the
|
|
237
|
+
pnpm build # turbo run _build -> tsdown && node scripts/generate-json-schemas.mjs (ESM + CJS + .d.ts in dist/, plus the two published .schema.json files in schemas/)
|
|
187
238
|
pnpm typecheck # turbo run _typecheck _typecheck:node -> tsc -p tsconfig.json && tsc -p tsconfig.node.json
|
|
188
239
|
pnpm lint # turbo run _lint -> eslint . --fix --cache --max-warnings 0
|
|
189
240
|
pnpm test # turbo run _test -> vitest run --project unit
|
|
190
241
|
pnpm test:workers # turbo run _test:workers -> vitest run --config vitest.workers.config.ts (runs the test/workers suite under the real Cloudflare Workers runtime via @cloudflare/vitest-pool-workers, turning "pure Zod, no Node-API usage" into a runtime-checked fact rather than an assertion)
|
|
191
242
|
pnpm test:watch # vitest --project unit
|
|
192
|
-
pnpm test:smoke # turbo run _test:smoke -> rebuilds dist/ and schemas/ first, then verifies the built ESM/CJS output loads and exposes the public surface, and that the
|
|
243
|
+
pnpm test:smoke # turbo run _test:smoke -> rebuilds dist/ and schemas/ first, then verifies the built ESM/CJS output loads and exposes the public surface, and that the two generated JSON Schema files exist and are correctly version-pinned
|
|
193
244
|
```
|
|
194
245
|
|
|
195
246
|
To run a single test file: `pnpm vitest run src/path/to/file.test.ts`.
|
package/dist/codec.d.cts
CHANGED
|
@@ -1,13 +1,8 @@
|
|
|
1
|
-
import { ContentDocument } from "./content.cjs";
|
|
2
|
-
import { LayoutDocument } from "./layout.cjs";
|
|
1
|
+
import { l as ContentDocument } from "./content-BN0PWqpt.cjs";
|
|
3
2
|
//#region src/codec.d.ts
|
|
4
3
|
interface ContentCodec<TOptions = unknown> {
|
|
5
4
|
read(bytes: Uint8Array, options?: TOptions): ContentDocument;
|
|
6
5
|
write?(content: ContentDocument, options?: TOptions): Uint8Array;
|
|
7
6
|
}
|
|
8
|
-
interface LayoutCodec<TOptions = unknown> {
|
|
9
|
-
read(bytes: Uint8Array, options?: TOptions): LayoutDocument;
|
|
10
|
-
write(layout: LayoutDocument, options?: TOptions): Uint8Array;
|
|
11
|
-
}
|
|
12
7
|
//#endregion
|
|
13
|
-
export { ContentCodec
|
|
8
|
+
export { ContentCodec };
|
package/dist/codec.d.ts
CHANGED
|
@@ -1,13 +1,8 @@
|
|
|
1
|
-
import { ContentDocument } from "./content.js";
|
|
2
|
-
import { LayoutDocument } from "./layout.js";
|
|
1
|
+
import { l as ContentDocument } from "./content-CiZf6Fqn.js";
|
|
3
2
|
//#region src/codec.d.ts
|
|
4
3
|
interface ContentCodec<TOptions = unknown> {
|
|
5
4
|
read(bytes: Uint8Array, options?: TOptions): ContentDocument;
|
|
6
5
|
write?(content: ContentDocument, options?: TOptions): Uint8Array;
|
|
7
6
|
}
|
|
8
|
-
interface LayoutCodec<TOptions = unknown> {
|
|
9
|
-
read(bytes: Uint8Array, options?: TOptions): LayoutDocument;
|
|
10
|
-
write(layout: LayoutDocument, options?: TOptions): Uint8Array;
|
|
11
|
-
}
|
|
12
7
|
//#endregion
|
|
13
|
-
export { ContentCodec
|
|
8
|
+
export { ContentCodec };
|