wolfy-reader 0.1.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.
Files changed (233) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +277 -0
  3. package/dist/core/book.d.ts +75 -0
  4. package/dist/core/book.d.ts.map +1 -0
  5. package/dist/core/book.js +2 -0
  6. package/dist/core/book.js.map +1 -0
  7. package/dist/core/errors.d.ts +17 -0
  8. package/dist/core/errors.d.ts.map +1 -0
  9. package/dist/core/errors.js +29 -0
  10. package/dist/core/errors.js.map +1 -0
  11. package/dist/core/format.d.ts +13 -0
  12. package/dist/core/format.d.ts.map +1 -0
  13. package/dist/core/format.js +2 -0
  14. package/dist/core/format.js.map +1 -0
  15. package/dist/core/graphemes.d.ts +36 -0
  16. package/dist/core/graphemes.d.ts.map +1 -0
  17. package/dist/core/graphemes.js +62 -0
  18. package/dist/core/graphemes.js.map +1 -0
  19. package/dist/core/index.d.ts +11 -0
  20. package/dist/core/index.d.ts.map +1 -0
  21. package/dist/core/index.js +6 -0
  22. package/dist/core/index.js.map +1 -0
  23. package/dist/core/lookup.d.ts +10 -0
  24. package/dist/core/lookup.d.ts.map +1 -0
  25. package/dist/core/lookup.js +16 -0
  26. package/dist/core/lookup.js.map +1 -0
  27. package/dist/core/open.d.ts +12 -0
  28. package/dist/core/open.d.ts.map +1 -0
  29. package/dist/core/open.js +23 -0
  30. package/dist/core/open.js.map +1 -0
  31. package/dist/core/position.d.ts +121 -0
  32. package/dist/core/position.d.ts.map +1 -0
  33. package/dist/core/position.js +327 -0
  34. package/dist/core/position.js.map +1 -0
  35. package/dist/core/reading-text.d.ts +77 -0
  36. package/dist/core/reading-text.d.ts.map +1 -0
  37. package/dist/core/reading-text.js +144 -0
  38. package/dist/core/reading-text.js.map +1 -0
  39. package/dist/core/source.d.ts +17 -0
  40. package/dist/core/source.d.ts.map +1 -0
  41. package/dist/core/source.js +39 -0
  42. package/dist/core/source.js.map +1 -0
  43. package/dist/core/storage.d.ts +7 -0
  44. package/dist/core/storage.d.ts.map +1 -0
  45. package/dist/core/storage.js +2 -0
  46. package/dist/core/storage.js.map +1 -0
  47. package/dist/core/text.d.ts +18 -0
  48. package/dist/core/text.d.ts.map +1 -0
  49. package/dist/core/text.js +32 -0
  50. package/dist/core/text.js.map +1 -0
  51. package/dist/formats/epub/href.d.ts +14 -0
  52. package/dist/formats/epub/href.d.ts.map +1 -0
  53. package/dist/formats/epub/href.js +52 -0
  54. package/dist/formats/epub/href.js.map +1 -0
  55. package/dist/formats/epub/index.d.ts +3 -0
  56. package/dist/formats/epub/index.d.ts.map +1 -0
  57. package/dist/formats/epub/index.js +223 -0
  58. package/dist/formats/epub/index.js.map +1 -0
  59. package/dist/formats/epub/opf.d.ts +27 -0
  60. package/dist/formats/epub/opf.d.ts.map +1 -0
  61. package/dist/formats/epub/opf.js +110 -0
  62. package/dist/formats/epub/opf.js.map +1 -0
  63. package/dist/formats/epub/toc.d.ts +6 -0
  64. package/dist/formats/epub/toc.d.ts.map +1 -0
  65. package/dist/formats/epub/toc.js +88 -0
  66. package/dist/formats/epub/toc.js.map +1 -0
  67. package/dist/formats/fb2/index.d.ts +13 -0
  68. package/dist/formats/fb2/index.d.ts.map +1 -0
  69. package/dist/formats/fb2/index.js +317 -0
  70. package/dist/formats/fb2/index.js.map +1 -0
  71. package/dist/formats/index.d.ts +4 -0
  72. package/dist/formats/index.d.ts.map +1 -0
  73. package/dist/formats/index.js +4 -0
  74. package/dist/formats/index.js.map +1 -0
  75. package/dist/formats/text/index.d.ts +11 -0
  76. package/dist/formats/text/index.d.ts.map +1 -0
  77. package/dist/formats/text/index.js +201 -0
  78. package/dist/formats/text/index.js.map +1 -0
  79. package/dist/formats/xml.d.ts +33 -0
  80. package/dist/formats/xml.d.ts.map +1 -0
  81. package/dist/formats/xml.js +321 -0
  82. package/dist/formats/xml.js.map +1 -0
  83. package/dist/index.d.ts +27 -0
  84. package/dist/index.d.ts.map +1 -0
  85. package/dist/index.js +28 -0
  86. package/dist/index.js.map +1 -0
  87. package/dist/layout/chunk.d.ts +51 -0
  88. package/dist/layout/chunk.d.ts.map +1 -0
  89. package/dist/layout/chunk.js +131 -0
  90. package/dist/layout/chunk.js.map +1 -0
  91. package/dist/layout/index.d.ts +238 -0
  92. package/dist/layout/index.d.ts.map +1 -0
  93. package/dist/layout/index.js +476 -0
  94. package/dist/layout/index.js.map +1 -0
  95. package/dist/reader/index.d.ts +231 -0
  96. package/dist/reader/index.d.ts.map +1 -0
  97. package/dist/reader/index.js +958 -0
  98. package/dist/reader/index.js.map +1 -0
  99. package/dist/reader/input.d.ts +69 -0
  100. package/dist/reader/input.d.ts.map +1 -0
  101. package/dist/reader/input.js +75 -0
  102. package/dist/reader/input.js.map +1 -0
  103. package/dist/search/extract.d.ts +51 -0
  104. package/dist/search/extract.d.ts.map +1 -0
  105. package/dist/search/extract.js +401 -0
  106. package/dist/search/extract.js.map +1 -0
  107. package/dist/search/index.d.ts +11 -0
  108. package/dist/search/index.d.ts.map +1 -0
  109. package/dist/search/index.js +11 -0
  110. package/dist/search/index.js.map +1 -0
  111. package/dist/search/matcher.d.ts +56 -0
  112. package/dist/search/matcher.d.ts.map +1 -0
  113. package/dist/search/matcher.js +128 -0
  114. package/dist/search/matcher.js.map +1 -0
  115. package/dist/search/normalize.d.ts +48 -0
  116. package/dist/search/normalize.d.ts.map +1 -0
  117. package/dist/search/normalize.js +103 -0
  118. package/dist/search/normalize.js.map +1 -0
  119. package/dist/view/allowlist.d.ts +59 -0
  120. package/dist/view/allowlist.d.ts.map +1 -0
  121. package/dist/view/allowlist.js +198 -0
  122. package/dist/view/allowlist.js.map +1 -0
  123. package/dist/view/appearance.d.ts +137 -0
  124. package/dist/view/appearance.d.ts.map +1 -0
  125. package/dist/view/appearance.js +274 -0
  126. package/dist/view/appearance.js.map +1 -0
  127. package/dist/view/css.d.ts +24 -0
  128. package/dist/view/css.d.ts.map +1 -0
  129. package/dist/view/css.js +153 -0
  130. package/dist/view/css.js.map +1 -0
  131. package/dist/view/frame.d.ts +54 -0
  132. package/dist/view/frame.d.ts.map +1 -0
  133. package/dist/view/frame.js +980 -0
  134. package/dist/view/frame.js.map +1 -0
  135. package/dist/view/host.d.ts +160 -0
  136. package/dist/view/host.d.ts.map +1 -0
  137. package/dist/view/host.js +385 -0
  138. package/dist/view/host.js.map +1 -0
  139. package/dist/view/index.d.ts +12 -0
  140. package/dist/view/index.d.ts.map +1 -0
  141. package/dist/view/index.js +7 -0
  142. package/dist/view/index.js.map +1 -0
  143. package/dist/view/protocol.d.ts +220 -0
  144. package/dist/view/protocol.d.ts.map +1 -0
  145. package/dist/view/protocol.js +246 -0
  146. package/dist/view/protocol.js.map +1 -0
  147. package/dist/view/reference.d.ts +18 -0
  148. package/dist/view/reference.d.ts.map +1 -0
  149. package/dist/view/reference.js +25 -0
  150. package/dist/view/reference.js.map +1 -0
  151. package/dist/view/resources.d.ts +51 -0
  152. package/dist/view/resources.d.ts.map +1 -0
  153. package/dist/view/resources.js +242 -0
  154. package/dist/view/resources.js.map +1 -0
  155. package/dist/view/sanitize.d.ts +32 -0
  156. package/dist/view/sanitize.d.ts.map +1 -0
  157. package/dist/view/sanitize.js +226 -0
  158. package/dist/view/sanitize.js.map +1 -0
  159. package/dist/zip/central.d.ts +12 -0
  160. package/dist/zip/central.d.ts.map +1 -0
  161. package/dist/zip/central.js +72 -0
  162. package/dist/zip/central.js.map +1 -0
  163. package/dist/zip/crc32.d.ts +3 -0
  164. package/dist/zip/crc32.d.ts.map +1 -0
  165. package/dist/zip/crc32.js +28 -0
  166. package/dist/zip/crc32.js.map +1 -0
  167. package/dist/zip/eocd.d.ts +9 -0
  168. package/dist/zip/eocd.d.ts.map +1 -0
  169. package/dist/zip/eocd.js +84 -0
  170. package/dist/zip/eocd.js.map +1 -0
  171. package/dist/zip/errors.d.ts +19 -0
  172. package/dist/zip/errors.d.ts.map +1 -0
  173. package/dist/zip/errors.js +37 -0
  174. package/dist/zip/errors.js.map +1 -0
  175. package/dist/zip/index.d.ts +18 -0
  176. package/dist/zip/index.d.ts.map +1 -0
  177. package/dist/zip/index.js +78 -0
  178. package/dist/zip/index.js.map +1 -0
  179. package/dist/zip/inflate.d.ts +2 -0
  180. package/dist/zip/inflate.d.ts.map +1 -0
  181. package/dist/zip/inflate.js +43 -0
  182. package/dist/zip/inflate.js.map +1 -0
  183. package/dist/zip/source.d.ts +15 -0
  184. package/dist/zip/source.d.ts.map +1 -0
  185. package/dist/zip/source.js +34 -0
  186. package/dist/zip/source.js.map +1 -0
  187. package/package.json +75 -0
  188. package/src/core/book.ts +80 -0
  189. package/src/core/errors.ts +31 -0
  190. package/src/core/format.ts +14 -0
  191. package/src/core/graphemes.ts +73 -0
  192. package/src/core/index.ts +27 -0
  193. package/src/core/lookup.ts +16 -0
  194. package/src/core/open.ts +31 -0
  195. package/src/core/position.ts +485 -0
  196. package/src/core/reading-text.ts +200 -0
  197. package/src/core/source.ts +61 -0
  198. package/src/core/storage.ts +6 -0
  199. package/src/core/text.ts +35 -0
  200. package/src/formats/epub/href.ts +54 -0
  201. package/src/formats/epub/index.ts +235 -0
  202. package/src/formats/epub/opf.ts +143 -0
  203. package/src/formats/epub/toc.ts +120 -0
  204. package/src/formats/fb2/index.ts +348 -0
  205. package/src/formats/index.ts +3 -0
  206. package/src/formats/text/index.ts +208 -0
  207. package/src/formats/xml.ts +326 -0
  208. package/src/index.ts +26 -0
  209. package/src/layout/chunk.ts +169 -0
  210. package/src/layout/index.ts +573 -0
  211. package/src/reader/index.ts +1206 -0
  212. package/src/reader/input.ts +122 -0
  213. package/src/search/extract.ts +403 -0
  214. package/src/search/index.ts +10 -0
  215. package/src/search/matcher.ts +187 -0
  216. package/src/search/normalize.ts +122 -0
  217. package/src/view/allowlist.ts +273 -0
  218. package/src/view/appearance.ts +380 -0
  219. package/src/view/css.ts +172 -0
  220. package/src/view/frame.ts +1018 -0
  221. package/src/view/host.ts +527 -0
  222. package/src/view/index.ts +24 -0
  223. package/src/view/protocol.ts +412 -0
  224. package/src/view/reference.ts +30 -0
  225. package/src/view/resources.ts +265 -0
  226. package/src/view/sanitize.ts +266 -0
  227. package/src/zip/central.ts +85 -0
  228. package/src/zip/crc32.ts +30 -0
  229. package/src/zip/eocd.ts +97 -0
  230. package/src/zip/errors.ts +41 -0
  231. package/src/zip/index.ts +114 -0
  232. package/src/zip/inflate.ts +41 -0
  233. package/src/zip/source.ts +51 -0
@@ -0,0 +1,242 @@
1
+ import { baseMediaType, isServableResource } from '../core/reading-text.js';
2
+ import { XLINK_NAMESPACE } from './allowlist.js';
3
+ import { rewriteCssReferences } from './css.js';
4
+ import { classifyReference, joinReference, normalizeReference } from './reference.js';
5
+ import { decodeText } from '../core/text.js';
6
+ /** What a reference that names nothing loadable is rewritten to. */
7
+ export const UNRESOLVABLE_URL = 'about:invalid';
8
+ const NO_ANCESTORS = new Set();
9
+ const STYLESHEET_TYPE = 'text/css';
10
+ const BASE64_CHUNK = 0x8000;
11
+ const SAFE_MEDIA_TYPE = /^[a-z0-9][a-z0-9!#$&^_.+-]*\/[a-z0-9][a-z0-9!#$&^_.+-]*$/;
12
+ function toBase64(bytes) {
13
+ let binary = '';
14
+ for (let i = 0; i < bytes.length; i += BASE64_CHUNK) {
15
+ binary += String.fromCharCode(...bytes.subarray(i, i + BASE64_CHUNK));
16
+ }
17
+ return btoa(binary);
18
+ }
19
+ // Which media types are minted a URL at all is part of the canonical reading
20
+ // text (an unservable <img> falls back to its alt), so the predicate lives in
21
+ // src/core/reading-text.ts where the headless search extractor shares it.
22
+ /**
23
+ * Turns references found in book content into `data:` URLs.
24
+ *
25
+ * `blob:` is what the plan asked for and it cannot work here: a blob URL
26
+ * belongs to the origin that created it, and the frame's origin is opaque, so
27
+ * the frame is refused the host's blob URLs outright — measured in Chromium as
28
+ * "Not allowed to load local resource", below CSP, with no violation event to
29
+ * observe. A `data:` URL carries its own bytes and has no origin to check, so
30
+ * it crosses the boundary the sandbox deliberately makes uncrossable. The cost
31
+ * is base64's 33% and no revocation handle; both are bounded by one section.
32
+ */
33
+ export class ResourceRegistry {
34
+ #resolve;
35
+ #resources = new Map();
36
+ #stylesheets = new Map();
37
+ #resolved = new Set();
38
+ #unresolved = new Set();
39
+ #refused = new Set();
40
+ #remote = new Set();
41
+ #minted = 0;
42
+ #released = false;
43
+ constructor(resolve) {
44
+ this.#resolve = resolve;
45
+ }
46
+ get summary() {
47
+ return {
48
+ dataUrls: this.#minted,
49
+ resolved: [...this.#resolved],
50
+ unresolved: [...this.#unresolved],
51
+ refused: [...this.#refused],
52
+ remote: [...this.#remote],
53
+ };
54
+ }
55
+ /**
56
+ * Memoized by reference string, not by the resource object: the contract
57
+ * promises equal bytes for equal references, not one shared object.
58
+ */
59
+ urlForResource(reference) {
60
+ const key = normalizeReference(reference);
61
+ const existing = this.#resources.get(key);
62
+ if (existing !== undefined)
63
+ return existing;
64
+ const created = this.#loadResource(key);
65
+ this.#resources.set(key, created);
66
+ return created;
67
+ }
68
+ urlForStylesheet(reference, ancestors = NO_ANCESTORS) {
69
+ const key = normalizeReference(reference);
70
+ // A sheet that is already an ancestor of itself is an @import cycle. Refuse
71
+ // it here and the recursion terminates rather than awaiting its own result.
72
+ if (ancestors.has(key))
73
+ return Promise.resolve(undefined);
74
+ const existing = this.#stylesheets.get(key);
75
+ if (existing !== undefined)
76
+ return existing;
77
+ const created = this.#loadStylesheet(key, new Set([...ancestors, key]));
78
+ this.#stylesheets.set(key, created);
79
+ return created;
80
+ }
81
+ rewriteStylesheet(css, base, ancestors = NO_ANCESTORS) {
82
+ return rewriteCssReferences(css, async (reference) => {
83
+ const classified = classifyReference(reference.value);
84
+ if (classified.kind !== 'relative') {
85
+ // data: URIs carry their own bytes and remote ones are the CSP's to
86
+ // refuse — visibly. Neither is ours to rewrite.
87
+ if (classified.kind === 'scheme' && classified.scheme !== 'data')
88
+ this.#remote.add(classified.value);
89
+ return undefined;
90
+ }
91
+ const target = joinReference(base, classified.value);
92
+ const url = reference.kind === 'import'
93
+ ? await this.urlForStylesheet(target, ancestors)
94
+ : await this.urlForResource(target);
95
+ return url ?? UNRESOLVABLE_URL;
96
+ });
97
+ }
98
+ /**
99
+ * Drops everything this render allocated. Data URLs hold no handle to
100
+ * release, so this frees memoized strings and stops any in-flight load from
101
+ * minting more — there is no orphan URL a render can leave behind.
102
+ */
103
+ release() {
104
+ this.#released = true;
105
+ this.#resources.clear();
106
+ this.#stylesheets.clear();
107
+ }
108
+ async #loadResource(key) {
109
+ const resource = this.#resolve?.(key);
110
+ if (resource === undefined) {
111
+ this.#unresolved.add(key);
112
+ return undefined;
113
+ }
114
+ if (!isServableResource(resource.mediaType)) {
115
+ this.#refused.add(key);
116
+ return undefined;
117
+ }
118
+ let bytes;
119
+ try {
120
+ bytes = await resource.load();
121
+ }
122
+ catch {
123
+ this.#unresolved.add(key);
124
+ return undefined;
125
+ }
126
+ return this.#dataUrl(bytes, resource.mediaType, key);
127
+ }
128
+ async #loadStylesheet(key, ancestors) {
129
+ const resource = this.#resolve?.(key);
130
+ if (resource === undefined) {
131
+ this.#unresolved.add(key);
132
+ return undefined;
133
+ }
134
+ if (baseMediaType(resource.mediaType) !== STYLESHEET_TYPE) {
135
+ this.#refused.add(key);
136
+ return undefined;
137
+ }
138
+ let css;
139
+ try {
140
+ css = decodeText(await resource.load());
141
+ }
142
+ catch {
143
+ this.#unresolved.add(key);
144
+ return undefined;
145
+ }
146
+ // Relative references inside the sheet are resolved against the sheet, then
147
+ // re-expressed relative to the section, because that is the only space
148
+ // Section.resolve() understands.
149
+ const rewritten = await this.rewriteStylesheet(css, key, ancestors);
150
+ return this.#dataUrl(new TextEncoder().encode(rewritten), STYLESHEET_TYPE, key);
151
+ }
152
+ #dataUrl(bytes, mediaType, key) {
153
+ if (this.#released)
154
+ return undefined;
155
+ const type = baseMediaType(mediaType);
156
+ const declared = SAFE_MEDIA_TYPE.test(type) ? type : 'application/octet-stream';
157
+ this.#minted += 1;
158
+ this.#resolved.add(key);
159
+ return `data:${declared};base64,${toBase64(bytes)}`;
160
+ }
161
+ }
162
+ async function applySvgReference(element, registry) {
163
+ const namespaced = element.getAttributeNS(XLINK_NAMESPACE, 'href');
164
+ const plain = element.getAttribute('href');
165
+ const raw = plain ?? namespaced;
166
+ if (raw === null)
167
+ return;
168
+ const classified = classifyReference(raw);
169
+ if (classified.kind !== 'relative')
170
+ return;
171
+ const url = await registry.urlForResource(classified.value);
172
+ if (url === undefined) {
173
+ element.remove();
174
+ return;
175
+ }
176
+ if (namespaced !== null)
177
+ element.setAttributeNS(XLINK_NAMESPACE, 'xlink:href', url);
178
+ if (plain !== null || namespaced === null)
179
+ element.setAttribute('href', url);
180
+ }
181
+ /**
182
+ * Replaces every in-book reference in a sanitized document with a `blob:` URL.
183
+ * Runs after sanitization, so it only ever sees attributes the allowlist kept.
184
+ */
185
+ export async function applyResources(doc, registry) {
186
+ let altSubstituted = 0;
187
+ for (const link of [...doc.querySelectorAll('link')]) {
188
+ const href = link.getAttribute('href');
189
+ if (href === null) {
190
+ link.remove();
191
+ continue;
192
+ }
193
+ const classified = classifyReference(href);
194
+ if (classified.kind !== 'relative')
195
+ continue;
196
+ const url = await registry.urlForStylesheet(classified.value);
197
+ if (url === undefined)
198
+ link.remove();
199
+ else
200
+ link.setAttribute('href', url);
201
+ }
202
+ for (const style of [...doc.querySelectorAll('style')]) {
203
+ style.textContent = await registry.rewriteStylesheet(style.textContent ?? '', '');
204
+ }
205
+ for (const styled of [...doc.querySelectorAll('[style]')]) {
206
+ const declarations = styled.getAttribute('style');
207
+ if (declarations === null)
208
+ continue;
209
+ styled.setAttribute('style', await registry.rewriteStylesheet(declarations, ''));
210
+ }
211
+ // This branch decides part of the canonical reading text: the alt text it
212
+ // substitutes is text a search anchor may span, so `imageReadingText` in
213
+ // src/core/reading-text.ts mirrors the whole decision headlessly (minus the
214
+ // load-failure edge, which it cannot see). Change the two together.
215
+ for (const image of [...doc.querySelectorAll('img')]) {
216
+ const src = image.getAttribute('src');
217
+ const classified = src === null ? undefined : classifyReference(src);
218
+ if (classified !== undefined && classified.kind !== 'relative')
219
+ continue;
220
+ const url = classified === undefined ? undefined : await registry.urlForResource(classified.value);
221
+ if (url !== undefined) {
222
+ image.setAttribute('src', url);
223
+ continue;
224
+ }
225
+ // Gutenberg's ebookmaker sets each chapter's drop cap as <img alt="T">, so
226
+ // silently dropping an image that cannot load deletes the first letter of
227
+ // the chapter. The alt text comes back as a text node instead.
228
+ const alt = image.getAttribute('alt') ?? '';
229
+ if (alt === '') {
230
+ image.remove();
231
+ }
232
+ else {
233
+ image.replaceWith(doc.createTextNode(alt));
234
+ altSubstituted += 1;
235
+ }
236
+ }
237
+ for (const image of [...doc.querySelectorAll('image')]) {
238
+ await applySvgReference(image, registry);
239
+ }
240
+ return { ...registry.summary, altSubstituted };
241
+ }
242
+ //# sourceMappingURL=resources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resources.js","sourceRoot":"","sources":["../../src/view/resources.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ export interface RemovalCount {
2
+ readonly name: string;
3
+ readonly count: number;
4
+ }
5
+ export interface SanitizationSummary {
6
+ /** The media type the markup was finally parsed as. */
7
+ readonly parsedAs: string;
8
+ /** True when XML parsing failed and the HTML parser took over. */
9
+ readonly parseFallback: boolean;
10
+ /** Elements removed with everything inside them. */
11
+ readonly elementsRemoved: readonly RemovalCount[];
12
+ /** Elements removed while keeping their children. */
13
+ readonly elementsUnwrapped: readonly RemovalCount[];
14
+ readonly attributesRemoved: readonly RemovalCount[];
15
+ }
16
+ export interface SanitizedSection {
17
+ /** A fresh HTML document holding the sanitized head and body. */
18
+ readonly document: Document;
19
+ readonly summary: SanitizationSummary;
20
+ }
21
+ /**
22
+ * Parses, normalizes and sanitizes a section in one pass, returning a fresh
23
+ * HTML document that owns the result.
24
+ *
25
+ * The three are one pass on purpose. Normalization — an XHTML `<a id="x"/>`
26
+ * becoming `<a id="x"></a>` rather than swallowing the rest of the chapter —
27
+ * is a property of parsing as XML and re-serializing as HTML, and the sanitizer
28
+ * is the only place the markup is parsed at all. Doing it twice would be both
29
+ * wasteful and a chance for the two DOMs to disagree.
30
+ */
31
+ export declare function sanitizeSection(source: string, mediaType: string): SanitizedSection;
32
+ //# sourceMappingURL=sanitize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../../src/view/sanitize.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,mBAAmB;IAClC,uDAAuD;IACvD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,kEAAkE;IAClE,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,oDAAoD;IACpD,QAAQ,CAAC,eAAe,EAAE,SAAS,YAAY,EAAE,CAAC;IAClD,qDAAqD;IACrD,QAAQ,CAAC,iBAAiB,EAAE,SAAS,YAAY,EAAE,CAAC;IACpD,QAAQ,CAAC,iBAAiB,EAAE,SAAS,YAAY,EAAE,CAAC;CACrD;AAED,MAAM,WAAW,gBAAgB;IAC/B,iEAAiE;IACjE,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC;CACvC;AA8KD;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,gBAAgB,CA6CnF"}
@@ -0,0 +1,226 @@
1
+ import { HTML_DISCARDED, HTML_ELEMENTS, HTML_GLOBAL_ATTRIBUTES, HTML_NAMESPACE, isAllowedReference, SVG_DISCARDED, SVG_ELEMENTS, SVG_GLOBAL_ATTRIBUTES, SVG_NAMESPACE, URL_ATTRIBUTES, XLINK_NAMESPACE, } from './allowlist.js';
2
+ import { classifyReference } from './reference.js';
3
+ const XHTML_TYPE = 'application/xhtml+xml';
4
+ const HTML_TYPE = 'text/html';
5
+ const SVG_TYPE = 'image/svg+xml';
6
+ const PARSE_ERROR_NAMESPACE = 'http://www.mozilla.org/newlayout/xml/parseerror.xml';
7
+ const XMLNS_NAMESPACE = 'http://www.w3.org/2000/xmlns/';
8
+ const SHOW_CDATA_SECTION = 8;
9
+ const SHOW_COMMENT = 128;
10
+ const SHOW_PROCESSING_INSTRUCTION = 64;
11
+ const CDATA_SECTION_NODE = 4;
12
+ // A <style> element is serialized as raw text, so text that closes the element
13
+ // escapes back into markup. XHTML lets an author write exactly that; HTML never
14
+ // could. Nothing legitimate needs it, so such a stylesheet is dropped whole.
15
+ const STYLE_BREAKOUT = /<\/style/i;
16
+ class Tally {
17
+ #counts = new Map();
18
+ add(name) {
19
+ this.#counts.set(name, (this.#counts.get(name) ?? 0) + 1);
20
+ }
21
+ toList() {
22
+ return [...this.#counts]
23
+ .map(([name, count]) => ({ name, count }))
24
+ .sort((a, b) => b.count - a.count || a.name.localeCompare(b.name));
25
+ }
26
+ }
27
+ // Browsers disagree on where the error marker lands: Firefox makes it the root
28
+ // in its own namespace, Chromium makes it the first child of the root in the
29
+ // XHTML namespace. Miss it and a malformed book renders as a truncated tree
30
+ // with the browser's red error box wedged into the chapter.
31
+ function hasParseError(doc) {
32
+ const root = doc.documentElement;
33
+ if (root === null)
34
+ return true;
35
+ if (root.localName === 'parsererror')
36
+ return true;
37
+ if (doc.getElementsByTagNameNS(PARSE_ERROR_NAMESPACE, 'parsererror').length > 0)
38
+ return true;
39
+ for (const child of root.children) {
40
+ if (child.localName === 'parsererror')
41
+ return true;
42
+ }
43
+ return false;
44
+ }
45
+ function parseSource(source, mediaType) {
46
+ const parser = new DOMParser();
47
+ const requested = mediaType === HTML_TYPE ? HTML_TYPE : mediaType === SVG_TYPE ? SVG_TYPE : XHTML_TYPE;
48
+ if (requested !== HTML_TYPE) {
49
+ const parsed = parser.parseFromString(source, requested);
50
+ if (!hasParseError(parsed))
51
+ return { document: parsed, parsedAs: requested, parseFallback: false };
52
+ }
53
+ // Real books are malformed often enough that an XML parse failure is a
54
+ // routine outcome, not a corrupt book: reparse as HTML and carry on.
55
+ return {
56
+ document: parser.parseFromString(source, HTML_TYPE),
57
+ parsedAs: HTML_TYPE,
58
+ parseFallback: requested !== HTML_TYPE,
59
+ };
60
+ }
61
+ // Comments and processing instructions serialize raw and can break out of the
62
+ // markup on the way back into the frame; CDATA cannot exist in an HTML document
63
+ // at all, and EPUB wraps stylesheets in it constantly.
64
+ function flattenNonElementNodes(doc, root) {
65
+ const walker = doc.createTreeWalker(root, SHOW_CDATA_SECTION | SHOW_COMMENT | SHOW_PROCESSING_INSTRUCTION);
66
+ const discarded = [];
67
+ const sections = [];
68
+ while (walker.nextNode() !== null) {
69
+ const node = walker.currentNode;
70
+ if (node.nodeType === CDATA_SECTION_NODE)
71
+ sections.push(node);
72
+ else
73
+ discarded.push(node);
74
+ }
75
+ for (const node of sections) {
76
+ node.parentNode?.replaceChild(doc.createTextNode(node.textContent ?? ''), node);
77
+ }
78
+ for (const node of discarded)
79
+ node.parentNode?.removeChild(node);
80
+ }
81
+ function unwrap(element) {
82
+ const parent = element.parentNode;
83
+ if (parent === null)
84
+ return;
85
+ while (element.firstChild !== null)
86
+ parent.insertBefore(element.firstChild, element);
87
+ parent.removeChild(element);
88
+ }
89
+ function scrubAttributes(element, name, allowed, svg, tallies) {
90
+ const globals = svg ? SVG_GLOBAL_ATTRIBUTES : HTML_GLOBAL_ATTRIBUTES;
91
+ const urlAttributes = URL_ATTRIBUTES.get(name);
92
+ for (const attribute of [...element.attributes]) {
93
+ // Namespace declarations are dropped silently: the HTML parser puts SVG
94
+ // back in its own namespace by tag name when the frame document is read.
95
+ if (attribute.namespaceURI === XMLNS_NAMESPACE || attribute.name === 'xmlns') {
96
+ element.removeAttributeNode(attribute);
97
+ continue;
98
+ }
99
+ const key = (svg ? attribute.localName : attribute.name).toLowerCase();
100
+ if (key.startsWith('on')) {
101
+ element.removeAttributeNode(attribute);
102
+ tallies.attributes.add(attribute.name.toLowerCase());
103
+ continue;
104
+ }
105
+ if (!globals.has(key) && !allowed.has(key) && !key.startsWith('aria-')) {
106
+ element.removeAttributeNode(attribute);
107
+ tallies.attributes.add(attribute.name.toLowerCase());
108
+ continue;
109
+ }
110
+ if (urlAttributes === undefined || !urlAttributes.has(key))
111
+ continue;
112
+ const reference = classifyReference(attribute.value);
113
+ if (isAllowedReference(name, key, reference.kind, reference.scheme))
114
+ continue;
115
+ element.removeAttributeNode(attribute);
116
+ tallies.attributes.add(`${key} (${reference.scheme ?? reference.kind})`);
117
+ }
118
+ }
119
+ function scrub(element, tallies) {
120
+ const namespace = element.namespaceURI;
121
+ const svg = namespace === SVG_NAMESPACE;
122
+ const name = element.localName.toLowerCase();
123
+ // Foreign vocabularies (MathML and anything else a book invents) keep their
124
+ // text and lose their elements: nothing in them is known to be safe, and
125
+ // their text is still part of the book.
126
+ if (!svg && namespace !== null && namespace !== HTML_NAMESPACE) {
127
+ for (const child of [...element.children])
128
+ scrub(child, tallies);
129
+ tallies.unwrapped.add(name);
130
+ unwrap(element);
131
+ return;
132
+ }
133
+ const discarded = svg ? SVG_DISCARDED : HTML_DISCARDED;
134
+ if (discarded.has(name)) {
135
+ tallies.removed.add(name);
136
+ element.remove();
137
+ return;
138
+ }
139
+ if (name === 'link' && !/(^|\s)stylesheet(\s|$)/i.test(element.getAttribute('rel') ?? '')) {
140
+ tallies.removed.add('link');
141
+ element.remove();
142
+ return;
143
+ }
144
+ if (name === 'style' && STYLE_BREAKOUT.test(element.textContent ?? '')) {
145
+ tallies.removed.add('style');
146
+ element.remove();
147
+ return;
148
+ }
149
+ for (const child of [...element.children])
150
+ scrub(child, tallies);
151
+ const allowed = (svg ? SVG_ELEMENTS : HTML_ELEMENTS).get(name);
152
+ if (allowed === undefined) {
153
+ tallies.unwrapped.add(name);
154
+ unwrap(element);
155
+ return;
156
+ }
157
+ scrubAttributes(element, name, allowed, svg, tallies);
158
+ // A <use> whose reference was refused points at nothing and can only be a
159
+ // leftover of the data:-document vector, so it goes rather than lingering.
160
+ if (svg && name === 'use' && !element.hasAttribute('href') && !element.hasAttributeNS(XLINK_NAMESPACE, 'href')) {
161
+ tallies.removed.add('use');
162
+ element.remove();
163
+ }
164
+ }
165
+ /**
166
+ * Parses, normalizes and sanitizes a section in one pass, returning a fresh
167
+ * HTML document that owns the result.
168
+ *
169
+ * The three are one pass on purpose. Normalization — an XHTML `<a id="x"/>`
170
+ * becoming `<a id="x"></a>` rather than swallowing the rest of the chapter —
171
+ * is a property of parsing as XML and re-serializing as HTML, and the sanitizer
172
+ * is the only place the markup is parsed at all. Doing it twice would be both
173
+ * wasteful and a chance for the two DOMs to disagree.
174
+ */
175
+ export function sanitizeSection(source, mediaType) {
176
+ const parsed = parseSource(source, mediaType);
177
+ const doc = parsed.document;
178
+ const tallies = { removed: new Tally(), unwrapped: new Tally(), attributes: new Tally() };
179
+ const root = doc.documentElement;
180
+ if (root !== null)
181
+ flattenNonElementNodes(doc, root);
182
+ const head = doc.head ?? doc.querySelector('head');
183
+ const body = doc.body ?? doc.querySelector('body');
184
+ const svgRoot = body === null && root !== null && root.namespaceURI === SVG_NAMESPACE;
185
+ if (body === null) {
186
+ // A spine item may be an SVG document in its own right; anything else
187
+ // without a <body> contributes its root element's children.
188
+ if (svgRoot && root !== null)
189
+ scrub(root, tallies);
190
+ else if (root !== null)
191
+ for (const child of [...root.children])
192
+ scrub(child, tallies);
193
+ }
194
+ else {
195
+ if (head !== null)
196
+ for (const child of [...head.children])
197
+ scrub(child, tallies);
198
+ for (const child of [...body.children])
199
+ scrub(child, tallies);
200
+ }
201
+ const out = document.implementation.createHTMLDocument('');
202
+ if (head !== null) {
203
+ for (const element of head.querySelectorAll('link, style')) {
204
+ out.head.append(out.importNode(element, true));
205
+ }
206
+ }
207
+ const content = body !== null ? body : svgRoot ? null : root;
208
+ if (content !== null) {
209
+ for (const node of [...content.childNodes])
210
+ out.body.append(out.importNode(node, true));
211
+ }
212
+ else if (root !== null) {
213
+ out.body.append(out.importNode(root, true));
214
+ }
215
+ return {
216
+ document: out,
217
+ summary: {
218
+ parsedAs: parsed.parsedAs,
219
+ parseFallback: parsed.parseFallback,
220
+ elementsRemoved: tallies.removed.toList(),
221
+ elementsUnwrapped: tallies.unwrapped.toList(),
222
+ attributesRemoved: tallies.attributes.toList(),
223
+ },
224
+ };
225
+ }
226
+ //# sourceMappingURL=sanitize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitize.js","sourceRoot":"","sources":["../../src/view/sanitize.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export interface CentralRecord {
2
+ name: string;
3
+ flags: number;
4
+ method: number;
5
+ crc32: number;
6
+ compressedSize: number;
7
+ uncompressedSize: number;
8
+ localHeaderOffset: number;
9
+ encrypted: boolean;
10
+ }
11
+ export declare function parseCentralDirectory(bytes: Uint8Array, entryCount: number): CentralRecord[];
12
+ //# sourceMappingURL=central.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"central.d.ts","sourceRoot":"","sources":["../../src/zip/central.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,aAAa,EAAE,CAiE5F"}
@@ -0,0 +1,72 @@
1
+ import { ZipFormatError } from './errors.js';
2
+ import { toSafeNumber } from './eocd.js';
3
+ import { view } from './source.js';
4
+ const CEN_SIG = 0x02014b50;
5
+ const CEN_MIN = 46;
6
+ const ZIP64_EXTRA_ID = 0x0001;
7
+ export function parseCentralDirectory(bytes, entryCount) {
8
+ const dv = view(bytes);
9
+ // Names without the UTF-8 flag (bit 11) are cp437 by spec; TextDecoder has no
10
+ // cp437, so non-fatal UTF-8 covers the ASCII names real ebooks use.
11
+ const utf8 = new TextDecoder('utf-8', { fatal: false });
12
+ const records = [];
13
+ let pos = 0;
14
+ for (let i = 0; i < entryCount; i++) {
15
+ if (pos + CEN_MIN > bytes.byteLength || dv.getUint32(pos, true) !== CEN_SIG) {
16
+ throw new ZipFormatError(`malformed central directory: bad record ${i} at offset ${pos}`);
17
+ }
18
+ const flags = dv.getUint16(pos + 8, true);
19
+ const method = dv.getUint16(pos + 10, true);
20
+ const crc32 = dv.getUint32(pos + 16, true);
21
+ let compressedSize = dv.getUint32(pos + 20, true);
22
+ let uncompressedSize = dv.getUint32(pos + 24, true);
23
+ const nameLen = dv.getUint16(pos + 28, true);
24
+ const extraLen = dv.getUint16(pos + 30, true);
25
+ const commentLen = dv.getUint16(pos + 32, true);
26
+ let localHeaderOffset = dv.getUint32(pos + 42, true);
27
+ const recordEnd = pos + CEN_MIN + nameLen + extraLen + commentLen;
28
+ if (recordEnd > bytes.byteLength) {
29
+ throw new ZipFormatError(`malformed central directory: record ${i} overruns the directory`);
30
+ }
31
+ const name = utf8.decode(bytes.subarray(pos + CEN_MIN, pos + CEN_MIN + nameLen));
32
+ const extraStart = pos + CEN_MIN + nameLen;
33
+ let extraPos = extraStart;
34
+ while (extraPos + 4 <= extraStart + extraLen) {
35
+ const id = dv.getUint16(extraPos, true);
36
+ const size = dv.getUint16(extraPos + 2, true);
37
+ if (extraPos + 4 + size > extraStart + extraLen)
38
+ break;
39
+ if (id === ZIP64_EXTRA_ID) {
40
+ // The zip64 extra holds values only for the fields masked with
41
+ // 0xffffffff, in this fixed order.
42
+ let fieldPos = extraPos + 4;
43
+ if (uncompressedSize === 0xffffffff) {
44
+ uncompressedSize = toSafeNumber(dv.getBigUint64(fieldPos, true), `entry ${name} uncompressed size`);
45
+ fieldPos += 8;
46
+ }
47
+ if (compressedSize === 0xffffffff) {
48
+ compressedSize = toSafeNumber(dv.getBigUint64(fieldPos, true), `entry ${name} compressed size`);
49
+ fieldPos += 8;
50
+ }
51
+ if (localHeaderOffset === 0xffffffff) {
52
+ localHeaderOffset = toSafeNumber(dv.getBigUint64(fieldPos, true), `entry ${name} local header offset`);
53
+ fieldPos += 8;
54
+ }
55
+ }
56
+ extraPos += 4 + size;
57
+ }
58
+ records.push({
59
+ name,
60
+ flags,
61
+ method,
62
+ crc32,
63
+ compressedSize,
64
+ uncompressedSize,
65
+ localHeaderOffset,
66
+ encrypted: (flags & 0x0001) !== 0,
67
+ });
68
+ pos = recordEnd;
69
+ }
70
+ return records;
71
+ }
72
+ //# sourceMappingURL=central.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"central.js","sourceRoot":"","sources":["../../src/zip/central.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ /** CRC-32 of `bytes` (zip polynomial), as an unsigned 32-bit integer. */
2
+ export declare function crc32(bytes: Uint8Array): number;
3
+ //# sourceMappingURL=crc32.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crc32.d.ts","sourceRoot":"","sources":["../../src/zip/crc32.ts"],"names":[],"mappings":"AAsBA,yEAAyE;AACzE,wBAAgB,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAM/C"}