doc-model.js 2.7.4 → 2.7.6

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 CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  > The canonical, format-agnostic content and layout 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
- Both `ooxml.js` and `documents.js` independently arrived at the same content vocabulary -- paragraphs, runs, tables, images, shapes, slides -- because `documents.js`'s docx/pptx-to-PDF pipeline needed a richer model than `ooxml.js`'s own readers originally produced, and that model was later ported back into `ooxml.js` itself. The result was two field-identical copies maintained 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 that would otherwise appear once `odf.js` exists, since `documents.js` depends on both `ooxml.js` and `odf.js`.
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
 
9
9
  ```mermaid
10
10
  graph TD
@@ -48,13 +48,11 @@ 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-style sections of paragraphs/runs/tables/images), `presentation` (pptx/odp-style slides of shapes), `spreadsheet` (xlsx/ods-style sheets of cells, columns, rows, and print settings), `drawing` (odg-style pages of shapes plus vector primitives -- rect/ellipse/line/path), and `formula` (an equation, carrying its own MathML presentation-layer node tree plus the StarMath source when the producing format had one). `ContentEmbeddedObjectSchema` lets any of the five embed another whole `ContentDocument` -- including a `formula` one, which is what an embedded equation inside a document or slide now carries. `LayoutDocument` (the PDF-rendering pivot) is pages of positioned `LayoutItem`s -- `text`/`image`/`rect`/`line`/`ellipse`/`path`/`link` -- in PDF user-space coordinates, with `LayoutPathSchema` modelling a general vector path rather than only axis-aligned rectangles. `DocumentPackageSchema` is a small envelope pairing the two: `content` required, `layout` optional (it's a derived artifact, absent until something lays the content out), correlated via each item's own `sourcePath` when both are present -- a pairing this schema does not itself keep in sync or detect as stale.
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). `ContentEmbeddedObjectSchema` lets any of the five embed another whole `ContentDocument`. `LayoutDocument` (the PDF-rendering pivot) is pages of positioned `LayoutItem`s (`text`/`image`/`rect`/`line`/`ellipse`/`path`/`link`) in PDF user-space coordinates. `DocumentPackageSchema` pairs the two: `content` required, `layout` optional (derived, absent until something lays content out); the schema does not keep them in sync or detect staleness.
52
52
 
53
- It contains only [Zod](https://zod.dev) schemas, their inferred types, a handful of trivial schema-attached helpers (hex-colour conversion, recursive structural type guards for the mutually-recursive table/block/embedded-object types and for the MathML node tree), and two small structural interfaces (`ContentCodec`/`LayoutCodec`, see [Codecs](#codecs) below) that a sibling package's own format codec can implement -- no runtime code of their own either. There is no XML, ZIP, PDF, or other binary handling here, and no `zod` dependency other than `zod` itself.
53
+ The package contains only [Zod](https://zod.dev) schemas, their inferred types, trivial schema-attached helpers (hex-colour conversion, recursive structural type guards), and two small structural interfaces (`ContentCodec`/`LayoutCodec`, see [Codecs](#codecs)). No XML, ZIP, PDF, or binary handling; the sole dependency is `zod`.
54
54
 
55
- Beyond the schemas, the package ships two small format-agnostic helpers that live here rather than in any one format package because they operate on the content model itself: cell-addressing utilities in `src/a1.ts` (`columnIndexToLetters`/`columnLettersToIndex`, `cellReference(row, column)`/`parseCellReference`, `parseRangeReference`/`rangeReference`), shared by odf.js, ooxml.js, documents.js, document-cli, and document-mcp — the canonical A1-notation module, 0-based row/column indices with a row-first argument order matching `ContentSheetCell`'s own `{row, column}` field order; and the `FontFace` interface in `src/font-port.ts` (`{family, bold, italic}`, `ProvidedFont` minus `bytes`), the structured result of inspecting a standalone font file — a concept the content model already carries through its layout-side font fields.
56
-
57
- The GitHub repository is [`ExaDev/document-schema.js`](https://github.com/ExaDev/document-schema.js), matching the published npm package name.
55
+ 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}`).
58
56
 
59
57
  ## Usage
60
58
 
@@ -66,7 +64,7 @@ const layout = LayoutDocumentSchema.parse(somePageLayoutValue);
66
64
  const pkg = DocumentPackageSchema.parse({ formatVersion: 1, content, layout });
67
65
  ```
68
66
 
69
- Every module is also importable directly, without going through the barrel above -- `tsdown` builds one file per source module rather than a single bundle, and `package.json`'s `"./*"` export makes each one individually resolvable by name:
67
+ Every module is also importable directly `tsdown` builds one file per source module, and `package.json`'s `"./*"` export makes each individually resolvable:
70
68
 
71
69
  ```ts
72
70
  import { schemaUriFor } from 'document-schema.js/schema-io';
@@ -75,7 +73,7 @@ import { ColorSchema } from 'document-schema.js/color';
75
73
 
76
74
  ## Codecs
77
75
 
78
- Alongside the schemas themselves, `ContentCodec`/`LayoutCodec` (`src/codec.ts`) are the format-agnostic *interfaces* a sibling package's own docx/pptx/odt/odp/ods/odg/xlsx/markdown/PDF codec can implement, so a caller working across formats can hold one of these instead of a format-specific function pair:
76
+ `ContentCodec`/`LayoutCodec` (`src/codec.ts`) are the format-agnostic *interfaces* a sibling package's docx/pptx/odt/odp/ods/odg/xlsx/markdown/PDF codec can implement, so a caller working across formats holds one of these instead of a format-specific function pair:
79
77
 
80
78
  ```ts
81
79
  import type { ContentCodec, LayoutCodec } from 'document-schema.js';
@@ -84,15 +82,15 @@ declare const docxCodec: ContentCodec; // read(bytes) -> ContentDocument; write(
84
82
  declare const pdfCodec: LayoutCodec; // read(bytes) -> LayoutDocument; write(layout) -> bytes -- write is required
85
83
  ```
86
84
 
87
- The two are deliberately separate interfaces rather than one `DocumentCodec` shaped like `DocumentPackage`, because the formats they model are asymmetric: most formats (docx/pptx/odt/odp/ods/odg/markdown) only ever produce *content* on read, with layout always a later, engine-driven step; PDF is the mirror image, producing *layout* cheaply on read and content only via a separate, expensive, lossy, opt-in reconstruction pass that is emphatically not part of "reading" a PDF. `ContentCodec.write` is deliberately optional -- this models a real, permanent asymmetry, not a temporary gap: the `odf` format (a standalone ODF formula document) has a reader but genuinely no builder at all, since recovering structured MathML from rendered glyphs is a categorically different, OCR-adjacent problem than generating them. `LayoutCodec.write` is not optional, since PDF -- the only format with a `LayoutCodec` implementation anywhere in this family -- always supports both directions equally readily. Both interfaces are generic over their own `TOptions` (defaulting to `unknown`) rather than sharing one options shape across every implementation, since each real format's own read/write options are format-specific today (an `AbortSignal`, a font-substitution callback, a diagnostic sink) and forcing them into one shared shape would either be too narrow for some formats or carry fields meaningless to others.
85
+ The two are separate interfaces (not one `DocumentCodec`) because the formats are asymmetric: most produce only *content* on read (layout is a later engine-driven step); PDF produces *layout* cheaply and content only via a separate, lossy reconstruction pass. `ContentCodec.write` is optional (`odf` has a reader but no builder recovering MathML from glyphs is OCR-adjacent); `LayoutCodec.write` is required. Both are generic over their own `TOptions`.
88
86
 
89
- Neither interface constructs a `DocumentPackage` itself: a codec's `read()` returns one half (content, or layout, never both), and composing a `DocumentPackage` from a `ContentCodec.read()` result plus a separately-run layout-engine pass is the caller's job, one level up from either interface -- `documents.js`'s own `DOCUMENT_FORMAT_CODECS` registry (`src/codecs/registry.ts`) is the concrete example, implementing a `ContentCodec`/`LayoutCodec` pair per format over its own existing read/build/layout functions.
87
+ Neither interface constructs a `DocumentPackage`; composing one is the caller's job (`documents.js`'s `DOCUMENT_FORMAT_CODECS` registry is the concrete example).
90
88
 
91
- Alongside the schemas and the codec interfaces, this package also hosts the **port contracts** a layout engine consumes the interfaces and data shapes a neutral layer needs, independent of any concrete rendering backend. `src/text-layout.ts` (`TextMeasurer`, `StyledRun`, `WrappedLine`, `WrapOptions`), `src/font-port.ts` (`ProvidedFont`, `FontSubstitution`, `FontRegistryOptions`), `src/math-layout.ts` (`MathBox`, `MathLayoutItem`, `MathFontMetrics`, `PositionedFormula`), and `Point` in `src/geometry.ts` are all here, so a layout engine never reaches into a specific backend (pdf-codec) for its contracts — the backend implements them, the engine consumes them.
89
+ 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`).
92
90
 
93
91
  ## JSON Schema
94
92
 
95
- Alongside the Zod schemas/types above, the package publishes three plain [JSON Schema](https://json-schema.org) files -- generated from the same Zod definitions via [`z.toJSONSchema()`](https://zod.dev/json-schema) at build time (`scripts/generate-json-schemas.mjs`) -- for non-TypeScript consumers that want to validate against or generate types from these shapes without depending on Zod at all:
93
+ Three 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:
96
94
 
97
95
  ```ts
98
96
  const documentPackageSchema = require('document-schema.js/schemas/document-package.schema.json');
@@ -108,13 +106,13 @@ node_modules/document-schema.js/schemas/content-document.schema.json
108
106
  node_modules/document-schema.js/schemas/layout-document.schema.json
109
107
  ```
110
108
 
111
- Each file's `$id` is a `https://cdn.jsdelivr.net/npm/document-schema.js@<version>/schemas/<file>` URL, pinned to the exact npm version that generated it -- immutable (jsdelivr serves each version's own published tarball contents forever, with an immutable cache header) and genuinely live the moment that version is published, unlike a commit-SHA-pinned raw GitHub URL would be: a gitignored, generated file can never actually be committed at the commit whose SHA it would need to embed, since committing it changes the tree and thus the hash. The three files are cross-referenced via real `$ref`s (e.g. `document-package.schema.json`'s `content`/`layout` properties `$ref` the other two files directly, at that same version), so a JSON Schema validator that resolves `$ref`s over HTTP (or against local copies of all three files) can validate a whole `DocumentPackage` value. `content-document.schema.json` additionally carries a `$defs` block for the recursive paragraph/table/embedded-object block model and for the recursive MathML node tree the `formula` kind carries, neither of which Zod's own converter can express directly (see that script's own top-of-file comment for why). `content-json-schema-defs.ts` (a normal, fully typechecked/linted `src/` module, not part of the `scripts/` build step) is where those hand-authored `$defs` fragments actually live; a regression test (`content-json-schema-defs.test.ts`) holds every fragment with a real, non-recursive, non-`z.custom()` exported Zod schema counterpart (`Color`, `Box`, `Alignment`, `ContentStrokeStyle`, `ContentBorder`, `ContentCellBorders`, `ContentListMembership`, `ContentRun`, `ContentParagraph`, `ContentImageBlock`, `ContentPageBreak`) to a live `z.toJSONSchema()` comparison against that real schema, so a field added to (or removed from) one of those schemas without updating its hand-authored `$defs` fragment now fails a test rather than silently drifting. The remaining fragments (`ContentBlock`, `ContentTable`/`ContentTableRow`/`ContentTableCell`, `ContentEmbeddedObjectBlock`, `MathMlNode`/`MathMlElement`/`MathMlAttribute`) sit downstream of one of the three genuinely un-representable `z.custom()` nodes and still need re-verifying by hand against `src/content.ts`/`src/mathml.ts` whenever those files change -- see the `z.lazy()` investigation immediately below for why those three nodes are `z.custom()` in the first place, and whether that still has to be true.
109
+ Each file's `$id` is a jsdelivr URL pinned to the exact npm version immutable and live on publish. The three files cross-reference via `$ref`s, so a validator resolving refs over HTTP can validate a whole `DocumentPackage`. `content-document.schema.json` carries a hand-authored `$defs` block for the recursive paragraph/table/embedded-object and MathML node models (Zod's converter cannot express these directly); `content-json-schema-defs.ts` holds those fragments, and a regression test compares each against a live `z.toJSONSchema()` of its real Zod counterpart so a field changed without updating its fragment fails a test. Fragments downstream of a `z.custom()` node (`ContentBlock`, `ContentTable`/`Cell`/`Row`, `ContentEmbeddedObjectBlock`, `MathMlNode`/`Element`/`Attribute`) still need hand re-verification against `src/content.ts`/`src/mathml.ts` see below.
112
110
 
113
111
  ### `z.custom()` vs `z.lazy()` for recursive schemas
114
112
 
115
- `ContentBlockSchema`, `ContentEmbeddedObjectSchema` (`src/content.ts`), and `MathMlNodeSchema` (`src/mathml.ts`) are all `z.custom()` type-guard predicates rather than real Zod schemas, because -- per each file's own long-standing code comment -- `z.lazy()` was believed to "collapse to `unknown`" for recursive children in the pinned Zod version. That belief was re-tested directly against the version actually installed today (`zod@4.4.3`, confirmed via `node_modules/zod/package.json`) by converting `MathMlNodeSchema`/`MathMlElementSchema` (the simplest of the three -- a self-recursive discriminated union, not `ContentBlock`'s mutual table/cell recursion or the cross-cutting `ContentDocument` cycle `ContentEmbeddedObject` carries) to a genuine `z.lazy()`-based pair, as a throwaway spike later reverted in full (`git diff -- src/mathml.ts` shows no changes on the commit this note was added in).
113
+ `ContentBlockSchema`, `ContentEmbeddedObjectSchema`, and `MathMlNodeSchema` 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`.
116
114
 
117
- **Finding: `zod@4.4.3`'s `z.lazy()` genuinely supports this now, with one real constructional gotcha.** The naive rewrite --
115
+ **Finding: `z.lazy()` works now, with one constructional gotcha.** The naive rewrite
118
116
 
119
117
  ```ts
120
118
  export const MathMlElementSchema: z.ZodType<MathMlElement> = z.object({
@@ -128,15 +126,13 @@ export const MathMlNodeSchema: z.ZodType<MathMlNode> = z.discriminatedUnion('typ
128
126
  ]);
129
127
  ```
130
128
 
131
- fails to typecheck: annotating `MathMlElementSchema` itself as `z.ZodType<MathMlElement>` widens it enough that `z.discriminatedUnion` (which needs each member's own internal `propValues` metadata to dispatch on the discriminant) rejects it with a real, correct type error (`Types of property '_zod.propValues' are incompatible ... Type 'undefined' is not assignable to type 'PropValues'`) -- not a Zod bug, but a genuine consequence of erasing a `ZodObject`'s specific shape down to the generic `ZodType` interface. Dropping the annotation entirely instead produces TypeScript's own classic circular-inference error (`'MathMlElementSchema' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer`) -- this is the failure the original "collapses to `unknown`" comment was almost certainly describing. The fix that actually works: annotate **only the outer union's own binding** (`MathMlNodeSchema: z.ZodType<MathMlNode> = z.discriminatedUnion(...)`), leaving every member schema, `MathMlElementSchema` included, unannotated and fully inferred. That one annotation breaks the circularity for the type-checker without erasing any member's own internal shape, since the erasure only ever applies to the final union result, not to what's passed into `z.discriminatedUnion`'s own argument list.
132
-
133
- With that one change, every existing test in `mathml.test.ts` passed unmodified -- including the four-level-deep nested-element test and the exact-failure-at-the-deepest-level negative test -- proving recursion genuinely terminates and validates correctly at runtime, not just at the type level. `z.toJSONSchema(MathMlNodeSchema, { unrepresentable: 'any' })`, called on its own (no registry, no `override()`), produced a fully real, standard `oneOf`-based JSON Schema with a genuine `{ "$ref": "#" }` at the exact recursion point (`element.children.items`) -- no empty `{}` degenerate node anywhere, and `unrepresentable: 'any'` never actually had to activate for it.
129
+ fails to typecheck: annotating `MathMlElementSchema` as `z.ZodType<MathMlElement>` widens it so `z.discriminatedUnion` (which needs each member's internal `propValues`) rejects it, and dropping the annotation hits TypeScript's circular-inference error. The fix: annotate **only the outer union's binding** (`MathMlNodeSchema`), leaving every member schema unannotated and fully inferred all tests passed, and `z.toJSONSchema()` produced a real `oneOf` with `{ "$ref": "#" }` at the recursion point.
134
130
 
135
- **What this means, and what it doesn't (yet):** this is a confirmed, empirically-verified capability upgrade in the currently-pinned Zod version for the *simplest* of the three `z.custom()` cases in this package. It was **not** implemented as a real change in this task -- it's flagged here as a scoped, tracked follow-up, not carried out speculatively alongside unrelated work. Converting `MathMlNodeSchema` for real would let `scripts/generate-json-schemas.mjs` drop its `MathMlNode`/`MathMlElement`/`MathMlAttribute` hand-authored `$defs` entries (and the `override()` branch for `MathMlNodeSchema`) entirely, replacing them with Zod's own native recursive output. `ContentBlockSchema`/`ContentEmbeddedObjectSchema` are meaningfully harder (mutual recursion across `ContentBlock ↔ ContentTableCell ↔ ContentTableRow ↔ ContentTable`, plus `ContentEmbeddedObject`'s cycle back through a whole five-variant `ContentDocument` union) and were not spiked at all -- the same "annotate only the outermost binding" technique is the natural starting point, but the actual mutual-recursion shape needs its own from-scratch verification before assuming it generalises.
131
+ **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.
136
132
 
137
133
  ### Self-describing JSON
138
134
 
139
- `documentPackageWithSchema`/`contentDocumentWithSchema`/`layoutDocumentWithSchema` each take a real `DocumentPackage`/`ContentDocument`/`LayoutDocument` value and return the same value with a `$schema` property added, pointing at the `.schema.json` file above for the *currently installed* package version:
135
+ `documentPackageWithSchema`/`contentDocumentWithSchema`/`layoutDocumentWithSchema` each stamp a `$schema` property pointing at the `.schema.json` file for the currently installed version:
140
136
 
141
137
  ```ts
142
138
  import { documentPackageWithSchema } from 'document-schema.js';
@@ -146,7 +142,7 @@ const tagged = documentPackageWithSchema(pkg);
146
142
  writeFileSync('package.json.doc', JSON.stringify(tagged, null, 2));
147
143
  ```
148
144
 
149
- A caller who already knows the kind can keep ingesting with the existing schemas directly -- `DocumentPackageSchema.parse(value)` (etc.) already tolerates and silently strips an incoming `$schema` property, since none of these schemas are `.strict()`. `documentFromJson` exists for the "don't yet know the kind" case: it reads `$schema` to decide which of the three schemas to run, then that schema does the real structural validation:
145
+ 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:
150
146
 
151
147
  ```ts
152
148
  import { documentFromJson, UnrecognizedDocumentSchemaError } from 'document-schema.js';
@@ -161,17 +157,17 @@ try {
161
157
  }
162
158
  ```
163
159
 
164
- `documentSchemaKindOf(value)` is the lower-level building block `documentFromJson` uses internally -- exported on its own for a caller that only wants to know which kind a value claims to be (version-agnostically: a `$schema` from an older or newer installed version still resolves), without also parsing it. `schemaUriFor(kind)` is the URL builder itself, also exported directly. Deliberately not added: a JSON-Schema-validator dependency (e.g. `ajv`) for ingest -- the generated `.schema.json` files are already a strictly *weaker* approximation of the real Zod schemas (see the hand-authored `$defs` fragments above), so re-validating against them on ingest would be a fidelity regression, not an improvement.
160
+ `documentSchemaKindOf(value)` returns the kind version-agnostically without parsing; `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.
165
161
 
166
162
  ## Used by
167
163
 
168
- - [ooxml.js](https://github.com/ExaDev/ooxml.js) — its `readDocx`/`readPptx`/`readXlsxContent` return `ContentSection[]`/`ContentSlide[]`/spreadsheet `ContentSheet[]` typed against this package's own schemas, not a locally-defined lookalike.
169
- - [odf.js](https://github.com/ExaDev/odf.js) — its ODF typed readers (`readOdt`, `readOdp`, `readOds`, `readOdg`, …) return the same shared types, so an ODF document and an OOXML document speak the identical pivot.
170
- - [documents.js](https://github.com/ExaDev/documents.js) — the primary consumer of both `ContentDocument` and `LayoutDocument`, which it converts between via its layout engines and its `pdf-codec` dependency, and of `DocumentPackage` as the `onDocument` side-channel value its conversion functions hand back. Its own `DOCUMENT_FORMAT_CODECS` registry additionally implements this package's `ContentCodec`/`LayoutCodec` interfaces per format, over its existing read/build/layout functions.
171
- - [pdf-codec](https://github.com/ExaDev/pdf-codec) — the hand-written PDF codec extracted from `documents.js`: `readPdf`/`writePdf` and its own `pdfCodec` z.codec() pair operate entirely in terms of this package's `LayoutDocument` (plus the item kinds it's built from -- `LayoutItem`/`LayoutText`/`LayoutImage`/`LayoutRect`/`LayoutEllipse`/`LayoutLink`/`LayoutPath`/`LayoutSubpath`/`LayoutPathSegment`/`LayoutPage`/`LayoutImageAsset`/`LayoutMetadata`), `Color`/`LayoutFont` (aliased `LayoutColor`/`LayoutFont` at its own call sites), and `LAYOUT_FORMAT_VERSION`/`COLOR_BLACK`/`LayoutDocumentSchema` -- it never redeclares any of these itself, unlike its own `MathBox`/`PositionedFormula` mirror of `documents.js`'s MathML types (a deliberate, narrower exception -- see pdf-codec's own README).
172
- - [markdown-codec](https://github.com/ExaDev/markdown-codec) — the hand-written CommonMark+GFM codec: `readMarkdown`/`writeMarkdown` and its own `markdownCodec` z.codec() pair read and write this package's `ContentDocument` directly, the identical `wordprocessing` pivot `ooxml.js`'s `readDocx` and `odf.js`'s `readOdt` also produce.
164
+ - [ooxml.js](https://github.com/ExaDev/ooxml.js) — `readDocx`/`readPptx`/`readXlsxContent` return types are typed against this package's schemas, not a local lookalike.
165
+ - [odf.js](https://github.com/ExaDev/odf.js) — ODF typed readers return the same shared types, so ODF and OOXML speak the identical pivot.
166
+ - [documents.js](https://github.com/ExaDev/documents.js) — primary consumer of `ContentDocument`, `LayoutDocument`, and `DocumentPackage`; its `DOCUMENT_FORMAT_CODECS` registry implements `ContentCodec`/`LayoutCodec` per format.
167
+ - [pdf-codec](https://github.com/ExaDev/pdf-codec) — `readPdf`/`writePdf` operate on this package's `LayoutDocument` and item kinds, never redeclaring them.
168
+ - [markdown-codec](https://github.com/ExaDev/markdown-codec) — `readMarkdown`/`writeMarkdown` read and write this package's `ContentDocument` directly.
173
169
 
174
- None of these five packages depend on each other for this vocabulary — each depends on `document-schema.js` directly, which is the whole point: one schema, not five independently-maintained, drift-prone copies.
170
+ None depend on each other for this vocabulary — each depends on `document-schema.js` directly.
175
171
 
176
172
  ## Build, test, and lint
177
173
 
package/dist/layout.d.cts CHANGED
@@ -9,12 +9,12 @@ declare const LayoutTextSchema: z.ZodObject<{
9
9
  font: z.ZodObject<{
10
10
  family: z.ZodString;
11
11
  weight: z.ZodEnum<{
12
- bold: "bold";
13
12
  normal: "normal";
13
+ bold: "bold";
14
14
  }>;
15
15
  style: z.ZodEnum<{
16
- italic: "italic";
17
16
  normal: "normal";
17
+ italic: "italic";
18
18
  }>;
19
19
  }, z.core.$strip>;
20
20
  sizePt: z.ZodNumber;
@@ -202,12 +202,12 @@ declare const LayoutItemSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
202
202
  font: z.ZodObject<{
203
203
  family: z.ZodString;
204
204
  weight: z.ZodEnum<{
205
- bold: "bold";
206
205
  normal: "normal";
206
+ bold: "bold";
207
207
  }>;
208
208
  style: z.ZodEnum<{
209
- italic: "italic";
210
209
  normal: "normal";
210
+ italic: "italic";
211
211
  }>;
212
212
  }, z.core.$strip>;
213
213
  sizePt: z.ZodNumber;
@@ -353,12 +353,12 @@ declare const LayoutPageSchema: z.ZodObject<{
353
353
  font: z.ZodObject<{
354
354
  family: z.ZodString;
355
355
  weight: z.ZodEnum<{
356
- bold: "bold";
357
356
  normal: "normal";
357
+ bold: "bold";
358
358
  }>;
359
359
  style: z.ZodEnum<{
360
- italic: "italic";
361
360
  normal: "normal";
361
+ italic: "italic";
362
362
  }>;
363
363
  }, z.core.$strip>;
364
364
  sizePt: z.ZodNumber;
@@ -528,12 +528,12 @@ declare const LayoutDocumentSchema: z.ZodObject<{
528
528
  font: z.ZodObject<{
529
529
  family: z.ZodString;
530
530
  weight: z.ZodEnum<{
531
- bold: "bold";
532
531
  normal: "normal";
532
+ bold: "bold";
533
533
  }>;
534
534
  style: z.ZodEnum<{
535
- italic: "italic";
536
535
  normal: "normal";
536
+ italic: "italic";
537
537
  }>;
538
538
  }, z.core.$strip>;
539
539
  sizePt: z.ZodNumber;
package/dist/layout.d.ts CHANGED
@@ -9,12 +9,12 @@ declare const LayoutTextSchema: z.ZodObject<{
9
9
  font: z.ZodObject<{
10
10
  family: z.ZodString;
11
11
  weight: z.ZodEnum<{
12
- bold: "bold";
13
12
  normal: "normal";
13
+ bold: "bold";
14
14
  }>;
15
15
  style: z.ZodEnum<{
16
- italic: "italic";
17
16
  normal: "normal";
17
+ italic: "italic";
18
18
  }>;
19
19
  }, z.core.$strip>;
20
20
  sizePt: z.ZodNumber;
@@ -202,12 +202,12 @@ declare const LayoutItemSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
202
202
  font: z.ZodObject<{
203
203
  family: z.ZodString;
204
204
  weight: z.ZodEnum<{
205
- bold: "bold";
206
205
  normal: "normal";
206
+ bold: "bold";
207
207
  }>;
208
208
  style: z.ZodEnum<{
209
- italic: "italic";
210
209
  normal: "normal";
210
+ italic: "italic";
211
211
  }>;
212
212
  }, z.core.$strip>;
213
213
  sizePt: z.ZodNumber;
@@ -353,12 +353,12 @@ declare const LayoutPageSchema: z.ZodObject<{
353
353
  font: z.ZodObject<{
354
354
  family: z.ZodString;
355
355
  weight: z.ZodEnum<{
356
- bold: "bold";
357
356
  normal: "normal";
357
+ bold: "bold";
358
358
  }>;
359
359
  style: z.ZodEnum<{
360
- italic: "italic";
361
360
  normal: "normal";
361
+ italic: "italic";
362
362
  }>;
363
363
  }, z.core.$strip>;
364
364
  sizePt: z.ZodNumber;
@@ -528,12 +528,12 @@ declare const LayoutDocumentSchema: z.ZodObject<{
528
528
  font: z.ZodObject<{
529
529
  family: z.ZodString;
530
530
  weight: z.ZodEnum<{
531
- bold: "bold";
532
531
  normal: "normal";
532
+ bold: "bold";
533
533
  }>;
534
534
  style: z.ZodEnum<{
535
- italic: "italic";
536
535
  normal: "normal";
536
+ italic: "italic";
537
537
  }>;
538
538
  }, z.core.$strip>;
539
539
  sizePt: z.ZodNumber;
@@ -515,12 +515,12 @@ declare const DocumentPackageSchema: z.ZodObject<{
515
515
  font: z.ZodObject<{
516
516
  family: z.ZodString;
517
517
  weight: z.ZodEnum<{
518
- bold: "bold";
519
518
  normal: "normal";
519
+ bold: "bold";
520
520
  }>;
521
521
  style: z.ZodEnum<{
522
- italic: "italic";
523
522
  normal: "normal";
523
+ italic: "italic";
524
524
  }>;
525
525
  }, z.core.$strip>;
526
526
  sizePt: z.ZodNumber;
package/dist/package.d.ts CHANGED
@@ -515,12 +515,12 @@ declare const DocumentPackageSchema: z.ZodObject<{
515
515
  font: z.ZodObject<{
516
516
  family: z.ZodString;
517
517
  weight: z.ZodEnum<{
518
- bold: "bold";
519
518
  normal: "normal";
519
+ bold: "bold";
520
520
  }>;
521
521
  style: z.ZodEnum<{
522
- italic: "italic";
523
522
  normal: "normal";
523
+ italic: "italic";
524
524
  }>;
525
525
  }, z.core.$strip>;
526
526
  sizePt: z.ZodNumber;
@@ -9,7 +9,7 @@ const SCHEMA_FILE_NAMES = {
9
9
  LayoutDocument: "layout-document.schema.json"
10
10
  };
11
11
  function schemaUriFor(kind) {
12
- return `https://cdn.jsdelivr.net/npm/document-schema.js@2.7.4/schemas/${SCHEMA_FILE_NAMES[kind]}`;
12
+ return `https://cdn.jsdelivr.net/npm/document-schema.js@2.7.6/schemas/${SCHEMA_FILE_NAMES[kind]}`;
13
13
  }
14
14
  const SCHEMA_URI_PATTERN = /^https:\/\/cdn\.jsdelivr\.net\/npm\/document-schema\.js@[^/]+\/schemas\/(document-package|content-document|layout-document)\.schema\.json$/;
15
15
  function kindForFileStem(stem) {
package/dist/schema-io.js CHANGED
@@ -8,7 +8,7 @@ const SCHEMA_FILE_NAMES = {
8
8
  LayoutDocument: "layout-document.schema.json"
9
9
  };
10
10
  function schemaUriFor(kind) {
11
- return `https://cdn.jsdelivr.net/npm/document-schema.js@2.7.4/schemas/${SCHEMA_FILE_NAMES[kind]}`;
11
+ return `https://cdn.jsdelivr.net/npm/document-schema.js@2.7.6/schemas/${SCHEMA_FILE_NAMES[kind]}`;
12
12
  }
13
13
  const SCHEMA_URI_PATTERN = /^https:\/\/cdn\.jsdelivr\.net\/npm\/document-schema\.js@[^/]+\/schemas\/(document-package|content-document|layout-document)\.schema\.json$/;
14
14
  function kindForFileStem(stem) {
package/dist/style.d.cts CHANGED
@@ -10,12 +10,12 @@ type Alignment = z.infer<typeof AlignmentSchema>;
10
10
  declare const LayoutFontSchema: z.ZodObject<{
11
11
  family: z.ZodString;
12
12
  weight: z.ZodEnum<{
13
- bold: "bold";
14
13
  normal: "normal";
14
+ bold: "bold";
15
15
  }>;
16
16
  style: z.ZodEnum<{
17
- italic: "italic";
18
17
  normal: "normal";
18
+ italic: "italic";
19
19
  }>;
20
20
  }, z.core.$strip>;
21
21
  type LayoutFont = z.infer<typeof LayoutFontSchema>;
package/dist/style.d.ts CHANGED
@@ -10,12 +10,12 @@ type Alignment = z.infer<typeof AlignmentSchema>;
10
10
  declare const LayoutFontSchema: z.ZodObject<{
11
11
  family: z.ZodString;
12
12
  weight: z.ZodEnum<{
13
- bold: "bold";
14
13
  normal: "normal";
14
+ bold: "bold";
15
15
  }>;
16
16
  style: z.ZodEnum<{
17
- italic: "italic";
18
17
  normal: "normal";
18
+ italic: "italic";
19
19
  }>;
20
20
  }, z.core.$strip>;
21
21
  type LayoutFont = z.infer<typeof LayoutFontSchema>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "doc-model.js",
3
- "version": "2.7.4",
3
+ "version": "2.7.6",
4
4
  "description": "The canonical, format-agnostic content and layout schemas shared by ooxml.js, odf.js, and documents.js -- pure Zod schemas, no behaviour.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://cdn.jsdelivr.net/npm/document-schema.js@2.7.4/schemas/content-document.schema.json",
3
+ "$id": "https://cdn.jsdelivr.net/npm/document-schema.js@2.7.6/schemas/content-document.schema.json",
4
4
  "oneOf": [
5
5
  {
6
6
  "type": "object",
@@ -1208,7 +1208,7 @@
1208
1208
  ]
1209
1209
  },
1210
1210
  "document": {
1211
- "$ref": "https://cdn.jsdelivr.net/npm/document-schema.js@2.7.4/schemas/content-document.schema.json"
1211
+ "$ref": "https://cdn.jsdelivr.net/npm/document-schema.js@2.7.6/schemas/content-document.schema.json"
1212
1212
  },
1213
1213
  "frame": {
1214
1214
  "$ref": "#/$defs/Box"
@@ -2499,7 +2499,7 @@
2499
2499
  ]
2500
2500
  },
2501
2501
  "document": {
2502
- "$ref": "https://cdn.jsdelivr.net/npm/document-schema.js@2.7.4/schemas/content-document.schema.json"
2502
+ "$ref": "https://cdn.jsdelivr.net/npm/document-schema.js@2.7.6/schemas/content-document.schema.json"
2503
2503
  },
2504
2504
  "frame": {
2505
2505
  "$ref": "#/$defs/Box"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://cdn.jsdelivr.net/npm/document-schema.js@2.7.4/schemas/document-package.schema.json",
3
+ "$id": "https://cdn.jsdelivr.net/npm/document-schema.js@2.7.6/schemas/document-package.schema.json",
4
4
  "type": "object",
5
5
  "properties": {
6
6
  "formatVersion": {
@@ -8,10 +8,10 @@
8
8
  "const": 1
9
9
  },
10
10
  "content": {
11
- "$ref": "https://cdn.jsdelivr.net/npm/document-schema.js@2.7.4/schemas/content-document.schema.json"
11
+ "$ref": "https://cdn.jsdelivr.net/npm/document-schema.js@2.7.6/schemas/content-document.schema.json"
12
12
  },
13
13
  "layout": {
14
- "$ref": "https://cdn.jsdelivr.net/npm/document-schema.js@2.7.4/schemas/layout-document.schema.json"
14
+ "$ref": "https://cdn.jsdelivr.net/npm/document-schema.js@2.7.6/schemas/layout-document.schema.json"
15
15
  }
16
16
  },
17
17
  "required": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://cdn.jsdelivr.net/npm/document-schema.js@2.7.4/schemas/layout-document.schema.json",
3
+ "$id": "https://cdn.jsdelivr.net/npm/document-schema.js@2.7.6/schemas/layout-document.schema.json",
4
4
  "type": "object",
5
5
  "properties": {
6
6
  "formatVersion": {