web-doc 0.6.1 → 0.7.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 (188) hide show
  1. package/THIRD_PARTY_NOTICES.md +26 -1
  2. package/dist/adapters/docx-images.d.ts +13 -4
  3. package/dist/adapters/docx-images.js +3 -0
  4. package/dist/adapters/docx-paragraphs.d.ts +65 -0
  5. package/dist/adapters/docx-paragraphs.js +165 -0
  6. package/dist/adapters/docx-prepass.d.ts +17 -0
  7. package/dist/adapters/docx-prepass.js +230 -0
  8. package/dist/adapters/office.d.ts +31 -5
  9. package/dist/adapters/office.js +59 -28
  10. package/dist/adapters/pdf.d.ts +9 -0
  11. package/dist/adapters/pdf.js +10 -0
  12. package/dist/assets/pdfium/pdfium.wasm +0 -0
  13. package/dist/contracts.d.ts +47 -2
  14. package/dist/edit/assets.d.ts +23 -0
  15. package/dist/edit/assets.js +75 -0
  16. package/dist/edit/docx/elements.d.ts +6 -0
  17. package/dist/edit/docx/elements.js +90 -0
  18. package/dist/edit/docx/engine.d.ts +51 -0
  19. package/dist/edit/docx/engine.js +473 -0
  20. package/dist/edit/docx/handlers.d.ts +3 -0
  21. package/dist/edit/docx/handlers.js +15 -0
  22. package/dist/edit/docx/ids.d.ts +45 -0
  23. package/dist/edit/docx/ids.js +101 -0
  24. package/dist/edit/docx/model.d.ts +92 -0
  25. package/dist/edit/docx/model.js +339 -0
  26. package/dist/edit/docx/operations.d.ts +37 -0
  27. package/dist/edit/docx/operations.js +3 -0
  28. package/dist/edit/docx/provider.d.ts +13 -0
  29. package/dist/edit/docx/provider.js +34 -0
  30. package/dist/edit/docx/schemas.d.ts +6 -0
  31. package/dist/edit/docx/schemas.js +192 -0
  32. package/dist/edit/docx/session.d.ts +37 -0
  33. package/dist/edit/docx/session.js +426 -0
  34. package/dist/edit/docx/structure-ops.d.ts +6 -0
  35. package/dist/edit/docx/structure-ops.js +494 -0
  36. package/dist/edit/docx/style.d.ts +45 -0
  37. package/dist/edit/docx/style.js +375 -0
  38. package/dist/edit/docx/table-ops.d.ts +4 -0
  39. package/dist/edit/docx/table-ops.js +219 -0
  40. package/dist/edit/docx/text-ops.d.ts +18 -0
  41. package/dist/edit/docx/text-ops.js +458 -0
  42. package/dist/edit/docx/text.d.ts +34 -0
  43. package/dist/edit/docx/text.js +263 -0
  44. package/dist/edit/docx/types.d.ts +191 -0
  45. package/dist/edit/docx/types.js +1 -0
  46. package/dist/edit/docx/write.d.ts +50 -0
  47. package/dist/edit/docx/write.js +352 -0
  48. package/dist/edit/engine.d.ts +117 -0
  49. package/dist/edit/engine.js +1 -0
  50. package/dist/edit/history.d.ts +53 -0
  51. package/dist/edit/history.js +101 -0
  52. package/dist/edit/ooxml/names.d.ts +20 -0
  53. package/dist/edit/ooxml/names.js +61 -0
  54. package/dist/edit/ooxml/opc.d.ts +50 -0
  55. package/dist/edit/ooxml/opc.js +150 -0
  56. package/dist/edit/ooxml/package.d.ts +82 -0
  57. package/dist/edit/ooxml/package.js +233 -0
  58. package/dist/edit/ooxml/patch.d.ts +51 -0
  59. package/dist/edit/ooxml/patch.js +250 -0
  60. package/dist/edit/ooxml/transaction.d.ts +39 -0
  61. package/dist/edit/ooxml/transaction.js +316 -0
  62. package/dist/edit/ooxml/worker.d.ts +8 -0
  63. package/dist/edit/ooxml/worker.js +12 -0
  64. package/dist/edit/ooxml/writer.d.ts +21 -0
  65. package/dist/edit/ooxml/writer.js +187 -0
  66. package/dist/edit/ooxml/xml.d.ts +74 -0
  67. package/dist/edit/ooxml/xml.js +451 -0
  68. package/dist/edit/ooxml/zip.d.ts +54 -0
  69. package/dist/edit/ooxml/zip.js +280 -0
  70. package/dist/edit/operations.d.ts +19 -0
  71. package/dist/edit/operations.js +137 -0
  72. package/dist/edit/pdf/engine/compact.d.ts +6 -0
  73. package/dist/edit/pdf/engine/compact.js +442 -0
  74. package/dist/edit/pdf/engine/document.d.ts +95 -0
  75. package/dist/edit/pdf/engine/document.js +868 -0
  76. package/dist/edit/pdf/engine/elements.d.ts +45 -0
  77. package/dist/edit/pdf/engine/elements.js +313 -0
  78. package/dist/edit/pdf/engine/existing-text.d.ts +4 -0
  79. package/dist/edit/pdf/engine/existing-text.js +424 -0
  80. package/dist/edit/pdf/engine/fonts.d.ts +78 -0
  81. package/dist/edit/pdf/engine/fonts.js +466 -0
  82. package/dist/edit/pdf/engine/geometry.d.ts +36 -0
  83. package/dist/edit/pdf/engine/geometry.js +97 -0
  84. package/dist/edit/pdf/engine/handler.d.ts +21 -0
  85. package/dist/edit/pdf/engine/handler.js +108 -0
  86. package/dist/edit/pdf/engine/images.d.ts +33 -0
  87. package/dist/edit/pdf/engine/images.js +188 -0
  88. package/dist/edit/pdf/engine/layout.d.ts +23 -0
  89. package/dist/edit/pdf/engine/layout.js +177 -0
  90. package/dist/edit/pdf/engine/operations.d.ts +67 -0
  91. package/dist/edit/pdf/engine/operations.js +3 -0
  92. package/dist/edit/pdf/engine/pages.d.ts +6 -0
  93. package/dist/edit/pdf/engine/pages.js +98 -0
  94. package/dist/edit/pdf/engine/pdfium.d.ts +195 -0
  95. package/dist/edit/pdf/engine/pdfium.js +249 -0
  96. package/dist/edit/pdf/engine/shapes.d.ts +4 -0
  97. package/dist/edit/pdf/engine/shapes.js +183 -0
  98. package/dist/edit/pdf/engine/tables.d.ts +44 -0
  99. package/dist/edit/pdf/engine/tables.js +315 -0
  100. package/dist/edit/pdf/engine/text-box.d.ts +71 -0
  101. package/dist/edit/pdf/engine/text-box.js +317 -0
  102. package/dist/edit/pdf/engine/text-layout.d.ts +28 -0
  103. package/dist/edit/pdf/engine/text-layout.js +67 -0
  104. package/dist/edit/pdf/engine/transform.d.ts +5 -0
  105. package/dist/edit/pdf/engine/transform.js +137 -0
  106. package/dist/edit/pdf/provider.d.ts +35 -0
  107. package/dist/edit/pdf/provider.js +92 -0
  108. package/dist/edit/pdf/range-map.d.ts +14 -0
  109. package/dist/edit/pdf/range-map.js +107 -0
  110. package/dist/edit/pdf/schemas.d.ts +11 -0
  111. package/dist/edit/pdf/schemas.js +291 -0
  112. package/dist/edit/pdf/selection.d.ts +5 -0
  113. package/dist/edit/pdf/selection.js +152 -0
  114. package/dist/edit/pdf/session.d.ts +53 -0
  115. package/dist/edit/pdf/session.js +273 -0
  116. package/dist/edit/pdf/types.d.ts +336 -0
  117. package/dist/edit/pdf/types.js +1 -0
  118. package/dist/edit/pptx/elements.d.ts +74 -0
  119. package/dist/edit/pptx/elements.js +301 -0
  120. package/dist/edit/pptx/engine.d.ts +37 -0
  121. package/dist/edit/pptx/engine.js +466 -0
  122. package/dist/edit/pptx/geometry.d.ts +72 -0
  123. package/dist/edit/pptx/geometry.js +193 -0
  124. package/dist/edit/pptx/handler.d.ts +7 -0
  125. package/dist/edit/pptx/handler.js +91 -0
  126. package/dist/edit/pptx/handlers.d.ts +3 -0
  127. package/dist/edit/pptx/handlers.js +21 -0
  128. package/dist/edit/pptx/image-table-ops.d.ts +5 -0
  129. package/dist/edit/pptx/image-table-ops.js +233 -0
  130. package/dist/edit/pptx/model.d.ts +69 -0
  131. package/dist/edit/pptx/model.js +170 -0
  132. package/dist/edit/pptx/operations.d.ts +50 -0
  133. package/dist/edit/pptx/operations.js +11 -0
  134. package/dist/edit/pptx/provider.d.ts +17 -0
  135. package/dist/edit/pptx/provider.js +40 -0
  136. package/dist/edit/pptx/schemas.d.ts +6 -0
  137. package/dist/edit/pptx/schemas.js +220 -0
  138. package/dist/edit/pptx/session.d.ts +44 -0
  139. package/dist/edit/pptx/session.js +114 -0
  140. package/dist/edit/pptx/shape-ops.d.ts +10 -0
  141. package/dist/edit/pptx/shape-ops.js +486 -0
  142. package/dist/edit/pptx/slide-ops.d.ts +6 -0
  143. package/dist/edit/pptx/slide-ops.js +353 -0
  144. package/dist/edit/pptx/style.d.ts +31 -0
  145. package/dist/edit/pptx/style.js +181 -0
  146. package/dist/edit/pptx/text-ops.d.ts +13 -0
  147. package/dist/edit/pptx/text-ops.js +267 -0
  148. package/dist/edit/pptx/text-write.d.ts +67 -0
  149. package/dist/edit/pptx/text-write.js +293 -0
  150. package/dist/edit/pptx/text.d.ts +41 -0
  151. package/dist/edit/pptx/text.js +99 -0
  152. package/dist/edit/pptx/types.d.ts +232 -0
  153. package/dist/edit/pptx/types.js +1 -0
  154. package/dist/edit/schema.d.ts +11 -0
  155. package/dist/edit/schema.js +275 -0
  156. package/dist/edit/session.d.ts +66 -0
  157. package/dist/edit/session.js +701 -0
  158. package/dist/edit/sessions.d.ts +8 -0
  159. package/dist/edit/sessions.js +1 -0
  160. package/dist/edit/types.d.ts +276 -0
  161. package/dist/edit/types.js +1 -0
  162. package/dist/edit/worker-engine.d.ts +31 -0
  163. package/dist/edit/worker-engine.js +91 -0
  164. package/dist/fonts/THIRD_PARTY_NOTICES.md +3 -0
  165. package/dist/fonts/manifest.json +6 -1
  166. package/dist/fonts/noto-sans-latin-cyrillic.ttf +0 -0
  167. package/dist/fonts.d.ts +2 -0
  168. package/dist/fonts.js +4 -0
  169. package/dist/headless.d.ts +4 -0
  170. package/dist/headless.js +4 -0
  171. package/dist/index.d.ts +5 -0
  172. package/dist/index.js +5 -0
  173. package/dist/limits.js +3 -0
  174. package/dist/ooxml-edit-worker.d.ts +1 -0
  175. package/dist/ooxml-edit-worker.js +8 -0
  176. package/dist/pdf-edit-worker.d.ts +1 -0
  177. package/dist/pdf-edit-worker.js +35 -0
  178. package/dist/spreadsheet-viewport.d.ts +5 -0
  179. package/dist/spreadsheet-viewport.js +11 -0
  180. package/dist/ui.js +7 -0
  181. package/dist/viewer.d.ts +6 -0
  182. package/dist/viewer.js +293 -19
  183. package/dist/viewport.d.ts +13 -0
  184. package/dist/viewport.js +126 -1
  185. package/dist/worker-protocol.d.ts +39 -2
  186. package/dist/workers/ooxml-edit-worker.js +8671 -0
  187. package/dist/workers/pdf-edit-worker.js +10844 -0
  188. package/package.json +3 -2
@@ -0,0 +1,451 @@
1
+ import { ViewerError } from "../../errors.js";
2
+ const encoder = new TextEncoder();
3
+ const BOM = "";
4
+ /** Decodes a part as UTF-8; a part that is not UTF-8 cannot be patched. */
5
+ export function decodePart(name, bytes) {
6
+ let text;
7
+ try {
8
+ text = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes);
9
+ }
10
+ catch (error) {
11
+ throw new ViewerError("unsupported-part", `Part ${name} is not UTF-8`, {
12
+ cause: error,
13
+ details: { part: name },
14
+ });
15
+ }
16
+ return { text, hasBom: text.startsWith(BOM) };
17
+ }
18
+ /** The bytes of a part's text; `decodePart` guarantees they equal the original. */
19
+ export function encodePart(text) {
20
+ return encoder.encode(text);
21
+ }
22
+ /** Scans a part's text; throws `malformed-xml` for what it cannot read. */
23
+ export function scanXml(name, text, revision = 0) {
24
+ return new Scanner(name, text, revision).scan();
25
+ }
26
+ /** Escapes character data for element content. */
27
+ export function escapeText(value) {
28
+ return value
29
+ .replaceAll("&", "&")
30
+ .replaceAll("<", "&lt;")
31
+ .replaceAll(">", "&gt;");
32
+ }
33
+ /** Escapes an attribute value and quotes it with double quotes. */
34
+ export function escapeAttribute(value) {
35
+ return `"${value
36
+ .replaceAll("&", "&amp;")
37
+ .replaceAll("<", "&lt;")
38
+ .replaceAll('"', "&quot;")
39
+ .replaceAll("\t", "&#9;")
40
+ .replaceAll("\n", "&#10;")
41
+ .replaceAll("\r", "&#13;")}"`;
42
+ }
43
+ /** Decodes the predefined and numeric entities; anything else stays as written. */
44
+ export function decodeEntities(raw) {
45
+ if (!raw.includes("&"))
46
+ return raw;
47
+ return raw.replace(/&(#x[0-9a-fA-F]+|#[0-9]+|lt|gt|amp|quot|apos);/g, (match, body) => {
48
+ switch (body) {
49
+ case "lt":
50
+ return "<";
51
+ case "gt":
52
+ return ">";
53
+ case "amp":
54
+ return "&";
55
+ case "quot":
56
+ return '"';
57
+ case "apos":
58
+ return "'";
59
+ default: {
60
+ const code = body[1] === "x"
61
+ ? Number.parseInt(body.slice(2), 16)
62
+ : Number.parseInt(body.slice(1), 10);
63
+ return Number.isFinite(code) && code >= 0 && code <= 0x10ffff
64
+ ? String.fromCodePoint(code)
65
+ : match;
66
+ }
67
+ }
68
+ });
69
+ }
70
+ class Scanner {
71
+ #name;
72
+ #text;
73
+ #revision;
74
+ #at = 0;
75
+ #opaque = [];
76
+ #declaration;
77
+ #root;
78
+ #stack = [];
79
+ constructor(name, text, revision) {
80
+ this.#name = name;
81
+ this.#text = text;
82
+ this.#revision = revision;
83
+ }
84
+ scan() {
85
+ const text = this.#text;
86
+ const hasBom = text.startsWith(BOM);
87
+ this.#at = hasBom ? 1 : 0;
88
+ while (this.#at < text.length) {
89
+ const open = text.indexOf("<", this.#at);
90
+ if (open < 0) {
91
+ this.#characterData(this.#at, text.length);
92
+ this.#at = text.length;
93
+ break;
94
+ }
95
+ if (open > this.#at)
96
+ this.#characterData(this.#at, open);
97
+ this.#at = open;
98
+ if (text.startsWith("<?", open))
99
+ this.#processingInstruction();
100
+ else if (text.startsWith("<!--", open))
101
+ this.#comment();
102
+ else if (text.startsWith("<![CDATA[", open))
103
+ this.#cdata();
104
+ else if (text.startsWith("<!DOCTYPE", open))
105
+ this.#doctype();
106
+ else if (text.startsWith("</", open))
107
+ this.#endTag();
108
+ else
109
+ this.#startTag();
110
+ }
111
+ if (this.#stack.length > 0)
112
+ throw this.#malformed(`element ${this.#stack.at(-1).name} is never closed`, text.length);
113
+ if (!this.#root)
114
+ throw this.#malformed("no root element", 0);
115
+ return new ScannedPart(this.#name, text, hasBom, this.#revision, this.#declaration, this.#root, this.#opaque);
116
+ }
117
+ #characterData(start, end) {
118
+ if (this.#stack.length > 0)
119
+ return;
120
+ // Outside the root only whitespace may appear.
121
+ for (let index = start; index < end; index += 1) {
122
+ const code = this.#text.charCodeAt(index);
123
+ if (code !== 0x20 && code !== 0x09 && code !== 0x0a && code !== 0x0d)
124
+ throw this.#malformed("text outside the root element", index);
125
+ }
126
+ }
127
+ #processingInstruction() {
128
+ const close = this.#text.indexOf("?>", this.#at + 2);
129
+ if (close < 0)
130
+ throw this.#malformed("unterminated processing instruction", this.#at);
131
+ const isDeclaration = this.#text.startsWith("<?xml", this.#at) &&
132
+ /^<\?xml[\s?]/.test(this.#text.slice(this.#at, this.#at + 6));
133
+ const range = {
134
+ kind: isDeclaration ? "declaration" : "pi",
135
+ start: this.#at,
136
+ end: close + 2,
137
+ };
138
+ if (isDeclaration) {
139
+ if (this.#declaration || this.#root || this.#stack.length > 0)
140
+ throw this.#malformed("an XML declaration after the start", this.#at);
141
+ this.#declaration = range;
142
+ }
143
+ else
144
+ this.#opaque.push(range);
145
+ this.#at = close + 2;
146
+ }
147
+ #comment() {
148
+ const close = this.#text.indexOf("-->", this.#at + 4);
149
+ if (close < 0)
150
+ throw this.#malformed("unterminated comment", this.#at);
151
+ this.#opaque.push({ kind: "comment", start: this.#at, end: close + 3 });
152
+ this.#at = close + 3;
153
+ }
154
+ #cdata() {
155
+ if (this.#stack.length === 0)
156
+ throw this.#malformed("CDATA outside the root element", this.#at);
157
+ const close = this.#text.indexOf("]]>", this.#at + 9);
158
+ if (close < 0)
159
+ throw this.#malformed("unterminated CDATA section", this.#at);
160
+ this.#opaque.push({ kind: "cdata", start: this.#at, end: close + 3 });
161
+ this.#at = close + 3;
162
+ }
163
+ #doctype() {
164
+ if (this.#root || this.#stack.length > 0)
165
+ throw this.#malformed("DOCTYPE after the root element", this.#at);
166
+ // The internal subset may hold ">" inside brackets.
167
+ let depth = 0;
168
+ let index = this.#at + 9;
169
+ for (; index < this.#text.length; index += 1) {
170
+ const char = this.#text[index];
171
+ if (char === "[")
172
+ depth += 1;
173
+ else if (char === "]")
174
+ depth -= 1;
175
+ else if (char === ">" && depth <= 0)
176
+ break;
177
+ }
178
+ if (index >= this.#text.length)
179
+ throw this.#malformed("unterminated DOCTYPE", this.#at);
180
+ this.#opaque.push({ kind: "doctype", start: this.#at, end: index + 1 });
181
+ this.#at = index + 1;
182
+ }
183
+ #endTag() {
184
+ const text = this.#text;
185
+ const nameStart = this.#at + 2;
186
+ const nameEnd = this.#nameEnd(nameStart);
187
+ if (nameEnd === nameStart)
188
+ throw this.#malformed("end tag without a name", this.#at);
189
+ const name = text.slice(nameStart, nameEnd);
190
+ let close = nameEnd;
191
+ while (close < text.length && isWhitespace(text.charCodeAt(close)))
192
+ close += 1;
193
+ if (text[close] !== ">")
194
+ throw this.#malformed(`end tag </${name}> is not closed`, this.#at);
195
+ const open = this.#stack.pop();
196
+ if (!open)
197
+ throw this.#malformed(`end tag </${name}> without a start tag`, this.#at);
198
+ if (open.name !== name)
199
+ throw this.#malformed(`end tag </${name}> closes <${open.name}>`, this.#at);
200
+ open.contentEnd = this.#at;
201
+ open.end = close + 1;
202
+ this.#at = close + 1;
203
+ }
204
+ #startTag() {
205
+ const text = this.#text;
206
+ const start = this.#at;
207
+ const nameStart = start + 1;
208
+ const nameEnd = this.#nameEnd(nameStart);
209
+ if (nameEnd === nameStart)
210
+ throw this.#malformed("start tag without a name", start);
211
+ const name = text.slice(nameStart, nameEnd);
212
+ const attributes = [];
213
+ let index = nameEnd;
214
+ let selfClosing = false;
215
+ for (;;) {
216
+ while (index < text.length && isWhitespace(text.charCodeAt(index)))
217
+ index += 1;
218
+ if (index >= text.length)
219
+ throw this.#malformed(`start tag <${name}> is not closed`, start);
220
+ const char = text[index];
221
+ if (char === ">") {
222
+ index += 1;
223
+ break;
224
+ }
225
+ if (char === "/") {
226
+ if (text[index + 1] !== ">")
227
+ throw this.#malformed(`stray "/" in <${name}>`, index);
228
+ selfClosing = true;
229
+ index += 2;
230
+ break;
231
+ }
232
+ const attributeStart = index;
233
+ const attributeNameEnd = this.#nameEnd(index);
234
+ if (attributeNameEnd === index)
235
+ throw this.#malformed(`bad attribute in <${name}>`, index);
236
+ const attributeName = text.slice(index, attributeNameEnd);
237
+ index = attributeNameEnd;
238
+ while (index < text.length && isWhitespace(text.charCodeAt(index)))
239
+ index += 1;
240
+ if (text[index] !== "=")
241
+ throw this.#malformed(`attribute ${attributeName} has no value`, index);
242
+ index += 1;
243
+ while (index < text.length && isWhitespace(text.charCodeAt(index)))
244
+ index += 1;
245
+ const quote = text[index];
246
+ if (quote !== '"' && quote !== "'")
247
+ throw this.#malformed(`attribute ${attributeName} is not quoted`, index);
248
+ const valueEnd = text.indexOf(quote, index + 1);
249
+ if (valueEnd < 0)
250
+ throw this.#malformed(`attribute ${attributeName} is not closed`, index);
251
+ const rawValue = text.slice(index + 1, valueEnd);
252
+ if (rawValue.includes("<"))
253
+ throw this.#malformed(`attribute ${attributeName} contains "<"`, index);
254
+ attributes.push({
255
+ name: attributeName,
256
+ value: decodeEntities(rawValue),
257
+ rawValue,
258
+ start: attributeStart,
259
+ end: valueEnd + 1,
260
+ });
261
+ index = valueEnd + 1;
262
+ }
263
+ const parent = this.#stack.at(-1);
264
+ if (!parent && this.#root)
265
+ throw this.#malformed("a second root element", start);
266
+ const colon = name.indexOf(":");
267
+ const prefix = colon < 0 ? "" : name.slice(0, colon);
268
+ const local = colon < 0 ? name : name.slice(colon + 1);
269
+ const scope = scopeOf(parent?.scope, attributes);
270
+ const namespace = scope.get(prefix);
271
+ const element = {
272
+ name,
273
+ prefix,
274
+ local,
275
+ ...(namespace === undefined ? {} : { namespace }),
276
+ attributes,
277
+ start,
278
+ end: index,
279
+ contentStart: index,
280
+ contentEnd: index,
281
+ selfClosing,
282
+ children: [],
283
+ ...(parent ? { parent } : {}),
284
+ index: parent ? parent.children.length : 0,
285
+ path: parent ? [...parent.path, parent.children.length] : [],
286
+ scope,
287
+ };
288
+ if (parent)
289
+ parent.children.push(element);
290
+ else
291
+ this.#root = element;
292
+ if (selfClosing) {
293
+ element.contentStart = element.contentEnd = index - 2;
294
+ }
295
+ else
296
+ this.#stack.push(element);
297
+ this.#at = index;
298
+ }
299
+ #nameEnd(from) {
300
+ const text = this.#text;
301
+ let index = from;
302
+ while (index < text.length) {
303
+ const code = text.charCodeAt(index);
304
+ if (isWhitespace(code) ||
305
+ code === 0x3e || // >
306
+ code === 0x2f || // /
307
+ code === 0x3d || // =
308
+ code === 0x3c || // <
309
+ code === 0x22 || // "
310
+ code === 0x27 // '
311
+ )
312
+ break;
313
+ index += 1;
314
+ }
315
+ return index;
316
+ }
317
+ #malformed(message, at) {
318
+ return new ViewerError("malformed-xml", `Part ${this.#name}: ${message}`, {
319
+ details: { part: this.#name, offset: at },
320
+ });
321
+ }
322
+ }
323
+ function scopeOf(parent, attributes) {
324
+ let scope;
325
+ for (const attribute of attributes) {
326
+ if (attribute.name === "xmlns")
327
+ (scope ??= new Map(parent)).set("", attribute.value);
328
+ else if (attribute.name.startsWith("xmlns:"))
329
+ (scope ??= new Map(parent)).set(attribute.name.slice(6), attribute.value);
330
+ }
331
+ return scope ?? parent ?? EMPTY_SCOPE;
332
+ }
333
+ const EMPTY_SCOPE = new Map();
334
+ function isWhitespace(code) {
335
+ return code === 0x20 || code === 0x09 || code === 0x0a || code === 0x0d;
336
+ }
337
+ class ScannedPart {
338
+ name;
339
+ text;
340
+ hasBom;
341
+ revision;
342
+ declaration;
343
+ root;
344
+ opaque;
345
+ encoding = "utf-8";
346
+ #opaqueByStart;
347
+ constructor(name, text, hasBom, revision, declaration, root, opaque) {
348
+ this.name = name;
349
+ this.text = text;
350
+ this.hasBom = hasBom;
351
+ this.revision = revision;
352
+ this.declaration = declaration;
353
+ this.root = root;
354
+ this.opaque = opaque;
355
+ this.#opaqueByStart = opaque;
356
+ }
357
+ find(name, under = this.root) {
358
+ const matches = matcher(name);
359
+ const stack = [under];
360
+ while (stack.length > 0) {
361
+ const node = stack.pop();
362
+ if (matches(node))
363
+ return node;
364
+ for (let index = node.children.length - 1; index >= 0; index -= 1)
365
+ stack.push(node.children[index]);
366
+ }
367
+ return undefined;
368
+ }
369
+ findAll(name, under = this.root) {
370
+ const matches = matcher(name);
371
+ const found = [];
372
+ const stack = [under];
373
+ while (stack.length > 0) {
374
+ const node = stack.pop();
375
+ if (matches(node))
376
+ found.push(node);
377
+ for (let index = node.children.length - 1; index >= 0; index -= 1)
378
+ stack.push(node.children[index]);
379
+ }
380
+ return found;
381
+ }
382
+ at(path) {
383
+ let node = this.root;
384
+ for (const index of path) {
385
+ node = node?.children[index];
386
+ if (!node)
387
+ return undefined;
388
+ }
389
+ return node;
390
+ }
391
+ textOf(node) {
392
+ if (node.selfClosing)
393
+ return "";
394
+ let out = "";
395
+ let cursor = node.contentStart;
396
+ const stop = node.contentEnd;
397
+ const children = node.children;
398
+ let child = 0;
399
+ let opaqueIndex = firstOpaqueAt(this.#opaqueByStart, cursor);
400
+ while (cursor < stop) {
401
+ const nextChild = child < children.length ? children[child] : undefined;
402
+ const nextOpaque = opaqueIndex < this.#opaqueByStart.length
403
+ ? this.#opaqueByStart[opaqueIndex]
404
+ : undefined;
405
+ const childStart = nextChild ? nextChild.start : Number.POSITIVE_INFINITY;
406
+ const opaqueStart = nextOpaque && nextOpaque.start < stop
407
+ ? nextOpaque.start
408
+ : Number.POSITIVE_INFINITY;
409
+ const next = Math.min(childStart, opaqueStart, stop);
410
+ if (next > cursor)
411
+ out += decodeEntities(this.text.slice(cursor, next));
412
+ if (next === stop)
413
+ break;
414
+ if (next === childStart) {
415
+ out += this.textOf(nextChild);
416
+ cursor = nextChild.end;
417
+ child += 1;
418
+ while (opaqueIndex < this.#opaqueByStart.length &&
419
+ this.#opaqueByStart[opaqueIndex].start < cursor)
420
+ opaqueIndex += 1;
421
+ }
422
+ else {
423
+ if (nextOpaque.kind === "cdata")
424
+ out += this.text.slice(nextOpaque.start + 9, nextOpaque.end - 3);
425
+ cursor = nextOpaque.end;
426
+ opaqueIndex += 1;
427
+ }
428
+ }
429
+ return out;
430
+ }
431
+ attribute(node, name) {
432
+ return node.attributes.find((attribute) => attribute.name === name)?.value;
433
+ }
434
+ }
435
+ function matcher(name) {
436
+ return name.includes(":")
437
+ ? (node) => node.name === name
438
+ : (node) => node.local === name;
439
+ }
440
+ function firstOpaqueAt(opaque, position) {
441
+ let low = 0;
442
+ let high = opaque.length;
443
+ while (low < high) {
444
+ const middle = (low + high) >>> 1;
445
+ if (opaque[middle].start < position)
446
+ low = middle + 1;
447
+ else
448
+ high = middle;
449
+ }
450
+ return low;
451
+ }
@@ -0,0 +1,54 @@
1
+ import type { ResourceLimits } from "../../contracts.js";
2
+ export interface ZipEntry {
3
+ /** The entry name as stored, decoded as UTF-8 (OPC names are ASCII). */
4
+ readonly name: string;
5
+ readonly nameBytes: Uint8Array;
6
+ readonly method: number;
7
+ readonly flags: number;
8
+ readonly crc32: number;
9
+ readonly compressedSize: number;
10
+ readonly uncompressedSize: number;
11
+ readonly localHeaderOffset: number;
12
+ /** Byte range of the central record, for verbatim copies. */
13
+ readonly centralOffset: number;
14
+ readonly centralLength: number;
15
+ readonly extra: Uint8Array;
16
+ readonly comment: Uint8Array;
17
+ readonly modTime: number;
18
+ readonly modDate: number;
19
+ readonly versionMadeBy: number;
20
+ readonly versionNeeded: number;
21
+ readonly internalAttributes: number;
22
+ readonly externalAttributes: number;
23
+ }
24
+ export interface ZipArchive {
25
+ readonly bytes: Uint8Array;
26
+ /** In central-directory order. */
27
+ readonly entries: readonly ZipEntry[];
28
+ readonly centralDirectoryOffset: number;
29
+ readonly centralDirectoryLength: number;
30
+ readonly eocdOffset: number;
31
+ readonly comment: Uint8Array;
32
+ }
33
+ /** Where an entry's local record lies: header, data and any data descriptor. */
34
+ export interface LocalRecord {
35
+ readonly headerOffset: number;
36
+ readonly dataStart: number;
37
+ readonly dataEnd: number;
38
+ /** After the data descriptor when the entry has one. */
39
+ readonly recordEnd: number;
40
+ }
41
+ export declare const METHOD_STORED = 0;
42
+ export declare const METHOD_DEFLATED = 8;
43
+ /** Parses the end-of-central-directory record and the central directory. */
44
+ export declare function parseZip(bytes: Uint8Array, limits: ResourceLimits): ZipArchive;
45
+ /**
46
+ * Locates an entry's local record and checks it against the central record:
47
+ * same name, same method, and the same sizes and CRC unless the entry
48
+ * carries them in a data descriptor instead.
49
+ */
50
+ export declare function localRecordOf(archive: ZipArchive, entry: ZipEntry): LocalRecord;
51
+ /** The entry's bytes, inflated when needed, metered and CRC-checked. */
52
+ export declare function inflateEntry(archive: ZipArchive, entry: ZipEntry, limits: ResourceLimits, signal?: AbortSignal): Promise<Uint8Array>;
53
+ /** CRC-32 as ZIP uses it (IEEE 802.3, reflected). */
54
+ export declare function crc32(bytes: Uint8Array, seed?: number): number;