epub-codec 1.1.0 → 1.2.1

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.
Files changed (48) hide show
  1. package/README.md +3 -1
  2. package/dist/{diagnostics-tvGwPH46.d.cts → diagnostics-C0CRdoda.d.cts} +8 -0
  3. package/dist/{diagnostics-tvGwPH46.d.ts → diagnostics-C0CRdoda.d.ts} +8 -0
  4. package/dist/diagnostics.cjs +8 -0
  5. package/dist/diagnostics.d.cts +1 -1
  6. package/dist/diagnostics.d.ts +1 -1
  7. package/dist/diagnostics.js +8 -0
  8. package/dist/index.cjs +8 -3
  9. package/dist/index.d.cts +6 -6
  10. package/dist/index.d.ts +6 -6
  11. package/dist/index.js +5 -5
  12. package/dist/opf/metadata.cjs +4 -5
  13. package/dist/opf/metadata.d.cts +1 -1
  14. package/dist/opf/metadata.d.ts +1 -1
  15. package/dist/opf/metadata.js +5 -6
  16. package/dist/opf/parse.d.cts +1 -1
  17. package/dist/opf/parse.d.ts +1 -1
  18. package/dist/read.d.cts +1 -1
  19. package/dist/read.d.ts +1 -1
  20. package/dist/write.cjs +1 -1
  21. package/dist/write.d.cts +1 -1
  22. package/dist/write.d.ts +1 -1
  23. package/dist/write.js +1 -1
  24. package/dist/xhtml/context.cjs +18 -0
  25. package/dist/xhtml/context.d.cts +4 -2
  26. package/dist/xhtml/context.d.ts +4 -2
  27. package/dist/xhtml/context.js +16 -0
  28. package/dist/xhtml/inline.cjs +9 -3
  29. package/dist/xhtml/inline.js +9 -3
  30. package/dist/xhtml/read.cjs +330 -47
  31. package/dist/xhtml/read.js +331 -48
  32. package/dist/xhtml/write.cjs +72 -23
  33. package/dist/xhtml/write.d.cts +1 -1
  34. package/dist/xhtml/write.d.ts +1 -1
  35. package/dist/xhtml/write.js +72 -23
  36. package/dist/xml/entities.cjs +6 -10
  37. package/dist/xml/entities.d.cts +3 -1
  38. package/dist/xml/entities.d.ts +3 -1
  39. package/dist/xml/entities.js +6 -11
  40. package/dist/xml/node.cjs +4 -0
  41. package/dist/xml/node.d.cts +2 -1
  42. package/dist/xml/node.d.ts +2 -1
  43. package/dist/xml/node.js +4 -1
  44. package/dist/xml/query.cjs +9 -0
  45. package/dist/xml/query.d.cts +2 -1
  46. package/dist/xml/query.d.ts +2 -1
  47. package/dist/xml/query.js +9 -1
  48. package/package.json +3 -2
@@ -59,24 +59,31 @@ function writeSectionChildren(children, context) {
59
59
  return out;
60
60
  }
61
61
  function writeSectionChild(child, context) {
62
- if (isHeadingGroupNode(child)) return [writeHeading(child), ...writeSectionChildren(child.children, context)];
62
+ if (isHeadingGroupNode(child)) return [writeHeading(child, context), ...writeSectionChildren(child.children, context)];
63
63
  if (isListGroupNode(child)) return [writeList([child], context)];
64
64
  if (isSectionConstructGroupNode(child)) return writeSectionConstructGroup(child, context);
65
65
  return writeLeafBlock(child, context);
66
66
  }
67
- function writeHeading(group) {
67
+ function writeHeading(group, context) {
68
68
  const level = Math.min(6, Math.max(1, group.node.headingLevel));
69
- return element(`h${String(level)}`, {}, writeRunsToNodes(group.node.runs, group.node.constructs));
69
+ return element(`h${String(level)}`, {}, writeRunsToNodes(group.node.runs, group.node.constructs, context));
70
70
  }
71
71
  function writeList(items, context) {
72
72
  const numId = items[0]?.node.list.numId;
73
73
  const info = numId === void 0 ? void 0 : parseListNumId(numId);
74
- return element(info?.type === "ordered" ? "ol" : "ul", info?.type === "ordered" && info.start !== void 0 ? { start: String(info.start) } : {}, items.map((item) => {
75
- const paragraph = item.node;
76
- const anchorNodes = writeRunsToNodes(paragraph.runs, paragraph.constructs);
77
- const nested = writeSectionChildren(item.children, context);
78
- return element("li", {}, [...anchorNodes, ...nested]);
79
- }));
74
+ const tag = info?.type === "ordered" ? "ol" : "ul";
75
+ const attrs = info?.type === "ordered" && info.start !== void 0 ? { start: String(info.start) } : {};
76
+ const liNodes = [];
77
+ let index = 0;
78
+ while (index < items.length) {
79
+ const itemId = items[index]?.node.list.itemId;
80
+ let end = index + 1;
81
+ if (itemId !== void 0) while (end < items.length && items[end]?.node.list.itemId === itemId) end += 1;
82
+ const liChildren = items.slice(index, end).flatMap((item, position) => [...writeParagraphAsEmbeddedNodes(item.node, context, position > 0), ...writeSectionChildren(item.children, context)]);
83
+ liNodes.push(element("li", {}, liChildren));
84
+ index = end;
85
+ }
86
+ return element(tag, attrs, liNodes);
80
87
  }
81
88
  function writeSectionConstructGroup(group, context) {
82
89
  const descriptor = group.node;
@@ -95,7 +102,7 @@ function writeSectionConstructGroup(group, context) {
95
102
  }
96
103
  function writeLeafBlock(block, context) {
97
104
  switch (block.kind) {
98
- case "paragraph": return [writeParagraph(block)];
105
+ case "paragraph": return [writeParagraph(block, context)];
99
106
  case "table": return [writeTable(block, context)];
100
107
  case "image": return [writeImage(block, context)];
101
108
  case "pageBreak": return [];
@@ -110,38 +117,80 @@ function writeLeafBlock(block, context) {
110
117
  }
111
118
  }
112
119
  function isPreBlockParagraph(paragraph) {
120
+ if (paragraph.preformatted === true) return true;
113
121
  if (paragraph.codeLanguage !== void 0) return true;
114
122
  return paragraph.runs.length === 1 && paragraph.runs[0]?.fontFamily === "Courier New" && paragraph.runs[0].text.includes("\n");
115
123
  }
116
- function writeParagraph(paragraph) {
117
- if (paragraph.styleId === "HorizontalRule" && paragraph.runs.length === 0) return element("hr");
118
- if (isPreBlockParagraph(paragraph)) return element("pre", {}, [element("code", paragraph.codeLanguage === void 0 ? {} : { class: `language-${paragraph.codeLanguage}` }, [text(paragraph.runs.map((run) => run.text).join(""))])]);
119
- return element("p", {}, writeRunsToNodes(paragraph.runs, paragraph.constructs));
124
+ function isHorizontalRuleParagraph(paragraph) {
125
+ return paragraph.styleId === "HorizontalRule" && paragraph.runs.length === 0;
126
+ }
127
+ function writePreElement(paragraph, context) {
128
+ return element("pre", {}, [element("code", paragraph.codeLanguage === void 0 ? {} : { class: `language-${paragraph.codeLanguage}` }, writePreRunsToNodes(paragraph.runs, paragraph.constructs, context))]);
129
+ }
130
+ function writeParagraph(paragraph, context) {
131
+ if (isHorizontalRuleParagraph(paragraph)) return element("hr");
132
+ if (isPreBlockParagraph(paragraph)) return writePreElement(paragraph, context);
133
+ return element("p", {}, writeRunsToNodes(paragraph.runs, paragraph.constructs, context));
134
+ }
135
+ function writeParagraphAsEmbeddedNodes(paragraph, context, wrapOrdinaryInParagraph) {
136
+ if (isHorizontalRuleParagraph(paragraph)) return [element("hr")];
137
+ if (isPreBlockParagraph(paragraph)) return [writePreElement(paragraph, context)];
138
+ const runNodes = writeRunsToNodes(paragraph.runs, paragraph.constructs, context);
139
+ return wrapOrdinaryInParagraph ? [element("p", {}, runNodes)] : runNodes;
120
140
  }
121
141
  function isFootnoteExtent(construct) {
122
142
  return construct.descriptor.kind === "anchor" && construct.descriptor.anchorType === "footnote";
123
143
  }
124
- function writeRunsToNodes(runs, constructs) {
144
+ function describeUnrepresentedFootnoteExtent(extent) {
145
+ if (extent.endRun === extent.startRun) return `a footnote reference ('${extent.descriptor.name}') marking the boundary before run ${extent.startRun} is never reached by the write walk -- either because it sits inside another footnote reference's own winning run range, or because its own run range falls outside this paragraph's actual runs; a point anchor wraps no run text of its own, so nothing besides this reference's own <a> link is missing from the output`;
146
+ return `a footnote reference ('${extent.descriptor.name}') is never reached by the write walk and cannot be represented as its own <a> element -- either because it overlaps another footnote reference's own winning run range, or because its own run range falls outside this paragraph's actual runs; wherever an overlap is the cause, the run text underneath it is not lost, since it is still written, wrapped by whichever extent's range actually claims it, or written unwrapped past it, but this reference's own anchor link is not emitted regardless of cause`;
147
+ }
148
+ function writeRunRangeNodes(runs, constructs, renderRun, renderExtentRange, context) {
125
149
  const footnoteExtents = (constructs ?? []).filter(isFootnoteExtent);
150
+ const emittedExtents = /* @__PURE__ */ new Set();
126
151
  const out = [];
127
152
  let index = 0;
128
- while (index < runs.length) {
129
- const extent = footnoteExtents.find((e) => e.startRun === index);
130
- if (extent !== void 0) {
131
- const inner = runs.slice(extent.startRun, extent.endRun).flatMap((run) => writeRunNodes(run));
153
+ while (index <= runs.length) {
154
+ const extentsHere = footnoteExtents.filter((e) => e.startRun === index);
155
+ for (const point of extentsHere.filter((e) => e.endRun === e.startRun)) {
156
+ emittedExtents.add(point);
132
157
  out.push(element("a", {
133
158
  "epub:type": "noteref",
134
- href: `#${extent.descriptor.name}`
135
- }, inner));
136
- index = Math.max(extent.endRun, index + 1);
159
+ href: `#${point.descriptor.name}`
160
+ }, renderExtentRange([])));
161
+ }
162
+ const [primaryRange] = index < runs.length ? extentsHere.filter((e) => e.endRun > e.startRun) : [];
163
+ if (primaryRange !== void 0) {
164
+ emittedExtents.add(primaryRange);
165
+ const rangeRuns = runs.slice(primaryRange.startRun, primaryRange.endRun);
166
+ out.push(element("a", {
167
+ "epub:type": "noteref",
168
+ href: `#${primaryRange.descriptor.name}`
169
+ }, renderExtentRange(rangeRuns)));
170
+ index = primaryRange.endRun;
137
171
  continue;
138
172
  }
139
173
  const run = runs[index];
140
- if (run !== void 0) out.push(...writeRunNodes(run));
174
+ if (run !== void 0) out.push(...renderRun(run));
141
175
  index += 1;
142
176
  }
177
+ for (const extent of footnoteExtents) {
178
+ if (emittedExtents.has(extent)) continue;
179
+ context.sink({
180
+ code: EpubDiagnosticCodes.CONSTRUCT_UNREPRESENTED,
181
+ severity: "info",
182
+ message: describeUnrepresentedFootnoteExtent(extent),
183
+ href: context.sourceHref
184
+ });
185
+ }
143
186
  return out;
144
187
  }
188
+ function writeRunsToNodes(runs, constructs, context) {
189
+ return writeRunRangeNodes(runs, constructs, writeRunNodes, (rangeRuns) => rangeRuns.flatMap((run) => writeRunNodes(run)), context);
190
+ }
191
+ function writePreRunsToNodes(runs, constructs, context) {
192
+ return writeRunRangeNodes(runs, constructs, (run) => run.text.length > 0 ? [text(run.text)] : [], (rangeRuns) => [text(rangeRuns.map((run) => run.text).join(""))], context);
193
+ }
145
194
  function writeRunNodes(run) {
146
195
  const segments = run.text.split("\n");
147
196
  let nodes = [];
@@ -1,20 +1,16 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let entities = require("entities");
2
3
  //#region src/xml/entities.ts
3
4
  function decodeEntities(value) {
4
- return value.replace(/&(?:amp|lt|gt|quot|apos);/g, (entity) => {
5
- switch (entity) {
6
- case "&amp;": return "&";
7
- case "&lt;": return "<";
8
- case "&gt;": return ">";
9
- case "&quot;": return "\"";
10
- case "&apos;": return "'";
11
- default: return entity;
12
- }
13
- });
5
+ return (0, entities.decode)(value, entities.EntityLevel.HTML);
6
+ }
7
+ function decodeTextLikeNode(node) {
8
+ return node.type === "cdata" ? node.value : decodeEntities(node.value);
14
9
  }
15
10
  function encodeEntities(value) {
16
11
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
17
12
  }
18
13
  //#endregion
19
14
  exports.decodeEntities = decodeEntities;
15
+ exports.decodeTextLikeNode = decodeTextLikeNode;
20
16
  exports.encodeEntities = encodeEntities;
@@ -1,5 +1,7 @@
1
+ import { XmlCdata, XmlText } from "./node.cjs";
1
2
  //#region src/xml/entities.d.ts
2
3
  declare function decodeEntities(value: string): string;
4
+ declare function decodeTextLikeNode(node: XmlText | XmlCdata): string;
3
5
  declare function encodeEntities(value: string): string;
4
6
  //#endregion
5
- export { decodeEntities, encodeEntities };
7
+ export { decodeEntities, decodeTextLikeNode, encodeEntities };
@@ -1,5 +1,7 @@
1
+ import { XmlCdata, XmlText } from "./node.js";
1
2
  //#region src/xml/entities.d.ts
2
3
  declare function decodeEntities(value: string): string;
4
+ declare function decodeTextLikeNode(node: XmlText | XmlCdata): string;
3
5
  declare function encodeEntities(value: string): string;
4
6
  //#endregion
5
- export { decodeEntities, encodeEntities };
7
+ export { decodeEntities, decodeTextLikeNode, encodeEntities };
@@ -1,18 +1,13 @@
1
+ import { EntityLevel, decode } from "entities";
1
2
  //#region src/xml/entities.ts
2
3
  function decodeEntities(value) {
3
- return value.replace(/&(?:amp|lt|gt|quot|apos);/g, (entity) => {
4
- switch (entity) {
5
- case "&amp;": return "&";
6
- case "&lt;": return "<";
7
- case "&gt;": return ">";
8
- case "&quot;": return "\"";
9
- case "&apos;": return "'";
10
- default: return entity;
11
- }
12
- });
4
+ return decode(value, EntityLevel.HTML);
5
+ }
6
+ function decodeTextLikeNode(node) {
7
+ return node.type === "cdata" ? node.value : decodeEntities(node.value);
13
8
  }
14
9
  function encodeEntities(value) {
15
10
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
16
11
  }
17
12
  //#endregion
18
- export { decodeEntities, encodeEntities };
13
+ export { decodeEntities, decodeTextLikeNode, encodeEntities };
package/dist/xml/node.cjs CHANGED
@@ -26,6 +26,9 @@ const XmlPiSchema = zod.z.object({
26
26
  target: zod.z.string(),
27
27
  content: zod.z.string()
28
28
  });
29
+ function isTextLikeNode(node) {
30
+ return node.type === "text" || node.type === "cdata";
31
+ }
29
32
  function isRecord(value) {
30
33
  return typeof value === "object" && value !== null && !Array.isArray(value);
31
34
  }
@@ -64,4 +67,5 @@ exports.XmlElementSchema = XmlElementSchema;
64
67
  exports.XmlNodeSchema = XmlNodeSchema;
65
68
  exports.XmlPiSchema = XmlPiSchema;
66
69
  exports.XmlTextSchema = XmlTextSchema;
70
+ exports.isTextLikeNode = isTextLikeNode;
67
71
  exports.isXmlNode = isXmlNode;
@@ -41,6 +41,7 @@ interface XmlElement {
41
41
  children: XmlNode[];
42
42
  }
43
43
  type XmlNode = XmlText | XmlCdata | XmlComment | XmlDeclaration | XmlPi | XmlElement;
44
+ declare function isTextLikeNode(node: XmlNode): node is XmlText | XmlCdata;
44
45
  declare function isXmlNode(value: unknown): value is XmlNode;
45
46
  declare const XmlElementSchema: z.ZodObject<{
46
47
  type: z.ZodLiteral<"element">;
@@ -80,4 +81,4 @@ declare const XmlNodeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
80
81
  children: z.ZodArray<z.ZodCustom<XmlNode, XmlNode>>;
81
82
  }, z.core.$strip>], "type">;
82
83
  //#endregion
83
- export { Attribute, AttributeSchema, XmlCdata, XmlCdataSchema, XmlComment, XmlCommentSchema, XmlDeclaration, XmlDeclarationSchema, XmlElement, XmlElementSchema, XmlNode, XmlNodeSchema, XmlPi, XmlPiSchema, XmlText, XmlTextSchema, isXmlNode };
84
+ export { Attribute, AttributeSchema, XmlCdata, XmlCdataSchema, XmlComment, XmlCommentSchema, XmlDeclaration, XmlDeclarationSchema, XmlElement, XmlElementSchema, XmlNode, XmlNodeSchema, XmlPi, XmlPiSchema, XmlText, XmlTextSchema, isTextLikeNode, isXmlNode };
@@ -41,6 +41,7 @@ interface XmlElement {
41
41
  children: XmlNode[];
42
42
  }
43
43
  type XmlNode = XmlText | XmlCdata | XmlComment | XmlDeclaration | XmlPi | XmlElement;
44
+ declare function isTextLikeNode(node: XmlNode): node is XmlText | XmlCdata;
44
45
  declare function isXmlNode(value: unknown): value is XmlNode;
45
46
  declare const XmlElementSchema: z.ZodObject<{
46
47
  type: z.ZodLiteral<"element">;
@@ -80,4 +81,4 @@ declare const XmlNodeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
80
81
  children: z.ZodArray<z.ZodCustom<XmlNode, XmlNode>>;
81
82
  }, z.core.$strip>], "type">;
82
83
  //#endregion
83
- export { Attribute, AttributeSchema, XmlCdata, XmlCdataSchema, XmlComment, XmlCommentSchema, XmlDeclaration, XmlDeclarationSchema, XmlElement, XmlElementSchema, XmlNode, XmlNodeSchema, XmlPi, XmlPiSchema, XmlText, XmlTextSchema, isXmlNode };
84
+ export { Attribute, AttributeSchema, XmlCdata, XmlCdataSchema, XmlComment, XmlCommentSchema, XmlDeclaration, XmlDeclarationSchema, XmlElement, XmlElementSchema, XmlNode, XmlNodeSchema, XmlPi, XmlPiSchema, XmlText, XmlTextSchema, isTextLikeNode, isXmlNode };
package/dist/xml/node.js CHANGED
@@ -25,6 +25,9 @@ const XmlPiSchema = z.object({
25
25
  target: z.string(),
26
26
  content: z.string()
27
27
  });
28
+ function isTextLikeNode(node) {
29
+ return node.type === "text" || node.type === "cdata";
30
+ }
28
31
  function isRecord(value) {
29
32
  return typeof value === "object" && value !== null && !Array.isArray(value);
30
33
  }
@@ -55,4 +58,4 @@ const XmlNodeSchema = z.discriminatedUnion("type", [
55
58
  XmlElementSchema
56
59
  ]);
57
60
  //#endregion
58
- export { AttributeSchema, XmlCdataSchema, XmlCommentSchema, XmlDeclarationSchema, XmlElementSchema, XmlNodeSchema, XmlPiSchema, XmlTextSchema, isXmlNode };
61
+ export { AttributeSchema, XmlCdataSchema, XmlCommentSchema, XmlDeclarationSchema, XmlElementSchema, XmlNodeSchema, XmlPiSchema, XmlTextSchema, isTextLikeNode, isXmlNode };
@@ -1,4 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_xml_entities = require("./entities.cjs");
3
+ const require_xml_node = require("./node.cjs");
2
4
  //#region src/xml/query.ts
3
5
  function rootElement(nodes) {
4
6
  for (const node of nodes) if (node.type === "element") return node;
@@ -34,9 +36,16 @@ function textContent(nodes) {
34
36
  else if (node.type === "element") out += textContent(node.children);
35
37
  return out;
36
38
  }
39
+ function decodedTextContent(nodes) {
40
+ let out = "";
41
+ for (const node of nodes) if (require_xml_node.isTextLikeNode(node)) out += require_xml_entities.decodeTextLikeNode(node);
42
+ else if (node.type === "element") out += decodedTextContent(node.children);
43
+ return out;
44
+ }
37
45
  //#endregion
38
46
  exports.attrValue = attrValue;
39
47
  exports.childrenWithTag = childrenWithTag;
48
+ exports.decodedTextContent = decodedTextContent;
40
49
  exports.elementsWithTag = elementsWithTag;
41
50
  exports.findChildElement = findChildElement;
42
51
  exports.findElement = findElement;
@@ -7,5 +7,6 @@ declare function elementsWithTag(nodes: readonly XmlNode[], tag: string): XmlEle
7
7
  declare function findElement(nodes: readonly XmlNode[], predicate: (element: XmlElement) => boolean): XmlElement | undefined;
8
8
  declare function attrValue(element: XmlElement, name: string): string | undefined;
9
9
  declare function textContent(nodes: readonly XmlNode[]): string;
10
+ declare function decodedTextContent(nodes: readonly XmlNode[]): string;
10
11
  //#endregion
11
- export { attrValue, childrenWithTag, elementsWithTag, findChildElement, findElement, rootElement, textContent };
12
+ export { attrValue, childrenWithTag, decodedTextContent, elementsWithTag, findChildElement, findElement, rootElement, textContent };
@@ -7,5 +7,6 @@ declare function elementsWithTag(nodes: readonly XmlNode[], tag: string): XmlEle
7
7
  declare function findElement(nodes: readonly XmlNode[], predicate: (element: XmlElement) => boolean): XmlElement | undefined;
8
8
  declare function attrValue(element: XmlElement, name: string): string | undefined;
9
9
  declare function textContent(nodes: readonly XmlNode[]): string;
10
+ declare function decodedTextContent(nodes: readonly XmlNode[]): string;
10
11
  //#endregion
11
- export { attrValue, childrenWithTag, elementsWithTag, findChildElement, findElement, rootElement, textContent };
12
+ export { attrValue, childrenWithTag, decodedTextContent, elementsWithTag, findChildElement, findElement, rootElement, textContent };
package/dist/xml/query.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { decodeTextLikeNode } from "./entities.js";
2
+ import { isTextLikeNode } from "./node.js";
1
3
  //#region src/xml/query.ts
2
4
  function rootElement(nodes) {
3
5
  for (const node of nodes) if (node.type === "element") return node;
@@ -33,5 +35,11 @@ function textContent(nodes) {
33
35
  else if (node.type === "element") out += textContent(node.children);
34
36
  return out;
35
37
  }
38
+ function decodedTextContent(nodes) {
39
+ let out = "";
40
+ for (const node of nodes) if (isTextLikeNode(node)) out += decodeTextLikeNode(node);
41
+ else if (node.type === "element") out += decodedTextContent(node.children);
42
+ return out;
43
+ }
36
44
  //#endregion
37
- export { attrValue, childrenWithTag, elementsWithTag, findChildElement, findElement, rootElement, textContent };
45
+ export { attrValue, childrenWithTag, decodedTextContent, elementsWithTag, findChildElement, findElement, rootElement, textContent };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "epub-codec",
3
- "version": "1.1.0",
3
+ "version": "1.2.1",
4
4
  "description": "EPUB 2/3 reading and deterministic EPUB 3 writing against the shared document-schema.js content pivot.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -78,7 +78,8 @@
78
78
  "license": "MIT",
79
79
  "packageManager": "pnpm@11.6.0",
80
80
  "dependencies": {
81
- "document-schema.js": "^6.1.0",
81
+ "document-schema.js": "^6.2.0",
82
+ "entities": "^8.0.0",
82
83
  "fast-xml-parser": "^5.10.1",
83
84
  "fflate": "^0.8.3",
84
85
  "zod": "^4.4.3"