@prose-reader/archive-parser 1.296.0 → 1.298.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/dist/index.js CHANGED
@@ -1,16 +1,16 @@
1
- import { XmlDocument as m } from "xmldoc";
2
- const fe = "com.apple.ibooks.display-options.xml", Y = (e) => e.childrenNamed("option").map((t) => ({
1
+ import { XmlDocument as f } from "xmldoc";
2
+ const me = "com.apple.ibooks.display-options.xml", Y = (e) => e.childrenNamed("option").map((t) => ({
3
3
  name: t.attr?.name,
4
4
  value: t.val
5
5
  })), pe = (e) => {
6
- const t = new m(e);
6
+ const t = new f(e);
7
7
  if (t.name?.toLowerCase() !== "display_options")
8
8
  return { kind: "apple" };
9
- const r = t.childNamed("platform");
9
+ const o = t.childNamed("platform");
10
10
  return {
11
11
  kind: "apple",
12
12
  displayOptions: {
13
- ...r !== void 0 ? { platform: { options: Y(r) } } : {}
13
+ ...o !== void 0 ? { platform: { options: Y(o) } } : {}
14
14
  }
15
15
  };
16
16
  }, G = [
@@ -28,170 +28,174 @@ const fe = "com.apple.ibooks.display-options.xml", Y = (e) => e.childrenNamed("o
28
28
  }, ge = (e) => {
29
29
  let t;
30
30
  try {
31
- t = new m(e);
32
- } catch (r) {
33
- const n = r instanceof Error ? r.message : String(r);
31
+ t = new f(e);
32
+ } catch (o) {
33
+ const n = o instanceof Error ? o.message : String(o);
34
34
  throw new Error(`${K} is malformed: ${n}`, {
35
- cause: r
35
+ cause: o
36
36
  });
37
37
  }
38
- const o = {};
39
- return t.eachChild((r) => {
40
- if (r.type !== "element" || H.has(r.name) || U(r)) return;
41
- const n = z(r);
42
- n !== void 0 && o[r.name] === void 0 && (o[r.name] = n);
43
- }), { kind: "comicInfo", ...o };
38
+ const r = {};
39
+ return t.eachChild((o) => {
40
+ if (o.type !== "element" || H.has(o.name) || U(o)) return;
41
+ const n = z(o);
42
+ n !== void 0 && r[o.name] === void 0 && (r[o.name] = n);
43
+ }), { kind: "comicInfo", ...r };
44
44
  }, he = "com.kobobooks.display-options.xml", V = (e) => {
45
45
  const t = e.childNamed("platform");
46
46
  if (!t) return {};
47
- for (const o of t.childrenNamed("option"))
48
- if (o.attr?.name === "fixed-layout")
49
- return o.val.trim().toLowerCase() === "true" ? { renditionLayout: "pre-paginated" } : {};
47
+ for (const r of t.childrenNamed("option"))
48
+ if (r.attr?.name === "fixed-layout")
49
+ return r.val.trim().toLowerCase() === "true" ? { renditionLayout: "pre-paginated" } : {};
50
50
  return {};
51
51
  }, ye = (e) => {
52
- const t = new m(e);
52
+ const t = new f(e);
53
53
  return t.name?.toLowerCase() === "display_options" ? { kind: "kobo", ...V(t) } : { kind: "kobo" };
54
- }, A = (e) => e === void 0 || e.trim().length === 0 ? [] : e.trim().split(/\s+/).filter((t) => t.length > 0), $ = (e) => {
55
- const t = A(e);
54
+ }, M = (e) => e === void 0 || e.trim().length === 0 ? [] : e.trim().split(/\s+/).filter((t) => t.length > 0), $ = (e) => {
55
+ const t = M(e);
56
56
  if (t.length === 0)
57
57
  return {};
58
+ let r;
59
+ t.includes("rendition:layout-reflowable") && (r = "reflowable"), t.includes("rendition:layout-pre-paginated") && (r = "pre-paginated");
58
60
  let o;
59
- return t.includes("rendition:layout-reflowable") && (o = "reflowable"), t.includes("rendition:layout-pre-paginated") && (o = "pre-paginated"), {
60
- ...o !== void 0 ? { renditionLayout: o } : {},
61
+ return t.includes("rendition:flow-auto") && (o = "auto"), t.includes("rendition:flow-paginated") && (o = "paginated"), t.includes("rendition:flow-scrolled-doc") && (o = "scrolled-doc"), t.includes("rendition:flow-scrolled-continuous") && (o = "scrolled-continuous"), {
62
+ ...r !== void 0 ? { renditionLayout: r } : {},
63
+ ...o !== void 0 ? { renditionFlow: o } : {},
61
64
  ...t.includes("page-spread-left") ? { pageSpreadLeft: !0 } : {},
62
65
  ...t.includes("page-spread-right") ? { pageSpreadRight: !0 } : {}
63
66
  };
64
- }, a = (e) => e.includes(":") ? e.slice(e.lastIndexOf(":") + 1) : e, k = (e, t) => a(e).toLowerCase() === t.toLowerCase(), _ = (e) => e.type === "element", c = (e, t) => {
65
- for (const o of e.children)
66
- if (_(o) && k(o.name, t))
67
- return o;
68
- }, f = (e, t) => {
69
- const o = [];
67
+ }, a = (e) => e.includes(":") ? e.slice(e.lastIndexOf(":") + 1) : e, A = (e, t) => a(e).toLowerCase() === t.toLowerCase(), k = (e) => e.type === "element", c = (e, t) => {
70
68
  for (const r of e.children)
71
- _(r) && k(r.name, t) && o.push(r);
72
- return o;
69
+ if (k(r) && A(r.name, t))
70
+ return r;
71
+ }, m = (e, t) => {
72
+ const r = [];
73
+ for (const o of e.children)
74
+ k(o) && A(o.name, t) && r.push(o);
75
+ return r;
73
76
  }, W = (e) => {
74
77
  const t = [];
75
- return e.eachChild((o) => {
76
- if (a(o.name).toLowerCase() !== "identifier") return;
77
- const r = o.val.trim();
78
- if (r.length === 0) return;
79
- const i = (o.attr["opf:scheme"] ?? o.attr["opf:Scheme"] ?? o.attr.scheme)?.trim();
78
+ return e.eachChild((r) => {
79
+ if (a(r.name).toLowerCase() !== "identifier") return;
80
+ const o = r.val.trim();
81
+ if (o.length === 0) return;
82
+ const i = (r.attr["opf:scheme"] ?? r.attr["opf:Scheme"] ?? r.attr.scheme)?.trim();
80
83
  t.push({
81
- value: r,
84
+ value: o,
82
85
  ...i !== void 0 && i.length > 0 ? { scheme: i } : {}
83
86
  });
84
87
  }), t;
85
88
  }, d = (e, t) => {
86
- let o;
87
- return e.eachChild((r) => {
88
- if (o !== void 0 || a(r.name).toLowerCase() !== t.toLowerCase())
89
+ let r;
90
+ return e.eachChild((o) => {
91
+ if (r !== void 0 || a(o.name).toLowerCase() !== t.toLowerCase())
89
92
  return;
90
- const n = r.val.trim();
91
- n.length > 0 && (o = n);
92
- }), o;
93
+ const n = o.val.trim();
94
+ n.length > 0 && (r = n);
95
+ }), r;
93
96
  }, g = (e, t) => {
94
- const o = [];
95
- return e.eachChild((r) => {
96
- if (a(r.name).toLowerCase() !== t.toLowerCase())
97
+ const r = [];
98
+ return e.eachChild((o) => {
99
+ if (a(o.name).toLowerCase() !== t.toLowerCase())
97
100
  return;
98
- const n = r.val.trim();
99
- n.length > 0 && o.push(n);
100
- }), o;
101
+ const n = o.val.trim();
102
+ n.length > 0 && r.push(n);
103
+ }), r;
101
104
  }, q = (e) => {
102
105
  let t;
103
- return e.eachChild((o) => {
104
- if (t !== void 0 || a(o.name).toLowerCase() !== "meta" || o.attr.name?.toLowerCase() !== "cover") return;
105
- const r = o.attr.content?.trim();
106
- r !== void 0 && r.length > 0 && (t = r);
106
+ return e.eachChild((r) => {
107
+ if (t !== void 0 || a(r.name).toLowerCase() !== "meta" || r.attr.name?.toLowerCase() !== "cover") return;
108
+ const o = r.attr.content?.trim();
109
+ o !== void 0 && o.length > 0 && (t = o);
107
110
  }), t;
108
111
  }, J = ({
109
112
  manifestItems: e,
110
113
  metadataEl: t
111
114
  }) => {
112
- const o = (n) => n.mediaType?.toLowerCase().includes("image/") === !0, r = e.find((n) => o(n) ? A(n.properties).includes(
115
+ const r = (n) => n.mediaType?.toLowerCase().includes("image/") === !0, o = e.find((n) => r(n) ? M(n.properties).includes(
113
116
  "cover-image"
114
117
  ) : !1);
115
- if (r !== void 0) return r.href;
118
+ if (o !== void 0) return o.href;
116
119
  if (t !== void 0) {
117
120
  const n = q(t);
118
121
  if (n !== void 0) {
119
122
  const i = e.find(
120
- (s) => s.id === n && o(s)
123
+ (s) => s.id === n && r(s)
121
124
  );
122
125
  if (i !== void 0) return i.href;
123
126
  }
124
127
  }
125
128
  return e.find(
126
- (n) => n.id.toLowerCase().includes("cover") && o(n)
129
+ (n) => n.id.toLowerCase().includes("cover") && r(n)
127
130
  )?.href;
128
131
  }, h = (e, t) => {
129
- const r = f(e, "meta").find(
132
+ const o = m(e, "meta").find(
130
133
  (n) => n.attr.property === t
131
134
  )?.val;
132
- if (!(r === void 0 || r.trim().length === 0))
133
- return r;
135
+ if (!(o === void 0 || o.trim().length === 0))
136
+ return o;
134
137
  }, Q = (e) => {
135
138
  const t = c(e, "guide");
136
139
  if (t === void 0) return [];
137
- const o = [];
138
- for (const r of f(t, "reference")) {
139
- const n = r.attr.href?.trim();
140
- n === void 0 || n.length === 0 || o.push({
140
+ const r = [];
141
+ for (const o of m(t, "reference")) {
142
+ const n = o.attr.href?.trim();
143
+ n === void 0 || n.length === 0 || r.push({
141
144
  href: n,
142
- title: r.attr.title?.trim() ?? "",
143
- type: r.attr.type?.trim() ?? ""
145
+ title: o.attr.title?.trim() ?? "",
146
+ type: o.attr.type?.trim() ?? ""
144
147
  });
145
148
  }
146
- return o;
149
+ return r;
147
150
  }, Z = (e) => {
148
- const t = e.attr.id, o = e.attr.href;
149
- if (t === void 0 || t.length === 0 || o === void 0 || o.length === 0) return;
150
- const r = e.attr["media-type"], n = e.attr.properties?.trim();
151
+ const t = e.attr.id, r = e.attr.href;
152
+ if (t === void 0 || t.length === 0 || r === void 0 || r.length === 0) return;
153
+ const o = e.attr["media-type"], n = e.attr.properties?.trim();
151
154
  return {
152
155
  id: t,
153
- href: o,
154
- ...r !== void 0 && r.length > 0 ? { mediaType: r } : {},
156
+ href: r,
157
+ ...o !== void 0 && o.length > 0 ? { mediaType: o } : {},
155
158
  ...n !== void 0 && n.length > 0 ? { properties: n } : {}
156
159
  };
157
160
  }, ee = (e) => {
158
- const t = [], o = /* @__PURE__ */ new Map();
159
- for (const r of f(e, "item")) {
160
- const n = Z(r);
161
- n !== void 0 && (t.push(n), o.set(n.id, n));
161
+ const t = [], r = /* @__PURE__ */ new Map();
162
+ for (const o of m(e, "item")) {
163
+ const n = Z(o);
164
+ n !== void 0 && (t.push(n), r.set(n.id, n));
162
165
  }
163
- return { items: t, byId: o };
166
+ return { items: t, byId: r };
164
167
  }, te = (e, t) => {
165
- const o = [];
166
- for (const r of f(t, "itemref")) {
167
- const n = r.attr.idref;
168
+ const r = [];
169
+ for (const o of m(t, "itemref")) {
170
+ const n = o.attr.idref;
168
171
  if (n === void 0 || n.trim().length === 0) continue;
169
172
  const i = e.get(n);
170
173
  if (i === void 0) continue;
171
- const s = $(r.attr.properties);
172
- o.push({
174
+ const s = $(o.attr.properties);
175
+ r.push({
173
176
  idref: n,
174
177
  id: i.id,
175
178
  href: i.href,
176
179
  ...i.mediaType !== void 0 ? { mediaType: i.mediaType } : {},
177
180
  ...i.properties !== void 0 ? { properties: i.properties } : {},
178
181
  ...s.renditionLayout !== void 0 ? { renditionLayout: s.renditionLayout } : {},
182
+ ...s.renditionFlow !== void 0 ? { renditionFlow: s.renditionFlow } : {},
179
183
  ...s.pageSpreadLeft !== void 0 ? { pageSpreadLeft: s.pageSpreadLeft } : {},
180
184
  ...s.pageSpreadRight !== void 0 ? { pageSpreadRight: s.pageSpreadRight } : {}
181
185
  });
182
186
  }
183
- return o;
187
+ return r;
184
188
  }, Le = (e) => {
185
- const t = new m(e), o = c(t, "manifest"), r = c(t, "spine"), n = c(t, "metadata");
189
+ const t = new f(e), r = c(t, "manifest"), o = c(t, "spine"), n = c(t, "metadata");
186
190
  let i = [], s = [];
187
- if (o !== void 0) {
188
- const { items: B, byId: X } = ee(o);
189
- i = B, r !== void 0 && (s = te(X, r));
191
+ if (r !== void 0) {
192
+ const { items: B, byId: X } = ee(r);
193
+ i = B, o !== void 0 && (s = te(X, o));
190
194
  }
191
- const p = r?.attr["page-progression-direction"], P = p !== void 0 && p.trim().length > 0 ? p : void 0, v = r?.attr.toc, x = v !== void 0 && v.trim().length > 0 ? v.trim() : void 0;
192
- let I, b, C, w, N = [], S = [], E = [], T, D, O;
193
- const M = [];
194
- n !== void 0 && (I = d(n, "title"), b = d(n, "publisher"), C = d(n, "rights"), w = d(n, "date"), N = g(n, "creator"), S = g(n, "language"), E = g(n, "subject"), T = h(n, "rendition:layout"), D = h(n, "rendition:flow"), O = h(n, "rendition:spread"), M.push(...W(n)));
195
+ const p = o?.attr["page-progression-direction"], P = p !== void 0 && p.trim().length > 0 ? p : void 0, v = o?.attr.toc, x = v !== void 0 && v.trim().length > 0 ? v.trim() : void 0;
196
+ let I, b, w, C, N = [], S = [], E = [], T, D, O;
197
+ const F = [];
198
+ n !== void 0 && (I = d(n, "title"), b = d(n, "publisher"), w = d(n, "rights"), C = d(n, "date"), N = g(n, "creator"), S = g(n, "language"), E = g(n, "subject"), T = h(n, "rendition:layout"), D = h(n, "rendition:flow"), O = h(n, "rendition:spread"), F.push(...W(n)));
195
199
  const R = J({
196
200
  manifestItems: i,
197
201
  metadataEl: n
@@ -201,14 +205,14 @@ const fe = "com.apple.ibooks.display-options.xml", Y = (e) => e.childrenNamed("o
201
205
  manifestItems: i,
202
206
  spineRows: s,
203
207
  spineTocIdref: x,
204
- identifiers: M,
208
+ identifiers: F,
205
209
  title: I,
206
210
  creators: N,
207
211
  publisher: b,
208
- rights: C,
212
+ rights: w,
209
213
  languages: S,
210
214
  subjects: E,
211
- date: w,
215
+ date: C,
212
216
  coverHref: R,
213
217
  renditionLayoutMeta: T,
214
218
  renditionFlowMeta: D,
@@ -221,7 +225,7 @@ const fe = "com.apple.ibooks.display-options.xml", Y = (e) => e.childrenNamed("o
221
225
  isbn: void 0,
222
226
  readingDirection: void 0,
223
227
  renditionLayout: e.displayOptions?.platform?.options?.find(
224
- (r) => r.name === "fixed-layout"
228
+ (o) => o.name === "fixed-layout"
225
229
  )?.value?.trim().toLowerCase() === "true" ? "pre-paginated" : void 0,
226
230
  title: void 0,
227
231
  authors: void 0,
@@ -230,18 +234,18 @@ const fe = "com.apple.ibooks.display-options.xml", Y = (e) => e.childrenNamed("o
230
234
  languages: void 0,
231
235
  date: void 0,
232
236
  subjects: void 0
233
- }), re = /* @__PURE__ */ new Set([8, 12, 13, 14]), F = (e) => {
237
+ }), re = /* @__PURE__ */ new Set([8, 12, 13, 14]), _ = (e) => {
234
238
  if (e == null) return;
235
239
  const t = String(e).replace(/\D/g, "");
236
240
  if (!(t.length === 0 || !re.has(t.length)))
237
241
  return t;
238
242
  }, ne = /(?:97[89])?\d{9}[\dXx]/, u = (e) => {
239
243
  if (e == null) return;
240
- const t = String(e).trim().replace(/^urn:isbn:/i, "").replace(/^isbn[:\s-]*/i, ""), o = t.replace(/[^0-9Xx]/g, "");
241
- if (o.length === 10 || o.length === 13)
242
- return o.toUpperCase();
243
- const r = t.match(ne);
244
- if (r) return r[0].toUpperCase();
244
+ const t = String(e).trim().replace(/^urn:isbn:/i, "").replace(/^isbn[:\s-]*/i, ""), r = t.replace(/[^0-9Xx]/g, "");
245
+ if (r.length === 10 || r.length === 13)
246
+ return r.toUpperCase();
247
+ const o = t.match(ne);
248
+ if (o) return o[0].toUpperCase();
245
249
  }, ie = (e) => e.Manga === "YesAndRightToLeft" ? "rtl" : "ltr", l = (e) => {
246
250
  if (e === void 0) return;
247
251
  const t = e.trim();
@@ -249,28 +253,28 @@ const fe = "com.apple.ibooks.display-options.xml", Y = (e) => e.childrenNamed("o
249
253
  }, y = (e) => e === void 0 ? [] : e.split(",").map((t) => t.trim()).filter((t) => t.length > 0), L = (e) => {
250
254
  const t = l(e);
251
255
  if (t === void 0 || !/^\d+$/.test(t)) return;
252
- const o = Number.parseInt(t, 10);
253
- return Number.isFinite(o) ? o : void 0;
256
+ const r = Number.parseInt(t, 10);
257
+ return Number.isFinite(r) ? r : void 0;
254
258
  }, se = (e) => {
255
- const t = L(e.Year), o = L(e.Month), r = L(e.Day);
256
- if (!(t === void 0 && o === void 0 && r === void 0))
259
+ const t = L(e.Year), r = L(e.Month), o = L(e.Day);
260
+ if (!(t === void 0 && r === void 0 && o === void 0))
257
261
  return {
258
262
  ...t !== void 0 ? { year: t } : {},
259
- ...o !== void 0 ? { month: o } : {},
260
- ...r !== void 0 ? { day: r } : {}
263
+ ...r !== void 0 ? { month: r } : {},
264
+ ...o !== void 0 ? { day: o } : {}
261
265
  };
262
266
  }, ae = (e) => {
263
- const t = e.GTIN, o = l(e.LanguageISO), r = y(e.Writer), n = [...y(e.Genre), ...y(e.Tags)];
267
+ const t = e.GTIN, r = l(e.LanguageISO), o = y(e.Writer), n = [...y(e.Genre), ...y(e.Tags)];
264
268
  return {
265
- gtin: F(t),
269
+ gtin: _(t),
266
270
  isbn: u(t),
267
271
  readingDirection: ie(e),
268
272
  renditionLayout: void 0,
269
273
  title: l(e.Title),
270
- authors: r.length > 0 ? r : void 0,
274
+ authors: o.length > 0 ? o : void 0,
271
275
  publisher: l(e.Publisher),
272
276
  rights: void 0,
273
- languages: o !== void 0 ? [o] : void 0,
277
+ languages: r !== void 0 ? [r] : void 0,
274
278
  date: se(e),
275
279
  subjects: n.length > 0 ? n : void 0
276
280
  };
@@ -290,10 +294,10 @@ const fe = "com.apple.ibooks.display-options.xml", Y = (e) => e.childrenNamed("o
290
294
  if (e === void 0) return;
291
295
  const t = e.trim().match(/^(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?/);
292
296
  if (t === null) return;
293
- const [, o, r, n] = t;
297
+ const [, r, o, n] = t;
294
298
  return {
295
- ...o !== void 0 ? { year: Number.parseInt(o, 10) } : {},
296
- ...r !== void 0 ? { month: Number.parseInt(r, 10) } : {},
299
+ ...r !== void 0 ? { year: Number.parseInt(r, 10) } : {},
300
+ ...o !== void 0 ? { month: Number.parseInt(o, 10) } : {},
297
301
  ...n !== void 0 ? { day: Number.parseInt(n, 10) } : {}
298
302
  };
299
303
  }, le = (e) => {
@@ -303,11 +307,11 @@ const fe = "com.apple.ibooks.display-options.xml", Y = (e) => e.childrenNamed("o
303
307
  for (const t of e)
304
308
  if (u(t.value) !== void 0) return t.value;
305
309
  }, ue = (e) => {
306
- const t = e.pageProgressionDirection?.trim().toLowerCase(), o = t === "ltr" || t === "rtl" ? t : void 0, r = e.renditionLayoutMeta?.trim().toLowerCase(), n = r === "reflowable" || r === "pre-paginated" ? r : void 0, i = le(e.identifiers);
310
+ const t = e.pageProgressionDirection?.trim().toLowerCase(), r = t === "ltr" || t === "rtl" ? t : void 0, o = e.renditionLayoutMeta?.trim().toLowerCase(), n = o === "reflowable" || o === "pre-paginated" ? o : void 0, i = le(e.identifiers);
307
311
  return {
308
- gtin: F(i),
312
+ gtin: _(i),
309
313
  isbn: u(i),
310
- readingDirection: o,
314
+ readingDirection: r,
311
315
  renditionLayout: n,
312
316
  title: e.title,
313
317
  authors: e.creators.length > 0 ? [...e.creators] : void 0,
@@ -319,12 +323,12 @@ const fe = "com.apple.ibooks.display-options.xml", Y = (e) => e.childrenNamed("o
319
323
  };
320
324
  }, Ie = (e) => e.kind === "comicInfo" ? ae(e) : e.kind === "kobo" ? de(e) : e.kind === "apple" ? oe(e) : ue(e);
321
325
  export {
322
- fe as APPLE_IBOOKS_DISPLAY_OPTIONS_FILENAME,
326
+ me as APPLE_IBOOKS_DISPLAY_OPTIONS_FILENAME,
323
327
  K as COMIC_INFO_FILENAME,
324
328
  G as COMIC_INFO_MANGA_VALUES,
325
329
  he as KOBO_DISPLAY_OPTIONS_FILENAME,
326
330
  ve as isComicInfoManga,
327
- F as normalizeGtin,
331
+ _ as normalizeGtin,
328
332
  u as normalizeIsbn,
329
333
  pe as parseAppleDisplayOptionsXml,
330
334
  ge as parseComicInfo,
@@ -332,6 +336,6 @@ export {
332
336
  Le as parseOpf,
333
337
  ce as parseW3cDtfDate,
334
338
  Ie as resolveArchiveMetadata,
335
- A as tokenizeXmlSpaceSeparatedList
339
+ M as tokenizeXmlSpaceSeparatedList
336
340
  };
337
341
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/apple/parse.ts","../src/comicInfo/manga.ts","../src/comicInfo/parse.ts","../src/kobo/parse.ts","../src/utils/tokenizeXmlSpaceSeparatedList.ts","../src/opf/spineItemrefProperties.ts","../src/opf/parse.ts","../src/apple/resolve.ts","../src/utils/normalizeGtin.ts","../src/utils/normalizeIsbn.ts","../src/comicInfo/resolve.ts","../src/kobo/resolve.ts","../src/utils/parseW3cDtfDate.ts","../src/opf/resolve.ts","../src/resolve.ts"],"sourcesContent":["import type { XmlElement } from \"xmldoc\"\nimport { XmlDocument } from \"xmldoc\"\n\nexport const APPLE_IBOOKS_DISPLAY_OPTIONS_FILENAME =\n \"com.apple.ibooks.display-options.xml\"\n\nexport type AppleDisplayOption = {\n readonly name?: string\n readonly value: string\n}\n\nexport type AppleMetadata = {\n readonly kind: \"apple\"\n readonly displayOptions?: {\n readonly platform?: {\n readonly options: ReadonlyArray<AppleDisplayOption>\n }\n }\n}\n\nconst platformOptionsFromElement = (\n platform: XmlElement,\n): ReadonlyArray<AppleDisplayOption> =>\n platform.childrenNamed(\"option\").map((option) => ({\n name: option.attr?.name,\n value: option.val,\n }))\n\nexport const parseAppleDisplayOptionsXml = (xml: string): AppleMetadata => {\n const doc = new XmlDocument(xml)\n const root = doc.name?.toLowerCase()\n\n if (root !== \"display_options\") {\n return { kind: \"apple\" }\n }\n\n const platformEl = doc.childNamed(\"platform\")\n\n return {\n kind: \"apple\",\n displayOptions: {\n ...(platformEl !== undefined\n ? { platform: { options: platformOptionsFromElement(platformEl) } }\n : {}),\n },\n }\n}\n","/**\n * Allowed `Manga` element values (ComicInfo 2.0 `Manga` simpleType).\n *\n * @see https://anansi-project.github.io/docs/comicinfo/documentation#manga\n */\nexport const COMIC_INFO_MANGA_VALUES = [\n \"Unknown\",\n \"No\",\n \"Yes\",\n \"YesAndRightToLeft\",\n] as const\n\nexport type ComicInfoManga = (typeof COMIC_INFO_MANGA_VALUES)[number]\n\nexport const isComicInfoManga = (value: string): value is ComicInfoManga => {\n for (const v of COMIC_INFO_MANGA_VALUES) {\n if (v === value) return true\n }\n return false\n}\n","import type { XmlElement } from \"xmldoc\"\nimport { XmlDocument } from \"xmldoc\"\nimport type { ComicInfoManga } from \"./manga\"\n\n/** Canonical top-level filename; real archives may use any casing. */\nexport const COMIC_INFO_FILENAME = \"ComicInfo.xml\"\n\n/**\n * Parsed `ComicInfo.xml` root: one optional string property per child element,\n * using the same names as in the file (e.g. `Title`, `GTIN`, `LanguageISO`).\n * Nested blocks such as `Pages` are skipped. Other simple child elements are\n * still copied onto this object under their tag name.\n *\n * @see https://anansi-project.github.io/docs/comicinfo/intro\n * @see https://github.com/anansi-project/comicinfo/blob/main/drafts/v2.1/ComicInfo.xsd for schema\n */\nexport interface ComicInfo {\n readonly kind: \"comicInfo\"\n AgeRating?: string\n AlternateCount?: string\n AlternateNumber?: string\n AlternateSeries?: string\n BlackAndWhite?: string\n Characters?: string\n Colorist?: string\n CommunityRating?: string\n Count?: string\n CoverArtist?: string\n Day?: string\n Editor?: string\n Format?: string\n Genre?: string\n GTIN?: string\n Imprint?: string\n Inker?: string\n LanguageISO?: string\n Letterer?: string\n Locations?: string\n MainCharacterOrTeam?: string\n /** Schema literals per {@link ComicInfoManga}; files may still use other strings. */\n Manga?: ComicInfoManga | (string & {})\n Month?: string\n Notes?: string\n Number?: string\n PageCount?: string\n Penciller?: string\n Publisher?: string\n Review?: string\n ScanInformation?: string\n Series?: string\n SeriesGroup?: string\n StoryArc?: string\n StoryArcNumber?: string\n Summary?: string\n Tags?: string\n Teams?: string\n Title?: string\n Translator?: string\n Volume?: string\n Web?: string\n Writer?: string\n Year?: string\n [tag: string]: string | undefined\n}\n\nconst SKIP_ELEMENT_CHILDREN = new Set([\"Pages\", \"ComicInfo\"])\n\nconst hasNestedElement = (el: XmlElement) =>\n el.children.some((c) => c.type === \"element\")\n\nconst trimmedText = (el: XmlElement): string | undefined => {\n const t = el.val.trim()\n return t.length > 0 ? t : undefined\n}\n\n/**\n * Parse a raw `ComicInfo.xml` body. Each direct child element with plain text\n * becomes a property named after that tag. Malformed XML throws; the parser\n * error is attached as `cause`.\n */\nexport const parseComicInfo = (xml: string): ComicInfo => {\n let doc: XmlDocument\n try {\n doc = new XmlDocument(xml)\n } catch (cause) {\n const message = cause instanceof Error ? cause.message : String(cause)\n throw new Error(`${COMIC_INFO_FILENAME} is malformed: ${message}`, {\n cause,\n })\n }\n\n const fields: Record<string, string> = {}\n\n doc.eachChild((child) => {\n if (child.type !== \"element\") return\n if (SKIP_ELEMENT_CHILDREN.has(child.name)) return\n if (hasNestedElement(child)) return\n\n const text = trimmedText(child)\n if (text === undefined) return\n if (fields[child.name] !== undefined) return\n\n fields[child.name] = text\n })\n\n // `as ComicInfo`: TS cannot infer that spreading `Record<string, string>` into `{ kind }` satisfies the named optional fields plus `[tag: string]: string | undefined` on `ComicInfo`.\n return { kind: \"comicInfo\", ...fields } as ComicInfo\n}\n","import { XmlDocument } from \"xmldoc\"\n\nexport const KOBO_DISPLAY_OPTIONS_FILENAME = \"com.kobobooks.display-options.xml\"\n\n/**\n * Normalized fields from Kobo-specific XML sidecars. Grows as new Kobo\n * document kinds are supported; absent keys mean not present or unknown.\n */\nexport type KoboMetadata = {\n readonly kind: \"kobo\"\n renditionLayout?: `reflowable` | `pre-paginated`\n}\n\nconst parseKoboDisplayOptionsDocument = (\n doc: XmlDocument,\n): { renditionLayout?: \"pre-paginated\" } => {\n const platform = doc.childNamed(\"platform\")\n if (!platform) return {}\n\n for (const option of platform.childrenNamed(\"option\")) {\n if (option.attr?.name !== \"fixed-layout\") continue\n if (option.val.trim().toLowerCase() === \"true\") {\n return { renditionLayout: \"pre-paginated\" }\n }\n return {}\n }\n\n return {}\n}\n\n/**\n * Parse a Kobo-related XML document. Unsupported or unrecognized roots\n * yield an empty object. Malformed XML throws from the underlying parser.\n */\nexport const parseKoboXml = (xml: string): KoboMetadata => {\n const doc = new XmlDocument(xml)\n const root = doc.name?.toLowerCase()\n\n if (root === \"display_options\") {\n return { kind: \"kobo\", ...parseKoboDisplayOptionsDocument(doc) }\n }\n\n return { kind: \"kobo\" }\n}\n","/**\n * EPUB/XML space-separated token lists (e.g. `properties` on `item` / `itemref`).\n * Trims the raw string, splits on ASCII whitespace, drops empty segments.\n */\nexport const tokenizeXmlSpaceSeparatedList = (\n raw: string | undefined,\n): readonly string[] => {\n if (raw === undefined || raw.trim().length === 0) {\n return []\n }\n\n return raw\n .trim()\n .split(/\\s+/)\n .filter((t) => t.length > 0)\n}\n","import { tokenizeXmlSpaceSeparatedList } from \"../utils/tokenizeXmlSpaceSeparatedList\"\n\nexport type OpfItemrefLayoutHints = {\n readonly renditionLayout?: `reflowable` | `pre-paginated`\n readonly pageSpreadLeft?: true\n readonly pageSpreadRight?: true\n}\n\n/**\n * EPUB `itemref` `properties` attribute (space-separated tokens).\n *\n * @see https://www.w3.org/TR/epub/#attrdef-properties\n */\nexport const layoutHintsFromItemrefProperties = (\n properties: string | undefined,\n): OpfItemrefLayoutHints => {\n const tokens = tokenizeXmlSpaceSeparatedList(properties)\n if (tokens.length === 0) {\n return {}\n }\n\n let renditionLayout: `reflowable` | `pre-paginated` | undefined\n if (tokens.includes(`rendition:layout-reflowable`)) {\n renditionLayout = `reflowable`\n }\n if (tokens.includes(`rendition:layout-pre-paginated`)) {\n renditionLayout = `pre-paginated`\n }\n\n return {\n ...(renditionLayout !== undefined ? { renditionLayout } : {}),\n ...(tokens.includes(`page-spread-left`)\n ? { pageSpreadLeft: true as const }\n : {}),\n ...(tokens.includes(`page-spread-right`)\n ? { pageSpreadRight: true as const }\n : {}),\n }\n}\n","import type { XmlElement, XmlNodeBase } from \"xmldoc\"\nimport { XmlDocument } from \"xmldoc\"\nimport { tokenizeXmlSpaceSeparatedList } from \"../utils/tokenizeXmlSpaceSeparatedList\"\nimport { layoutHintsFromItemrefProperties } from \"./spineItemrefProperties\"\n\nexport type OpfSpineManifestItem = {\n readonly id: string\n readonly href: string\n readonly mediaType?: string\n readonly properties?: string\n}\n\nexport type OpfIdentifier = {\n readonly value: string\n readonly scheme?: string\n}\n\nexport type OpfSpineRow = {\n readonly idref: string\n readonly id: string\n readonly href: string\n readonly mediaType?: string\n readonly properties?: string\n readonly renditionLayout?: `reflowable` | `pre-paginated`\n readonly pageSpreadLeft?: true\n readonly pageSpreadRight?: true\n}\n\nexport type OpfGuideReference = {\n readonly href: string\n readonly title: string\n readonly type: string\n}\n\nconst elementLocalName = (name: string): string =>\n name.includes(\":\") ? name.slice(name.lastIndexOf(\":\") + 1) : name\n\nconst localNameEq = (elementName: string, wantLocal: string): boolean =>\n elementLocalName(elementName).toLowerCase() === wantLocal.toLowerCase()\n\nconst isXmlElement = (node: XmlNodeBase): node is XmlElement =>\n node.type === \"element\"\n\nconst childNamedLocal = (\n parent: XmlElement,\n localName: string,\n): XmlElement | undefined => {\n for (const node of parent.children) {\n if (!isXmlElement(node)) continue\n if (localNameEq(node.name, localName)) return node\n }\n return undefined\n}\n\nconst childrenNamedLocal = (\n parent: XmlElement,\n localName: string,\n): XmlElement[] => {\n const out: XmlElement[] = []\n for (const node of parent.children) {\n if (!isXmlElement(node)) continue\n if (localNameEq(node.name, localName)) out.push(node)\n }\n return out\n}\n\nconst identifiersFromMetadata = (metadataEl: XmlElement): OpfIdentifier[] => {\n const identifiers: OpfIdentifier[] = []\n\n metadataEl.eachChild((child) => {\n if (elementLocalName(child.name).toLowerCase() !== \"identifier\") return\n\n const value = child.val.trim()\n if (value.length === 0) return\n\n const scheme =\n child.attr[\"opf:scheme\"] ?? child.attr[\"opf:Scheme\"] ?? child.attr.scheme\n const schemeTrimmed = scheme?.trim()\n\n identifiers.push({\n value,\n ...(schemeTrimmed !== undefined && schemeTrimmed.length > 0\n ? { scheme: schemeTrimmed }\n : {}),\n })\n })\n\n return identifiers\n}\n\nconst firstTextByLocalName = (\n metadataEl: XmlElement,\n localName: string,\n): string | undefined => {\n let found: string | undefined\n metadataEl.eachChild((child) => {\n if (found !== undefined) return\n if (elementLocalName(child.name).toLowerCase() !== localName.toLowerCase())\n return\n const t = child.val.trim()\n if (t.length > 0) found = t\n })\n return found\n}\n\nconst textsByLocalName = (\n metadataEl: XmlElement,\n localName: string,\n): string[] => {\n const out: string[] = []\n metadataEl.eachChild((child) => {\n if (elementLocalName(child.name).toLowerCase() !== localName.toLowerCase())\n return\n const t = child.val.trim()\n if (t.length > 0) out.push(t)\n })\n return out\n}\n\nconst coverContentIdFromMetadata = (\n metadataEl: XmlElement,\n): string | undefined => {\n let coverId: string | undefined\n metadataEl.eachChild((child) => {\n if (coverId !== undefined) return\n if (elementLocalName(child.name).toLowerCase() !== \"meta\") return\n if (child.attr.name?.toLowerCase() !== \"cover\") return\n const content = child.attr.content?.trim()\n if (content !== undefined && content.length > 0) coverId = content\n })\n return coverId\n}\n\n/**\n * EPUB cover image inside the manifest. Resolution order, matching\n * what the spec lays out and what the bulk of EPUB producers in the\n * wild rely on:\n *\n * 1. EPUB 3 — the manifest item carrying the `cover-image` token in\n * its `properties` attribute (§ D.6.1).\n * 2. EPUB 2 — `<meta name=\"cover\" content=\"ID\"/>` in `metadata`,\n * resolved to the manifest item with that `id`.\n * 3. Last-resort fallback — any image manifest item whose `id`\n * contains the substring `cover` (case-insensitive); covers the\n * long tail of producers that emit neither the EPUB 3 property\n * nor the EPUB 2 meta.\n *\n * Each step requires the candidate manifest item to advertise an\n * `image/*` media type so non-image artefacts named `cover` (XHTML\n * cover pages, NCX entries) don't slip through.\n */\nconst coverHrefFromManifestAndMetadata = ({\n manifestItems,\n metadataEl,\n}: {\n manifestItems: ReadonlyArray<OpfSpineManifestItem>\n metadataEl: XmlElement | undefined\n}): string | undefined => {\n const isImage = (item: OpfSpineManifestItem): boolean =>\n item.mediaType?.toLowerCase().includes(\"image/\") === true\n\n const byCoverImageProperty = manifestItems.find((item) => {\n if (!isImage(item)) return false\n return tokenizeXmlSpaceSeparatedList(item.properties).includes(\n \"cover-image\",\n )\n })\n if (byCoverImageProperty !== undefined) return byCoverImageProperty.href\n\n if (metadataEl !== undefined) {\n const coverContentId = coverContentIdFromMetadata(metadataEl)\n if (coverContentId !== undefined) {\n const match = manifestItems.find(\n (item) => item.id === coverContentId && isImage(item),\n )\n if (match !== undefined) return match.href\n }\n }\n\n return manifestItems.find(\n (item) => item.id.toLowerCase().includes(\"cover\") && isImage(item),\n )?.href\n}\n\nconst metaValByProperty = (\n metadataEl: XmlElement,\n property: string,\n): string | undefined => {\n const meta = childrenNamedLocal(metadataEl, \"meta\").find(\n (m) => m.attr.property === property,\n )\n const raw = meta?.val\n if (raw === undefined || raw.trim().length === 0) return undefined\n return raw\n}\n\nconst guideFromPackage = (doc: XmlElement): OpfGuideReference[] => {\n const guideEl = childNamedLocal(doc, \"guide\")\n if (guideEl === undefined) return []\n\n const refs: OpfGuideReference[] = []\n\n for (const ref of childrenNamedLocal(guideEl, \"reference\")) {\n const href = ref.attr.href?.trim()\n if (href === undefined || href.length === 0) continue\n refs.push({\n href,\n title: ref.attr.title?.trim() ?? ``,\n type: ref.attr.type?.trim() ?? ``,\n })\n }\n\n return refs\n}\n\nconst manifestItemFromXmlElement = (\n item: XmlElement,\n): OpfSpineManifestItem | undefined => {\n const id = item.attr.id\n const href = item.attr.href\n if (id === undefined || id.length === 0) return undefined\n if (href === undefined || href.length === 0) return undefined\n\n const mediaType = item.attr[\"media-type\"]\n const properties = item.attr.properties?.trim()\n return {\n id,\n href,\n ...(mediaType !== undefined && mediaType.length > 0 ? { mediaType } : {}),\n ...(properties !== undefined && properties.length > 0\n ? { properties }\n : {}),\n }\n}\n\nconst manifestItemsAndById = (\n manifestEl: XmlElement,\n): {\n items: OpfSpineManifestItem[]\n byId: Map<string, OpfSpineManifestItem>\n} => {\n const items: OpfSpineManifestItem[] = []\n const byId = new Map<string, OpfSpineManifestItem>()\n\n for (const el of childrenNamedLocal(manifestEl, \"item\")) {\n const parsed = manifestItemFromXmlElement(el)\n if (parsed === undefined) continue\n items.push(parsed)\n byId.set(parsed.id, parsed)\n }\n\n return { items, byId }\n}\n\nconst spineRowsFromByIdAndSpine = (\n byId: Map<string, OpfSpineManifestItem>,\n spineEl: XmlElement,\n): OpfSpineRow[] => {\n const rows: OpfSpineRow[] = []\n\n for (const itemref of childrenNamedLocal(spineEl, \"itemref\")) {\n const idref = itemref.attr.idref\n if (idref === undefined || idref.trim().length === 0) continue\n\n const manifestItem = byId.get(idref)\n if (manifestItem === undefined) continue\n\n const hints = layoutHintsFromItemrefProperties(itemref.attr.properties)\n\n rows.push({\n idref,\n id: manifestItem.id,\n href: manifestItem.href,\n ...(manifestItem.mediaType !== undefined\n ? { mediaType: manifestItem.mediaType }\n : {}),\n ...(manifestItem.properties !== undefined\n ? { properties: manifestItem.properties }\n : {}),\n ...(hints.renditionLayout !== undefined\n ? { renditionLayout: hints.renditionLayout }\n : {}),\n ...(hints.pageSpreadLeft !== undefined\n ? { pageSpreadLeft: hints.pageSpreadLeft }\n : {}),\n ...(hints.pageSpreadRight !== undefined\n ? { pageSpreadRight: hints.pageSpreadRight }\n : {}),\n })\n }\n\n return rows\n}\n\nexport type OpfMetadata = {\n readonly kind: \"opf\"\n readonly manifestItems: ReadonlyArray<OpfSpineManifestItem>\n readonly spineRows: ReadonlyArray<OpfSpineRow>\n readonly spineTocIdref: string | undefined\n readonly identifiers: ReadonlyArray<OpfIdentifier>\n readonly title: string | undefined\n /** `dc:creator` values, in document order, trimmed; empty when none. */\n readonly creators: ReadonlyArray<string>\n /** First non-empty `dc:publisher`, trimmed. */\n readonly publisher: string | undefined\n /** First non-empty `dc:rights`, trimmed. */\n readonly rights: string | undefined\n /** `dc:language` values, in document order, trimmed; empty when none. */\n readonly languages: ReadonlyArray<string>\n /** `dc:subject` values, in document order, trimmed; empty when none. */\n readonly subjects: ReadonlyArray<string>\n /**\n * Raw `dc:date` value as authored. EPUB 3 requires W3CDTF (a profile\n * of ISO 8601), but real-world publishers also ship free text here,\n * so the value is exposed verbatim and consumers normalize as needed.\n */\n readonly date: string | undefined\n /**\n * Manifest-relative `href` of the cover image, when one can be\n * resolved from `cover-image` properties (EPUB 3), the EPUB 2\n * `<meta name=\"cover\">` convention, or an `id` that contains\n * `cover` on an image manifest item. The href is returned exactly\n * as it appears in the manifest — callers own folder-prefix\n * resolution against the OPF's location in the archive.\n */\n readonly coverHref: string | undefined\n readonly renditionLayoutMeta: string | undefined\n readonly renditionFlowMeta: string | undefined\n readonly renditionSpreadMeta: string | undefined\n readonly pageProgressionDirection: string | undefined\n readonly guide: ReadonlyArray<OpfGuideReference>\n}\n\n/**\n * Parses an EPUB package document (OPF) into structured metadata.\n *\n * Direct children of `package` (`metadata`, `manifest`, `spine`, `guide`) and\n * their structural children (`item`, `itemref`, `reference`, `meta`) are\n * matched by **local name** (ASCII case-insensitive), so prefixed tags such as\n * `opf:manifest` are supported the same as unprefixed `manifest`.\n *\n * Attribute names on `spine` / `itemref` are still read as emitted by xmldoc\n * (no QName normalization).\n */\nexport const parseOpf = (opfXml: string): OpfMetadata => {\n const doc = new XmlDocument(opfXml)\n const manifestEl = childNamedLocal(doc, \"manifest\")\n const spineEl = childNamedLocal(doc, \"spine\")\n const metadataEl = childNamedLocal(doc, \"metadata\")\n\n let manifestItems: OpfSpineManifestItem[] = []\n let spineRows: OpfSpineRow[] = []\n\n if (manifestEl !== undefined) {\n const { items, byId } = manifestItemsAndById(manifestEl)\n manifestItems = items\n if (spineEl !== undefined) {\n spineRows = spineRowsFromByIdAndSpine(byId, spineEl)\n }\n }\n\n const pageProgressionDirectionRaw =\n spineEl?.attr[\"page-progression-direction\"]\n const pageProgressionDirection =\n pageProgressionDirectionRaw !== undefined &&\n pageProgressionDirectionRaw.trim().length > 0\n ? pageProgressionDirectionRaw\n : undefined\n\n const spineTocRaw = spineEl?.attr.toc\n const spineTocIdref =\n spineTocRaw !== undefined && spineTocRaw.trim().length > 0\n ? spineTocRaw.trim()\n : undefined\n\n let title: string | undefined\n let publisher: string | undefined\n let rights: string | undefined\n let date: string | undefined\n let creators: string[] = []\n let languages: string[] = []\n let subjects: string[] = []\n let renditionLayoutMeta: string | undefined\n let renditionFlowMeta: string | undefined\n let renditionSpreadMeta: string | undefined\n const identifiers: OpfIdentifier[] = []\n\n if (metadataEl !== undefined) {\n title = firstTextByLocalName(metadataEl, \"title\")\n publisher = firstTextByLocalName(metadataEl, \"publisher\")\n rights = firstTextByLocalName(metadataEl, \"rights\")\n date = firstTextByLocalName(metadataEl, \"date\")\n creators = textsByLocalName(metadataEl, \"creator\")\n languages = textsByLocalName(metadataEl, \"language\")\n subjects = textsByLocalName(metadataEl, \"subject\")\n renditionLayoutMeta = metaValByProperty(metadataEl, \"rendition:layout\")\n renditionFlowMeta = metaValByProperty(metadataEl, \"rendition:flow\")\n renditionSpreadMeta = metaValByProperty(metadataEl, \"rendition:spread\")\n identifiers.push(...identifiersFromMetadata(metadataEl))\n }\n\n const coverHref = coverHrefFromManifestAndMetadata({\n manifestItems,\n metadataEl,\n })\n\n const guide = guideFromPackage(doc)\n\n return {\n kind: \"opf\",\n manifestItems,\n spineRows,\n spineTocIdref,\n identifiers,\n title,\n creators,\n publisher,\n rights,\n languages,\n subjects,\n date,\n coverHref,\n renditionLayoutMeta,\n renditionFlowMeta,\n renditionSpreadMeta,\n pageProgressionDirection,\n guide,\n }\n}\n","import type { ArchiveResolveResult } from \"../types/archiveResolve\"\nimport type { AppleMetadata } from \"./parse\"\n\nexport const resolveApple = (input: AppleMetadata): ArchiveResolveResult => {\n const fixedLayout = input.displayOptions?.platform?.options?.find(\n (o) => o.name === \"fixed-layout\",\n )?.value\n\n const renditionLayout =\n fixedLayout?.trim().toLowerCase() === \"true\" ? \"pre-paginated\" : undefined\n\n return {\n gtin: undefined,\n isbn: undefined,\n readingDirection: undefined,\n renditionLayout,\n title: undefined,\n authors: undefined,\n publisher: undefined,\n rights: undefined,\n languages: undefined,\n date: undefined,\n subjects: undefined,\n }\n}\n","const GTIN_LENGTHS = new Set([8, 12, 13, 14])\n\n/**\n * Normalize a raw GTIN / EAN / UPC string to digits only when the length\n * matches a GS1 GTIN family size (8, 12, 13, or 14). Does not verify check digits.\n */\nexport const normalizeGtin = (\n raw: string | number | undefined | null,\n): string | undefined => {\n if (raw === undefined || raw === null) return undefined\n\n const digits = String(raw).replace(/\\D/g, \"\")\n if (digits.length === 0 || !GTIN_LENGTHS.has(digits.length)) return undefined\n\n return digits\n}\n","const ISBN_CANDIDATE_PATTERN = /(?:97[89])?\\d{9}[\\dXx]/\n\n/**\n * Normalize a raw ISBN-ish string into a canonical 10- or 13-character\n * form, or `undefined` when no recognisable ISBN can be recovered.\n *\n * - Strips the common `urn:isbn:` / `isbn:` prefixes.\n * - Drops everything that isn't a digit or `X`.\n * - Validates the resulting length (10 or 13).\n * - Falls back to a lax regex scan so publishers that stuff free text\n * around the number still yield a usable value.\n */\nexport const normalizeIsbn = (\n raw: string | number | undefined | null,\n): string | undefined => {\n if (raw === undefined || raw === null) return undefined\n\n const stripped = String(raw)\n .trim()\n .replace(/^urn:isbn:/i, \"\")\n .replace(/^isbn[:\\s-]*/i, \"\")\n\n const digitsOnly = stripped.replace(/[^0-9Xx]/g, \"\")\n\n if (digitsOnly.length === 10 || digitsOnly.length === 13) {\n return digitsOnly.toUpperCase()\n }\n\n const match = stripped.match(ISBN_CANDIDATE_PATTERN)\n if (match) return match[0].toUpperCase()\n\n return undefined\n}\n","import type { ArchiveResolveResult } from \"../types/archiveResolve\"\nimport { normalizeGtin } from \"../utils/normalizeGtin\"\nimport { normalizeIsbn } from \"../utils/normalizeIsbn\"\nimport type { ComicInfo } from \"./parse\"\n\nconst readingDirection = (info: ComicInfo): \"ltr\" | \"rtl\" =>\n info.Manga === \"YesAndRightToLeft\" ? \"rtl\" : \"ltr\"\n\nconst trimToUndefined = (raw: string | undefined): string | undefined => {\n if (raw === undefined) return undefined\n const trimmed = raw.trim()\n return trimmed.length > 0 ? trimmed : undefined\n}\n\n/**\n * Split a comma-separated ComicInfo value into its individual tokens.\n * `Writer`, `Genre`, and `Tags` all follow the same de-facto convention:\n * tokens separated by `,`, with whitespace trimmed and empty tokens\n * dropped (real-world files leave trailing commas around).\n */\nconst splitCommaList = (raw: string | undefined): string[] => {\n if (raw === undefined) return []\n return raw\n .split(\",\")\n .map((token) => token.trim())\n .filter((token) => token.length > 0)\n}\n\nconst parseNonNegativeInt = (raw: string | undefined): number | undefined => {\n const trimmed = trimToUndefined(raw)\n if (trimmed === undefined) return undefined\n if (!/^\\d+$/.test(trimmed)) return undefined\n const n = Number.parseInt(trimmed, 10)\n return Number.isFinite(n) ? n : undefined\n}\n\nconst dateFromYearMonthDay = (\n info: ComicInfo,\n):\n | {\n year?: number\n month?: number\n day?: number\n }\n | undefined => {\n const year = parseNonNegativeInt(info.Year)\n const month = parseNonNegativeInt(info.Month)\n const day = parseNonNegativeInt(info.Day)\n\n if (year === undefined && month === undefined && day === undefined) {\n return undefined\n }\n\n return {\n ...(year !== undefined ? { year } : {}),\n ...(month !== undefined ? { month } : {}),\n ...(day !== undefined ? { day } : {}),\n }\n}\n\nexport const resolveComicInfo = (info: ComicInfo): ArchiveResolveResult => {\n const raw = info.GTIN\n const languageIso = trimToUndefined(info.LanguageISO)\n const authors = splitCommaList(info.Writer)\n const subjects = [...splitCommaList(info.Genre), ...splitCommaList(info.Tags)]\n\n return {\n gtin: normalizeGtin(raw),\n isbn: normalizeIsbn(raw),\n readingDirection: readingDirection(info),\n renditionLayout: undefined,\n title: trimToUndefined(info.Title),\n authors: authors.length > 0 ? authors : undefined,\n publisher: trimToUndefined(info.Publisher),\n rights: undefined,\n languages: languageIso !== undefined ? [languageIso] : undefined,\n date: dateFromYearMonthDay(info),\n subjects: subjects.length > 0 ? subjects : undefined,\n }\n}\n","import type { ArchiveResolveResult } from \"../types/archiveResolve\"\nimport type { KoboMetadata } from \"./parse\"\n\nexport const resolveKobo = (input: KoboMetadata): ArchiveResolveResult => ({\n gtin: undefined,\n isbn: undefined,\n readingDirection: undefined,\n renditionLayout: input.renditionLayout,\n title: undefined,\n authors: undefined,\n publisher: undefined,\n rights: undefined,\n languages: undefined,\n date: undefined,\n subjects: undefined,\n})\n","/**\n * Extract the calendar components of a W3CDTF literal — the\n * date subset of ISO 8601 that EPUB 3.3 § 5.5.3.2.4 mandates for\n * `dc:date`. Accepted forms: `YYYY`, `YYYY-MM`, `YYYY-MM-DD`, and\n * any of the above followed by a `Thh:mm[:ss[.s]][TZD]` time\n * portion that we ignore.\n *\n * Components are returned as plain integers (`month` is 1-12,\n * `day` is 1-31), independent of the host timezone — using\n * `Date.parse` would shift `2011-01-01` by a day in negative-offset\n * locales, which is the exact bug this regex-based approach exists\n * to avoid. Returns `undefined` when the input doesn't even match\n * a leading 4-digit year so consumers can fall back without\n * branching on partial shapes.\n */\nexport const parseW3cDtfDate = (\n raw: string | undefined,\n):\n | {\n year?: number\n month?: number\n day?: number\n }\n | undefined => {\n if (raw === undefined) return undefined\n\n const match = raw.trim().match(/^(\\d{4})(?:-(\\d{2})(?:-(\\d{2}))?)?/)\n if (match === null) return undefined\n\n const [, yearRaw, monthRaw, dayRaw] = match\n\n return {\n ...(yearRaw !== undefined ? { year: Number.parseInt(yearRaw, 10) } : {}),\n ...(monthRaw !== undefined ? { month: Number.parseInt(monthRaw, 10) } : {}),\n ...(dayRaw !== undefined ? { day: Number.parseInt(dayRaw, 10) } : {}),\n }\n}\n","import type { ArchiveResolveResult } from \"../types/archiveResolve\"\nimport { normalizeGtin } from \"../utils/normalizeGtin\"\nimport { normalizeIsbn } from \"../utils/normalizeIsbn\"\nimport { parseW3cDtfDate } from \"../utils/parseW3cDtfDate\"\nimport type { OpfIdentifier, OpfMetadata } from \"./parse\"\n\nconst rawIdentifierValueForIsbn = (\n identifiers: ReadonlyArray<OpfIdentifier>,\n): string | undefined => {\n for (const i of identifiers) {\n if (i.scheme !== undefined && i.scheme.trim().toLowerCase() === \"isbn\") {\n if (normalizeIsbn(i.value) !== undefined) return i.value\n }\n }\n\n for (const i of identifiers) {\n if (normalizeIsbn(i.value) !== undefined) return i.value\n }\n\n return undefined\n}\n\nexport const resolveOpf = (input: OpfMetadata): ArchiveResolveResult => {\n const ppd = input.pageProgressionDirection?.trim().toLowerCase()\n const readingDirection = ppd === \"ltr\" || ppd === \"rtl\" ? ppd : undefined\n\n const rl = input.renditionLayoutMeta?.trim().toLowerCase()\n const renditionLayout =\n rl === \"reflowable\" || rl === \"pre-paginated\" ? rl : undefined\n\n const raw = rawIdentifierValueForIsbn(input.identifiers)\n\n return {\n gtin: normalizeGtin(raw),\n isbn: normalizeIsbn(raw),\n readingDirection,\n renditionLayout,\n title: input.title,\n authors: input.creators.length > 0 ? [...input.creators] : undefined,\n publisher: input.publisher,\n rights: input.rights,\n languages: input.languages.length > 0 ? [...input.languages] : undefined,\n date: parseW3cDtfDate(input.date),\n subjects: input.subjects.length > 0 ? [...input.subjects] : undefined,\n }\n}\n","import type { AppleMetadata } from \"./apple/parse\"\nimport { resolveApple } from \"./apple/resolve\"\nimport type { ComicInfo } from \"./comicInfo/parse\"\nimport { resolveComicInfo } from \"./comicInfo/resolve\"\nimport type { KoboMetadata } from \"./kobo/parse\"\nimport { resolveKobo } from \"./kobo/resolve\"\nimport type { OpfMetadata } from \"./opf/parse\"\nimport { resolveOpf } from \"./opf/resolve\"\nimport type { ArchiveResolveResult } from \"./types/archiveResolve\"\n\nexport type ResolvedArchiveInput =\n | ComicInfo\n | KoboMetadata\n | AppleMetadata\n | OpfMetadata\n\nexport const resolveArchiveMetadata = (\n input: ResolvedArchiveInput,\n): ArchiveResolveResult => {\n if (input.kind === \"comicInfo\") return resolveComicInfo(input)\n if (input.kind === \"kobo\") return resolveKobo(input)\n if (input.kind === \"apple\") return resolveApple(input)\n return resolveOpf(input)\n}\n"],"names":["APPLE_IBOOKS_DISPLAY_OPTIONS_FILENAME","platformOptionsFromElement","platform","option","parseAppleDisplayOptionsXml","xml","doc","XmlDocument","platformEl","COMIC_INFO_MANGA_VALUES","isComicInfoManga","value","v","COMIC_INFO_FILENAME","SKIP_ELEMENT_CHILDREN","hasNestedElement","el","c","trimmedText","parseComicInfo","cause","message","fields","child","text","KOBO_DISPLAY_OPTIONS_FILENAME","parseKoboDisplayOptionsDocument","parseKoboXml","tokenizeXmlSpaceSeparatedList","raw","layoutHintsFromItemrefProperties","properties","tokens","renditionLayout","elementLocalName","name","localNameEq","elementName","wantLocal","isXmlElement","node","childNamedLocal","parent","localName","childrenNamedLocal","out","identifiersFromMetadata","metadataEl","identifiers","schemeTrimmed","firstTextByLocalName","found","t","textsByLocalName","coverContentIdFromMetadata","coverId","content","coverHrefFromManifestAndMetadata","manifestItems","isImage","item","byCoverImageProperty","coverContentId","match","metaValByProperty","property","m","guideFromPackage","guideEl","refs","ref","href","manifestItemFromXmlElement","id","mediaType","manifestItemsAndById","manifestEl","items","byId","parsed","spineRowsFromByIdAndSpine","spineEl","rows","itemref","idref","manifestItem","hints","parseOpf","opfXml","spineRows","pageProgressionDirectionRaw","pageProgressionDirection","spineTocRaw","spineTocIdref","title","publisher","rights","date","creators","languages","subjects","renditionLayoutMeta","renditionFlowMeta","renditionSpreadMeta","coverHref","guide","resolveApple","input","o","GTIN_LENGTHS","normalizeGtin","digits","ISBN_CANDIDATE_PATTERN","normalizeIsbn","stripped","digitsOnly","readingDirection","info","trimToUndefined","trimmed","splitCommaList","token","parseNonNegativeInt","n","dateFromYearMonthDay","year","month","day","resolveComicInfo","languageIso","authors","resolveKobo","parseW3cDtfDate","yearRaw","monthRaw","dayRaw","rawIdentifierValueForIsbn","i","resolveOpf","ppd","rl","resolveArchiveMetadata"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/apple/parse.ts","../src/comicInfo/manga.ts","../src/comicInfo/parse.ts","../src/kobo/parse.ts","../src/utils/tokenizeXmlSpaceSeparatedList.ts","../src/opf/spineItemrefProperties.ts","../src/opf/parse.ts","../src/apple/resolve.ts","../src/utils/normalizeGtin.ts","../src/utils/normalizeIsbn.ts","../src/comicInfo/resolve.ts","../src/kobo/resolve.ts","../src/utils/parseW3cDtfDate.ts","../src/opf/resolve.ts","../src/resolve.ts"],"sourcesContent":["import type { XmlElement } from \"xmldoc\"\nimport { XmlDocument } from \"xmldoc\"\n\nexport const APPLE_IBOOKS_DISPLAY_OPTIONS_FILENAME =\n \"com.apple.ibooks.display-options.xml\"\n\nexport type AppleDisplayOption = {\n readonly name?: string\n readonly value: string\n}\n\nexport type AppleMetadata = {\n readonly kind: \"apple\"\n readonly displayOptions?: {\n readonly platform?: {\n readonly options: ReadonlyArray<AppleDisplayOption>\n }\n }\n}\n\nconst platformOptionsFromElement = (\n platform: XmlElement,\n): ReadonlyArray<AppleDisplayOption> =>\n platform.childrenNamed(\"option\").map((option) => ({\n name: option.attr?.name,\n value: option.val,\n }))\n\nexport const parseAppleDisplayOptionsXml = (xml: string): AppleMetadata => {\n const doc = new XmlDocument(xml)\n const root = doc.name?.toLowerCase()\n\n if (root !== \"display_options\") {\n return { kind: \"apple\" }\n }\n\n const platformEl = doc.childNamed(\"platform\")\n\n return {\n kind: \"apple\",\n displayOptions: {\n ...(platformEl !== undefined\n ? { platform: { options: platformOptionsFromElement(platformEl) } }\n : {}),\n },\n }\n}\n","/**\n * Allowed `Manga` element values (ComicInfo 2.0 `Manga` simpleType).\n *\n * @see https://anansi-project.github.io/docs/comicinfo/documentation#manga\n */\nexport const COMIC_INFO_MANGA_VALUES = [\n \"Unknown\",\n \"No\",\n \"Yes\",\n \"YesAndRightToLeft\",\n] as const\n\nexport type ComicInfoManga = (typeof COMIC_INFO_MANGA_VALUES)[number]\n\nexport const isComicInfoManga = (value: string): value is ComicInfoManga => {\n for (const v of COMIC_INFO_MANGA_VALUES) {\n if (v === value) return true\n }\n return false\n}\n","import type { XmlElement } from \"xmldoc\"\nimport { XmlDocument } from \"xmldoc\"\nimport type { ComicInfoManga } from \"./manga\"\n\n/** Canonical top-level filename; real archives may use any casing. */\nexport const COMIC_INFO_FILENAME = \"ComicInfo.xml\"\n\n/**\n * Parsed `ComicInfo.xml` root: one optional string property per child element,\n * using the same names as in the file (e.g. `Title`, `GTIN`, `LanguageISO`).\n * Nested blocks such as `Pages` are skipped. Other simple child elements are\n * still copied onto this object under their tag name.\n *\n * @see https://anansi-project.github.io/docs/comicinfo/intro\n * @see https://github.com/anansi-project/comicinfo/blob/main/drafts/v2.1/ComicInfo.xsd for schema\n */\nexport interface ComicInfo {\n readonly kind: \"comicInfo\"\n AgeRating?: string\n AlternateCount?: string\n AlternateNumber?: string\n AlternateSeries?: string\n BlackAndWhite?: string\n Characters?: string\n Colorist?: string\n CommunityRating?: string\n Count?: string\n CoverArtist?: string\n Day?: string\n Editor?: string\n Format?: string\n Genre?: string\n GTIN?: string\n Imprint?: string\n Inker?: string\n LanguageISO?: string\n Letterer?: string\n Locations?: string\n MainCharacterOrTeam?: string\n /** Schema literals per {@link ComicInfoManga}; files may still use other strings. */\n Manga?: ComicInfoManga | (string & {})\n Month?: string\n Notes?: string\n Number?: string\n PageCount?: string\n Penciller?: string\n Publisher?: string\n Review?: string\n ScanInformation?: string\n Series?: string\n SeriesGroup?: string\n StoryArc?: string\n StoryArcNumber?: string\n Summary?: string\n Tags?: string\n Teams?: string\n Title?: string\n Translator?: string\n Volume?: string\n Web?: string\n Writer?: string\n Year?: string\n [tag: string]: string | undefined\n}\n\nconst SKIP_ELEMENT_CHILDREN = new Set([\"Pages\", \"ComicInfo\"])\n\nconst hasNestedElement = (el: XmlElement) =>\n el.children.some((c) => c.type === \"element\")\n\nconst trimmedText = (el: XmlElement): string | undefined => {\n const t = el.val.trim()\n return t.length > 0 ? t : undefined\n}\n\n/**\n * Parse a raw `ComicInfo.xml` body. Each direct child element with plain text\n * becomes a property named after that tag. Malformed XML throws; the parser\n * error is attached as `cause`.\n */\nexport const parseComicInfo = (xml: string): ComicInfo => {\n let doc: XmlDocument\n try {\n doc = new XmlDocument(xml)\n } catch (cause) {\n const message = cause instanceof Error ? cause.message : String(cause)\n throw new Error(`${COMIC_INFO_FILENAME} is malformed: ${message}`, {\n cause,\n })\n }\n\n const fields: Record<string, string> = {}\n\n doc.eachChild((child) => {\n if (child.type !== \"element\") return\n if (SKIP_ELEMENT_CHILDREN.has(child.name)) return\n if (hasNestedElement(child)) return\n\n const text = trimmedText(child)\n if (text === undefined) return\n if (fields[child.name] !== undefined) return\n\n fields[child.name] = text\n })\n\n // `as ComicInfo`: TS cannot infer that spreading `Record<string, string>` into `{ kind }` satisfies the named optional fields plus `[tag: string]: string | undefined` on `ComicInfo`.\n return { kind: \"comicInfo\", ...fields } as ComicInfo\n}\n","import { XmlDocument } from \"xmldoc\"\n\nexport const KOBO_DISPLAY_OPTIONS_FILENAME = \"com.kobobooks.display-options.xml\"\n\n/**\n * Normalized fields from Kobo-specific XML sidecars. Grows as new Kobo\n * document kinds are supported; absent keys mean not present or unknown.\n */\nexport type KoboMetadata = {\n readonly kind: \"kobo\"\n renditionLayout?: `reflowable` | `pre-paginated`\n}\n\nconst parseKoboDisplayOptionsDocument = (\n doc: XmlDocument,\n): { renditionLayout?: \"pre-paginated\" } => {\n const platform = doc.childNamed(\"platform\")\n if (!platform) return {}\n\n for (const option of platform.childrenNamed(\"option\")) {\n if (option.attr?.name !== \"fixed-layout\") continue\n if (option.val.trim().toLowerCase() === \"true\") {\n return { renditionLayout: \"pre-paginated\" }\n }\n return {}\n }\n\n return {}\n}\n\n/**\n * Parse a Kobo-related XML document. Unsupported or unrecognized roots\n * yield an empty object. Malformed XML throws from the underlying parser.\n */\nexport const parseKoboXml = (xml: string): KoboMetadata => {\n const doc = new XmlDocument(xml)\n const root = doc.name?.toLowerCase()\n\n if (root === \"display_options\") {\n return { kind: \"kobo\", ...parseKoboDisplayOptionsDocument(doc) }\n }\n\n return { kind: \"kobo\" }\n}\n","/**\n * EPUB/XML space-separated token lists (e.g. `properties` on `item` / `itemref`).\n * Trims the raw string, splits on ASCII whitespace, drops empty segments.\n */\nexport const tokenizeXmlSpaceSeparatedList = (\n raw: string | undefined,\n): readonly string[] => {\n if (raw === undefined || raw.trim().length === 0) {\n return []\n }\n\n return raw\n .trim()\n .split(/\\s+/)\n .filter((t) => t.length > 0)\n}\n","import { tokenizeXmlSpaceSeparatedList } from \"../utils/tokenizeXmlSpaceSeparatedList\"\n\nexport type OpfItemrefLayoutHints = {\n readonly renditionLayout?: `reflowable` | `pre-paginated`\n readonly renditionFlow?:\n | `scrolled-continuous`\n | `scrolled-doc`\n | `paginated`\n | `auto`\n readonly pageSpreadLeft?: true\n readonly pageSpreadRight?: true\n}\n\n/**\n * EPUB `itemref` `properties` attribute (space-separated tokens).\n *\n * @see https://www.w3.org/TR/epub/#attrdef-properties\n */\nexport const layoutHintsFromItemrefProperties = (\n properties: string | undefined,\n): OpfItemrefLayoutHints => {\n const tokens = tokenizeXmlSpaceSeparatedList(properties)\n if (tokens.length === 0) {\n return {}\n }\n\n let renditionLayout: `reflowable` | `pre-paginated` | undefined\n if (tokens.includes(`rendition:layout-reflowable`)) {\n renditionLayout = `reflowable`\n }\n if (tokens.includes(`rendition:layout-pre-paginated`)) {\n renditionLayout = `pre-paginated`\n }\n\n let renditionFlow: OpfItemrefLayoutHints[\"renditionFlow\"]\n if (tokens.includes(`rendition:flow-auto`)) {\n renditionFlow = `auto`\n }\n if (tokens.includes(`rendition:flow-paginated`)) {\n renditionFlow = `paginated`\n }\n if (tokens.includes(`rendition:flow-scrolled-doc`)) {\n renditionFlow = `scrolled-doc`\n }\n if (tokens.includes(`rendition:flow-scrolled-continuous`)) {\n renditionFlow = `scrolled-continuous`\n }\n\n return {\n ...(renditionLayout !== undefined ? { renditionLayout } : {}),\n ...(renditionFlow !== undefined ? { renditionFlow } : {}),\n ...(tokens.includes(`page-spread-left`)\n ? { pageSpreadLeft: true as const }\n : {}),\n ...(tokens.includes(`page-spread-right`)\n ? { pageSpreadRight: true as const }\n : {}),\n }\n}\n","import type { XmlElement, XmlNodeBase } from \"xmldoc\"\nimport { XmlDocument } from \"xmldoc\"\nimport { tokenizeXmlSpaceSeparatedList } from \"../utils/tokenizeXmlSpaceSeparatedList\"\nimport { layoutHintsFromItemrefProperties } from \"./spineItemrefProperties\"\n\nexport type OpfSpineManifestItem = {\n readonly id: string\n readonly href: string\n readonly mediaType?: string\n readonly properties?: string\n}\n\nexport type OpfIdentifier = {\n readonly value: string\n readonly scheme?: string\n}\n\nexport type OpfSpineRow = {\n readonly idref: string\n readonly id: string\n readonly href: string\n readonly mediaType?: string\n readonly properties?: string\n readonly renditionLayout?: `reflowable` | `pre-paginated`\n readonly renditionFlow?:\n | `scrolled-continuous`\n | `scrolled-doc`\n | `paginated`\n | `auto`\n readonly pageSpreadLeft?: true\n readonly pageSpreadRight?: true\n}\n\nexport type OpfGuideReference = {\n readonly href: string\n readonly title: string\n readonly type: string\n}\n\nconst elementLocalName = (name: string): string =>\n name.includes(\":\") ? name.slice(name.lastIndexOf(\":\") + 1) : name\n\nconst localNameEq = (elementName: string, wantLocal: string): boolean =>\n elementLocalName(elementName).toLowerCase() === wantLocal.toLowerCase()\n\nconst isXmlElement = (node: XmlNodeBase): node is XmlElement =>\n node.type === \"element\"\n\nconst childNamedLocal = (\n parent: XmlElement,\n localName: string,\n): XmlElement | undefined => {\n for (const node of parent.children) {\n if (!isXmlElement(node)) continue\n if (localNameEq(node.name, localName)) return node\n }\n return undefined\n}\n\nconst childrenNamedLocal = (\n parent: XmlElement,\n localName: string,\n): XmlElement[] => {\n const out: XmlElement[] = []\n for (const node of parent.children) {\n if (!isXmlElement(node)) continue\n if (localNameEq(node.name, localName)) out.push(node)\n }\n return out\n}\n\nconst identifiersFromMetadata = (metadataEl: XmlElement): OpfIdentifier[] => {\n const identifiers: OpfIdentifier[] = []\n\n metadataEl.eachChild((child) => {\n if (elementLocalName(child.name).toLowerCase() !== \"identifier\") return\n\n const value = child.val.trim()\n if (value.length === 0) return\n\n const scheme =\n child.attr[\"opf:scheme\"] ?? child.attr[\"opf:Scheme\"] ?? child.attr.scheme\n const schemeTrimmed = scheme?.trim()\n\n identifiers.push({\n value,\n ...(schemeTrimmed !== undefined && schemeTrimmed.length > 0\n ? { scheme: schemeTrimmed }\n : {}),\n })\n })\n\n return identifiers\n}\n\nconst firstTextByLocalName = (\n metadataEl: XmlElement,\n localName: string,\n): string | undefined => {\n let found: string | undefined\n metadataEl.eachChild((child) => {\n if (found !== undefined) return\n if (elementLocalName(child.name).toLowerCase() !== localName.toLowerCase())\n return\n const t = child.val.trim()\n if (t.length > 0) found = t\n })\n return found\n}\n\nconst textsByLocalName = (\n metadataEl: XmlElement,\n localName: string,\n): string[] => {\n const out: string[] = []\n metadataEl.eachChild((child) => {\n if (elementLocalName(child.name).toLowerCase() !== localName.toLowerCase())\n return\n const t = child.val.trim()\n if (t.length > 0) out.push(t)\n })\n return out\n}\n\nconst coverContentIdFromMetadata = (\n metadataEl: XmlElement,\n): string | undefined => {\n let coverId: string | undefined\n metadataEl.eachChild((child) => {\n if (coverId !== undefined) return\n if (elementLocalName(child.name).toLowerCase() !== \"meta\") return\n if (child.attr.name?.toLowerCase() !== \"cover\") return\n const content = child.attr.content?.trim()\n if (content !== undefined && content.length > 0) coverId = content\n })\n return coverId\n}\n\n/**\n * EPUB cover image inside the manifest. Resolution order, matching\n * what the spec lays out and what the bulk of EPUB producers in the\n * wild rely on:\n *\n * 1. EPUB 3 — the manifest item carrying the `cover-image` token in\n * its `properties` attribute (§ D.6.1).\n * 2. EPUB 2 — `<meta name=\"cover\" content=\"ID\"/>` in `metadata`,\n * resolved to the manifest item with that `id`.\n * 3. Last-resort fallback — any image manifest item whose `id`\n * contains the substring `cover` (case-insensitive); covers the\n * long tail of producers that emit neither the EPUB 3 property\n * nor the EPUB 2 meta.\n *\n * Each step requires the candidate manifest item to advertise an\n * `image/*` media type so non-image artefacts named `cover` (XHTML\n * cover pages, NCX entries) don't slip through.\n */\nconst coverHrefFromManifestAndMetadata = ({\n manifestItems,\n metadataEl,\n}: {\n manifestItems: ReadonlyArray<OpfSpineManifestItem>\n metadataEl: XmlElement | undefined\n}): string | undefined => {\n const isImage = (item: OpfSpineManifestItem): boolean =>\n item.mediaType?.toLowerCase().includes(\"image/\") === true\n\n const byCoverImageProperty = manifestItems.find((item) => {\n if (!isImage(item)) return false\n return tokenizeXmlSpaceSeparatedList(item.properties).includes(\n \"cover-image\",\n )\n })\n if (byCoverImageProperty !== undefined) return byCoverImageProperty.href\n\n if (metadataEl !== undefined) {\n const coverContentId = coverContentIdFromMetadata(metadataEl)\n if (coverContentId !== undefined) {\n const match = manifestItems.find(\n (item) => item.id === coverContentId && isImage(item),\n )\n if (match !== undefined) return match.href\n }\n }\n\n return manifestItems.find(\n (item) => item.id.toLowerCase().includes(\"cover\") && isImage(item),\n )?.href\n}\n\nconst metaValByProperty = (\n metadataEl: XmlElement,\n property: string,\n): string | undefined => {\n const meta = childrenNamedLocal(metadataEl, \"meta\").find(\n (m) => m.attr.property === property,\n )\n const raw = meta?.val\n if (raw === undefined || raw.trim().length === 0) return undefined\n return raw\n}\n\nconst guideFromPackage = (doc: XmlElement): OpfGuideReference[] => {\n const guideEl = childNamedLocal(doc, \"guide\")\n if (guideEl === undefined) return []\n\n const refs: OpfGuideReference[] = []\n\n for (const ref of childrenNamedLocal(guideEl, \"reference\")) {\n const href = ref.attr.href?.trim()\n if (href === undefined || href.length === 0) continue\n refs.push({\n href,\n title: ref.attr.title?.trim() ?? ``,\n type: ref.attr.type?.trim() ?? ``,\n })\n }\n\n return refs\n}\n\nconst manifestItemFromXmlElement = (\n item: XmlElement,\n): OpfSpineManifestItem | undefined => {\n const id = item.attr.id\n const href = item.attr.href\n if (id === undefined || id.length === 0) return undefined\n if (href === undefined || href.length === 0) return undefined\n\n const mediaType = item.attr[\"media-type\"]\n const properties = item.attr.properties?.trim()\n return {\n id,\n href,\n ...(mediaType !== undefined && mediaType.length > 0 ? { mediaType } : {}),\n ...(properties !== undefined && properties.length > 0\n ? { properties }\n : {}),\n }\n}\n\nconst manifestItemsAndById = (\n manifestEl: XmlElement,\n): {\n items: OpfSpineManifestItem[]\n byId: Map<string, OpfSpineManifestItem>\n} => {\n const items: OpfSpineManifestItem[] = []\n const byId = new Map<string, OpfSpineManifestItem>()\n\n for (const el of childrenNamedLocal(manifestEl, \"item\")) {\n const parsed = manifestItemFromXmlElement(el)\n if (parsed === undefined) continue\n items.push(parsed)\n byId.set(parsed.id, parsed)\n }\n\n return { items, byId }\n}\n\nconst spineRowsFromByIdAndSpine = (\n byId: Map<string, OpfSpineManifestItem>,\n spineEl: XmlElement,\n): OpfSpineRow[] => {\n const rows: OpfSpineRow[] = []\n\n for (const itemref of childrenNamedLocal(spineEl, \"itemref\")) {\n const idref = itemref.attr.idref\n if (idref === undefined || idref.trim().length === 0) continue\n\n const manifestItem = byId.get(idref)\n if (manifestItem === undefined) continue\n\n const hints = layoutHintsFromItemrefProperties(itemref.attr.properties)\n\n rows.push({\n idref,\n id: manifestItem.id,\n href: manifestItem.href,\n ...(manifestItem.mediaType !== undefined\n ? { mediaType: manifestItem.mediaType }\n : {}),\n ...(manifestItem.properties !== undefined\n ? { properties: manifestItem.properties }\n : {}),\n ...(hints.renditionLayout !== undefined\n ? { renditionLayout: hints.renditionLayout }\n : {}),\n ...(hints.renditionFlow !== undefined\n ? { renditionFlow: hints.renditionFlow }\n : {}),\n ...(hints.pageSpreadLeft !== undefined\n ? { pageSpreadLeft: hints.pageSpreadLeft }\n : {}),\n ...(hints.pageSpreadRight !== undefined\n ? { pageSpreadRight: hints.pageSpreadRight }\n : {}),\n })\n }\n\n return rows\n}\n\nexport type OpfMetadata = {\n readonly kind: \"opf\"\n readonly manifestItems: ReadonlyArray<OpfSpineManifestItem>\n readonly spineRows: ReadonlyArray<OpfSpineRow>\n readonly spineTocIdref: string | undefined\n readonly identifiers: ReadonlyArray<OpfIdentifier>\n readonly title: string | undefined\n /** `dc:creator` values, in document order, trimmed; empty when none. */\n readonly creators: ReadonlyArray<string>\n /** First non-empty `dc:publisher`, trimmed. */\n readonly publisher: string | undefined\n /** First non-empty `dc:rights`, trimmed. */\n readonly rights: string | undefined\n /** `dc:language` values, in document order, trimmed; empty when none. */\n readonly languages: ReadonlyArray<string>\n /** `dc:subject` values, in document order, trimmed; empty when none. */\n readonly subjects: ReadonlyArray<string>\n /**\n * Raw `dc:date` value as authored. EPUB 3 requires W3CDTF (a profile\n * of ISO 8601), but real-world publishers also ship free text here,\n * so the value is exposed verbatim and consumers normalize as needed.\n */\n readonly date: string | undefined\n /**\n * Manifest-relative `href` of the cover image, when one can be\n * resolved from `cover-image` properties (EPUB 3), the EPUB 2\n * `<meta name=\"cover\">` convention, or an `id` that contains\n * `cover` on an image manifest item. The href is returned exactly\n * as it appears in the manifest — callers own folder-prefix\n * resolution against the OPF's location in the archive.\n */\n readonly coverHref: string | undefined\n readonly renditionLayoutMeta: string | undefined\n readonly renditionFlowMeta: string | undefined\n readonly renditionSpreadMeta: string | undefined\n readonly pageProgressionDirection: string | undefined\n readonly guide: ReadonlyArray<OpfGuideReference>\n}\n\n/**\n * Parses an EPUB package document (OPF) into structured metadata.\n *\n * Direct children of `package` (`metadata`, `manifest`, `spine`, `guide`) and\n * their structural children (`item`, `itemref`, `reference`, `meta`) are\n * matched by **local name** (ASCII case-insensitive), so prefixed tags such as\n * `opf:manifest` are supported the same as unprefixed `manifest`.\n *\n * Attribute names on `spine` / `itemref` are still read as emitted by xmldoc\n * (no QName normalization).\n */\nexport const parseOpf = (opfXml: string): OpfMetadata => {\n const doc = new XmlDocument(opfXml)\n const manifestEl = childNamedLocal(doc, \"manifest\")\n const spineEl = childNamedLocal(doc, \"spine\")\n const metadataEl = childNamedLocal(doc, \"metadata\")\n\n let manifestItems: OpfSpineManifestItem[] = []\n let spineRows: OpfSpineRow[] = []\n\n if (manifestEl !== undefined) {\n const { items, byId } = manifestItemsAndById(manifestEl)\n manifestItems = items\n if (spineEl !== undefined) {\n spineRows = spineRowsFromByIdAndSpine(byId, spineEl)\n }\n }\n\n const pageProgressionDirectionRaw =\n spineEl?.attr[\"page-progression-direction\"]\n const pageProgressionDirection =\n pageProgressionDirectionRaw !== undefined &&\n pageProgressionDirectionRaw.trim().length > 0\n ? pageProgressionDirectionRaw\n : undefined\n\n const spineTocRaw = spineEl?.attr.toc\n const spineTocIdref =\n spineTocRaw !== undefined && spineTocRaw.trim().length > 0\n ? spineTocRaw.trim()\n : undefined\n\n let title: string | undefined\n let publisher: string | undefined\n let rights: string | undefined\n let date: string | undefined\n let creators: string[] = []\n let languages: string[] = []\n let subjects: string[] = []\n let renditionLayoutMeta: string | undefined\n let renditionFlowMeta: string | undefined\n let renditionSpreadMeta: string | undefined\n const identifiers: OpfIdentifier[] = []\n\n if (metadataEl !== undefined) {\n title = firstTextByLocalName(metadataEl, \"title\")\n publisher = firstTextByLocalName(metadataEl, \"publisher\")\n rights = firstTextByLocalName(metadataEl, \"rights\")\n date = firstTextByLocalName(metadataEl, \"date\")\n creators = textsByLocalName(metadataEl, \"creator\")\n languages = textsByLocalName(metadataEl, \"language\")\n subjects = textsByLocalName(metadataEl, \"subject\")\n renditionLayoutMeta = metaValByProperty(metadataEl, \"rendition:layout\")\n renditionFlowMeta = metaValByProperty(metadataEl, \"rendition:flow\")\n renditionSpreadMeta = metaValByProperty(metadataEl, \"rendition:spread\")\n identifiers.push(...identifiersFromMetadata(metadataEl))\n }\n\n const coverHref = coverHrefFromManifestAndMetadata({\n manifestItems,\n metadataEl,\n })\n\n const guide = guideFromPackage(doc)\n\n return {\n kind: \"opf\",\n manifestItems,\n spineRows,\n spineTocIdref,\n identifiers,\n title,\n creators,\n publisher,\n rights,\n languages,\n subjects,\n date,\n coverHref,\n renditionLayoutMeta,\n renditionFlowMeta,\n renditionSpreadMeta,\n pageProgressionDirection,\n guide,\n }\n}\n","import type { ArchiveResolveResult } from \"../types/archiveResolve\"\nimport type { AppleMetadata } from \"./parse\"\n\nexport const resolveApple = (input: AppleMetadata): ArchiveResolveResult => {\n const fixedLayout = input.displayOptions?.platform?.options?.find(\n (o) => o.name === \"fixed-layout\",\n )?.value\n\n const renditionLayout =\n fixedLayout?.trim().toLowerCase() === \"true\" ? \"pre-paginated\" : undefined\n\n return {\n gtin: undefined,\n isbn: undefined,\n readingDirection: undefined,\n renditionLayout,\n title: undefined,\n authors: undefined,\n publisher: undefined,\n rights: undefined,\n languages: undefined,\n date: undefined,\n subjects: undefined,\n }\n}\n","const GTIN_LENGTHS = new Set([8, 12, 13, 14])\n\n/**\n * Normalize a raw GTIN / EAN / UPC string to digits only when the length\n * matches a GS1 GTIN family size (8, 12, 13, or 14). Does not verify check digits.\n */\nexport const normalizeGtin = (\n raw: string | number | undefined | null,\n): string | undefined => {\n if (raw === undefined || raw === null) return undefined\n\n const digits = String(raw).replace(/\\D/g, \"\")\n if (digits.length === 0 || !GTIN_LENGTHS.has(digits.length)) return undefined\n\n return digits\n}\n","const ISBN_CANDIDATE_PATTERN = /(?:97[89])?\\d{9}[\\dXx]/\n\n/**\n * Normalize a raw ISBN-ish string into a canonical 10- or 13-character\n * form, or `undefined` when no recognisable ISBN can be recovered.\n *\n * - Strips the common `urn:isbn:` / `isbn:` prefixes.\n * - Drops everything that isn't a digit or `X`.\n * - Validates the resulting length (10 or 13).\n * - Falls back to a lax regex scan so publishers that stuff free text\n * around the number still yield a usable value.\n */\nexport const normalizeIsbn = (\n raw: string | number | undefined | null,\n): string | undefined => {\n if (raw === undefined || raw === null) return undefined\n\n const stripped = String(raw)\n .trim()\n .replace(/^urn:isbn:/i, \"\")\n .replace(/^isbn[:\\s-]*/i, \"\")\n\n const digitsOnly = stripped.replace(/[^0-9Xx]/g, \"\")\n\n if (digitsOnly.length === 10 || digitsOnly.length === 13) {\n return digitsOnly.toUpperCase()\n }\n\n const match = stripped.match(ISBN_CANDIDATE_PATTERN)\n if (match) return match[0].toUpperCase()\n\n return undefined\n}\n","import type { ArchiveResolveResult } from \"../types/archiveResolve\"\nimport { normalizeGtin } from \"../utils/normalizeGtin\"\nimport { normalizeIsbn } from \"../utils/normalizeIsbn\"\nimport type { ComicInfo } from \"./parse\"\n\nconst readingDirection = (info: ComicInfo): \"ltr\" | \"rtl\" =>\n info.Manga === \"YesAndRightToLeft\" ? \"rtl\" : \"ltr\"\n\nconst trimToUndefined = (raw: string | undefined): string | undefined => {\n if (raw === undefined) return undefined\n const trimmed = raw.trim()\n return trimmed.length > 0 ? trimmed : undefined\n}\n\n/**\n * Split a comma-separated ComicInfo value into its individual tokens.\n * `Writer`, `Genre`, and `Tags` all follow the same de-facto convention:\n * tokens separated by `,`, with whitespace trimmed and empty tokens\n * dropped (real-world files leave trailing commas around).\n */\nconst splitCommaList = (raw: string | undefined): string[] => {\n if (raw === undefined) return []\n return raw\n .split(\",\")\n .map((token) => token.trim())\n .filter((token) => token.length > 0)\n}\n\nconst parseNonNegativeInt = (raw: string | undefined): number | undefined => {\n const trimmed = trimToUndefined(raw)\n if (trimmed === undefined) return undefined\n if (!/^\\d+$/.test(trimmed)) return undefined\n const n = Number.parseInt(trimmed, 10)\n return Number.isFinite(n) ? n : undefined\n}\n\nconst dateFromYearMonthDay = (\n info: ComicInfo,\n):\n | {\n year?: number\n month?: number\n day?: number\n }\n | undefined => {\n const year = parseNonNegativeInt(info.Year)\n const month = parseNonNegativeInt(info.Month)\n const day = parseNonNegativeInt(info.Day)\n\n if (year === undefined && month === undefined && day === undefined) {\n return undefined\n }\n\n return {\n ...(year !== undefined ? { year } : {}),\n ...(month !== undefined ? { month } : {}),\n ...(day !== undefined ? { day } : {}),\n }\n}\n\nexport const resolveComicInfo = (info: ComicInfo): ArchiveResolveResult => {\n const raw = info.GTIN\n const languageIso = trimToUndefined(info.LanguageISO)\n const authors = splitCommaList(info.Writer)\n const subjects = [...splitCommaList(info.Genre), ...splitCommaList(info.Tags)]\n\n return {\n gtin: normalizeGtin(raw),\n isbn: normalizeIsbn(raw),\n readingDirection: readingDirection(info),\n renditionLayout: undefined,\n title: trimToUndefined(info.Title),\n authors: authors.length > 0 ? authors : undefined,\n publisher: trimToUndefined(info.Publisher),\n rights: undefined,\n languages: languageIso !== undefined ? [languageIso] : undefined,\n date: dateFromYearMonthDay(info),\n subjects: subjects.length > 0 ? subjects : undefined,\n }\n}\n","import type { ArchiveResolveResult } from \"../types/archiveResolve\"\nimport type { KoboMetadata } from \"./parse\"\n\nexport const resolveKobo = (input: KoboMetadata): ArchiveResolveResult => ({\n gtin: undefined,\n isbn: undefined,\n readingDirection: undefined,\n renditionLayout: input.renditionLayout,\n title: undefined,\n authors: undefined,\n publisher: undefined,\n rights: undefined,\n languages: undefined,\n date: undefined,\n subjects: undefined,\n})\n","/**\n * Extract the calendar components of a W3CDTF literal — the\n * date subset of ISO 8601 that EPUB 3.3 § 5.5.3.2.4 mandates for\n * `dc:date`. Accepted forms: `YYYY`, `YYYY-MM`, `YYYY-MM-DD`, and\n * any of the above followed by a `Thh:mm[:ss[.s]][TZD]` time\n * portion that we ignore.\n *\n * Components are returned as plain integers (`month` is 1-12,\n * `day` is 1-31), independent of the host timezone — using\n * `Date.parse` would shift `2011-01-01` by a day in negative-offset\n * locales, which is the exact bug this regex-based approach exists\n * to avoid. Returns `undefined` when the input doesn't even match\n * a leading 4-digit year so consumers can fall back without\n * branching on partial shapes.\n */\nexport const parseW3cDtfDate = (\n raw: string | undefined,\n):\n | {\n year?: number\n month?: number\n day?: number\n }\n | undefined => {\n if (raw === undefined) return undefined\n\n const match = raw.trim().match(/^(\\d{4})(?:-(\\d{2})(?:-(\\d{2}))?)?/)\n if (match === null) return undefined\n\n const [, yearRaw, monthRaw, dayRaw] = match\n\n return {\n ...(yearRaw !== undefined ? { year: Number.parseInt(yearRaw, 10) } : {}),\n ...(monthRaw !== undefined ? { month: Number.parseInt(monthRaw, 10) } : {}),\n ...(dayRaw !== undefined ? { day: Number.parseInt(dayRaw, 10) } : {}),\n }\n}\n","import type { ArchiveResolveResult } from \"../types/archiveResolve\"\nimport { normalizeGtin } from \"../utils/normalizeGtin\"\nimport { normalizeIsbn } from \"../utils/normalizeIsbn\"\nimport { parseW3cDtfDate } from \"../utils/parseW3cDtfDate\"\nimport type { OpfIdentifier, OpfMetadata } from \"./parse\"\n\nconst rawIdentifierValueForIsbn = (\n identifiers: ReadonlyArray<OpfIdentifier>,\n): string | undefined => {\n for (const i of identifiers) {\n if (i.scheme !== undefined && i.scheme.trim().toLowerCase() === \"isbn\") {\n if (normalizeIsbn(i.value) !== undefined) return i.value\n }\n }\n\n for (const i of identifiers) {\n if (normalizeIsbn(i.value) !== undefined) return i.value\n }\n\n return undefined\n}\n\nexport const resolveOpf = (input: OpfMetadata): ArchiveResolveResult => {\n const ppd = input.pageProgressionDirection?.trim().toLowerCase()\n const readingDirection = ppd === \"ltr\" || ppd === \"rtl\" ? ppd : undefined\n\n const rl = input.renditionLayoutMeta?.trim().toLowerCase()\n const renditionLayout =\n rl === \"reflowable\" || rl === \"pre-paginated\" ? rl : undefined\n\n const raw = rawIdentifierValueForIsbn(input.identifiers)\n\n return {\n gtin: normalizeGtin(raw),\n isbn: normalizeIsbn(raw),\n readingDirection,\n renditionLayout,\n title: input.title,\n authors: input.creators.length > 0 ? [...input.creators] : undefined,\n publisher: input.publisher,\n rights: input.rights,\n languages: input.languages.length > 0 ? [...input.languages] : undefined,\n date: parseW3cDtfDate(input.date),\n subjects: input.subjects.length > 0 ? [...input.subjects] : undefined,\n }\n}\n","import type { AppleMetadata } from \"./apple/parse\"\nimport { resolveApple } from \"./apple/resolve\"\nimport type { ComicInfo } from \"./comicInfo/parse\"\nimport { resolveComicInfo } from \"./comicInfo/resolve\"\nimport type { KoboMetadata } from \"./kobo/parse\"\nimport { resolveKobo } from \"./kobo/resolve\"\nimport type { OpfMetadata } from \"./opf/parse\"\nimport { resolveOpf } from \"./opf/resolve\"\nimport type { ArchiveResolveResult } from \"./types/archiveResolve\"\n\nexport type ResolvedArchiveInput =\n | ComicInfo\n | KoboMetadata\n | AppleMetadata\n | OpfMetadata\n\nexport const resolveArchiveMetadata = (\n input: ResolvedArchiveInput,\n): ArchiveResolveResult => {\n if (input.kind === \"comicInfo\") return resolveComicInfo(input)\n if (input.kind === \"kobo\") return resolveKobo(input)\n if (input.kind === \"apple\") return resolveApple(input)\n return resolveOpf(input)\n}\n"],"names":["APPLE_IBOOKS_DISPLAY_OPTIONS_FILENAME","platformOptionsFromElement","platform","option","parseAppleDisplayOptionsXml","xml","doc","XmlDocument","platformEl","COMIC_INFO_MANGA_VALUES","isComicInfoManga","value","v","COMIC_INFO_FILENAME","SKIP_ELEMENT_CHILDREN","hasNestedElement","el","c","trimmedText","parseComicInfo","cause","message","fields","child","text","KOBO_DISPLAY_OPTIONS_FILENAME","parseKoboDisplayOptionsDocument","parseKoboXml","tokenizeXmlSpaceSeparatedList","raw","layoutHintsFromItemrefProperties","properties","tokens","renditionLayout","renditionFlow","elementLocalName","name","localNameEq","elementName","wantLocal","isXmlElement","node","childNamedLocal","parent","localName","childrenNamedLocal","out","identifiersFromMetadata","metadataEl","identifiers","schemeTrimmed","firstTextByLocalName","found","t","textsByLocalName","coverContentIdFromMetadata","coverId","content","coverHrefFromManifestAndMetadata","manifestItems","isImage","item","byCoverImageProperty","coverContentId","match","metaValByProperty","property","m","guideFromPackage","guideEl","refs","ref","href","manifestItemFromXmlElement","id","mediaType","manifestItemsAndById","manifestEl","items","byId","parsed","spineRowsFromByIdAndSpine","spineEl","rows","itemref","idref","manifestItem","hints","parseOpf","opfXml","spineRows","pageProgressionDirectionRaw","pageProgressionDirection","spineTocRaw","spineTocIdref","title","publisher","rights","date","creators","languages","subjects","renditionLayoutMeta","renditionFlowMeta","renditionSpreadMeta","coverHref","guide","resolveApple","input","GTIN_LENGTHS","normalizeGtin","digits","ISBN_CANDIDATE_PATTERN","normalizeIsbn","stripped","digitsOnly","readingDirection","info","trimToUndefined","trimmed","splitCommaList","token","parseNonNegativeInt","n","dateFromYearMonthDay","year","month","day","resolveComicInfo","languageIso","authors","resolveKobo","parseW3cDtfDate","yearRaw","monthRaw","dayRaw","rawIdentifierValueForIsbn","i","resolveOpf","ppd","rl","resolveArchiveMetadata"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- (function(s,d){typeof exports=="object"&&typeof module<"u"?d(exports,require("xmldoc")):typeof define=="function"&&define.amd?define(["exports","xmldoc"],d):(s=typeof globalThis<"u"?globalThis:s||self,d(s["prose-reader-archive-parser"]={},s.xmldoc))})(this,(function(s,d){"use strict";const B="com.apple.ibooks.display-options.xml",K=e=>e.childrenNamed("option").map(t=>({name:t.attr?.name,value:t.val})),Y=e=>{const t=new d.XmlDocument(e);if(t.name?.toLowerCase()!=="display_options")return{kind:"apple"};const n=t.childNamed("platform");return{kind:"apple",displayOptions:{...n!==void 0?{platform:{options:K(n)}}:{}}}},N=["Unknown","No","Yes","YesAndRightToLeft"],G=e=>{for(const t of N)if(t===e)return!0;return!1},S="ComicInfo.xml",z=new Set(["Pages","ComicInfo"]),U=e=>e.children.some(t=>t.type==="element"),H=e=>{const t=e.val.trim();return t.length>0?t:void 0},V=e=>{let t;try{t=new d.XmlDocument(e)}catch(n){const r=n instanceof Error?n.message:String(n);throw new Error(`${S} is malformed: ${r}`,{cause:n})}const o={};return t.eachChild(n=>{if(n.type!=="element"||z.has(n.name)||U(n))return;const r=H(n);r!==void 0&&o[n.name]===void 0&&(o[n.name]=r)}),{kind:"comicInfo",...o}},x="com.kobobooks.display-options.xml",W=e=>{const t=e.childNamed("platform");if(!t)return{};for(const o of t.childrenNamed("option"))if(o.attr?.name==="fixed-layout")return o.val.trim().toLowerCase()==="true"?{renditionLayout:"pre-paginated"}:{};return{}},$=e=>{const t=new d.XmlDocument(e);return t.name?.toLowerCase()==="display_options"?{kind:"kobo",...W(t)}:{kind:"kobo"}},v=e=>e===void 0||e.trim().length===0?[]:e.trim().split(/\s+/).filter(t=>t.length>0),q=e=>{const t=v(e);if(t.length===0)return{};let o;return t.includes("rendition:layout-reflowable")&&(o="reflowable"),t.includes("rendition:layout-pre-paginated")&&(o="pre-paginated"),{...o!==void 0?{renditionLayout:o}:{},...t.includes("page-spread-left")?{pageSpreadLeft:!0}:{},...t.includes("page-spread-right")?{pageSpreadRight:!0}:{}}},c=e=>e.includes(":")?e.slice(e.lastIndexOf(":")+1):e,O=(e,t)=>c(e).toLowerCase()===t.toLowerCase(),w=e=>e.type==="element",u=(e,t)=>{for(const o of e.children)if(w(o)&&O(o.name,t))return o},m=(e,t)=>{const o=[];for(const n of e.children)w(n)&&O(n.name,t)&&o.push(n);return o},J=e=>{const t=[];return e.eachChild(o=>{if(c(o.name).toLowerCase()!=="identifier")return;const n=o.val.trim();if(n.length===0)return;const i=(o.attr["opf:scheme"]??o.attr["opf:Scheme"]??o.attr.scheme)?.trim();t.push({value:n,...i!==void 0&&i.length>0?{scheme:i}:{}})}),t},f=(e,t)=>{let o;return e.eachChild(n=>{if(o!==void 0||c(n.name).toLowerCase()!==t.toLowerCase())return;const r=n.val.trim();r.length>0&&(o=r)}),o},g=(e,t)=>{const o=[];return e.eachChild(n=>{if(c(n.name).toLowerCase()!==t.toLowerCase())return;const r=n.val.trim();r.length>0&&o.push(r)}),o},Q=e=>{let t;return e.eachChild(o=>{if(t!==void 0||c(o.name).toLowerCase()!=="meta"||o.attr.name?.toLowerCase()!=="cover")return;const n=o.attr.content?.trim();n!==void 0&&n.length>0&&(t=n)}),t},Z=({manifestItems:e,metadataEl:t})=>{const o=r=>r.mediaType?.toLowerCase().includes("image/")===!0,n=e.find(r=>o(r)?v(r.properties).includes("cover-image"):!1);if(n!==void 0)return n.href;if(t!==void 0){const r=Q(t);if(r!==void 0){const i=e.find(a=>a.id===r&&o(a));if(i!==void 0)return i.href}}return e.find(r=>r.id.toLowerCase().includes("cover")&&o(r))?.href},h=(e,t)=>{const n=m(e,"meta").find(r=>r.attr.property===t)?.val;if(!(n===void 0||n.trim().length===0))return n},ee=e=>{const t=u(e,"guide");if(t===void 0)return[];const o=[];for(const n of m(t,"reference")){const r=n.attr.href?.trim();r===void 0||r.length===0||o.push({href:r,title:n.attr.title?.trim()??"",type:n.attr.type?.trim()??""})}return o},te=e=>{const t=e.attr.id,o=e.attr.href;if(t===void 0||t.length===0||o===void 0||o.length===0)return;const n=e.attr["media-type"],r=e.attr.properties?.trim();return{id:t,href:o,...n!==void 0&&n.length>0?{mediaType:n}:{},...r!==void 0&&r.length>0?{properties:r}:{}}},oe=e=>{const t=[],o=new Map;for(const n of m(e,"item")){const r=te(n);r!==void 0&&(t.push(r),o.set(r.id,r))}return{items:t,byId:o}},ne=(e,t)=>{const o=[];for(const n of m(t,"itemref")){const r=n.attr.idref;if(r===void 0||r.trim().length===0)continue;const i=e.get(r);if(i===void 0)continue;const a=q(n.attr.properties);o.push({idref:r,id:i.id,href:i.href,...i.mediaType!==void 0?{mediaType:i.mediaType}:{},...i.properties!==void 0?{properties:i.properties}:{},...a.renditionLayout!==void 0?{renditionLayout:a.renditionLayout}:{},...a.pageSpreadLeft!==void 0?{pageSpreadLeft:a.pageSpreadLeft}:{},...a.pageSpreadRight!==void 0?{pageSpreadRight:a.pageSpreadRight}:{}})}return o},re=e=>{const t=new d.XmlDocument(e),o=u(t,"manifest"),n=u(t,"spine"),r=u(t,"metadata");let i=[],a=[];if(o!==void 0){const{items:Le,byId:Ie}=oe(o);i=Le,n!==void 0&&(a=ne(Ie,n))}const b=n?.attr["page-progression-direction"],ve=b!==void 0&&b.trim().length>0?b:void 0,C=n?.attr.toc,ge=C!==void 0&&C.trim().length>0?C.trim():void 0;let A,_,D,M,T=[],P=[],F=[],k,R,X;const j=[];r!==void 0&&(A=f(r,"title"),_=f(r,"publisher"),D=f(r,"rights"),M=f(r,"date"),T=g(r,"creator"),P=g(r,"language"),F=g(r,"subject"),k=h(r,"rendition:layout"),R=h(r,"rendition:flow"),X=h(r,"rendition:spread"),j.push(...J(r)));const he=Z({manifestItems:i,metadataEl:r}),ye=ee(t);return{kind:"opf",manifestItems:i,spineRows:a,spineTocIdref:ge,identifiers:j,title:A,creators:T,publisher:_,rights:D,languages:P,subjects:F,date:M,coverHref:he,renditionLayoutMeta:k,renditionFlowMeta:R,renditionSpreadMeta:X,pageProgressionDirection:ve,guide:ye}},ie=e=>({gtin:void 0,isbn:void 0,readingDirection:void 0,renditionLayout:e.displayOptions?.platform?.options?.find(n=>n.name==="fixed-layout")?.value?.trim().toLowerCase()==="true"?"pre-paginated":void 0,title:void 0,authors:void 0,publisher:void 0,rights:void 0,languages:void 0,date:void 0,subjects:void 0}),se=new Set([8,12,13,14]),y=e=>{if(e==null)return;const t=String(e).replace(/\D/g,"");if(!(t.length===0||!se.has(t.length)))return t},ae=/(?:97[89])?\d{9}[\dXx]/,l=e=>{if(e==null)return;const t=String(e).trim().replace(/^urn:isbn:/i,"").replace(/^isbn[:\s-]*/i,""),o=t.replace(/[^0-9Xx]/g,"");if(o.length===10||o.length===13)return o.toUpperCase();const n=t.match(ae);if(n)return n[0].toUpperCase()},de=e=>e.Manga==="YesAndRightToLeft"?"rtl":"ltr",p=e=>{if(e===void 0)return;const t=e.trim();return t.length>0?t:void 0},L=e=>e===void 0?[]:e.split(",").map(t=>t.trim()).filter(t=>t.length>0),I=e=>{const t=p(e);if(t===void 0||!/^\d+$/.test(t))return;const o=Number.parseInt(t,10);return Number.isFinite(o)?o:void 0},ce=e=>{const t=I(e.Year),o=I(e.Month),n=I(e.Day);if(!(t===void 0&&o===void 0&&n===void 0))return{...t!==void 0?{year:t}:{},...o!==void 0?{month:o}:{},...n!==void 0?{day:n}:{}}},le=e=>{const t=e.GTIN,o=p(e.LanguageISO),n=L(e.Writer),r=[...L(e.Genre),...L(e.Tags)];return{gtin:y(t),isbn:l(t),readingDirection:de(e),renditionLayout:void 0,title:p(e.Title),authors:n.length>0?n:void 0,publisher:p(e.Publisher),rights:void 0,languages:o!==void 0?[o]:void 0,date:ce(e),subjects:r.length>0?r:void 0}},ue=e=>({gtin:void 0,isbn:void 0,readingDirection:void 0,renditionLayout:e.renditionLayout,title:void 0,authors:void 0,publisher:void 0,rights:void 0,languages:void 0,date:void 0,subjects:void 0}),E=e=>{if(e===void 0)return;const t=e.trim().match(/^(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?/);if(t===null)return;const[,o,n,r]=t;return{...o!==void 0?{year:Number.parseInt(o,10)}:{},...n!==void 0?{month:Number.parseInt(n,10)}:{},...r!==void 0?{day:Number.parseInt(r,10)}:{}}},me=e=>{for(const t of e)if(t.scheme!==void 0&&t.scheme.trim().toLowerCase()==="isbn"&&l(t.value)!==void 0)return t.value;for(const t of e)if(l(t.value)!==void 0)return t.value},fe=e=>{const t=e.pageProgressionDirection?.trim().toLowerCase(),o=t==="ltr"||t==="rtl"?t:void 0,n=e.renditionLayoutMeta?.trim().toLowerCase(),r=n==="reflowable"||n==="pre-paginated"?n:void 0,i=me(e.identifiers);return{gtin:y(i),isbn:l(i),readingDirection:o,renditionLayout:r,title:e.title,authors:e.creators.length>0?[...e.creators]:void 0,publisher:e.publisher,rights:e.rights,languages:e.languages.length>0?[...e.languages]:void 0,date:E(e.date),subjects:e.subjects.length>0?[...e.subjects]:void 0}},pe=e=>e.kind==="comicInfo"?le(e):e.kind==="kobo"?ue(e):e.kind==="apple"?ie(e):fe(e);s.APPLE_IBOOKS_DISPLAY_OPTIONS_FILENAME=B,s.COMIC_INFO_FILENAME=S,s.COMIC_INFO_MANGA_VALUES=N,s.KOBO_DISPLAY_OPTIONS_FILENAME=x,s.isComicInfoManga=G,s.normalizeGtin=y,s.normalizeIsbn=l,s.parseAppleDisplayOptionsXml=Y,s.parseComicInfo=V,s.parseKoboXml=$,s.parseOpf=re,s.parseW3cDtfDate=E,s.resolveArchiveMetadata=pe,s.tokenizeXmlSpaceSeparatedList=v,Object.defineProperty(s,Symbol.toStringTag,{value:"Module"})}));
1
+ (function(s,d){typeof exports=="object"&&typeof module<"u"?d(exports,require("xmldoc")):typeof define=="function"&&define.amd?define(["exports","xmldoc"],d):(s=typeof globalThis<"u"?globalThis:s||self,d(s["prose-reader-archive-parser"]={},s.xmldoc))})(this,(function(s,d){"use strict";const B="com.apple.ibooks.display-options.xml",K=e=>e.childrenNamed("option").map(t=>({name:t.attr?.name,value:t.val})),Y=e=>{const t=new d.XmlDocument(e);if(t.name?.toLowerCase()!=="display_options")return{kind:"apple"};const o=t.childNamed("platform");return{kind:"apple",displayOptions:{...o!==void 0?{platform:{options:K(o)}}:{}}}},w=["Unknown","No","Yes","YesAndRightToLeft"],G=e=>{for(const t of w)if(t===e)return!0;return!1},N="ComicInfo.xml",z=new Set(["Pages","ComicInfo"]),U=e=>e.children.some(t=>t.type==="element"),H=e=>{const t=e.val.trim();return t.length>0?t:void 0},V=e=>{let t;try{t=new d.XmlDocument(e)}catch(o){const i=o instanceof Error?o.message:String(o);throw new Error(`${N} is malformed: ${i}`,{cause:o})}const n={};return t.eachChild(o=>{if(o.type!=="element"||z.has(o.name)||U(o))return;const i=H(o);i!==void 0&&n[o.name]===void 0&&(n[o.name]=i)}),{kind:"comicInfo",...n}},x="com.kobobooks.display-options.xml",W=e=>{const t=e.childNamed("platform");if(!t)return{};for(const n of t.childrenNamed("option"))if(n.attr?.name==="fixed-layout")return n.val.trim().toLowerCase()==="true"?{renditionLayout:"pre-paginated"}:{};return{}},$=e=>{const t=new d.XmlDocument(e);return t.name?.toLowerCase()==="display_options"?{kind:"kobo",...W(t)}:{kind:"kobo"}},v=e=>e===void 0||e.trim().length===0?[]:e.trim().split(/\s+/).filter(t=>t.length>0),q=e=>{const t=v(e);if(t.length===0)return{};let n;t.includes("rendition:layout-reflowable")&&(n="reflowable"),t.includes("rendition:layout-pre-paginated")&&(n="pre-paginated");let o;return t.includes("rendition:flow-auto")&&(o="auto"),t.includes("rendition:flow-paginated")&&(o="paginated"),t.includes("rendition:flow-scrolled-doc")&&(o="scrolled-doc"),t.includes("rendition:flow-scrolled-continuous")&&(o="scrolled-continuous"),{...n!==void 0?{renditionLayout:n}:{},...o!==void 0?{renditionFlow:o}:{},...t.includes("page-spread-left")?{pageSpreadLeft:!0}:{},...t.includes("page-spread-right")?{pageSpreadRight:!0}:{}}},c=e=>e.includes(":")?e.slice(e.lastIndexOf(":")+1):e,S=(e,t)=>c(e).toLowerCase()===t.toLowerCase(),O=e=>e.type==="element",u=(e,t)=>{for(const n of e.children)if(O(n)&&S(n.name,t))return n},f=(e,t)=>{const n=[];for(const o of e.children)O(o)&&S(o.name,t)&&n.push(o);return n},J=e=>{const t=[];return e.eachChild(n=>{if(c(n.name).toLowerCase()!=="identifier")return;const o=n.val.trim();if(o.length===0)return;const r=(n.attr["opf:scheme"]??n.attr["opf:Scheme"]??n.attr.scheme)?.trim();t.push({value:o,...r!==void 0&&r.length>0?{scheme:r}:{}})}),t},m=(e,t)=>{let n;return e.eachChild(o=>{if(n!==void 0||c(o.name).toLowerCase()!==t.toLowerCase())return;const i=o.val.trim();i.length>0&&(n=i)}),n},g=(e,t)=>{const n=[];return e.eachChild(o=>{if(c(o.name).toLowerCase()!==t.toLowerCase())return;const i=o.val.trim();i.length>0&&n.push(i)}),n},Q=e=>{let t;return e.eachChild(n=>{if(t!==void 0||c(n.name).toLowerCase()!=="meta"||n.attr.name?.toLowerCase()!=="cover")return;const o=n.attr.content?.trim();o!==void 0&&o.length>0&&(t=o)}),t},Z=({manifestItems:e,metadataEl:t})=>{const n=i=>i.mediaType?.toLowerCase().includes("image/")===!0,o=e.find(i=>n(i)?v(i.properties).includes("cover-image"):!1);if(o!==void 0)return o.href;if(t!==void 0){const i=Q(t);if(i!==void 0){const r=e.find(a=>a.id===i&&n(a));if(r!==void 0)return r.href}}return e.find(i=>i.id.toLowerCase().includes("cover")&&n(i))?.href},h=(e,t)=>{const o=f(e,"meta").find(i=>i.attr.property===t)?.val;if(!(o===void 0||o.trim().length===0))return o},ee=e=>{const t=u(e,"guide");if(t===void 0)return[];const n=[];for(const o of f(t,"reference")){const i=o.attr.href?.trim();i===void 0||i.length===0||n.push({href:i,title:o.attr.title?.trim()??"",type:o.attr.type?.trim()??""})}return n},te=e=>{const t=e.attr.id,n=e.attr.href;if(t===void 0||t.length===0||n===void 0||n.length===0)return;const o=e.attr["media-type"],i=e.attr.properties?.trim();return{id:t,href:n,...o!==void 0&&o.length>0?{mediaType:o}:{},...i!==void 0&&i.length>0?{properties:i}:{}}},oe=e=>{const t=[],n=new Map;for(const o of f(e,"item")){const i=te(o);i!==void 0&&(t.push(i),n.set(i.id,i))}return{items:t,byId:n}},ne=(e,t)=>{const n=[];for(const o of f(t,"itemref")){const i=o.attr.idref;if(i===void 0||i.trim().length===0)continue;const r=e.get(i);if(r===void 0)continue;const a=q(o.attr.properties);n.push({idref:i,id:r.id,href:r.href,...r.mediaType!==void 0?{mediaType:r.mediaType}:{},...r.properties!==void 0?{properties:r.properties}:{},...a.renditionLayout!==void 0?{renditionLayout:a.renditionLayout}:{},...a.renditionFlow!==void 0?{renditionFlow:a.renditionFlow}:{},...a.pageSpreadLeft!==void 0?{pageSpreadLeft:a.pageSpreadLeft}:{},...a.pageSpreadRight!==void 0?{pageSpreadRight:a.pageSpreadRight}:{}})}return n},ie=e=>{const t=new d.XmlDocument(e),n=u(t,"manifest"),o=u(t,"spine"),i=u(t,"metadata");let r=[],a=[];if(n!==void 0){const{items:Le,byId:Ie}=oe(n);r=Le,o!==void 0&&(a=ne(Ie,o))}const b=o?.attr["page-progression-direction"],ve=b!==void 0&&b.trim().length>0?b:void 0,C=o?.attr.toc,ge=C!==void 0&&C.trim().length>0?C.trim():void 0;let A,_,D,M,T=[],F=[],P=[],k,R,X;const j=[];i!==void 0&&(A=m(i,"title"),_=m(i,"publisher"),D=m(i,"rights"),M=m(i,"date"),T=g(i,"creator"),F=g(i,"language"),P=g(i,"subject"),k=h(i,"rendition:layout"),R=h(i,"rendition:flow"),X=h(i,"rendition:spread"),j.push(...J(i)));const he=Z({manifestItems:r,metadataEl:i}),ye=ee(t);return{kind:"opf",manifestItems:r,spineRows:a,spineTocIdref:ge,identifiers:j,title:A,creators:T,publisher:_,rights:D,languages:F,subjects:P,date:M,coverHref:he,renditionLayoutMeta:k,renditionFlowMeta:R,renditionSpreadMeta:X,pageProgressionDirection:ve,guide:ye}},re=e=>({gtin:void 0,isbn:void 0,readingDirection:void 0,renditionLayout:e.displayOptions?.platform?.options?.find(o=>o.name==="fixed-layout")?.value?.trim().toLowerCase()==="true"?"pre-paginated":void 0,title:void 0,authors:void 0,publisher:void 0,rights:void 0,languages:void 0,date:void 0,subjects:void 0}),se=new Set([8,12,13,14]),y=e=>{if(e==null)return;const t=String(e).replace(/\D/g,"");if(!(t.length===0||!se.has(t.length)))return t},ae=/(?:97[89])?\d{9}[\dXx]/,l=e=>{if(e==null)return;const t=String(e).trim().replace(/^urn:isbn:/i,"").replace(/^isbn[:\s-]*/i,""),n=t.replace(/[^0-9Xx]/g,"");if(n.length===10||n.length===13)return n.toUpperCase();const o=t.match(ae);if(o)return o[0].toUpperCase()},de=e=>e.Manga==="YesAndRightToLeft"?"rtl":"ltr",p=e=>{if(e===void 0)return;const t=e.trim();return t.length>0?t:void 0},L=e=>e===void 0?[]:e.split(",").map(t=>t.trim()).filter(t=>t.length>0),I=e=>{const t=p(e);if(t===void 0||!/^\d+$/.test(t))return;const n=Number.parseInt(t,10);return Number.isFinite(n)?n:void 0},ce=e=>{const t=I(e.Year),n=I(e.Month),o=I(e.Day);if(!(t===void 0&&n===void 0&&o===void 0))return{...t!==void 0?{year:t}:{},...n!==void 0?{month:n}:{},...o!==void 0?{day:o}:{}}},le=e=>{const t=e.GTIN,n=p(e.LanguageISO),o=L(e.Writer),i=[...L(e.Genre),...L(e.Tags)];return{gtin:y(t),isbn:l(t),readingDirection:de(e),renditionLayout:void 0,title:p(e.Title),authors:o.length>0?o:void 0,publisher:p(e.Publisher),rights:void 0,languages:n!==void 0?[n]:void 0,date:ce(e),subjects:i.length>0?i:void 0}},ue=e=>({gtin:void 0,isbn:void 0,readingDirection:void 0,renditionLayout:e.renditionLayout,title:void 0,authors:void 0,publisher:void 0,rights:void 0,languages:void 0,date:void 0,subjects:void 0}),E=e=>{if(e===void 0)return;const t=e.trim().match(/^(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?/);if(t===null)return;const[,n,o,i]=t;return{...n!==void 0?{year:Number.parseInt(n,10)}:{},...o!==void 0?{month:Number.parseInt(o,10)}:{},...i!==void 0?{day:Number.parseInt(i,10)}:{}}},fe=e=>{for(const t of e)if(t.scheme!==void 0&&t.scheme.trim().toLowerCase()==="isbn"&&l(t.value)!==void 0)return t.value;for(const t of e)if(l(t.value)!==void 0)return t.value},me=e=>{const t=e.pageProgressionDirection?.trim().toLowerCase(),n=t==="ltr"||t==="rtl"?t:void 0,o=e.renditionLayoutMeta?.trim().toLowerCase(),i=o==="reflowable"||o==="pre-paginated"?o:void 0,r=fe(e.identifiers);return{gtin:y(r),isbn:l(r),readingDirection:n,renditionLayout:i,title:e.title,authors:e.creators.length>0?[...e.creators]:void 0,publisher:e.publisher,rights:e.rights,languages:e.languages.length>0?[...e.languages]:void 0,date:E(e.date),subjects:e.subjects.length>0?[...e.subjects]:void 0}},pe=e=>e.kind==="comicInfo"?le(e):e.kind==="kobo"?ue(e):e.kind==="apple"?re(e):me(e);s.APPLE_IBOOKS_DISPLAY_OPTIONS_FILENAME=B,s.COMIC_INFO_FILENAME=N,s.COMIC_INFO_MANGA_VALUES=w,s.KOBO_DISPLAY_OPTIONS_FILENAME=x,s.isComicInfoManga=G,s.normalizeGtin=y,s.normalizeIsbn=l,s.parseAppleDisplayOptionsXml=Y,s.parseComicInfo=V,s.parseKoboXml=$,s.parseOpf=ie,s.parseW3cDtfDate=E,s.resolveArchiveMetadata=pe,s.tokenizeXmlSpaceSeparatedList=v,Object.defineProperty(s,Symbol.toStringTag,{value:"Module"})}));
2
2
  //# sourceMappingURL=index.umd.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.umd.cjs","sources":["../src/apple/parse.ts","../src/comicInfo/manga.ts","../src/comicInfo/parse.ts","../src/kobo/parse.ts","../src/utils/tokenizeXmlSpaceSeparatedList.ts","../src/opf/spineItemrefProperties.ts","../src/opf/parse.ts","../src/apple/resolve.ts","../src/utils/normalizeGtin.ts","../src/utils/normalizeIsbn.ts","../src/comicInfo/resolve.ts","../src/kobo/resolve.ts","../src/utils/parseW3cDtfDate.ts","../src/opf/resolve.ts","../src/resolve.ts"],"sourcesContent":["import type { XmlElement } from \"xmldoc\"\nimport { XmlDocument } from \"xmldoc\"\n\nexport const APPLE_IBOOKS_DISPLAY_OPTIONS_FILENAME =\n \"com.apple.ibooks.display-options.xml\"\n\nexport type AppleDisplayOption = {\n readonly name?: string\n readonly value: string\n}\n\nexport type AppleMetadata = {\n readonly kind: \"apple\"\n readonly displayOptions?: {\n readonly platform?: {\n readonly options: ReadonlyArray<AppleDisplayOption>\n }\n }\n}\n\nconst platformOptionsFromElement = (\n platform: XmlElement,\n): ReadonlyArray<AppleDisplayOption> =>\n platform.childrenNamed(\"option\").map((option) => ({\n name: option.attr?.name,\n value: option.val,\n }))\n\nexport const parseAppleDisplayOptionsXml = (xml: string): AppleMetadata => {\n const doc = new XmlDocument(xml)\n const root = doc.name?.toLowerCase()\n\n if (root !== \"display_options\") {\n return { kind: \"apple\" }\n }\n\n const platformEl = doc.childNamed(\"platform\")\n\n return {\n kind: \"apple\",\n displayOptions: {\n ...(platformEl !== undefined\n ? { platform: { options: platformOptionsFromElement(platformEl) } }\n : {}),\n },\n }\n}\n","/**\n * Allowed `Manga` element values (ComicInfo 2.0 `Manga` simpleType).\n *\n * @see https://anansi-project.github.io/docs/comicinfo/documentation#manga\n */\nexport const COMIC_INFO_MANGA_VALUES = [\n \"Unknown\",\n \"No\",\n \"Yes\",\n \"YesAndRightToLeft\",\n] as const\n\nexport type ComicInfoManga = (typeof COMIC_INFO_MANGA_VALUES)[number]\n\nexport const isComicInfoManga = (value: string): value is ComicInfoManga => {\n for (const v of COMIC_INFO_MANGA_VALUES) {\n if (v === value) return true\n }\n return false\n}\n","import type { XmlElement } from \"xmldoc\"\nimport { XmlDocument } from \"xmldoc\"\nimport type { ComicInfoManga } from \"./manga\"\n\n/** Canonical top-level filename; real archives may use any casing. */\nexport const COMIC_INFO_FILENAME = \"ComicInfo.xml\"\n\n/**\n * Parsed `ComicInfo.xml` root: one optional string property per child element,\n * using the same names as in the file (e.g. `Title`, `GTIN`, `LanguageISO`).\n * Nested blocks such as `Pages` are skipped. Other simple child elements are\n * still copied onto this object under their tag name.\n *\n * @see https://anansi-project.github.io/docs/comicinfo/intro\n * @see https://github.com/anansi-project/comicinfo/blob/main/drafts/v2.1/ComicInfo.xsd for schema\n */\nexport interface ComicInfo {\n readonly kind: \"comicInfo\"\n AgeRating?: string\n AlternateCount?: string\n AlternateNumber?: string\n AlternateSeries?: string\n BlackAndWhite?: string\n Characters?: string\n Colorist?: string\n CommunityRating?: string\n Count?: string\n CoverArtist?: string\n Day?: string\n Editor?: string\n Format?: string\n Genre?: string\n GTIN?: string\n Imprint?: string\n Inker?: string\n LanguageISO?: string\n Letterer?: string\n Locations?: string\n MainCharacterOrTeam?: string\n /** Schema literals per {@link ComicInfoManga}; files may still use other strings. */\n Manga?: ComicInfoManga | (string & {})\n Month?: string\n Notes?: string\n Number?: string\n PageCount?: string\n Penciller?: string\n Publisher?: string\n Review?: string\n ScanInformation?: string\n Series?: string\n SeriesGroup?: string\n StoryArc?: string\n StoryArcNumber?: string\n Summary?: string\n Tags?: string\n Teams?: string\n Title?: string\n Translator?: string\n Volume?: string\n Web?: string\n Writer?: string\n Year?: string\n [tag: string]: string | undefined\n}\n\nconst SKIP_ELEMENT_CHILDREN = new Set([\"Pages\", \"ComicInfo\"])\n\nconst hasNestedElement = (el: XmlElement) =>\n el.children.some((c) => c.type === \"element\")\n\nconst trimmedText = (el: XmlElement): string | undefined => {\n const t = el.val.trim()\n return t.length > 0 ? t : undefined\n}\n\n/**\n * Parse a raw `ComicInfo.xml` body. Each direct child element with plain text\n * becomes a property named after that tag. Malformed XML throws; the parser\n * error is attached as `cause`.\n */\nexport const parseComicInfo = (xml: string): ComicInfo => {\n let doc: XmlDocument\n try {\n doc = new XmlDocument(xml)\n } catch (cause) {\n const message = cause instanceof Error ? cause.message : String(cause)\n throw new Error(`${COMIC_INFO_FILENAME} is malformed: ${message}`, {\n cause,\n })\n }\n\n const fields: Record<string, string> = {}\n\n doc.eachChild((child) => {\n if (child.type !== \"element\") return\n if (SKIP_ELEMENT_CHILDREN.has(child.name)) return\n if (hasNestedElement(child)) return\n\n const text = trimmedText(child)\n if (text === undefined) return\n if (fields[child.name] !== undefined) return\n\n fields[child.name] = text\n })\n\n // `as ComicInfo`: TS cannot infer that spreading `Record<string, string>` into `{ kind }` satisfies the named optional fields plus `[tag: string]: string | undefined` on `ComicInfo`.\n return { kind: \"comicInfo\", ...fields } as ComicInfo\n}\n","import { XmlDocument } from \"xmldoc\"\n\nexport const KOBO_DISPLAY_OPTIONS_FILENAME = \"com.kobobooks.display-options.xml\"\n\n/**\n * Normalized fields from Kobo-specific XML sidecars. Grows as new Kobo\n * document kinds are supported; absent keys mean not present or unknown.\n */\nexport type KoboMetadata = {\n readonly kind: \"kobo\"\n renditionLayout?: `reflowable` | `pre-paginated`\n}\n\nconst parseKoboDisplayOptionsDocument = (\n doc: XmlDocument,\n): { renditionLayout?: \"pre-paginated\" } => {\n const platform = doc.childNamed(\"platform\")\n if (!platform) return {}\n\n for (const option of platform.childrenNamed(\"option\")) {\n if (option.attr?.name !== \"fixed-layout\") continue\n if (option.val.trim().toLowerCase() === \"true\") {\n return { renditionLayout: \"pre-paginated\" }\n }\n return {}\n }\n\n return {}\n}\n\n/**\n * Parse a Kobo-related XML document. Unsupported or unrecognized roots\n * yield an empty object. Malformed XML throws from the underlying parser.\n */\nexport const parseKoboXml = (xml: string): KoboMetadata => {\n const doc = new XmlDocument(xml)\n const root = doc.name?.toLowerCase()\n\n if (root === \"display_options\") {\n return { kind: \"kobo\", ...parseKoboDisplayOptionsDocument(doc) }\n }\n\n return { kind: \"kobo\" }\n}\n","/**\n * EPUB/XML space-separated token lists (e.g. `properties` on `item` / `itemref`).\n * Trims the raw string, splits on ASCII whitespace, drops empty segments.\n */\nexport const tokenizeXmlSpaceSeparatedList = (\n raw: string | undefined,\n): readonly string[] => {\n if (raw === undefined || raw.trim().length === 0) {\n return []\n }\n\n return raw\n .trim()\n .split(/\\s+/)\n .filter((t) => t.length > 0)\n}\n","import { tokenizeXmlSpaceSeparatedList } from \"../utils/tokenizeXmlSpaceSeparatedList\"\n\nexport type OpfItemrefLayoutHints = {\n readonly renditionLayout?: `reflowable` | `pre-paginated`\n readonly pageSpreadLeft?: true\n readonly pageSpreadRight?: true\n}\n\n/**\n * EPUB `itemref` `properties` attribute (space-separated tokens).\n *\n * @see https://www.w3.org/TR/epub/#attrdef-properties\n */\nexport const layoutHintsFromItemrefProperties = (\n properties: string | undefined,\n): OpfItemrefLayoutHints => {\n const tokens = tokenizeXmlSpaceSeparatedList(properties)\n if (tokens.length === 0) {\n return {}\n }\n\n let renditionLayout: `reflowable` | `pre-paginated` | undefined\n if (tokens.includes(`rendition:layout-reflowable`)) {\n renditionLayout = `reflowable`\n }\n if (tokens.includes(`rendition:layout-pre-paginated`)) {\n renditionLayout = `pre-paginated`\n }\n\n return {\n ...(renditionLayout !== undefined ? { renditionLayout } : {}),\n ...(tokens.includes(`page-spread-left`)\n ? { pageSpreadLeft: true as const }\n : {}),\n ...(tokens.includes(`page-spread-right`)\n ? { pageSpreadRight: true as const }\n : {}),\n }\n}\n","import type { XmlElement, XmlNodeBase } from \"xmldoc\"\nimport { XmlDocument } from \"xmldoc\"\nimport { tokenizeXmlSpaceSeparatedList } from \"../utils/tokenizeXmlSpaceSeparatedList\"\nimport { layoutHintsFromItemrefProperties } from \"./spineItemrefProperties\"\n\nexport type OpfSpineManifestItem = {\n readonly id: string\n readonly href: string\n readonly mediaType?: string\n readonly properties?: string\n}\n\nexport type OpfIdentifier = {\n readonly value: string\n readonly scheme?: string\n}\n\nexport type OpfSpineRow = {\n readonly idref: string\n readonly id: string\n readonly href: string\n readonly mediaType?: string\n readonly properties?: string\n readonly renditionLayout?: `reflowable` | `pre-paginated`\n readonly pageSpreadLeft?: true\n readonly pageSpreadRight?: true\n}\n\nexport type OpfGuideReference = {\n readonly href: string\n readonly title: string\n readonly type: string\n}\n\nconst elementLocalName = (name: string): string =>\n name.includes(\":\") ? name.slice(name.lastIndexOf(\":\") + 1) : name\n\nconst localNameEq = (elementName: string, wantLocal: string): boolean =>\n elementLocalName(elementName).toLowerCase() === wantLocal.toLowerCase()\n\nconst isXmlElement = (node: XmlNodeBase): node is XmlElement =>\n node.type === \"element\"\n\nconst childNamedLocal = (\n parent: XmlElement,\n localName: string,\n): XmlElement | undefined => {\n for (const node of parent.children) {\n if (!isXmlElement(node)) continue\n if (localNameEq(node.name, localName)) return node\n }\n return undefined\n}\n\nconst childrenNamedLocal = (\n parent: XmlElement,\n localName: string,\n): XmlElement[] => {\n const out: XmlElement[] = []\n for (const node of parent.children) {\n if (!isXmlElement(node)) continue\n if (localNameEq(node.name, localName)) out.push(node)\n }\n return out\n}\n\nconst identifiersFromMetadata = (metadataEl: XmlElement): OpfIdentifier[] => {\n const identifiers: OpfIdentifier[] = []\n\n metadataEl.eachChild((child) => {\n if (elementLocalName(child.name).toLowerCase() !== \"identifier\") return\n\n const value = child.val.trim()\n if (value.length === 0) return\n\n const scheme =\n child.attr[\"opf:scheme\"] ?? child.attr[\"opf:Scheme\"] ?? child.attr.scheme\n const schemeTrimmed = scheme?.trim()\n\n identifiers.push({\n value,\n ...(schemeTrimmed !== undefined && schemeTrimmed.length > 0\n ? { scheme: schemeTrimmed }\n : {}),\n })\n })\n\n return identifiers\n}\n\nconst firstTextByLocalName = (\n metadataEl: XmlElement,\n localName: string,\n): string | undefined => {\n let found: string | undefined\n metadataEl.eachChild((child) => {\n if (found !== undefined) return\n if (elementLocalName(child.name).toLowerCase() !== localName.toLowerCase())\n return\n const t = child.val.trim()\n if (t.length > 0) found = t\n })\n return found\n}\n\nconst textsByLocalName = (\n metadataEl: XmlElement,\n localName: string,\n): string[] => {\n const out: string[] = []\n metadataEl.eachChild((child) => {\n if (elementLocalName(child.name).toLowerCase() !== localName.toLowerCase())\n return\n const t = child.val.trim()\n if (t.length > 0) out.push(t)\n })\n return out\n}\n\nconst coverContentIdFromMetadata = (\n metadataEl: XmlElement,\n): string | undefined => {\n let coverId: string | undefined\n metadataEl.eachChild((child) => {\n if (coverId !== undefined) return\n if (elementLocalName(child.name).toLowerCase() !== \"meta\") return\n if (child.attr.name?.toLowerCase() !== \"cover\") return\n const content = child.attr.content?.trim()\n if (content !== undefined && content.length > 0) coverId = content\n })\n return coverId\n}\n\n/**\n * EPUB cover image inside the manifest. Resolution order, matching\n * what the spec lays out and what the bulk of EPUB producers in the\n * wild rely on:\n *\n * 1. EPUB 3 — the manifest item carrying the `cover-image` token in\n * its `properties` attribute (§ D.6.1).\n * 2. EPUB 2 — `<meta name=\"cover\" content=\"ID\"/>` in `metadata`,\n * resolved to the manifest item with that `id`.\n * 3. Last-resort fallback — any image manifest item whose `id`\n * contains the substring `cover` (case-insensitive); covers the\n * long tail of producers that emit neither the EPUB 3 property\n * nor the EPUB 2 meta.\n *\n * Each step requires the candidate manifest item to advertise an\n * `image/*` media type so non-image artefacts named `cover` (XHTML\n * cover pages, NCX entries) don't slip through.\n */\nconst coverHrefFromManifestAndMetadata = ({\n manifestItems,\n metadataEl,\n}: {\n manifestItems: ReadonlyArray<OpfSpineManifestItem>\n metadataEl: XmlElement | undefined\n}): string | undefined => {\n const isImage = (item: OpfSpineManifestItem): boolean =>\n item.mediaType?.toLowerCase().includes(\"image/\") === true\n\n const byCoverImageProperty = manifestItems.find((item) => {\n if (!isImage(item)) return false\n return tokenizeXmlSpaceSeparatedList(item.properties).includes(\n \"cover-image\",\n )\n })\n if (byCoverImageProperty !== undefined) return byCoverImageProperty.href\n\n if (metadataEl !== undefined) {\n const coverContentId = coverContentIdFromMetadata(metadataEl)\n if (coverContentId !== undefined) {\n const match = manifestItems.find(\n (item) => item.id === coverContentId && isImage(item),\n )\n if (match !== undefined) return match.href\n }\n }\n\n return manifestItems.find(\n (item) => item.id.toLowerCase().includes(\"cover\") && isImage(item),\n )?.href\n}\n\nconst metaValByProperty = (\n metadataEl: XmlElement,\n property: string,\n): string | undefined => {\n const meta = childrenNamedLocal(metadataEl, \"meta\").find(\n (m) => m.attr.property === property,\n )\n const raw = meta?.val\n if (raw === undefined || raw.trim().length === 0) return undefined\n return raw\n}\n\nconst guideFromPackage = (doc: XmlElement): OpfGuideReference[] => {\n const guideEl = childNamedLocal(doc, \"guide\")\n if (guideEl === undefined) return []\n\n const refs: OpfGuideReference[] = []\n\n for (const ref of childrenNamedLocal(guideEl, \"reference\")) {\n const href = ref.attr.href?.trim()\n if (href === undefined || href.length === 0) continue\n refs.push({\n href,\n title: ref.attr.title?.trim() ?? ``,\n type: ref.attr.type?.trim() ?? ``,\n })\n }\n\n return refs\n}\n\nconst manifestItemFromXmlElement = (\n item: XmlElement,\n): OpfSpineManifestItem | undefined => {\n const id = item.attr.id\n const href = item.attr.href\n if (id === undefined || id.length === 0) return undefined\n if (href === undefined || href.length === 0) return undefined\n\n const mediaType = item.attr[\"media-type\"]\n const properties = item.attr.properties?.trim()\n return {\n id,\n href,\n ...(mediaType !== undefined && mediaType.length > 0 ? { mediaType } : {}),\n ...(properties !== undefined && properties.length > 0\n ? { properties }\n : {}),\n }\n}\n\nconst manifestItemsAndById = (\n manifestEl: XmlElement,\n): {\n items: OpfSpineManifestItem[]\n byId: Map<string, OpfSpineManifestItem>\n} => {\n const items: OpfSpineManifestItem[] = []\n const byId = new Map<string, OpfSpineManifestItem>()\n\n for (const el of childrenNamedLocal(manifestEl, \"item\")) {\n const parsed = manifestItemFromXmlElement(el)\n if (parsed === undefined) continue\n items.push(parsed)\n byId.set(parsed.id, parsed)\n }\n\n return { items, byId }\n}\n\nconst spineRowsFromByIdAndSpine = (\n byId: Map<string, OpfSpineManifestItem>,\n spineEl: XmlElement,\n): OpfSpineRow[] => {\n const rows: OpfSpineRow[] = []\n\n for (const itemref of childrenNamedLocal(spineEl, \"itemref\")) {\n const idref = itemref.attr.idref\n if (idref === undefined || idref.trim().length === 0) continue\n\n const manifestItem = byId.get(idref)\n if (manifestItem === undefined) continue\n\n const hints = layoutHintsFromItemrefProperties(itemref.attr.properties)\n\n rows.push({\n idref,\n id: manifestItem.id,\n href: manifestItem.href,\n ...(manifestItem.mediaType !== undefined\n ? { mediaType: manifestItem.mediaType }\n : {}),\n ...(manifestItem.properties !== undefined\n ? { properties: manifestItem.properties }\n : {}),\n ...(hints.renditionLayout !== undefined\n ? { renditionLayout: hints.renditionLayout }\n : {}),\n ...(hints.pageSpreadLeft !== undefined\n ? { pageSpreadLeft: hints.pageSpreadLeft }\n : {}),\n ...(hints.pageSpreadRight !== undefined\n ? { pageSpreadRight: hints.pageSpreadRight }\n : {}),\n })\n }\n\n return rows\n}\n\nexport type OpfMetadata = {\n readonly kind: \"opf\"\n readonly manifestItems: ReadonlyArray<OpfSpineManifestItem>\n readonly spineRows: ReadonlyArray<OpfSpineRow>\n readonly spineTocIdref: string | undefined\n readonly identifiers: ReadonlyArray<OpfIdentifier>\n readonly title: string | undefined\n /** `dc:creator` values, in document order, trimmed; empty when none. */\n readonly creators: ReadonlyArray<string>\n /** First non-empty `dc:publisher`, trimmed. */\n readonly publisher: string | undefined\n /** First non-empty `dc:rights`, trimmed. */\n readonly rights: string | undefined\n /** `dc:language` values, in document order, trimmed; empty when none. */\n readonly languages: ReadonlyArray<string>\n /** `dc:subject` values, in document order, trimmed; empty when none. */\n readonly subjects: ReadonlyArray<string>\n /**\n * Raw `dc:date` value as authored. EPUB 3 requires W3CDTF (a profile\n * of ISO 8601), but real-world publishers also ship free text here,\n * so the value is exposed verbatim and consumers normalize as needed.\n */\n readonly date: string | undefined\n /**\n * Manifest-relative `href` of the cover image, when one can be\n * resolved from `cover-image` properties (EPUB 3), the EPUB 2\n * `<meta name=\"cover\">` convention, or an `id` that contains\n * `cover` on an image manifest item. The href is returned exactly\n * as it appears in the manifest — callers own folder-prefix\n * resolution against the OPF's location in the archive.\n */\n readonly coverHref: string | undefined\n readonly renditionLayoutMeta: string | undefined\n readonly renditionFlowMeta: string | undefined\n readonly renditionSpreadMeta: string | undefined\n readonly pageProgressionDirection: string | undefined\n readonly guide: ReadonlyArray<OpfGuideReference>\n}\n\n/**\n * Parses an EPUB package document (OPF) into structured metadata.\n *\n * Direct children of `package` (`metadata`, `manifest`, `spine`, `guide`) and\n * their structural children (`item`, `itemref`, `reference`, `meta`) are\n * matched by **local name** (ASCII case-insensitive), so prefixed tags such as\n * `opf:manifest` are supported the same as unprefixed `manifest`.\n *\n * Attribute names on `spine` / `itemref` are still read as emitted by xmldoc\n * (no QName normalization).\n */\nexport const parseOpf = (opfXml: string): OpfMetadata => {\n const doc = new XmlDocument(opfXml)\n const manifestEl = childNamedLocal(doc, \"manifest\")\n const spineEl = childNamedLocal(doc, \"spine\")\n const metadataEl = childNamedLocal(doc, \"metadata\")\n\n let manifestItems: OpfSpineManifestItem[] = []\n let spineRows: OpfSpineRow[] = []\n\n if (manifestEl !== undefined) {\n const { items, byId } = manifestItemsAndById(manifestEl)\n manifestItems = items\n if (spineEl !== undefined) {\n spineRows = spineRowsFromByIdAndSpine(byId, spineEl)\n }\n }\n\n const pageProgressionDirectionRaw =\n spineEl?.attr[\"page-progression-direction\"]\n const pageProgressionDirection =\n pageProgressionDirectionRaw !== undefined &&\n pageProgressionDirectionRaw.trim().length > 0\n ? pageProgressionDirectionRaw\n : undefined\n\n const spineTocRaw = spineEl?.attr.toc\n const spineTocIdref =\n spineTocRaw !== undefined && spineTocRaw.trim().length > 0\n ? spineTocRaw.trim()\n : undefined\n\n let title: string | undefined\n let publisher: string | undefined\n let rights: string | undefined\n let date: string | undefined\n let creators: string[] = []\n let languages: string[] = []\n let subjects: string[] = []\n let renditionLayoutMeta: string | undefined\n let renditionFlowMeta: string | undefined\n let renditionSpreadMeta: string | undefined\n const identifiers: OpfIdentifier[] = []\n\n if (metadataEl !== undefined) {\n title = firstTextByLocalName(metadataEl, \"title\")\n publisher = firstTextByLocalName(metadataEl, \"publisher\")\n rights = firstTextByLocalName(metadataEl, \"rights\")\n date = firstTextByLocalName(metadataEl, \"date\")\n creators = textsByLocalName(metadataEl, \"creator\")\n languages = textsByLocalName(metadataEl, \"language\")\n subjects = textsByLocalName(metadataEl, \"subject\")\n renditionLayoutMeta = metaValByProperty(metadataEl, \"rendition:layout\")\n renditionFlowMeta = metaValByProperty(metadataEl, \"rendition:flow\")\n renditionSpreadMeta = metaValByProperty(metadataEl, \"rendition:spread\")\n identifiers.push(...identifiersFromMetadata(metadataEl))\n }\n\n const coverHref = coverHrefFromManifestAndMetadata({\n manifestItems,\n metadataEl,\n })\n\n const guide = guideFromPackage(doc)\n\n return {\n kind: \"opf\",\n manifestItems,\n spineRows,\n spineTocIdref,\n identifiers,\n title,\n creators,\n publisher,\n rights,\n languages,\n subjects,\n date,\n coverHref,\n renditionLayoutMeta,\n renditionFlowMeta,\n renditionSpreadMeta,\n pageProgressionDirection,\n guide,\n }\n}\n","import type { ArchiveResolveResult } from \"../types/archiveResolve\"\nimport type { AppleMetadata } from \"./parse\"\n\nexport const resolveApple = (input: AppleMetadata): ArchiveResolveResult => {\n const fixedLayout = input.displayOptions?.platform?.options?.find(\n (o) => o.name === \"fixed-layout\",\n )?.value\n\n const renditionLayout =\n fixedLayout?.trim().toLowerCase() === \"true\" ? \"pre-paginated\" : undefined\n\n return {\n gtin: undefined,\n isbn: undefined,\n readingDirection: undefined,\n renditionLayout,\n title: undefined,\n authors: undefined,\n publisher: undefined,\n rights: undefined,\n languages: undefined,\n date: undefined,\n subjects: undefined,\n }\n}\n","const GTIN_LENGTHS = new Set([8, 12, 13, 14])\n\n/**\n * Normalize a raw GTIN / EAN / UPC string to digits only when the length\n * matches a GS1 GTIN family size (8, 12, 13, or 14). Does not verify check digits.\n */\nexport const normalizeGtin = (\n raw: string | number | undefined | null,\n): string | undefined => {\n if (raw === undefined || raw === null) return undefined\n\n const digits = String(raw).replace(/\\D/g, \"\")\n if (digits.length === 0 || !GTIN_LENGTHS.has(digits.length)) return undefined\n\n return digits\n}\n","const ISBN_CANDIDATE_PATTERN = /(?:97[89])?\\d{9}[\\dXx]/\n\n/**\n * Normalize a raw ISBN-ish string into a canonical 10- or 13-character\n * form, or `undefined` when no recognisable ISBN can be recovered.\n *\n * - Strips the common `urn:isbn:` / `isbn:` prefixes.\n * - Drops everything that isn't a digit or `X`.\n * - Validates the resulting length (10 or 13).\n * - Falls back to a lax regex scan so publishers that stuff free text\n * around the number still yield a usable value.\n */\nexport const normalizeIsbn = (\n raw: string | number | undefined | null,\n): string | undefined => {\n if (raw === undefined || raw === null) return undefined\n\n const stripped = String(raw)\n .trim()\n .replace(/^urn:isbn:/i, \"\")\n .replace(/^isbn[:\\s-]*/i, \"\")\n\n const digitsOnly = stripped.replace(/[^0-9Xx]/g, \"\")\n\n if (digitsOnly.length === 10 || digitsOnly.length === 13) {\n return digitsOnly.toUpperCase()\n }\n\n const match = stripped.match(ISBN_CANDIDATE_PATTERN)\n if (match) return match[0].toUpperCase()\n\n return undefined\n}\n","import type { ArchiveResolveResult } from \"../types/archiveResolve\"\nimport { normalizeGtin } from \"../utils/normalizeGtin\"\nimport { normalizeIsbn } from \"../utils/normalizeIsbn\"\nimport type { ComicInfo } from \"./parse\"\n\nconst readingDirection = (info: ComicInfo): \"ltr\" | \"rtl\" =>\n info.Manga === \"YesAndRightToLeft\" ? \"rtl\" : \"ltr\"\n\nconst trimToUndefined = (raw: string | undefined): string | undefined => {\n if (raw === undefined) return undefined\n const trimmed = raw.trim()\n return trimmed.length > 0 ? trimmed : undefined\n}\n\n/**\n * Split a comma-separated ComicInfo value into its individual tokens.\n * `Writer`, `Genre`, and `Tags` all follow the same de-facto convention:\n * tokens separated by `,`, with whitespace trimmed and empty tokens\n * dropped (real-world files leave trailing commas around).\n */\nconst splitCommaList = (raw: string | undefined): string[] => {\n if (raw === undefined) return []\n return raw\n .split(\",\")\n .map((token) => token.trim())\n .filter((token) => token.length > 0)\n}\n\nconst parseNonNegativeInt = (raw: string | undefined): number | undefined => {\n const trimmed = trimToUndefined(raw)\n if (trimmed === undefined) return undefined\n if (!/^\\d+$/.test(trimmed)) return undefined\n const n = Number.parseInt(trimmed, 10)\n return Number.isFinite(n) ? n : undefined\n}\n\nconst dateFromYearMonthDay = (\n info: ComicInfo,\n):\n | {\n year?: number\n month?: number\n day?: number\n }\n | undefined => {\n const year = parseNonNegativeInt(info.Year)\n const month = parseNonNegativeInt(info.Month)\n const day = parseNonNegativeInt(info.Day)\n\n if (year === undefined && month === undefined && day === undefined) {\n return undefined\n }\n\n return {\n ...(year !== undefined ? { year } : {}),\n ...(month !== undefined ? { month } : {}),\n ...(day !== undefined ? { day } : {}),\n }\n}\n\nexport const resolveComicInfo = (info: ComicInfo): ArchiveResolveResult => {\n const raw = info.GTIN\n const languageIso = trimToUndefined(info.LanguageISO)\n const authors = splitCommaList(info.Writer)\n const subjects = [...splitCommaList(info.Genre), ...splitCommaList(info.Tags)]\n\n return {\n gtin: normalizeGtin(raw),\n isbn: normalizeIsbn(raw),\n readingDirection: readingDirection(info),\n renditionLayout: undefined,\n title: trimToUndefined(info.Title),\n authors: authors.length > 0 ? authors : undefined,\n publisher: trimToUndefined(info.Publisher),\n rights: undefined,\n languages: languageIso !== undefined ? [languageIso] : undefined,\n date: dateFromYearMonthDay(info),\n subjects: subjects.length > 0 ? subjects : undefined,\n }\n}\n","import type { ArchiveResolveResult } from \"../types/archiveResolve\"\nimport type { KoboMetadata } from \"./parse\"\n\nexport const resolveKobo = (input: KoboMetadata): ArchiveResolveResult => ({\n gtin: undefined,\n isbn: undefined,\n readingDirection: undefined,\n renditionLayout: input.renditionLayout,\n title: undefined,\n authors: undefined,\n publisher: undefined,\n rights: undefined,\n languages: undefined,\n date: undefined,\n subjects: undefined,\n})\n","/**\n * Extract the calendar components of a W3CDTF literal — the\n * date subset of ISO 8601 that EPUB 3.3 § 5.5.3.2.4 mandates for\n * `dc:date`. Accepted forms: `YYYY`, `YYYY-MM`, `YYYY-MM-DD`, and\n * any of the above followed by a `Thh:mm[:ss[.s]][TZD]` time\n * portion that we ignore.\n *\n * Components are returned as plain integers (`month` is 1-12,\n * `day` is 1-31), independent of the host timezone — using\n * `Date.parse` would shift `2011-01-01` by a day in negative-offset\n * locales, which is the exact bug this regex-based approach exists\n * to avoid. Returns `undefined` when the input doesn't even match\n * a leading 4-digit year so consumers can fall back without\n * branching on partial shapes.\n */\nexport const parseW3cDtfDate = (\n raw: string | undefined,\n):\n | {\n year?: number\n month?: number\n day?: number\n }\n | undefined => {\n if (raw === undefined) return undefined\n\n const match = raw.trim().match(/^(\\d{4})(?:-(\\d{2})(?:-(\\d{2}))?)?/)\n if (match === null) return undefined\n\n const [, yearRaw, monthRaw, dayRaw] = match\n\n return {\n ...(yearRaw !== undefined ? { year: Number.parseInt(yearRaw, 10) } : {}),\n ...(monthRaw !== undefined ? { month: Number.parseInt(monthRaw, 10) } : {}),\n ...(dayRaw !== undefined ? { day: Number.parseInt(dayRaw, 10) } : {}),\n }\n}\n","import type { ArchiveResolveResult } from \"../types/archiveResolve\"\nimport { normalizeGtin } from \"../utils/normalizeGtin\"\nimport { normalizeIsbn } from \"../utils/normalizeIsbn\"\nimport { parseW3cDtfDate } from \"../utils/parseW3cDtfDate\"\nimport type { OpfIdentifier, OpfMetadata } from \"./parse\"\n\nconst rawIdentifierValueForIsbn = (\n identifiers: ReadonlyArray<OpfIdentifier>,\n): string | undefined => {\n for (const i of identifiers) {\n if (i.scheme !== undefined && i.scheme.trim().toLowerCase() === \"isbn\") {\n if (normalizeIsbn(i.value) !== undefined) return i.value\n }\n }\n\n for (const i of identifiers) {\n if (normalizeIsbn(i.value) !== undefined) return i.value\n }\n\n return undefined\n}\n\nexport const resolveOpf = (input: OpfMetadata): ArchiveResolveResult => {\n const ppd = input.pageProgressionDirection?.trim().toLowerCase()\n const readingDirection = ppd === \"ltr\" || ppd === \"rtl\" ? ppd : undefined\n\n const rl = input.renditionLayoutMeta?.trim().toLowerCase()\n const renditionLayout =\n rl === \"reflowable\" || rl === \"pre-paginated\" ? rl : undefined\n\n const raw = rawIdentifierValueForIsbn(input.identifiers)\n\n return {\n gtin: normalizeGtin(raw),\n isbn: normalizeIsbn(raw),\n readingDirection,\n renditionLayout,\n title: input.title,\n authors: input.creators.length > 0 ? [...input.creators] : undefined,\n publisher: input.publisher,\n rights: input.rights,\n languages: input.languages.length > 0 ? [...input.languages] : undefined,\n date: parseW3cDtfDate(input.date),\n subjects: input.subjects.length > 0 ? [...input.subjects] : undefined,\n }\n}\n","import type { AppleMetadata } from \"./apple/parse\"\nimport { resolveApple } from \"./apple/resolve\"\nimport type { ComicInfo } from \"./comicInfo/parse\"\nimport { resolveComicInfo } from \"./comicInfo/resolve\"\nimport type { KoboMetadata } from \"./kobo/parse\"\nimport { resolveKobo } from \"./kobo/resolve\"\nimport type { OpfMetadata } from \"./opf/parse\"\nimport { resolveOpf } from \"./opf/resolve\"\nimport type { ArchiveResolveResult } from \"./types/archiveResolve\"\n\nexport type ResolvedArchiveInput =\n | ComicInfo\n | KoboMetadata\n | AppleMetadata\n | OpfMetadata\n\nexport const resolveArchiveMetadata = (\n input: ResolvedArchiveInput,\n): ArchiveResolveResult => {\n if (input.kind === \"comicInfo\") return resolveComicInfo(input)\n if (input.kind === \"kobo\") return resolveKobo(input)\n if (input.kind === \"apple\") return resolveApple(input)\n return resolveOpf(input)\n}\n"],"names":["APPLE_IBOOKS_DISPLAY_OPTIONS_FILENAME","platformOptionsFromElement","platform","option","parseAppleDisplayOptionsXml","xml","doc","XmlDocument","platformEl","COMIC_INFO_MANGA_VALUES","isComicInfoManga","value","v","COMIC_INFO_FILENAME","SKIP_ELEMENT_CHILDREN","hasNestedElement","el","c","trimmedText","parseComicInfo","cause","message","fields","child","text","KOBO_DISPLAY_OPTIONS_FILENAME","parseKoboDisplayOptionsDocument","parseKoboXml","tokenizeXmlSpaceSeparatedList","raw","layoutHintsFromItemrefProperties","properties","tokens","renditionLayout","elementLocalName","name","localNameEq","elementName","wantLocal","isXmlElement","node","childNamedLocal","parent","localName","childrenNamedLocal","out","identifiersFromMetadata","metadataEl","identifiers","schemeTrimmed","firstTextByLocalName","found","t","textsByLocalName","coverContentIdFromMetadata","coverId","content","coverHrefFromManifestAndMetadata","manifestItems","isImage","item","byCoverImageProperty","coverContentId","match","metaValByProperty","property","m","guideFromPackage","guideEl","refs","ref","href","manifestItemFromXmlElement","id","mediaType","manifestItemsAndById","manifestEl","items","byId","parsed","spineRowsFromByIdAndSpine","spineEl","rows","itemref","idref","manifestItem","hints","parseOpf","opfXml","spineRows","pageProgressionDirectionRaw","pageProgressionDirection","spineTocRaw","spineTocIdref","title","publisher","rights","date","creators","languages","subjects","renditionLayoutMeta","renditionFlowMeta","renditionSpreadMeta","coverHref","guide","resolveApple","input","o","GTIN_LENGTHS","normalizeGtin","digits","ISBN_CANDIDATE_PATTERN","normalizeIsbn","stripped","digitsOnly","readingDirection","info","trimToUndefined","trimmed","splitCommaList","token","parseNonNegativeInt","n","dateFromYearMonthDay","year","month","day","resolveComicInfo","languageIso","authors","resolveKobo","parseW3cDtfDate","yearRaw","monthRaw","dayRaw","rawIdentifierValueForIsbn","i","resolveOpf","ppd","rl","resolveArchiveMetadata"],"mappings":"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"}
1
+ {"version":3,"file":"index.umd.cjs","sources":["../src/apple/parse.ts","../src/comicInfo/manga.ts","../src/comicInfo/parse.ts","../src/kobo/parse.ts","../src/utils/tokenizeXmlSpaceSeparatedList.ts","../src/opf/spineItemrefProperties.ts","../src/opf/parse.ts","../src/apple/resolve.ts","../src/utils/normalizeGtin.ts","../src/utils/normalizeIsbn.ts","../src/comicInfo/resolve.ts","../src/kobo/resolve.ts","../src/utils/parseW3cDtfDate.ts","../src/opf/resolve.ts","../src/resolve.ts"],"sourcesContent":["import type { XmlElement } from \"xmldoc\"\nimport { XmlDocument } from \"xmldoc\"\n\nexport const APPLE_IBOOKS_DISPLAY_OPTIONS_FILENAME =\n \"com.apple.ibooks.display-options.xml\"\n\nexport type AppleDisplayOption = {\n readonly name?: string\n readonly value: string\n}\n\nexport type AppleMetadata = {\n readonly kind: \"apple\"\n readonly displayOptions?: {\n readonly platform?: {\n readonly options: ReadonlyArray<AppleDisplayOption>\n }\n }\n}\n\nconst platformOptionsFromElement = (\n platform: XmlElement,\n): ReadonlyArray<AppleDisplayOption> =>\n platform.childrenNamed(\"option\").map((option) => ({\n name: option.attr?.name,\n value: option.val,\n }))\n\nexport const parseAppleDisplayOptionsXml = (xml: string): AppleMetadata => {\n const doc = new XmlDocument(xml)\n const root = doc.name?.toLowerCase()\n\n if (root !== \"display_options\") {\n return { kind: \"apple\" }\n }\n\n const platformEl = doc.childNamed(\"platform\")\n\n return {\n kind: \"apple\",\n displayOptions: {\n ...(platformEl !== undefined\n ? { platform: { options: platformOptionsFromElement(platformEl) } }\n : {}),\n },\n }\n}\n","/**\n * Allowed `Manga` element values (ComicInfo 2.0 `Manga` simpleType).\n *\n * @see https://anansi-project.github.io/docs/comicinfo/documentation#manga\n */\nexport const COMIC_INFO_MANGA_VALUES = [\n \"Unknown\",\n \"No\",\n \"Yes\",\n \"YesAndRightToLeft\",\n] as const\n\nexport type ComicInfoManga = (typeof COMIC_INFO_MANGA_VALUES)[number]\n\nexport const isComicInfoManga = (value: string): value is ComicInfoManga => {\n for (const v of COMIC_INFO_MANGA_VALUES) {\n if (v === value) return true\n }\n return false\n}\n","import type { XmlElement } from \"xmldoc\"\nimport { XmlDocument } from \"xmldoc\"\nimport type { ComicInfoManga } from \"./manga\"\n\n/** Canonical top-level filename; real archives may use any casing. */\nexport const COMIC_INFO_FILENAME = \"ComicInfo.xml\"\n\n/**\n * Parsed `ComicInfo.xml` root: one optional string property per child element,\n * using the same names as in the file (e.g. `Title`, `GTIN`, `LanguageISO`).\n * Nested blocks such as `Pages` are skipped. Other simple child elements are\n * still copied onto this object under their tag name.\n *\n * @see https://anansi-project.github.io/docs/comicinfo/intro\n * @see https://github.com/anansi-project/comicinfo/blob/main/drafts/v2.1/ComicInfo.xsd for schema\n */\nexport interface ComicInfo {\n readonly kind: \"comicInfo\"\n AgeRating?: string\n AlternateCount?: string\n AlternateNumber?: string\n AlternateSeries?: string\n BlackAndWhite?: string\n Characters?: string\n Colorist?: string\n CommunityRating?: string\n Count?: string\n CoverArtist?: string\n Day?: string\n Editor?: string\n Format?: string\n Genre?: string\n GTIN?: string\n Imprint?: string\n Inker?: string\n LanguageISO?: string\n Letterer?: string\n Locations?: string\n MainCharacterOrTeam?: string\n /** Schema literals per {@link ComicInfoManga}; files may still use other strings. */\n Manga?: ComicInfoManga | (string & {})\n Month?: string\n Notes?: string\n Number?: string\n PageCount?: string\n Penciller?: string\n Publisher?: string\n Review?: string\n ScanInformation?: string\n Series?: string\n SeriesGroup?: string\n StoryArc?: string\n StoryArcNumber?: string\n Summary?: string\n Tags?: string\n Teams?: string\n Title?: string\n Translator?: string\n Volume?: string\n Web?: string\n Writer?: string\n Year?: string\n [tag: string]: string | undefined\n}\n\nconst SKIP_ELEMENT_CHILDREN = new Set([\"Pages\", \"ComicInfo\"])\n\nconst hasNestedElement = (el: XmlElement) =>\n el.children.some((c) => c.type === \"element\")\n\nconst trimmedText = (el: XmlElement): string | undefined => {\n const t = el.val.trim()\n return t.length > 0 ? t : undefined\n}\n\n/**\n * Parse a raw `ComicInfo.xml` body. Each direct child element with plain text\n * becomes a property named after that tag. Malformed XML throws; the parser\n * error is attached as `cause`.\n */\nexport const parseComicInfo = (xml: string): ComicInfo => {\n let doc: XmlDocument\n try {\n doc = new XmlDocument(xml)\n } catch (cause) {\n const message = cause instanceof Error ? cause.message : String(cause)\n throw new Error(`${COMIC_INFO_FILENAME} is malformed: ${message}`, {\n cause,\n })\n }\n\n const fields: Record<string, string> = {}\n\n doc.eachChild((child) => {\n if (child.type !== \"element\") return\n if (SKIP_ELEMENT_CHILDREN.has(child.name)) return\n if (hasNestedElement(child)) return\n\n const text = trimmedText(child)\n if (text === undefined) return\n if (fields[child.name] !== undefined) return\n\n fields[child.name] = text\n })\n\n // `as ComicInfo`: TS cannot infer that spreading `Record<string, string>` into `{ kind }` satisfies the named optional fields plus `[tag: string]: string | undefined` on `ComicInfo`.\n return { kind: \"comicInfo\", ...fields } as ComicInfo\n}\n","import { XmlDocument } from \"xmldoc\"\n\nexport const KOBO_DISPLAY_OPTIONS_FILENAME = \"com.kobobooks.display-options.xml\"\n\n/**\n * Normalized fields from Kobo-specific XML sidecars. Grows as new Kobo\n * document kinds are supported; absent keys mean not present or unknown.\n */\nexport type KoboMetadata = {\n readonly kind: \"kobo\"\n renditionLayout?: `reflowable` | `pre-paginated`\n}\n\nconst parseKoboDisplayOptionsDocument = (\n doc: XmlDocument,\n): { renditionLayout?: \"pre-paginated\" } => {\n const platform = doc.childNamed(\"platform\")\n if (!platform) return {}\n\n for (const option of platform.childrenNamed(\"option\")) {\n if (option.attr?.name !== \"fixed-layout\") continue\n if (option.val.trim().toLowerCase() === \"true\") {\n return { renditionLayout: \"pre-paginated\" }\n }\n return {}\n }\n\n return {}\n}\n\n/**\n * Parse a Kobo-related XML document. Unsupported or unrecognized roots\n * yield an empty object. Malformed XML throws from the underlying parser.\n */\nexport const parseKoboXml = (xml: string): KoboMetadata => {\n const doc = new XmlDocument(xml)\n const root = doc.name?.toLowerCase()\n\n if (root === \"display_options\") {\n return { kind: \"kobo\", ...parseKoboDisplayOptionsDocument(doc) }\n }\n\n return { kind: \"kobo\" }\n}\n","/**\n * EPUB/XML space-separated token lists (e.g. `properties` on `item` / `itemref`).\n * Trims the raw string, splits on ASCII whitespace, drops empty segments.\n */\nexport const tokenizeXmlSpaceSeparatedList = (\n raw: string | undefined,\n): readonly string[] => {\n if (raw === undefined || raw.trim().length === 0) {\n return []\n }\n\n return raw\n .trim()\n .split(/\\s+/)\n .filter((t) => t.length > 0)\n}\n","import { tokenizeXmlSpaceSeparatedList } from \"../utils/tokenizeXmlSpaceSeparatedList\"\n\nexport type OpfItemrefLayoutHints = {\n readonly renditionLayout?: `reflowable` | `pre-paginated`\n readonly renditionFlow?:\n | `scrolled-continuous`\n | `scrolled-doc`\n | `paginated`\n | `auto`\n readonly pageSpreadLeft?: true\n readonly pageSpreadRight?: true\n}\n\n/**\n * EPUB `itemref` `properties` attribute (space-separated tokens).\n *\n * @see https://www.w3.org/TR/epub/#attrdef-properties\n */\nexport const layoutHintsFromItemrefProperties = (\n properties: string | undefined,\n): OpfItemrefLayoutHints => {\n const tokens = tokenizeXmlSpaceSeparatedList(properties)\n if (tokens.length === 0) {\n return {}\n }\n\n let renditionLayout: `reflowable` | `pre-paginated` | undefined\n if (tokens.includes(`rendition:layout-reflowable`)) {\n renditionLayout = `reflowable`\n }\n if (tokens.includes(`rendition:layout-pre-paginated`)) {\n renditionLayout = `pre-paginated`\n }\n\n let renditionFlow: OpfItemrefLayoutHints[\"renditionFlow\"]\n if (tokens.includes(`rendition:flow-auto`)) {\n renditionFlow = `auto`\n }\n if (tokens.includes(`rendition:flow-paginated`)) {\n renditionFlow = `paginated`\n }\n if (tokens.includes(`rendition:flow-scrolled-doc`)) {\n renditionFlow = `scrolled-doc`\n }\n if (tokens.includes(`rendition:flow-scrolled-continuous`)) {\n renditionFlow = `scrolled-continuous`\n }\n\n return {\n ...(renditionLayout !== undefined ? { renditionLayout } : {}),\n ...(renditionFlow !== undefined ? { renditionFlow } : {}),\n ...(tokens.includes(`page-spread-left`)\n ? { pageSpreadLeft: true as const }\n : {}),\n ...(tokens.includes(`page-spread-right`)\n ? { pageSpreadRight: true as const }\n : {}),\n }\n}\n","import type { XmlElement, XmlNodeBase } from \"xmldoc\"\nimport { XmlDocument } from \"xmldoc\"\nimport { tokenizeXmlSpaceSeparatedList } from \"../utils/tokenizeXmlSpaceSeparatedList\"\nimport { layoutHintsFromItemrefProperties } from \"./spineItemrefProperties\"\n\nexport type OpfSpineManifestItem = {\n readonly id: string\n readonly href: string\n readonly mediaType?: string\n readonly properties?: string\n}\n\nexport type OpfIdentifier = {\n readonly value: string\n readonly scheme?: string\n}\n\nexport type OpfSpineRow = {\n readonly idref: string\n readonly id: string\n readonly href: string\n readonly mediaType?: string\n readonly properties?: string\n readonly renditionLayout?: `reflowable` | `pre-paginated`\n readonly renditionFlow?:\n | `scrolled-continuous`\n | `scrolled-doc`\n | `paginated`\n | `auto`\n readonly pageSpreadLeft?: true\n readonly pageSpreadRight?: true\n}\n\nexport type OpfGuideReference = {\n readonly href: string\n readonly title: string\n readonly type: string\n}\n\nconst elementLocalName = (name: string): string =>\n name.includes(\":\") ? name.slice(name.lastIndexOf(\":\") + 1) : name\n\nconst localNameEq = (elementName: string, wantLocal: string): boolean =>\n elementLocalName(elementName).toLowerCase() === wantLocal.toLowerCase()\n\nconst isXmlElement = (node: XmlNodeBase): node is XmlElement =>\n node.type === \"element\"\n\nconst childNamedLocal = (\n parent: XmlElement,\n localName: string,\n): XmlElement | undefined => {\n for (const node of parent.children) {\n if (!isXmlElement(node)) continue\n if (localNameEq(node.name, localName)) return node\n }\n return undefined\n}\n\nconst childrenNamedLocal = (\n parent: XmlElement,\n localName: string,\n): XmlElement[] => {\n const out: XmlElement[] = []\n for (const node of parent.children) {\n if (!isXmlElement(node)) continue\n if (localNameEq(node.name, localName)) out.push(node)\n }\n return out\n}\n\nconst identifiersFromMetadata = (metadataEl: XmlElement): OpfIdentifier[] => {\n const identifiers: OpfIdentifier[] = []\n\n metadataEl.eachChild((child) => {\n if (elementLocalName(child.name).toLowerCase() !== \"identifier\") return\n\n const value = child.val.trim()\n if (value.length === 0) return\n\n const scheme =\n child.attr[\"opf:scheme\"] ?? child.attr[\"opf:Scheme\"] ?? child.attr.scheme\n const schemeTrimmed = scheme?.trim()\n\n identifiers.push({\n value,\n ...(schemeTrimmed !== undefined && schemeTrimmed.length > 0\n ? { scheme: schemeTrimmed }\n : {}),\n })\n })\n\n return identifiers\n}\n\nconst firstTextByLocalName = (\n metadataEl: XmlElement,\n localName: string,\n): string | undefined => {\n let found: string | undefined\n metadataEl.eachChild((child) => {\n if (found !== undefined) return\n if (elementLocalName(child.name).toLowerCase() !== localName.toLowerCase())\n return\n const t = child.val.trim()\n if (t.length > 0) found = t\n })\n return found\n}\n\nconst textsByLocalName = (\n metadataEl: XmlElement,\n localName: string,\n): string[] => {\n const out: string[] = []\n metadataEl.eachChild((child) => {\n if (elementLocalName(child.name).toLowerCase() !== localName.toLowerCase())\n return\n const t = child.val.trim()\n if (t.length > 0) out.push(t)\n })\n return out\n}\n\nconst coverContentIdFromMetadata = (\n metadataEl: XmlElement,\n): string | undefined => {\n let coverId: string | undefined\n metadataEl.eachChild((child) => {\n if (coverId !== undefined) return\n if (elementLocalName(child.name).toLowerCase() !== \"meta\") return\n if (child.attr.name?.toLowerCase() !== \"cover\") return\n const content = child.attr.content?.trim()\n if (content !== undefined && content.length > 0) coverId = content\n })\n return coverId\n}\n\n/**\n * EPUB cover image inside the manifest. Resolution order, matching\n * what the spec lays out and what the bulk of EPUB producers in the\n * wild rely on:\n *\n * 1. EPUB 3 — the manifest item carrying the `cover-image` token in\n * its `properties` attribute (§ D.6.1).\n * 2. EPUB 2 — `<meta name=\"cover\" content=\"ID\"/>` in `metadata`,\n * resolved to the manifest item with that `id`.\n * 3. Last-resort fallback — any image manifest item whose `id`\n * contains the substring `cover` (case-insensitive); covers the\n * long tail of producers that emit neither the EPUB 3 property\n * nor the EPUB 2 meta.\n *\n * Each step requires the candidate manifest item to advertise an\n * `image/*` media type so non-image artefacts named `cover` (XHTML\n * cover pages, NCX entries) don't slip through.\n */\nconst coverHrefFromManifestAndMetadata = ({\n manifestItems,\n metadataEl,\n}: {\n manifestItems: ReadonlyArray<OpfSpineManifestItem>\n metadataEl: XmlElement | undefined\n}): string | undefined => {\n const isImage = (item: OpfSpineManifestItem): boolean =>\n item.mediaType?.toLowerCase().includes(\"image/\") === true\n\n const byCoverImageProperty = manifestItems.find((item) => {\n if (!isImage(item)) return false\n return tokenizeXmlSpaceSeparatedList(item.properties).includes(\n \"cover-image\",\n )\n })\n if (byCoverImageProperty !== undefined) return byCoverImageProperty.href\n\n if (metadataEl !== undefined) {\n const coverContentId = coverContentIdFromMetadata(metadataEl)\n if (coverContentId !== undefined) {\n const match = manifestItems.find(\n (item) => item.id === coverContentId && isImage(item),\n )\n if (match !== undefined) return match.href\n }\n }\n\n return manifestItems.find(\n (item) => item.id.toLowerCase().includes(\"cover\") && isImage(item),\n )?.href\n}\n\nconst metaValByProperty = (\n metadataEl: XmlElement,\n property: string,\n): string | undefined => {\n const meta = childrenNamedLocal(metadataEl, \"meta\").find(\n (m) => m.attr.property === property,\n )\n const raw = meta?.val\n if (raw === undefined || raw.trim().length === 0) return undefined\n return raw\n}\n\nconst guideFromPackage = (doc: XmlElement): OpfGuideReference[] => {\n const guideEl = childNamedLocal(doc, \"guide\")\n if (guideEl === undefined) return []\n\n const refs: OpfGuideReference[] = []\n\n for (const ref of childrenNamedLocal(guideEl, \"reference\")) {\n const href = ref.attr.href?.trim()\n if (href === undefined || href.length === 0) continue\n refs.push({\n href,\n title: ref.attr.title?.trim() ?? ``,\n type: ref.attr.type?.trim() ?? ``,\n })\n }\n\n return refs\n}\n\nconst manifestItemFromXmlElement = (\n item: XmlElement,\n): OpfSpineManifestItem | undefined => {\n const id = item.attr.id\n const href = item.attr.href\n if (id === undefined || id.length === 0) return undefined\n if (href === undefined || href.length === 0) return undefined\n\n const mediaType = item.attr[\"media-type\"]\n const properties = item.attr.properties?.trim()\n return {\n id,\n href,\n ...(mediaType !== undefined && mediaType.length > 0 ? { mediaType } : {}),\n ...(properties !== undefined && properties.length > 0\n ? { properties }\n : {}),\n }\n}\n\nconst manifestItemsAndById = (\n manifestEl: XmlElement,\n): {\n items: OpfSpineManifestItem[]\n byId: Map<string, OpfSpineManifestItem>\n} => {\n const items: OpfSpineManifestItem[] = []\n const byId = new Map<string, OpfSpineManifestItem>()\n\n for (const el of childrenNamedLocal(manifestEl, \"item\")) {\n const parsed = manifestItemFromXmlElement(el)\n if (parsed === undefined) continue\n items.push(parsed)\n byId.set(parsed.id, parsed)\n }\n\n return { items, byId }\n}\n\nconst spineRowsFromByIdAndSpine = (\n byId: Map<string, OpfSpineManifestItem>,\n spineEl: XmlElement,\n): OpfSpineRow[] => {\n const rows: OpfSpineRow[] = []\n\n for (const itemref of childrenNamedLocal(spineEl, \"itemref\")) {\n const idref = itemref.attr.idref\n if (idref === undefined || idref.trim().length === 0) continue\n\n const manifestItem = byId.get(idref)\n if (manifestItem === undefined) continue\n\n const hints = layoutHintsFromItemrefProperties(itemref.attr.properties)\n\n rows.push({\n idref,\n id: manifestItem.id,\n href: manifestItem.href,\n ...(manifestItem.mediaType !== undefined\n ? { mediaType: manifestItem.mediaType }\n : {}),\n ...(manifestItem.properties !== undefined\n ? { properties: manifestItem.properties }\n : {}),\n ...(hints.renditionLayout !== undefined\n ? { renditionLayout: hints.renditionLayout }\n : {}),\n ...(hints.renditionFlow !== undefined\n ? { renditionFlow: hints.renditionFlow }\n : {}),\n ...(hints.pageSpreadLeft !== undefined\n ? { pageSpreadLeft: hints.pageSpreadLeft }\n : {}),\n ...(hints.pageSpreadRight !== undefined\n ? { pageSpreadRight: hints.pageSpreadRight }\n : {}),\n })\n }\n\n return rows\n}\n\nexport type OpfMetadata = {\n readonly kind: \"opf\"\n readonly manifestItems: ReadonlyArray<OpfSpineManifestItem>\n readonly spineRows: ReadonlyArray<OpfSpineRow>\n readonly spineTocIdref: string | undefined\n readonly identifiers: ReadonlyArray<OpfIdentifier>\n readonly title: string | undefined\n /** `dc:creator` values, in document order, trimmed; empty when none. */\n readonly creators: ReadonlyArray<string>\n /** First non-empty `dc:publisher`, trimmed. */\n readonly publisher: string | undefined\n /** First non-empty `dc:rights`, trimmed. */\n readonly rights: string | undefined\n /** `dc:language` values, in document order, trimmed; empty when none. */\n readonly languages: ReadonlyArray<string>\n /** `dc:subject` values, in document order, trimmed; empty when none. */\n readonly subjects: ReadonlyArray<string>\n /**\n * Raw `dc:date` value as authored. EPUB 3 requires W3CDTF (a profile\n * of ISO 8601), but real-world publishers also ship free text here,\n * so the value is exposed verbatim and consumers normalize as needed.\n */\n readonly date: string | undefined\n /**\n * Manifest-relative `href` of the cover image, when one can be\n * resolved from `cover-image` properties (EPUB 3), the EPUB 2\n * `<meta name=\"cover\">` convention, or an `id` that contains\n * `cover` on an image manifest item. The href is returned exactly\n * as it appears in the manifest — callers own folder-prefix\n * resolution against the OPF's location in the archive.\n */\n readonly coverHref: string | undefined\n readonly renditionLayoutMeta: string | undefined\n readonly renditionFlowMeta: string | undefined\n readonly renditionSpreadMeta: string | undefined\n readonly pageProgressionDirection: string | undefined\n readonly guide: ReadonlyArray<OpfGuideReference>\n}\n\n/**\n * Parses an EPUB package document (OPF) into structured metadata.\n *\n * Direct children of `package` (`metadata`, `manifest`, `spine`, `guide`) and\n * their structural children (`item`, `itemref`, `reference`, `meta`) are\n * matched by **local name** (ASCII case-insensitive), so prefixed tags such as\n * `opf:manifest` are supported the same as unprefixed `manifest`.\n *\n * Attribute names on `spine` / `itemref` are still read as emitted by xmldoc\n * (no QName normalization).\n */\nexport const parseOpf = (opfXml: string): OpfMetadata => {\n const doc = new XmlDocument(opfXml)\n const manifestEl = childNamedLocal(doc, \"manifest\")\n const spineEl = childNamedLocal(doc, \"spine\")\n const metadataEl = childNamedLocal(doc, \"metadata\")\n\n let manifestItems: OpfSpineManifestItem[] = []\n let spineRows: OpfSpineRow[] = []\n\n if (manifestEl !== undefined) {\n const { items, byId } = manifestItemsAndById(manifestEl)\n manifestItems = items\n if (spineEl !== undefined) {\n spineRows = spineRowsFromByIdAndSpine(byId, spineEl)\n }\n }\n\n const pageProgressionDirectionRaw =\n spineEl?.attr[\"page-progression-direction\"]\n const pageProgressionDirection =\n pageProgressionDirectionRaw !== undefined &&\n pageProgressionDirectionRaw.trim().length > 0\n ? pageProgressionDirectionRaw\n : undefined\n\n const spineTocRaw = spineEl?.attr.toc\n const spineTocIdref =\n spineTocRaw !== undefined && spineTocRaw.trim().length > 0\n ? spineTocRaw.trim()\n : undefined\n\n let title: string | undefined\n let publisher: string | undefined\n let rights: string | undefined\n let date: string | undefined\n let creators: string[] = []\n let languages: string[] = []\n let subjects: string[] = []\n let renditionLayoutMeta: string | undefined\n let renditionFlowMeta: string | undefined\n let renditionSpreadMeta: string | undefined\n const identifiers: OpfIdentifier[] = []\n\n if (metadataEl !== undefined) {\n title = firstTextByLocalName(metadataEl, \"title\")\n publisher = firstTextByLocalName(metadataEl, \"publisher\")\n rights = firstTextByLocalName(metadataEl, \"rights\")\n date = firstTextByLocalName(metadataEl, \"date\")\n creators = textsByLocalName(metadataEl, \"creator\")\n languages = textsByLocalName(metadataEl, \"language\")\n subjects = textsByLocalName(metadataEl, \"subject\")\n renditionLayoutMeta = metaValByProperty(metadataEl, \"rendition:layout\")\n renditionFlowMeta = metaValByProperty(metadataEl, \"rendition:flow\")\n renditionSpreadMeta = metaValByProperty(metadataEl, \"rendition:spread\")\n identifiers.push(...identifiersFromMetadata(metadataEl))\n }\n\n const coverHref = coverHrefFromManifestAndMetadata({\n manifestItems,\n metadataEl,\n })\n\n const guide = guideFromPackage(doc)\n\n return {\n kind: \"opf\",\n manifestItems,\n spineRows,\n spineTocIdref,\n identifiers,\n title,\n creators,\n publisher,\n rights,\n languages,\n subjects,\n date,\n coverHref,\n renditionLayoutMeta,\n renditionFlowMeta,\n renditionSpreadMeta,\n pageProgressionDirection,\n guide,\n }\n}\n","import type { ArchiveResolveResult } from \"../types/archiveResolve\"\nimport type { AppleMetadata } from \"./parse\"\n\nexport const resolveApple = (input: AppleMetadata): ArchiveResolveResult => {\n const fixedLayout = input.displayOptions?.platform?.options?.find(\n (o) => o.name === \"fixed-layout\",\n )?.value\n\n const renditionLayout =\n fixedLayout?.trim().toLowerCase() === \"true\" ? \"pre-paginated\" : undefined\n\n return {\n gtin: undefined,\n isbn: undefined,\n readingDirection: undefined,\n renditionLayout,\n title: undefined,\n authors: undefined,\n publisher: undefined,\n rights: undefined,\n languages: undefined,\n date: undefined,\n subjects: undefined,\n }\n}\n","const GTIN_LENGTHS = new Set([8, 12, 13, 14])\n\n/**\n * Normalize a raw GTIN / EAN / UPC string to digits only when the length\n * matches a GS1 GTIN family size (8, 12, 13, or 14). Does not verify check digits.\n */\nexport const normalizeGtin = (\n raw: string | number | undefined | null,\n): string | undefined => {\n if (raw === undefined || raw === null) return undefined\n\n const digits = String(raw).replace(/\\D/g, \"\")\n if (digits.length === 0 || !GTIN_LENGTHS.has(digits.length)) return undefined\n\n return digits\n}\n","const ISBN_CANDIDATE_PATTERN = /(?:97[89])?\\d{9}[\\dXx]/\n\n/**\n * Normalize a raw ISBN-ish string into a canonical 10- or 13-character\n * form, or `undefined` when no recognisable ISBN can be recovered.\n *\n * - Strips the common `urn:isbn:` / `isbn:` prefixes.\n * - Drops everything that isn't a digit or `X`.\n * - Validates the resulting length (10 or 13).\n * - Falls back to a lax regex scan so publishers that stuff free text\n * around the number still yield a usable value.\n */\nexport const normalizeIsbn = (\n raw: string | number | undefined | null,\n): string | undefined => {\n if (raw === undefined || raw === null) return undefined\n\n const stripped = String(raw)\n .trim()\n .replace(/^urn:isbn:/i, \"\")\n .replace(/^isbn[:\\s-]*/i, \"\")\n\n const digitsOnly = stripped.replace(/[^0-9Xx]/g, \"\")\n\n if (digitsOnly.length === 10 || digitsOnly.length === 13) {\n return digitsOnly.toUpperCase()\n }\n\n const match = stripped.match(ISBN_CANDIDATE_PATTERN)\n if (match) return match[0].toUpperCase()\n\n return undefined\n}\n","import type { ArchiveResolveResult } from \"../types/archiveResolve\"\nimport { normalizeGtin } from \"../utils/normalizeGtin\"\nimport { normalizeIsbn } from \"../utils/normalizeIsbn\"\nimport type { ComicInfo } from \"./parse\"\n\nconst readingDirection = (info: ComicInfo): \"ltr\" | \"rtl\" =>\n info.Manga === \"YesAndRightToLeft\" ? \"rtl\" : \"ltr\"\n\nconst trimToUndefined = (raw: string | undefined): string | undefined => {\n if (raw === undefined) return undefined\n const trimmed = raw.trim()\n return trimmed.length > 0 ? trimmed : undefined\n}\n\n/**\n * Split a comma-separated ComicInfo value into its individual tokens.\n * `Writer`, `Genre`, and `Tags` all follow the same de-facto convention:\n * tokens separated by `,`, with whitespace trimmed and empty tokens\n * dropped (real-world files leave trailing commas around).\n */\nconst splitCommaList = (raw: string | undefined): string[] => {\n if (raw === undefined) return []\n return raw\n .split(\",\")\n .map((token) => token.trim())\n .filter((token) => token.length > 0)\n}\n\nconst parseNonNegativeInt = (raw: string | undefined): number | undefined => {\n const trimmed = trimToUndefined(raw)\n if (trimmed === undefined) return undefined\n if (!/^\\d+$/.test(trimmed)) return undefined\n const n = Number.parseInt(trimmed, 10)\n return Number.isFinite(n) ? n : undefined\n}\n\nconst dateFromYearMonthDay = (\n info: ComicInfo,\n):\n | {\n year?: number\n month?: number\n day?: number\n }\n | undefined => {\n const year = parseNonNegativeInt(info.Year)\n const month = parseNonNegativeInt(info.Month)\n const day = parseNonNegativeInt(info.Day)\n\n if (year === undefined && month === undefined && day === undefined) {\n return undefined\n }\n\n return {\n ...(year !== undefined ? { year } : {}),\n ...(month !== undefined ? { month } : {}),\n ...(day !== undefined ? { day } : {}),\n }\n}\n\nexport const resolveComicInfo = (info: ComicInfo): ArchiveResolveResult => {\n const raw = info.GTIN\n const languageIso = trimToUndefined(info.LanguageISO)\n const authors = splitCommaList(info.Writer)\n const subjects = [...splitCommaList(info.Genre), ...splitCommaList(info.Tags)]\n\n return {\n gtin: normalizeGtin(raw),\n isbn: normalizeIsbn(raw),\n readingDirection: readingDirection(info),\n renditionLayout: undefined,\n title: trimToUndefined(info.Title),\n authors: authors.length > 0 ? authors : undefined,\n publisher: trimToUndefined(info.Publisher),\n rights: undefined,\n languages: languageIso !== undefined ? [languageIso] : undefined,\n date: dateFromYearMonthDay(info),\n subjects: subjects.length > 0 ? subjects : undefined,\n }\n}\n","import type { ArchiveResolveResult } from \"../types/archiveResolve\"\nimport type { KoboMetadata } from \"./parse\"\n\nexport const resolveKobo = (input: KoboMetadata): ArchiveResolveResult => ({\n gtin: undefined,\n isbn: undefined,\n readingDirection: undefined,\n renditionLayout: input.renditionLayout,\n title: undefined,\n authors: undefined,\n publisher: undefined,\n rights: undefined,\n languages: undefined,\n date: undefined,\n subjects: undefined,\n})\n","/**\n * Extract the calendar components of a W3CDTF literal — the\n * date subset of ISO 8601 that EPUB 3.3 § 5.5.3.2.4 mandates for\n * `dc:date`. Accepted forms: `YYYY`, `YYYY-MM`, `YYYY-MM-DD`, and\n * any of the above followed by a `Thh:mm[:ss[.s]][TZD]` time\n * portion that we ignore.\n *\n * Components are returned as plain integers (`month` is 1-12,\n * `day` is 1-31), independent of the host timezone — using\n * `Date.parse` would shift `2011-01-01` by a day in negative-offset\n * locales, which is the exact bug this regex-based approach exists\n * to avoid. Returns `undefined` when the input doesn't even match\n * a leading 4-digit year so consumers can fall back without\n * branching on partial shapes.\n */\nexport const parseW3cDtfDate = (\n raw: string | undefined,\n):\n | {\n year?: number\n month?: number\n day?: number\n }\n | undefined => {\n if (raw === undefined) return undefined\n\n const match = raw.trim().match(/^(\\d{4})(?:-(\\d{2})(?:-(\\d{2}))?)?/)\n if (match === null) return undefined\n\n const [, yearRaw, monthRaw, dayRaw] = match\n\n return {\n ...(yearRaw !== undefined ? { year: Number.parseInt(yearRaw, 10) } : {}),\n ...(monthRaw !== undefined ? { month: Number.parseInt(monthRaw, 10) } : {}),\n ...(dayRaw !== undefined ? { day: Number.parseInt(dayRaw, 10) } : {}),\n }\n}\n","import type { ArchiveResolveResult } from \"../types/archiveResolve\"\nimport { normalizeGtin } from \"../utils/normalizeGtin\"\nimport { normalizeIsbn } from \"../utils/normalizeIsbn\"\nimport { parseW3cDtfDate } from \"../utils/parseW3cDtfDate\"\nimport type { OpfIdentifier, OpfMetadata } from \"./parse\"\n\nconst rawIdentifierValueForIsbn = (\n identifiers: ReadonlyArray<OpfIdentifier>,\n): string | undefined => {\n for (const i of identifiers) {\n if (i.scheme !== undefined && i.scheme.trim().toLowerCase() === \"isbn\") {\n if (normalizeIsbn(i.value) !== undefined) return i.value\n }\n }\n\n for (const i of identifiers) {\n if (normalizeIsbn(i.value) !== undefined) return i.value\n }\n\n return undefined\n}\n\nexport const resolveOpf = (input: OpfMetadata): ArchiveResolveResult => {\n const ppd = input.pageProgressionDirection?.trim().toLowerCase()\n const readingDirection = ppd === \"ltr\" || ppd === \"rtl\" ? ppd : undefined\n\n const rl = input.renditionLayoutMeta?.trim().toLowerCase()\n const renditionLayout =\n rl === \"reflowable\" || rl === \"pre-paginated\" ? rl : undefined\n\n const raw = rawIdentifierValueForIsbn(input.identifiers)\n\n return {\n gtin: normalizeGtin(raw),\n isbn: normalizeIsbn(raw),\n readingDirection,\n renditionLayout,\n title: input.title,\n authors: input.creators.length > 0 ? [...input.creators] : undefined,\n publisher: input.publisher,\n rights: input.rights,\n languages: input.languages.length > 0 ? [...input.languages] : undefined,\n date: parseW3cDtfDate(input.date),\n subjects: input.subjects.length > 0 ? [...input.subjects] : undefined,\n }\n}\n","import type { AppleMetadata } from \"./apple/parse\"\nimport { resolveApple } from \"./apple/resolve\"\nimport type { ComicInfo } from \"./comicInfo/parse\"\nimport { resolveComicInfo } from \"./comicInfo/resolve\"\nimport type { KoboMetadata } from \"./kobo/parse\"\nimport { resolveKobo } from \"./kobo/resolve\"\nimport type { OpfMetadata } from \"./opf/parse\"\nimport { resolveOpf } from \"./opf/resolve\"\nimport type { ArchiveResolveResult } from \"./types/archiveResolve\"\n\nexport type ResolvedArchiveInput =\n | ComicInfo\n | KoboMetadata\n | AppleMetadata\n | OpfMetadata\n\nexport const resolveArchiveMetadata = (\n input: ResolvedArchiveInput,\n): ArchiveResolveResult => {\n if (input.kind === \"comicInfo\") return resolveComicInfo(input)\n if (input.kind === \"kobo\") return resolveKobo(input)\n if (input.kind === \"apple\") return resolveApple(input)\n return resolveOpf(input)\n}\n"],"names":["APPLE_IBOOKS_DISPLAY_OPTIONS_FILENAME","platformOptionsFromElement","platform","option","parseAppleDisplayOptionsXml","xml","doc","XmlDocument","platformEl","COMIC_INFO_MANGA_VALUES","isComicInfoManga","value","v","COMIC_INFO_FILENAME","SKIP_ELEMENT_CHILDREN","hasNestedElement","el","c","trimmedText","parseComicInfo","cause","message","fields","child","text","KOBO_DISPLAY_OPTIONS_FILENAME","parseKoboDisplayOptionsDocument","parseKoboXml","tokenizeXmlSpaceSeparatedList","raw","layoutHintsFromItemrefProperties","properties","tokens","renditionLayout","renditionFlow","elementLocalName","name","localNameEq","elementName","wantLocal","isXmlElement","node","childNamedLocal","parent","localName","childrenNamedLocal","out","identifiersFromMetadata","metadataEl","identifiers","schemeTrimmed","firstTextByLocalName","found","t","textsByLocalName","coverContentIdFromMetadata","coverId","content","coverHrefFromManifestAndMetadata","manifestItems","isImage","item","byCoverImageProperty","coverContentId","match","metaValByProperty","property","m","guideFromPackage","guideEl","refs","ref","href","manifestItemFromXmlElement","id","mediaType","manifestItemsAndById","manifestEl","items","byId","parsed","spineRowsFromByIdAndSpine","spineEl","rows","itemref","idref","manifestItem","hints","parseOpf","opfXml","spineRows","pageProgressionDirectionRaw","pageProgressionDirection","spineTocRaw","spineTocIdref","title","publisher","rights","date","creators","languages","subjects","renditionLayoutMeta","renditionFlowMeta","renditionSpreadMeta","coverHref","guide","resolveApple","input","GTIN_LENGTHS","normalizeGtin","digits","ISBN_CANDIDATE_PATTERN","normalizeIsbn","stripped","digitsOnly","readingDirection","info","trimToUndefined","trimmed","splitCommaList","token","parseNonNegativeInt","dateFromYearMonthDay","year","month","day","resolveComicInfo","languageIso","authors","resolveKobo","parseW3cDtfDate","yearRaw","monthRaw","dayRaw","rawIdentifierValueForIsbn","i","resolveOpf","ppd","rl","resolveArchiveMetadata"],"mappings":"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"}
@@ -15,6 +15,7 @@ export type OpfSpineRow = {
15
15
  readonly mediaType?: string;
16
16
  readonly properties?: string;
17
17
  readonly renditionLayout?: `reflowable` | `pre-paginated`;
18
+ readonly renditionFlow?: `scrolled-continuous` | `scrolled-doc` | `paginated` | `auto`;
18
19
  readonly pageSpreadLeft?: true;
19
20
  readonly pageSpreadRight?: true;
20
21
  };
@@ -1,5 +1,6 @@
1
1
  export type OpfItemrefLayoutHints = {
2
2
  readonly renditionLayout?: `reflowable` | `pre-paginated`;
3
+ readonly renditionFlow?: `scrolled-continuous` | `scrolled-doc` | `paginated` | `auto`;
3
4
  readonly pageSpreadLeft?: true;
4
5
  readonly pageSpreadRight?: true;
5
6
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prose-reader/archive-parser",
3
- "version": "1.296.0",
3
+ "version": "1.298.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.umd.cjs",
6
6
  "module": "./dist/index.js",
@@ -29,5 +29,5 @@
29
29
  "dependencies": {
30
30
  "xmldoc": "^2.0.0"
31
31
  },
32
- "gitHead": "c0d44e365c1456f94d2ece521af3546b97dc86b9"
32
+ "gitHead": "70dc850174a2782f66b31c5eebadd0950a8c7be1"
33
33
  }