@storyteller-platform/epub 0.8.2 → 1.0.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.
package/dist/upgrade.js CHANGED
@@ -1,6 +1,6 @@
1
- import { nanoid } from "nanoid";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "@storyteller-platform/epub/jsx-runtime";
2
2
  import { MediaType } from "@storyteller-platform/media-types";
3
- import { Epub, } from "./index.js";
3
+ import { findXmlChildByNameNS, findXmlDescendantByName, findXmlDescendantByNameNS, insert, NS, replace, serialize, text, x, xmlId, } from "./xml.js";
4
4
  const GUIDE_TO_EPUBTYPE = {
5
5
  acknowledgements: "acknowledgments",
6
6
  "other.afterword": "afterword",
@@ -50,54 +50,52 @@ const GUIDE_TO_EPUBTYPE = {
50
50
  const XHTML_MEDIA_TYPES = new Set([MediaType.XHTML, MediaType.HTML]);
51
51
  // xml helpers
52
52
  function getMetadataElement(pkg) {
53
- return Epub.findXmlChildByName("metadata", Epub.getXmlChildren(pkg));
53
+ return findXmlChildByNameNS(NS.opf, "metadata", pkg);
54
54
  }
55
55
  function getManifestElement(pkg) {
56
- return Epub.findXmlChildByName("manifest", Epub.getXmlChildren(pkg));
56
+ return findXmlChildByNameNS(NS.opf, "manifest", pkg);
57
57
  }
58
58
  function getSpineElement(pkg) {
59
- return Epub.findXmlChildByName("spine", Epub.getXmlChildren(pkg));
59
+ return findXmlChildByNameNS(NS.opf, "spine", pkg);
60
+ }
61
+ /**
62
+ * Replace an outdated document type declaration with `<!DOCTYPE html>`.
63
+ *
64
+ * EPUB 3 XHTML content documents must conform to the HTML5 conformance
65
+ * criteria, which require `<!DOCTYPE html>`.
66
+ */
67
+ export function normalizeDoctype(doc) {
68
+ const doctype = doc.doctype;
69
+ if (doctype && doctype.publicId === "" && doctype.systemId === "")
70
+ return;
71
+ if (doctype)
72
+ doc.removeChild(doctype);
73
+ doc.insertBefore(doc.implementation.createDocumentType("html", "", ""), doc.documentElement);
74
+ return;
60
75
  }
61
76
  function ensureId(element) {
62
- const existing = element[":@"]?.["@_id"];
77
+ const existing = element.getAttribute("id");
63
78
  if (existing)
64
79
  return existing;
65
- const id = `id-${nanoid(8)}`;
66
- element[":@"] ??= {};
67
- element[":@"]["@_id"] = id;
80
+ const id = `id-${xmlId(8)}`;
81
+ element.setAttribute("id", id);
68
82
  return id;
69
83
  }
70
- function findAllByName(name, xml) {
71
- return xml.filter((node) => !Epub.isXmlTextNode(node) && name in node);
72
- }
73
- function hasElementDeep(xml, name) {
74
- for (const node of xml) {
75
- if (Epub.isXmlTextNode(node))
76
- continue;
77
- if (Epub.getXmlElementName(node) === name)
78
- return true;
79
- if (hasElementDeep(Epub.getXmlChildren(node), name))
80
- return true;
81
- }
82
- return false;
83
- }
84
- function textContentOf(element) {
85
- return Epub.getXhtmlTextContent(Epub.getXmlChildren(element)).trim();
84
+ function findAllByNameNS(namespaceUri, name, element) {
85
+ return element.children.filter((node) => (!namespaceUri || node.namespaceURI === namespaceUri) &&
86
+ node.localName === name);
86
87
  }
87
- function removeFromArray(array, item) {
88
- const idx = array.indexOf(item);
89
- if (idx !== -1)
90
- array.splice(idx, 1);
88
+ function findAllByName(name, element) {
89
+ return findAllByNameNS(null, name, element);
91
90
  }
92
91
  // ---------------------------------------------------------------------------
93
92
  export function upgradeIdentifiers(pkg) {
94
93
  const metadata = getMetadataElement(pkg);
95
94
  if (!metadata)
96
95
  return;
97
- for (const ident of findAllByName("dc:identifier", metadata.metadata)) {
98
- const attrs = ident[":@"] ?? {};
99
- let val = textContentOf(ident);
100
- let scheme = attrs["@_opf:scheme"];
96
+ for (const ident of findAllByNameNS(NS.dc, "identifier", metadata)) {
97
+ let val = ident.textContent ?? "";
98
+ let scheme = ident.getAttributeNS(NS.opf, "scheme");
101
99
  if (val.toLowerCase().startsWith("urn:")) {
102
100
  const rest = val.slice(4);
103
101
  const colonIdx = rest.indexOf(":");
@@ -109,21 +107,20 @@ export function upgradeIdentifiers(pkg) {
109
107
  if (scheme && val && !scheme.toLowerCase().startsWith("uri")) {
110
108
  val = `${scheme}:${val}`;
111
109
  }
112
- const id = attrs["@_id"];
113
- ident[":@"] = id ? { "@_id": id } : {};
114
- Epub.replaceXmlChildren(ident, [Epub.createXmlTextNode(val)]);
110
+ const id = ident.getAttribute("id");
111
+ replace(metadata, _jsx("dc:identifier", { id: id, children: val }), ident);
115
112
  }
116
113
  }
117
114
  export function upgradeTitle(pkg) {
118
115
  const metadata = getMetadataElement(pkg);
119
116
  if (!metadata)
120
117
  return;
121
- const titles = findAllByName("dc:title", metadata.metadata);
118
+ const titles = findAllByNameNS(NS.dc, "title", metadata);
122
119
  let firstTitle = null;
123
120
  for (const title of titles) {
124
- const text = textContentOf(title);
121
+ const text = title.textContent?.trim();
125
122
  if (!text) {
126
- removeFromArray(metadata.metadata, title);
123
+ metadata.removeChild(title);
127
124
  continue;
128
125
  }
129
126
  firstTitle ??= title;
@@ -131,43 +128,38 @@ export function upgradeTitle(pkg) {
131
128
  if (!firstTitle)
132
129
  return;
133
130
  const titleId = ensureId(firstTitle);
134
- metadata.metadata.push(Epub.createXmlElement("meta", { refines: `#${titleId}`, property: "title-type" }, [Epub.createXmlTextNode("main")]));
131
+ insert(metadata, _jsx("meta", { refines: `#${titleId}`, property: "title-type", children: "main" }));
135
132
  }
136
133
  export function upgradeLanguages(pkg) {
137
134
  const metadata = getMetadataElement(pkg);
138
135
  if (!metadata)
139
136
  return;
140
- const langs = findAllByName("dc:language", metadata.metadata);
137
+ const langs = findAllByNameNS(NS.dc, "language", metadata);
141
138
  if (langs.length > 0) {
142
139
  for (const lang of langs) {
143
- const id = lang[":@"]?.["@_id"];
144
- lang[":@"] = id ? { "@_id": id } : {};
140
+ const id = lang.getAttribute("id");
141
+ replace(metadata, _jsx("dc:language", { id: id, children: Array.from(lang.childNodes) }), lang);
145
142
  }
146
143
  return;
147
144
  }
148
- metadata.metadata.push(Epub.createXmlElement("dc:language", {}, [Epub.createXmlTextNode("und")]));
145
+ insert(metadata, _jsx("dc:language", { children: "und" }));
149
146
  }
150
147
  export function upgradeAuthors(pkg) {
151
148
  const metadata = getMetadataElement(pkg);
152
149
  if (!metadata)
153
150
  return;
154
- for (const which of ["dc:creator", "dc:contributor"]) {
155
- for (const elem of findAllByName(which, metadata.metadata)) {
156
- const attrs = elem[":@"] ?? {};
157
- const role = attrs["@_opf:role"];
158
- const sort = attrs["@_opf:file-as"];
159
- const elemId = role || sort ? ensureId(elem) : attrs["@_id"];
151
+ for (const which of ["creator", "contributor"]) {
152
+ for (const elem of findAllByNameNS(NS.dc, which, metadata)) {
153
+ const role = elem.getAttributeNS(NS.opf, "role");
154
+ const sort = elem.getAttributeNS(NS.opf, "file-as");
155
+ const elemId = role || sort ? ensureId(elem) : elem.getAttribute("id");
160
156
  // strip all non-id attributes
161
- elem[":@"] = elemId ? { "@_id": elemId } : {};
157
+ replace(metadata, x(`dc:${which}`, elemId === null ? null : { id: elemId }, ...elem.childNodes), elem);
162
158
  if (role) {
163
- metadata.metadata.push(Epub.createXmlElement("meta", {
164
- refines: `#${elemId}`,
165
- property: "role",
166
- scheme: "marc:relators",
167
- }, [Epub.createXmlTextNode(role)]));
159
+ insert(metadata, _jsx("meta", { refines: `#${elemId}`, property: "role", scheme: "marc:relators", children: role }));
168
160
  }
169
161
  if (sort) {
170
- metadata.metadata.push(Epub.createXmlElement("meta", { refines: `#${elemId}`, property: "file-as" }, [Epub.createXmlTextNode(sort)]));
162
+ insert(metadata, _jsx("meta", { refines: `#${elemId}`, property: "file-as", children: sort }));
171
163
  }
172
164
  }
173
165
  }
@@ -176,27 +168,26 @@ export function upgradeDate(pkg) {
176
168
  const metadata = getMetadataElement(pkg);
177
169
  if (!metadata)
178
170
  return;
179
- const dates = findAllByName("dc:date", metadata.metadata);
171
+ const dates = findAllByNameNS(NS.dc, "date", metadata);
180
172
  let kept = false;
181
173
  for (const date of dates) {
182
- const text = textContentOf(date);
174
+ const text = date.textContent;
183
175
  if (!text || kept) {
184
- removeFromArray(metadata.metadata, date);
176
+ metadata.removeChild(date);
185
177
  continue;
186
178
  }
187
179
  kept = true;
188
- const id = date[":@"]?.["@_id"];
189
- date[":@"] = id ? { "@_id": id } : {};
180
+ const id = date.getAttribute("id");
181
+ replace(metadata, _jsx("dc:date", { id: id, children: Array.from(date.childNodes) }), date);
190
182
  }
191
183
  }
192
184
  export function upgradeMeta(pkg) {
193
185
  const metadata = getMetadataElement(pkg);
194
186
  if (!metadata)
195
187
  return;
196
- for (const meta of findAllByName("meta", metadata.metadata)) {
197
- const attrs = meta[":@"] ?? {};
198
- const name = attrs["@_name"] ?? "";
199
- const content = attrs["@_content"] ?? "";
188
+ for (const meta of findAllByName("meta", metadata)) {
189
+ const name = meta.getAttribute("name") ?? "";
190
+ const content = meta.getAttribute("content") ?? "";
200
191
  let prop = null;
201
192
  let value = content;
202
193
  const cleanName = name.startsWith("rendition:")
@@ -219,10 +210,13 @@ export function upgradeMeta(pkg) {
219
210
  }
220
211
  if (!prop)
221
212
  continue;
222
- delete attrs["@_name"];
223
- delete attrs["@_content"];
224
- attrs["@_property"] = prop;
225
- Epub.replaceXmlChildren(meta, [Epub.createXmlTextNode(value)]);
213
+ meta.removeAttribute("name");
214
+ meta.removeAttribute("content");
215
+ meta.setAttribute("property", prop);
216
+ while (meta.firstChild) {
217
+ meta.removeChild(meta.firstChild);
218
+ }
219
+ insert(meta, text(value));
226
220
  }
227
221
  }
228
222
  export function upgradeCover(pkg) {
@@ -230,27 +224,26 @@ export function upgradeCover(pkg) {
230
224
  const manifest = getManifestElement(pkg);
231
225
  if (!metadata || !manifest)
232
226
  return;
233
- for (const meta of findAllByName("meta", metadata.metadata)) {
234
- const attrs = meta[":@"];
235
- const isCoverMeta = attrs?.["@_name"] === "cover" && attrs["@_content"];
227
+ for (const meta of findAllByName("meta", metadata)) {
228
+ const content = meta.getAttribute("content");
229
+ const isCoverMeta = meta.getAttribute("name") === "cover" && content;
236
230
  if (!isCoverMeta)
237
231
  continue;
238
- const itemId = attrs["@_content"];
239
- for (const item of findAllByName("item", manifest.manifest)) {
240
- const itemAttrs = item[":@"];
241
- if (!itemAttrs || itemAttrs["@_id"] !== itemId)
232
+ const itemId = content;
233
+ for (const item of findAllByName("item", manifest)) {
234
+ if (item.getAttribute("id") !== itemId)
242
235
  continue;
243
- const mediaType = (itemAttrs["@_media-type"] ?? "").toLowerCase();
236
+ const mediaType = (item.getAttribute("media-type") ?? "").toLowerCase();
244
237
  // only mark actual images, not html wrappers
245
238
  const isImage = mediaType && !mediaType.includes("xml") && !mediaType.includes("html");
246
239
  if (!isImage)
247
240
  continue;
248
- const existing = (itemAttrs["@_properties"] ?? "")
241
+ const existing = (item.getAttribute("properties") ?? "")
249
242
  .split(" ")
250
243
  .filter(Boolean);
251
244
  const props = new Set(existing);
252
245
  props.add("cover-image");
253
- itemAttrs["@_properties"] = [...props].sort().join(" ");
246
+ item.setAttribute("properties", [...props].sort().join(" "));
254
247
  }
255
248
  }
256
249
  }
@@ -258,35 +251,27 @@ export function removeInvalidDcAttrs(pkg) {
258
251
  const metadata = getMetadataElement(pkg);
259
252
  if (!metadata)
260
253
  return;
261
- for (const node of metadata.metadata) {
262
- if (Epub.isXmlTextNode(node))
263
- continue;
264
- const name = Epub.getXmlElementName(node);
265
- if (!name.startsWith("dc:"))
254
+ for (const node of Array.from(metadata.children)) {
255
+ if (node.namespaceURI !== NS.dc)
266
256
  continue;
267
- const attrs = node[":@"];
268
- if (!attrs)
269
- continue;
270
- const id = attrs["@_id"];
271
- node[":@"] = id ? { "@_id": id } : {};
257
+ for (const attr of Array.from(node.attributes)) {
258
+ if (attr.name === "id" || attr.name.startsWith("xmlns:"))
259
+ continue;
260
+ node.removeAttributeNode(attr);
261
+ }
272
262
  }
273
263
  }
274
264
  export function setLastModified(pkg) {
275
265
  const metadata = getMetadataElement(pkg);
276
266
  if (!metadata)
277
267
  return;
278
- for (let i = metadata.metadata.length - 1; i >= 0; i--) {
279
- const node = metadata.metadata[i];
280
- if (!node || Epub.isXmlTextNode(node))
281
- continue;
282
- if (node[":@"]?.["@_property"] === "dcterms:modified") {
283
- metadata.metadata.splice(i, 1);
268
+ for (const node of Array.from(metadata.children)) {
269
+ if (node.getAttribute("property") === "dcterms:modified") {
270
+ metadata.removeChild(node);
284
271
  }
285
272
  }
286
273
  const now = new Date().toISOString().replace(/\.\d+/, "");
287
- metadata.metadata.push(Epub.createXmlElement("meta", { property: "dcterms:modified" }, [
288
- Epub.createXmlTextNode(now),
289
- ]));
274
+ insert(metadata, _jsx("meta", { property: "dcterms:modified", children: now }));
290
275
  }
291
276
  export function upgradePackageMetadata(pkg) {
292
277
  upgradeIdentifiers(pkg);
@@ -300,19 +285,16 @@ export function upgradePackageMetadata(pkg) {
300
285
  setLastModified(pkg);
301
286
  }
302
287
  export function extractGuideLandmarks(pkg) {
303
- const guide = Epub.findXmlChildByName("guide", Epub.getXmlChildren(pkg));
288
+ const guide = findXmlChildByNameNS(NS.opf, "guide", pkg);
304
289
  if (!guide)
305
290
  return [];
306
291
  const landmarks = [];
307
- for (const node of Epub.getXmlChildren(guide)) {
308
- if (Epub.isXmlTextNode(node))
309
- continue;
292
+ for (const node of Array.from(guide.children)) {
310
293
  if (!("reference" in node))
311
294
  continue;
312
- const attrs = node[":@"] ?? {};
313
- const href = attrs["@_href"] ?? "";
314
- const title = attrs["@_title"] ?? "";
315
- const guideType = (attrs["@_type"] ?? "").toLowerCase();
295
+ const href = node.getAttribute("href") ?? "";
296
+ const title = node.getAttribute("title") ?? "";
297
+ const guideType = (node.getAttribute("type") ?? "").toLowerCase();
316
298
  const epubType = GUIDE_TO_EPUBTYPE[guideType];
317
299
  if (epubType === undefined || !href)
318
300
  continue;
@@ -321,17 +303,14 @@ export function extractGuideLandmarks(pkg) {
321
303
  return landmarks;
322
304
  }
323
305
  export function removeGuide(pkg) {
324
- const children = Epub.getXmlChildren(pkg);
325
- const guide = Epub.findXmlChildByName("guide", children);
306
+ const guide = findXmlChildByNameNS(NS.opf, "guide", pkg);
326
307
  if (guide) {
327
- removeFromArray(children, guide);
308
+ pkg.removeChild(guide);
328
309
  }
329
310
  }
330
311
  export function removeSpineTocRef(pkg) {
331
312
  const spine = getSpineElement(pkg);
332
- if (!spine?.[":@"])
333
- return;
334
- delete spine[":@"]["@_toc"];
313
+ spine?.removeAttribute("toc");
335
314
  }
336
315
  export async function collectManifestProperties(epub) {
337
316
  const manifest = await epub.getManifest();
@@ -339,22 +318,25 @@ export async function collectManifestProperties(epub) {
339
318
  const mediaType = MediaType.fromMime(item.mediaType ?? "");
340
319
  if (!mediaType || !XHTML_MEDIA_TYPES.has(mediaType))
341
320
  continue;
342
- let xml;
321
+ let documentElement;
343
322
  try {
344
- xml = await epub.readXhtmlItemContents(item.id);
323
+ documentElement = (await epub.readXhtmlItemContents(item.id))
324
+ .documentElement;
345
325
  }
346
326
  catch {
347
327
  continue;
348
328
  }
329
+ if (!documentElement)
330
+ continue;
349
331
  const props = new Set(item.properties ?? []);
350
332
  const before = props.size;
351
- if (hasElementDeep(xml, "svg"))
333
+ if (findXmlDescendantByName("svg", documentElement))
352
334
  props.add("svg");
353
- if (hasElementDeep(xml, "script"))
335
+ if (findXmlDescendantByName("script", documentElement))
354
336
  props.add("scripted");
355
- if (hasElementDeep(xml, "math"))
337
+ if (findXmlDescendantByName("math", documentElement))
356
338
  props.add("mathml");
357
- if (hasElementDeep(xml, "epub:switch"))
339
+ if (findXmlDescendantByNameNS(NS.epub, "switch", documentElement))
358
340
  props.add("switch");
359
341
  if (props.size === before)
360
342
  continue;
@@ -368,15 +350,14 @@ export function fixFontMimeTypes(pkg) {
368
350
  const manifest = getManifestElement(pkg);
369
351
  if (!manifest)
370
352
  return;
371
- for (const item of findAllByName("item", manifest.manifest)) {
372
- const declared = item[":@"]?.["@_media-type"] ?? "";
353
+ for (const item of findAllByName("item", manifest)) {
354
+ const declared = item.getAttribute("media-type") ?? "";
373
355
  if (!MediaType.fromMime(declared)?.is("font"))
374
356
  continue;
375
- const corrected = MediaType.fromPath(item[":@"]?.["@_href"] ?? "");
357
+ const corrected = MediaType.fromPath(item.getAttribute("href") ?? "");
376
358
  if (!corrected?.is("font") || corrected.mime === declared)
377
359
  continue;
378
- item[":@"] ??= {};
379
- item[":@"]["@_media-type"] = corrected.mime;
360
+ item.setAttribute("media-type", corrected.mime);
380
361
  }
381
362
  }
382
363
  export function buildTocOl(entries) {
@@ -384,19 +365,17 @@ export function buildTocOl(entries) {
384
365
  const label = entry.title.replace(/\s+/g, " ").trim();
385
366
  const liChildren = [];
386
367
  if (entry.href) {
387
- liChildren.push(Epub.createXmlElement("a", { href: entry.href }, [
388
- Epub.createXmlTextNode(label),
389
- ]));
368
+ liChildren.push(_jsx("a", { href: entry.href, children: label }));
390
369
  }
391
370
  else {
392
- liChildren.push(Epub.createXmlElement("span", {}, [Epub.createXmlTextNode(label)]));
371
+ liChildren.push(_jsx("span", { children: label }));
393
372
  }
394
373
  if (entry.children && entry.children.length > 0) {
395
374
  liChildren.push(buildTocOl(entry.children));
396
375
  }
397
- return Epub.createXmlElement("li", {}, liChildren);
376
+ return _jsx("li", { children: liChildren });
398
377
  });
399
- return Epub.createXmlElement("ol", {}, children);
378
+ return _jsx("ol", { children: children });
400
379
  }
401
380
  export async function buildNavDocument(epub, tocEntries, landmarks) {
402
381
  let tocOl;
@@ -407,41 +386,21 @@ export async function buildNavDocument(epub, tocEntries, landmarks) {
407
386
  // epub 3 requires a non-empty <ol>, fall back to first spine item
408
387
  const spineItems = await epub.getSpineItems();
409
388
  const firstHref = spineItems[0]?.href ?? "#";
410
- tocOl = Epub.createXmlElement("ol", {}, [
411
- Epub.createXmlElement("li", {}, [
412
- Epub.createXmlElement("a", { href: firstHref }, [
413
- Epub.createXmlTextNode("Start"),
414
- ]),
415
- ]),
416
- ]);
389
+ tocOl = (_jsx("ol", { children: _jsx("li", { children: _jsx("a", { href: firstHref, children: "Start" }) }) }));
417
390
  }
418
- const tocNav = Epub.createXmlElement("nav", { "epub:type": "toc" }, [
419
- Epub.createXmlElement("h1", {}, [
420
- Epub.createXmlTextNode("Table of Contents"),
421
- ]),
422
- tocOl,
423
- ]);
424
- const body = [tocNav];
391
+ const tocNav = (_jsxs("nav", { "epub:type": "toc", children: [_jsx("h1", { children: "Table of Contents" }), tocOl] }));
392
+ const bodyChildren = [tocNav];
425
393
  const validLandmarks = landmarks.filter((lm) => lm.type);
426
394
  if (validLandmarks.length > 0) {
427
- const lmOl = Epub.createXmlElement("ol", {}, validLandmarks.map((lm) => Epub.createXmlElement("li", {}, [
428
- Epub.createXmlElement("a", { "epub:type": lm.type, href: lm.href }, [
429
- Epub.createXmlTextNode(lm.title || lm.type),
430
- ]),
431
- ])));
432
- body.push(Epub.createXmlElement("nav", { "epub:type": "landmarks", hidden: "" }, [
433
- lmOl,
434
- ]));
395
+ const lmOl = (_jsx("ol", { children: validLandmarks.map((lm) => (_jsx("li", { children: _jsx("a", { "epub:type": lm.type, href: lm.href, children: lm.title || lm.type }) }))) }));
396
+ bodyChildren.push(_jsx("nav", { "epub:type": "landmarks", hidden: "", children: lmOl }));
435
397
  }
436
- const head = [
437
- Epub.createXmlElement("title", {}, [Epub.createXmlTextNode("Navigation")]),
438
- ];
439
- const navDoc = await epub.createXhtmlDocument(body, head);
440
- return Epub.xhtmlBuilder.build(navDoc);
398
+ const head = [_jsx("title", { children: "Navigation" })];
399
+ const navDoc = await epub.createXhtmlDocument(bodyChildren, head);
400
+ return serialize(navDoc);
441
401
  }
442
402
  export function setPackageVersion(pkg, version) {
443
- pkg[":@"] ??= {};
444
- pkg[":@"]["@_version"] = version;
403
+ pkg.setAttribute("version", version);
445
404
  }
446
405
  export async function chooseNavHref(epub) {
447
406
  const manifest = await epub.getManifest();
package/dist/xml.d.ts ADDED
@@ -0,0 +1,104 @@
1
+ import xmldom, { type Document, type CharacterData, type Element, type Node, type Text, type DocumentType } from "@xmldom/xmldom";
2
+ export declare const xmlId: (size?: number) => string;
3
+ export declare const XMLSerializer: typeof xmldom.XMLSerializer;
4
+ export declare function isElement(n: Node): n is Element;
5
+ export declare function isText(n: Node): n is CharacterData;
6
+ export declare function isCdata(n: Node): n is CharacterData;
7
+ export declare function isComment(n: Node): n is CharacterData;
8
+ /**
9
+ * Give every element in `node`'s subtree the namespace its position implies,
10
+ * without overwriting any element that already has one.
11
+ *
12
+ * @privateremarks
13
+ *
14
+ * `x()` builds elements in no namespace. That serializes correctly, because a
15
+ * child in no namespace inherits the enclosing default namespace on the way
16
+ * out, but in memory the elements report `namespaceURI === null` and so are
17
+ * invisible to namespace-aware queries such as `getElementsByTagNameNS`.
18
+ * Calling this after inserting a node makes the tree report the namespace its
19
+ * position implies.
20
+ */
21
+ export declare function adoptNamespaces(node: Element): void;
22
+ /**
23
+ * Declare `uri` as the default namespace of `element` and bring the elements
24
+ * already under it in line with that declaration.
25
+ *
26
+ * xmldom records a namespace declaration without applying it to the tree, so a
27
+ * document read without `xmlns` stays in no namespace in memory even once the
28
+ * declaration is added. Namespace-aware lookups cannot see any of it until the
29
+ * subtree is fixed up, and the document element has no element parent for
30
+ * `adoptNamespaces` to inherit from, so it has to be bound explicitly.
31
+ */
32
+ export declare function declareDefaultNamespace(element: Element, uri: string): void;
33
+ /**
34
+ * Insert `child` into `parent`, then adopt its namespace.
35
+ *
36
+ * If `reference` is omitted, appends child to parent.
37
+ */
38
+ export declare function insert<T extends Node>(parent: Element, child: T, reference?: Node | null): T;
39
+ /**
40
+ * Replace `oldChild` with `newChild` in `parent`, then adopt the new child's
41
+ * namespace.
42
+ */
43
+ export declare function replace<T extends Node>(parent: Element, newChild: T, oldChild: Node): T;
44
+ /**
45
+ * Given an XHTML document, return the body element.
46
+ */
47
+ export declare function getXhtmlBody(document: Document): Element;
48
+ /**
49
+ * Given an Element, find the first child matching
50
+ * the provided namespace, name and optional filter.
51
+ */
52
+ export declare function findXmlChildByNameNS(namespaceUri: string | null, name: string, element: Element, filter?: (node: Element) => boolean): Element | undefined;
53
+ /**
54
+ * Given an Element, find the first child matching
55
+ * the provided name and optional filter.
56
+ */
57
+ export declare function findXmlChildByName(name: string, element: Element, filter?: (node: Element) => boolean): Element | undefined;
58
+ /**
59
+ * Given an Element, find the first descendant matching
60
+ * the provided name and optional filter.
61
+ *
62
+ * Will perform a breadth first search for the element, returning
63
+ * the highest element in the tree matching the name and filter.
64
+ */
65
+ export declare function findXmlDescendantByNameNS(namespaceUri: string | null, name: string, element: Element, filter?: (node: Element) => boolean): Element | undefined;
66
+ /**
67
+ * Given an Element, find the first descendant matching
68
+ * the provided name and optional filter.
69
+ *
70
+ * Will perform a breadth first search for the element, returning
71
+ * the highest element in the tree matching the name and filter.
72
+ */
73
+ export declare function findXmlDescendantByName(name: string, element: Element, filter?: (node: Element) => boolean): Element | undefined;
74
+ /**
75
+ * Given an XHTML document, add a `link` element to the
76
+ * `head` of the document.
77
+ */
78
+ export declare function addLinkToXhtmlHead(document: Document, link: {
79
+ rel: string;
80
+ href: string;
81
+ type: string;
82
+ }): void;
83
+ export type XmlDiagnostic = {
84
+ level: "warning" | "error" | "fatalError";
85
+ message: string;
86
+ line?: number | undefined;
87
+ column?: number | undefined;
88
+ };
89
+ export declare function parse(xml: string, mediaType: string, onError?: (diagnostic: XmlDiagnostic) => void): Document;
90
+ export declare function serialize(doc: Node, options?: {
91
+ validate?: boolean | undefined;
92
+ }): string;
93
+ export declare const NS: {
94
+ xmlns: string;
95
+ xml: string;
96
+ xhtml: string;
97
+ opf: string;
98
+ dc: string;
99
+ epub: string;
100
+ };
101
+ export declare function x(name: string, attributes?: Record<string, string | undefined> | null, ...children: Array<Node | string | null | undefined>): Element;
102
+ export declare function text(data: string): Text;
103
+ export declare function createDocument(namespaceUri: string | null, qualifiedName: string, docType?: DocumentType | null): Document;
104
+ export declare function createDocumentType(qualifiedName: string, publicId?: string, systemId?: string): DocumentType;