@office-kit/xlsx 0.10.0 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/README.md +7 -0
  2. package/dist/cell/cell.d.ts +1 -1
  3. package/dist/cell/index.d.ts +4 -4
  4. package/dist/cell/rich-text.d.ts +1 -1
  5. package/dist/cell-BucgGwl3.mjs.map +1 -1
  6. package/dist/cell-style-DuvS_Lsg.mjs.map +1 -1
  7. package/dist/chart/chart-xml.d.ts +1 -1
  8. package/dist/chart/chart.d.ts +3 -3
  9. package/dist/chart/cx/chartex-xml.d.ts +1 -1
  10. package/dist/chart/cx/chartex.d.ts +2 -2
  11. package/dist/chart/index.d.ts +6 -6
  12. package/dist/chart/user-shapes-xml.d.ts +1 -1
  13. package/dist/chart/user-shapes.d.ts +2 -2
  14. package/dist/chart.mjs.map +1 -1
  15. package/dist/chartsheet/chartsheet-xml.d.ts +1 -1
  16. package/dist/chartsheet/chartsheet.d.ts +4 -4
  17. package/dist/chartsheet/index.d.ts +2 -2
  18. package/dist/chartsheet-C3-tqkPy.mjs.map +1 -1
  19. package/dist/coordinate-96Ecci4d.mjs.map +1 -1
  20. package/dist/datetime-B2ySVlXt.mjs.map +1 -1
  21. package/dist/defined-names-CviWmtQg.mjs.map +1 -1
  22. package/dist/differential-DvKU8fJt.mjs.map +1 -1
  23. package/dist/drawing/dml/dml-xml.d.ts +8 -8
  24. package/dist/drawing/dml/effect.d.ts +2 -2
  25. package/dist/drawing/dml/fill.d.ts +1 -1
  26. package/dist/drawing/dml/geometry.d.ts +1 -1
  27. package/dist/drawing/dml/line.d.ts +1 -1
  28. package/dist/drawing/dml/shape-properties.d.ts +5 -5
  29. package/dist/drawing/dml/text.d.ts +4 -4
  30. package/dist/drawing/drawing-xml.d.ts +1 -1
  31. package/dist/drawing/drawing.d.ts +9 -9
  32. package/dist/drawing/index.d.ts +14 -14
  33. package/dist/drawing-BxzLuryn.mjs.map +1 -1
  34. package/dist/drawing.mjs.map +1 -1
  35. package/dist/{escape-DFTE7ZJc.mjs → escape-rnWSPpWY.mjs} +4 -6
  36. package/dist/{escape-DFTE7ZJc.mjs.map → escape-rnWSPpWY.mjs.map} +1 -1
  37. package/dist/formula/tokenizer.d.ts +1 -1
  38. package/dist/formula/translate.d.ts +2 -2
  39. package/dist/io/browser.d.ts +2 -2
  40. package/dist/io/index.d.ts +7 -7
  41. package/dist/io/load.d.ts +11 -4
  42. package/dist/io/node-fs.d.ts +2 -2
  43. package/dist/io/node-save.d.ts +2 -2
  44. package/dist/io/node.d.ts +2 -2
  45. package/dist/io/save.d.ts +2 -2
  46. package/dist/io.mjs +2 -2
  47. package/dist/io.mjs.map +1 -1
  48. package/dist/{load-CnPfhGo3.mjs → load-Bghb1YHY.mjs} +8 -8
  49. package/dist/load-Bghb1YHY.mjs.map +1 -0
  50. package/dist/{manifest-Cm9K7D62.mjs → manifest-pLmx7KYh.mjs} +3 -3
  51. package/dist/manifest-pLmx7KYh.mjs.map +1 -0
  52. package/dist/node.d.ts +3 -3
  53. package/dist/node.mjs +1 -1
  54. package/dist/node.mjs.map +1 -1
  55. package/dist/packaging/core.d.ts +1 -1
  56. package/dist/packaging/custom.d.ts +2 -2
  57. package/dist/packaging/extended.d.ts +2 -2
  58. package/dist/packaging/index.d.ts +10 -10
  59. package/dist/packaging.mjs +1 -1
  60. package/dist/{parser-D3SLaOFe.mjs → parser-By6RWZVW.mjs} +39 -10
  61. package/dist/parser-By6RWZVW.mjs.map +1 -0
  62. package/dist/reader-BNaUTVCy.mjs.map +1 -1
  63. package/dist/{save-2moCT3Af.mjs → save-Bqcf8w3g.mjs} +6 -6
  64. package/dist/save-Bqcf8w3g.mjs.map +1 -0
  65. package/dist/schema/core.d.ts +1 -1
  66. package/dist/schema/index.d.ts +3 -3
  67. package/dist/schema/serialize.d.ts +2 -2
  68. package/dist/serialize-BC2Wu3bR.mjs.map +1 -1
  69. package/dist/{serializer-BtO2TrI7.mjs → serializer-C6nm0mRJ.mjs} +2 -2
  70. package/dist/serializer-C6nm0mRJ.mjs.map +1 -0
  71. package/dist/streaming/index.d.ts +2 -2
  72. package/dist/streaming/read-only.d.ts +6 -4
  73. package/dist/streaming/write-only.d.ts +7 -7
  74. package/dist/streaming.mjs +13 -11
  75. package/dist/streaming.mjs.map +1 -1
  76. package/dist/styles/alignment.schema.d.ts +2 -2
  77. package/dist/styles/borders.d.ts +1 -1
  78. package/dist/styles/borders.schema.d.ts +2 -2
  79. package/dist/styles/cell-style.d.ts +9 -9
  80. package/dist/styles/colors.schema.d.ts +2 -2
  81. package/dist/styles/differential.d.ts +7 -7
  82. package/dist/styles/fills.d.ts +1 -1
  83. package/dist/styles/fills.schema.d.ts +2 -2
  84. package/dist/styles/fonts.d.ts +1 -1
  85. package/dist/styles/fonts.schema.d.ts +2 -2
  86. package/dist/styles/index.d.ts +21 -21
  87. package/dist/styles/named-styles.d.ts +6 -6
  88. package/dist/styles/numbers.schema.d.ts +2 -2
  89. package/dist/styles/protection.schema.d.ts +2 -2
  90. package/dist/styles/stylesheet-reader.d.ts +1 -1
  91. package/dist/styles/stylesheet-writer.d.ts +1 -1
  92. package/dist/styles/stylesheet.d.ts +8 -8
  93. package/dist/{stylesheet-writer-BrOw4jMY.mjs → stylesheet-writer-HxaX7bGy.mjs} +6 -6
  94. package/dist/stylesheet-writer-HxaX7bGy.mjs.map +1 -0
  95. package/dist/table-1P8we2gS.mjs.map +1 -1
  96. package/dist/tree-BX-sRRVp.mjs.map +1 -1
  97. package/dist/user-shapes-DfmCGKB0.mjs.map +1 -1
  98. package/dist/utf8-Tn3nzyik.mjs.map +1 -1
  99. package/dist/utils/escape.d.ts +3 -5
  100. package/dist/utils/index.d.ts +11 -11
  101. package/dist/utils.mjs +1 -1
  102. package/dist/workbook/calc-properties.d.ts +1 -1
  103. package/dist/workbook/defined-names.d.ts +3 -3
  104. package/dist/workbook/index.d.ts +24 -24
  105. package/dist/workbook/protection.d.ts +1 -1
  106. package/dist/workbook/shared-strings.d.ts +2 -2
  107. package/dist/workbook/views.d.ts +1 -1
  108. package/dist/workbook/workbook-properties.d.ts +1 -1
  109. package/dist/workbook/workbook.d.ts +46 -46
  110. package/dist/{workbook-CARZS94U.mjs → workbook-BlZVXiyG.mjs} +3 -3
  111. package/dist/workbook-BlZVXiyG.mjs.map +1 -0
  112. package/dist/workbook.mjs +1 -1
  113. package/dist/workbook.mjs.map +1 -1
  114. package/dist/worksheet/auto-filter.d.ts +1 -1
  115. package/dist/worksheet/cell-range.d.ts +2 -2
  116. package/dist/worksheet/comments-xml.d.ts +1 -1
  117. package/dist/worksheet/conditional-formatting.d.ts +2 -2
  118. package/dist/worksheet/custom-sheet-views.d.ts +2 -2
  119. package/dist/worksheet/data-validations.d.ts +2 -2
  120. package/dist/worksheet/errors.d.ts +1 -1
  121. package/dist/worksheet/hyperlinks.d.ts +1 -1
  122. package/dist/worksheet/index.d.ts +46 -46
  123. package/dist/worksheet/ole-objects.d.ts +1 -1
  124. package/dist/worksheet/page-setup.d.ts +1 -1
  125. package/dist/worksheet/properties.d.ts +1 -1
  126. package/dist/worksheet/protected-ranges.d.ts +1 -1
  127. package/dist/worksheet/protection.d.ts +1 -1
  128. package/dist/worksheet/reader.d.ts +9 -9
  129. package/dist/worksheet/scenarios.d.ts +1 -1
  130. package/dist/worksheet/table-xml.d.ts +1 -1
  131. package/dist/worksheet/table.d.ts +3 -3
  132. package/dist/worksheet/worksheet.d.ts +31 -31
  133. package/dist/worksheet/writer.d.ts +9 -9
  134. package/dist/worksheet-C5Ensf_j.mjs.map +1 -1
  135. package/dist/worksheet.mjs.map +1 -1
  136. package/dist/writer-IyFccCRJ.mjs.map +1 -1
  137. package/dist/xml/index.d.ts +10 -10
  138. package/dist/xml/parser.d.ts +1 -1
  139. package/dist/xml/serializer.d.ts +1 -1
  140. package/dist/xml/stream-writer.d.ts +1 -1
  141. package/dist/xml.mjs +3 -3
  142. package/dist/xml.mjs.map +1 -1
  143. package/dist/zip/decompression-guard.d.ts +1 -1
  144. package/dist/zip/index.d.ts +6 -6
  145. package/dist/zip/random-access-reader.d.ts +2 -2
  146. package/dist/zip/reader.d.ts +2 -2
  147. package/dist/zip/writer.d.ts +1 -1
  148. package/package.json +4 -1
  149. package/dist/load-CnPfhGo3.mjs.map +0 -1
  150. package/dist/manifest-Cm9K7D62.mjs.map +0 -1
  151. package/dist/parser-D3SLaOFe.mjs.map +0 -1
  152. package/dist/save-2moCT3Af.mjs.map +0 -1
  153. package/dist/serializer-BtO2TrI7.mjs.map +0 -1
  154. package/dist/stylesheet-writer-BrOw4jMY.mjs.map +0 -1
  155. package/dist/workbook-CARZS94U.mjs.map +0 -1
@@ -12,6 +12,8 @@ const checkForDoctype = (text) => {
12
12
  if (/<!DOCTYPE\b/.test(stripped)) throw new OpenXmlSchemaError("DTD declarations are not permitted in OOXML payloads");
13
13
  if (/<!ENTITY\b/.test(stripped)) throw new OpenXmlSchemaError("Entity declarations are not permitted in OOXML payloads");
14
14
  };
15
+ const TEXT_KEY = "#text";
16
+ const CDATA_KEY = "#cdata";
15
17
  const parser = new XMLParser({
16
18
  preserveOrder: true,
17
19
  ignoreAttributes: false,
@@ -20,11 +22,30 @@ const parser = new XMLParser({
20
22
  trimValues: false,
21
23
  parseTagValue: false,
22
24
  parseAttributeValue: false,
23
- processEntities: true,
24
- htmlEntities: false
25
+ processEntities: false,
26
+ htmlEntities: false,
27
+ cdataPropName: CDATA_KEY
28
+ });
29
+ const XML_ENTITY_RE = /&(amp|lt|gt|quot|apos|#(?:[0-9]+|x[0-9A-Fa-f]+));/g;
30
+ /** Decode the five predefined XML entities and decimal/hex character refs once. */
31
+ const decodeXmlEntities = (value) => value.replace(XML_ENTITY_RE, (reference, body) => {
32
+ switch (body) {
33
+ case "amp": return "&";
34
+ case "lt": return "<";
35
+ case "gt": return ">";
36
+ case "quot": return "\"";
37
+ case "apos": return "'";
38
+ default: {
39
+ const radix = body[1] === "x" ? 16 : 10;
40
+ const digits = radix === 16 ? body.slice(2) : body.slice(1);
41
+ const codePoint = Number.parseInt(digits, radix);
42
+ if (!isXmlCodePoint(codePoint)) throw new OpenXmlSchemaError(`parseXml: invalid XML character reference "${reference}"`);
43
+ return String.fromCodePoint(codePoint);
44
+ }
45
+ }
25
46
  });
47
+ const isXmlCodePoint = (codePoint) => codePoint === 9 || codePoint === 10 || codePoint === 13 || codePoint >= 32 && codePoint <= 55295 || codePoint >= 57344 && codePoint <= 65533 || codePoint >= 65536 && codePoint <= 1114111;
26
48
  const ATTR_KEY = ":@";
27
- const TEXT_KEY = "#text";
28
49
  /**
29
50
  * Parse a UTF-8 XML payload into an {@link XmlNode} tree. Element and attribute
30
51
  * names are returned in Clark notation. Throws {@link OpenXmlSchemaError} on
@@ -71,11 +92,11 @@ const declarationsOf = (attrs) => {
71
92
  const out = [];
72
93
  for (const [k, v] of Object.entries(attrs ?? {})) if (k === "xmlns") out.push({
73
94
  prefix: "",
74
- ns: v
95
+ ns: decodeXmlEntities(v)
75
96
  });
76
97
  else if (k.startsWith("xmlns:")) out.push({
77
98
  prefix: k.slice(6),
78
- ns: v
99
+ ns: decodeXmlEntities(v)
79
100
  });
80
101
  return out;
81
102
  };
@@ -103,13 +124,13 @@ const extendStack = (parent, attrs) => {
103
124
  let nextByPrefix;
104
125
  for (const [k, v] of Object.entries(attrs)) {
105
126
  if (k === "xmlns") {
106
- nextDefault = v;
127
+ nextDefault = decodeXmlEntities(v);
107
128
  continue;
108
129
  }
109
130
  if (k.startsWith("xmlns:")) {
110
131
  const prefix = k.slice(6);
111
132
  nextByPrefix ??= { ...parent.byPrefix };
112
- nextByPrefix[prefix] = v;
133
+ nextByPrefix[prefix] = decodeXmlEntities(v);
113
134
  }
114
135
  }
115
136
  if (nextDefault === parent.default && nextByPrefix === void 0) return parent;
@@ -138,7 +159,7 @@ const filterAttrs = (rawAttrs, stack) => {
138
159
  if (rawAttrs === void 0) return { resolved };
139
160
  for (const [k, v] of Object.entries(rawAttrs)) {
140
161
  if (k === "xmlns" || k.startsWith("xmlns:")) continue;
141
- resolved[resolveAttrName(k, stack)] = v;
162
+ resolved[resolveAttrName(k, stack)] = decodeXmlEntities(v);
142
163
  }
143
164
  return { resolved };
144
165
  };
@@ -157,7 +178,15 @@ const convertElement = (entry, parentStack) => {
157
178
  for (const child of childEntries) {
158
179
  if (Object.hasOwn(child, TEXT_KEY)) {
159
180
  const t = child[TEXT_KEY];
160
- if (typeof t === "string") textParts.push(t);
181
+ if (typeof t === "string") textParts.push(decodeXmlEntities(t));
182
+ continue;
183
+ }
184
+ if (Object.hasOwn(child, CDATA_KEY)) {
185
+ const section = child[CDATA_KEY];
186
+ for (const part of section) {
187
+ const t = part[TEXT_KEY];
188
+ if (typeof t === "string") textParts.push(t);
189
+ }
161
190
  continue;
162
191
  }
163
192
  if (textParts.length > 0 && node.children.length === 0) {} else if (textParts.length > 0 && node.children.length > 0) {
@@ -177,4 +206,4 @@ const convertElement = (entry, parentStack) => {
177
206
  //#endregion
178
207
  export { parseXmlDocument as n, parseXml as t };
179
208
 
180
- //# sourceMappingURL=parser-D3SLaOFe.mjs.map
209
+ //# sourceMappingURL=parser-By6RWZVW.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser-By6RWZVW.mjs","names":[],"sources":["../src/xml/parser.ts"],"sourcesContent":["// DOM-style XML parser. fast-xml-parser does the lexing, then we walk its\n// preserveOrder tree to:\n// 1. resolve `prefix:local` element + attribute names to Clark notation\n// (`{ns}local`) using a namespace-declaration stack;\n// 2. fold text segments into XmlNode.text for text-only elements;\n// 3. drop XML declarations and processing instructions.\n//\n// DOCTYPE / external entity declarations are rejected outright via a byte-level\n// prescan before the parser ever sees the input — fast-xml-parser does not\n// expand external entities, but we still want the offending document to fail\n// loudly.\n\nimport { XMLParser } from 'fast-xml-parser';\nimport { OpenXmlSchemaError } from '../utils/exceptions.js';\nimport { qname } from './namespaces.js';\nimport { el, type XmlNode } from './tree.js';\n\n// ---- DOCTYPE / DTD prescan --------------------------------------------------\n\nconst decoder = new TextDecoder('utf-8', { fatal: false });\n\nconst decodeForPrescan = (input: Uint8Array | string): string => {\n if (typeof input === 'string') return input;\n return decoder.decode(input);\n};\n\nconst checkForDoctype = (text: string): void => {\n // Strip XML declaration so any subsequent `<!DOCTYPE` is the real thing. The\n // declaration is always the first non-BOM token in well-formed XML.\n const stripped = text.replace(/^/, '');\n if (/<!DOCTYPE\\b/.test(stripped)) {\n throw new OpenXmlSchemaError('DTD declarations are not permitted in OOXML payloads');\n }\n if (/<!ENTITY\\b/.test(stripped)) {\n throw new OpenXmlSchemaError('Entity declarations are not permitted in OOXML payloads');\n }\n};\n\n// ---- fast-xml-parser configuration ------------------------------------------\n\nconst TEXT_KEY = '#text';\nconst CDATA_KEY = '#cdata';\n\nconst parser = new XMLParser({\n preserveOrder: true,\n ignoreAttributes: false,\n attributeNamePrefix: '',\n attributesGroupName: ':@',\n trimValues: false,\n parseTagValue: false,\n parseAttributeValue: false,\n // Decode entities ourselves below. fast-xml-parser expands the five named\n // XML entities but leaves numeric character references untouched. Keeping\n // its processing disabled also lets us preserve XML's single-pass semantics\n // (`&amp;#65;` is the literal text \"&#65;\", not \"A\").\n processEntities: false,\n htmlEntities: false,\n // Without this, CDATA content is merged into `#text` and would go through\n // entity decoding, but CDATA is literal: `<![CDATA[&amp;]]>` is \"&amp;\".\n cdataPropName: CDATA_KEY,\n});\n\nconst XML_ENTITY_RE = /&(amp|lt|gt|quot|apos|#(?:[0-9]+|x[0-9A-Fa-f]+));/g;\n\n/** Decode the five predefined XML entities and decimal/hex character refs once. */\nconst decodeXmlEntities = (value: string): string =>\n value.replace(XML_ENTITY_RE, (reference, body: string) => {\n switch (body) {\n case 'amp':\n return '&';\n case 'lt':\n return '<';\n case 'gt':\n return '>';\n case 'quot':\n return '\"';\n case 'apos':\n return \"'\";\n default: {\n const radix = body[1] === 'x' ? 16 : 10;\n const digits = radix === 16 ? body.slice(2) : body.slice(1);\n const codePoint = Number.parseInt(digits, radix);\n if (!isXmlCodePoint(codePoint)) {\n throw new OpenXmlSchemaError(`parseXml: invalid XML character reference \"${reference}\"`);\n }\n return String.fromCodePoint(codePoint);\n }\n }\n });\n\nconst isXmlCodePoint = (codePoint: number): boolean =>\n codePoint === 0x09 ||\n codePoint === 0x0a ||\n codePoint === 0x0d ||\n (codePoint >= 0x20 && codePoint <= 0xd7ff) ||\n (codePoint >= 0xe000 && codePoint <= 0xfffd) ||\n (codePoint >= 0x10000 && codePoint <= 0x10ffff);\n\n// ---- preserveOrder shape ----------------------------------------------------\n\ntype FxpAttrs = Record<string, string>;\ntype FxpEntry = { ':@'?: FxpAttrs } & { [tagOrText: string]: FxpEntry[] | string | FxpAttrs | undefined };\ntype FxpTree = FxpEntry[];\n\nconst ATTR_KEY = ':@';\n\n// ---- public API -------------------------------------------------------------\n\nexport interface ParsedDocument {\n root: XmlNode;\n /**\n * The root element's own `xmlns` declarations, in source order (`prefix` is\n * `''` for the default one). Clark notation carries the namespace but not\n * the prefix, so these are only needed by parts that must be written back\n * with the prefixes the producer chose — `xl/workbook.xml`, whose\n * `mc:Ignorable` names them by prefix.\n */\n rootNamespaces: Array<{ prefix: string; ns: string }>;\n}\n\n/**\n * Parse a UTF-8 XML payload into an {@link XmlNode} tree. Element and attribute\n * names are returned in Clark notation. Throws {@link OpenXmlSchemaError} on\n * DTD/entity declarations or on multi-root documents.\n *\n * Shorthand for {@link parseXmlDocument} when the root's namespace prefixes\n * don't matter — which is everywhere except the handful of parts that carry an\n * `mc:Ignorable`.\n */\nexport function parseXml(input: Uint8Array | string): XmlNode {\n return parseXmlDocument(input).root;\n}\n\n/** {@link parseXml} plus the root element's namespace declarations. */\nexport function parseXmlDocument(input: Uint8Array | string): ParsedDocument {\n const text = decodeForPrescan(input);\n checkForDoctype(text);\n\n let raw: FxpTree;\n try {\n raw = parser.parse(text) as FxpTree;\n } catch (cause) {\n throw new OpenXmlSchemaError('parseXml: failed to parse XML payload', { cause });\n }\n\n // Skip XML declaration, processing instructions and any leading whitespace\n // text nodes.\n const roots: FxpEntry[] = [];\n for (const entry of raw) {\n const tag = elementTag(entry);\n if (tag === undefined) continue; // text-only entry\n if (isProcessingInstruction(tag)) continue; // <?xml …?>, other PIs\n roots.push(entry);\n }\n if (roots.length === 0) {\n throw new OpenXmlSchemaError('parseXml: document has no root element');\n }\n if (roots.length > 1) {\n throw new OpenXmlSchemaError(`parseXml: document has ${roots.length} root elements; expected exactly one`);\n }\n\n const initial: NamespaceStack = { default: '', byPrefix: {} };\n const [root] = roots;\n if (root === undefined) {\n throw new OpenXmlSchemaError('parseXml: no root element');\n }\n return { root: convertElement(root, initial), rootNamespaces: declarationsOf(root[ATTR_KEY] as FxpAttrs | undefined) };\n}\n\nconst declarationsOf = (attrs: FxpAttrs | undefined): Array<{ prefix: string; ns: string }> => {\n const out: Array<{ prefix: string; ns: string }> = [];\n for (const [k, v] of Object.entries(attrs ?? {})) {\n if (k === 'xmlns') out.push({ prefix: '', ns: decodeXmlEntities(v) });\n else if (k.startsWith('xmlns:')) out.push({ prefix: k.slice('xmlns:'.length), ns: decodeXmlEntities(v) });\n }\n return out;\n};\n\n// ---- conversion -------------------------------------------------------------\n\ninterface NamespaceStack {\n /** Default namespace URI (xmlns=\"…\"); empty string means no default. */\n readonly default: string;\n /** Map of prefix → namespace URI declared in this scope or any ancestor. */\n readonly byPrefix: Readonly<Record<string, string>>;\n}\n\nconst elementTag = (entry: FxpEntry): string | undefined => {\n for (const k of Object.keys(entry)) {\n if (k === ATTR_KEY) continue;\n return k;\n }\n return undefined;\n};\n\nconst isProcessingInstruction = (tag: string): boolean => tag.startsWith('?');\n\nconst splitPrefixed = (qname0: string): { prefix: string; local: string } => {\n const idx = qname0.indexOf(':');\n if (idx < 0) return { prefix: '', local: qname0 };\n return { prefix: qname0.slice(0, idx), local: qname0.slice(idx + 1) };\n};\n\nconst extendStack = (parent: NamespaceStack, attrs: FxpAttrs | undefined): NamespaceStack => {\n if (attrs === undefined) return parent;\n let nextDefault = parent.default;\n let nextByPrefix: Record<string, string> | undefined;\n for (const [k, v] of Object.entries(attrs)) {\n if (k === 'xmlns') {\n nextDefault = decodeXmlEntities(v);\n continue;\n }\n if (k.startsWith('xmlns:')) {\n const prefix = k.slice('xmlns:'.length);\n nextByPrefix ??= { ...parent.byPrefix };\n nextByPrefix[prefix] = decodeXmlEntities(v);\n }\n }\n if (nextDefault === parent.default && nextByPrefix === undefined) return parent;\n return {\n default: nextDefault,\n byPrefix: nextByPrefix ?? parent.byPrefix,\n };\n};\n\nconst resolveElementName = (raw: string, stack: NamespaceStack): string => {\n const { prefix, local } = splitPrefixed(raw);\n if (prefix === '') return qname(stack.default, local);\n const ns = stack.byPrefix[prefix];\n if (ns === undefined) {\n throw new OpenXmlSchemaError(`parseXml: undeclared namespace prefix \"${prefix}\" on element <${raw}>`);\n }\n return qname(ns, local);\n};\n\nconst resolveAttrName = (raw: string, stack: NamespaceStack): string => {\n const { prefix, local } = splitPrefixed(raw);\n // Unprefixed attributes do NOT inherit the default namespace (XMLNS spec).\n if (prefix === '') return local;\n if (prefix === 'xml') return qname('http://www.w3.org/XML/1998/namespace', local);\n const ns = stack.byPrefix[prefix];\n if (ns === undefined) {\n throw new OpenXmlSchemaError(`parseXml: undeclared namespace prefix \"${prefix}\" on attribute \"${raw}\"`);\n }\n return qname(ns, local);\n};\n\nconst filterAttrs = (rawAttrs: FxpAttrs | undefined, stack: NamespaceStack): { resolved: Record<string, string> } => {\n const resolved: Record<string, string> = {};\n if (rawAttrs === undefined) return { resolved };\n for (const [k, v] of Object.entries(rawAttrs)) {\n // xmlns / xmlns:* declarations: dropped from the XmlNode attribute table.\n // The serializer rebuilds them from the Clark-notation namespaces it walks,\n // so round-tripping does not require preserving the declarations.\n if (k === 'xmlns' || k.startsWith('xmlns:')) continue;\n resolved[resolveAttrName(k, stack)] = decodeXmlEntities(v);\n }\n return { resolved };\n};\n\nconst isWhitespaceOnly = (s: string): boolean => /^\\s*$/.test(s);\n\nconst convertElement = (entry: FxpEntry, parentStack: NamespaceStack): XmlNode => {\n const rawTag = elementTag(entry);\n if (rawTag === undefined) {\n throw new OpenXmlSchemaError('parseXml: encountered an entry with no element tag');\n }\n if (isProcessingInstruction(rawTag)) {\n throw new OpenXmlSchemaError(`parseXml: processing instructions are not supported (saw \"<${rawTag}>\")`);\n }\n\n const rawAttrs = entry[ATTR_KEY] as FxpAttrs | undefined;\n const stack = extendStack(parentStack, rawAttrs);\n\n const { resolved } = filterAttrs(rawAttrs, stack);\n const node = el(resolveElementName(rawTag, stack), resolved);\n\n const childEntries = entry[rawTag] as FxpEntry[] | undefined;\n if (childEntries === undefined || childEntries.length === 0) return node;\n\n const textParts: string[] = [];\n for (const child of childEntries) {\n if (Object.hasOwn(child, TEXT_KEY)) {\n const t = child[TEXT_KEY];\n if (typeof t === 'string') textParts.push(decodeXmlEntities(t));\n continue;\n }\n if (Object.hasOwn(child, CDATA_KEY)) {\n const section = child[CDATA_KEY] as FxpEntry[];\n for (const part of section) {\n const t = part[TEXT_KEY];\n if (typeof t === 'string') textParts.push(t);\n }\n continue;\n }\n if (textParts.length > 0 && node.children.length === 0) {\n // text accumulated *before* any child element — keep collecting.\n } else if (textParts.length > 0 && node.children.length > 0) {\n const acc = textParts.join('');\n if (!isWhitespaceOnly(acc)) {\n throw new OpenXmlSchemaError(`parseXml: mixed content not supported (text between elements under <${rawTag}>)`);\n }\n // whitespace-only inter-element text: drop.\n textParts.length = 0;\n }\n node.children.push(convertElement(child, stack));\n }\n // Trailing text after the last child element.\n if (textParts.length > 0) {\n const acc = textParts.join('');\n if (node.children.length === 0) {\n // text-only element (the common case): keep as the element's text.\n node.text = acc;\n } else if (!isWhitespaceOnly(acc)) {\n throw new OpenXmlSchemaError(`parseXml: mixed content not supported (trailing text under <${rawTag}>)`);\n }\n }\n return node;\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"reader-BNaUTVCy.mjs","names":[],"sources":["../src/zip/decompression-guard.ts","../src/zip/random-access-reader.ts","../src/zip/reader.ts"],"sourcesContent":["// Decompression-bomb safeguards for the ZIP reader.\n//\n// Zip-bombs encode pathological compression ratios (e.g. 100 B → 1 GB) so that\n// a naive `inflate` exhausts memory before the caller can react. The guards\n// here apply three orthogonal bounds:\n//\n// 1. Per-entry uncompressed cap — hard ceiling on a single payload.\n// 2. Total-archive uncompressed cap — hard ceiling summed across the archive.\n// 3. Per-entry compression ratio — bounds the *amplification factor* so that\n// even small compressed entries can't decompress into hundreds of MB.\n//\n// Defaults are sized to admit any legitimate xlsx (xml compresses well but\n// rarely exceeds the per-entry cap; office templates with embedded media stay\n// well under the total cap) while rejecting plausible bombs. Trusted callers\n// (e.g. a backend that only ever loads xlsx it generated itself) can pass\n// `false` to disable the guard.\n\nimport { OpenXmlDecompressionBombError, OpenXmlError } from '../utils/exceptions';\n\n/** Per-archive limits enforced during {@link openZip}. */\nexport interface DecompressionLimits {\n /**\n * Maximum decompressed bytes for a single archive entry. Default 512 MiB.\n * Legitimate xlsx sheets stay well below this even with millions of cells.\n */\n maxEntryUncompressedBytes?: number;\n /**\n * Maximum decompressed bytes summed across every entry the caller reads.\n * Default 1 GiB.\n */\n maxTotalUncompressedBytes?: number;\n /**\n * Maximum allowed `uncompressed / compressed` ratio for a single entry.\n * Default 1000. xml usually compresses 5–20×, but highly repetitive payloads\n * (long runs of zeros, sparse worksheets) can hit several hundred ×\n * legitimately. Classic zip-bombs run 10 000× and up, so a 1000× ceiling\n * still catches them while admitting realistic content. The implementation\n * treats compressed sizes below 64 B as exempt — there's no amplification\n * budget worth policing on such small entries, and the absolute per-entry /\n * archive limits already cover them.\n */\n maxCompressionRatio?: number;\n}\n\n/** Resolved limits with no `undefined` fields. */\nexport interface ResolvedDecompressionLimits {\n readonly maxEntryUncompressedBytes: number;\n readonly maxTotalUncompressedBytes: number;\n readonly maxCompressionRatio: number;\n}\n\n/** Default safeguards applied when the caller doesn't override them. */\nexport const DEFAULT_DECOMPRESSION_LIMITS: ResolvedDecompressionLimits = {\n maxEntryUncompressedBytes: 512 * 1024 * 1024,\n maxTotalUncompressedBytes: 1024 * 1024 * 1024,\n maxCompressionRatio: 1000,\n};\n\n/** Below this compressed size, the ratio check is skipped — see {@link DecompressionLimits}. */\nconst RATIO_CHECK_MIN_COMPRESSED_BYTES = 64;\n\n/**\n * `DecompressionLimits` plus the sentinel `false` to disable the guard.\n * Anything else is treated as \"use defaults\".\n */\nexport type DecompressionLimitsInput = DecompressionLimits | false | undefined;\n\n// Each limit must be a positive finite number to be meaningful; NaN / Infinity\n// / 0 / negatives silently turn `emitted > cap` and `totalInflated > cap` into\n// no-ops and disable the guard. Reject those at the boundary so the only way\n// the guard is off is the explicit `false` sentinel.\nconst requirePositiveFinite = (field: string, value: number): void => {\n if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) {\n throw new OpenXmlError(\n `resolveDecompressionLimits: ${field} must be a positive finite number; got ${String(value)}`,\n );\n }\n};\n\n/** Returns null when the guard is disabled. */\nexport function resolveDecompressionLimits(\n input: DecompressionLimitsInput,\n): ResolvedDecompressionLimits | null {\n if (input === false) return null;\n if (!input) return DEFAULT_DECOMPRESSION_LIMITS;\n const resolved: ResolvedDecompressionLimits = {\n maxEntryUncompressedBytes:\n input.maxEntryUncompressedBytes ?? DEFAULT_DECOMPRESSION_LIMITS.maxEntryUncompressedBytes,\n maxTotalUncompressedBytes:\n input.maxTotalUncompressedBytes ?? DEFAULT_DECOMPRESSION_LIMITS.maxTotalUncompressedBytes,\n maxCompressionRatio:\n input.maxCompressionRatio ?? DEFAULT_DECOMPRESSION_LIMITS.maxCompressionRatio,\n };\n requirePositiveFinite('maxEntryUncompressedBytes', resolved.maxEntryUncompressedBytes);\n requirePositiveFinite('maxTotalUncompressedBytes', resolved.maxTotalUncompressedBytes);\n requirePositiveFinite('maxCompressionRatio', resolved.maxCompressionRatio);\n return resolved;\n}\n\n/**\n * Per-archive byte accounting shared across every read of an archive opened\n * with the given limits. Tracks total inflated bytes so the cap is enforced\n * even when individual entries stay below the per-entry ceiling.\n */\nexport interface DecompressionBudget {\n readonly limits: ResolvedDecompressionLimits;\n totalInflated: number;\n}\n\nexport function createBudget(limits: ResolvedDecompressionLimits): DecompressionBudget {\n return { limits, totalInflated: 0 };\n}\n\n/**\n * Per-entry inflate cap, accounting for both the absolute per-entry bound and\n * the ratio-based bound. Returns the smaller of the two — whichever fires\n * first stops the inflate loop.\n */\nexport function entryInflateCap(budget: DecompressionBudget, compressedSize: number): number {\n const { maxEntryUncompressedBytes, maxCompressionRatio } = budget.limits;\n if (compressedSize < RATIO_CHECK_MIN_COMPRESSED_BYTES) {\n return maxEntryUncompressedBytes;\n }\n const ratioCap = compressedSize * maxCompressionRatio;\n return Math.min(maxEntryUncompressedBytes, ratioCap);\n}\n\n/** Throw if the declared central-directory totals already exceed the limits. */\nexport function checkDeclaredTotals(\n budget: DecompressionBudget,\n declaredEntries: ReadonlyArray<{ path: string; compSize: number; uncompSize: number }>,\n): void {\n let declaredTotal = 0;\n for (const entry of declaredEntries) {\n declaredTotal += entry.uncompSize;\n if (entry.uncompSize > budget.limits.maxEntryUncompressedBytes) {\n throw new OpenXmlDecompressionBombError(\n `openZip: entry \"${entry.path}\" declares ${entry.uncompSize} uncompressed bytes,` +\n ` exceeding the ${budget.limits.maxEntryUncompressedBytes}-byte per-entry limit` +\n ` (decompression-bomb guard).`,\n );\n }\n if (\n entry.compSize >= RATIO_CHECK_MIN_COMPRESSED_BYTES &&\n entry.uncompSize > entry.compSize * budget.limits.maxCompressionRatio\n ) {\n throw new OpenXmlDecompressionBombError(\n `openZip: entry \"${entry.path}\" declares ratio ${(entry.uncompSize / entry.compSize).toFixed(1)}x` +\n ` (${entry.uncompSize}/${entry.compSize}), exceeding the ${budget.limits.maxCompressionRatio}x` +\n ` per-entry limit (decompression-bomb guard).`,\n );\n }\n }\n if (declaredTotal > budget.limits.maxTotalUncompressedBytes) {\n throw new OpenXmlDecompressionBombError(\n `openZip: declared total uncompressed size ${declaredTotal} bytes exceeds the` +\n ` ${budget.limits.maxTotalUncompressedBytes}-byte archive limit (decompression-bomb guard).`,\n );\n }\n}\n\n/**\n * Record `bytes` against the global budget. Throws when the running total\n * crosses {@link ResolvedDecompressionLimits.maxTotalUncompressedBytes}. Called\n * from both sync and streaming inflate code paths.\n */\nexport function recordInflated(budget: DecompressionBudget, path: string, bytes: number): void {\n budget.totalInflated += bytes;\n if (budget.totalInflated > budget.limits.maxTotalUncompressedBytes) {\n throw new OpenXmlDecompressionBombError(\n `openZip: archive-wide inflated size exceeded ${budget.limits.maxTotalUncompressedBytes} bytes` +\n ` while reading \"${path}\" (decompression-bomb guard).`,\n );\n }\n}\n\n/** Build the message thrown when a single entry exceeds its cap mid-inflate. */\nexport function entryOverflowError(path: string, cap: number): OpenXmlDecompressionBombError {\n return new OpenXmlDecompressionBombError(\n `openZip: inflated size of \"${path}\" exceeded ${cap} bytes (decompression-bomb guard).`,\n );\n}\n","// Random-access ZIP reader. / §2.3 streaming-read residual.\n//\n// The previous reader handed every entry to `fflate.unzipSync` up front, which\n// materialises *every* uncompressed payload into memory at once. For a 100 MB\n// xlsx with ~500 MB of decompressed sheet data the resident set spikes\n// accordingly. The random-access path keeps only the compressed archive bytes\n// resident, parses the central directory once (cheap — ~46 B per entry plus\n// filename), and inflates each entry lazily on `read(path)`.\n//\n// Limitations:\n// - ZIP64 reads only when the standard ZIP32 fields fit. EOCD with\n// sentinel values (0xFFFF / 0xFFFFFFFF) falls back to fflate's `unzipSync` so\n// external ZIP64 archives still load (the writer side has its own ZIP32 cap\n// guard).\n// - Compression methods: STORE (0) and DEFLATE (8). Anything else\n// throws OpenXmlIoError.\n\nimport { Inflate, unzipSync } from 'fflate';\nimport { OpenXmlDecompressionBombError, OpenXmlIoError } from '../utils/exceptions';\nimport {\n checkDeclaredTotals,\n createBudget,\n type DecompressionBudget,\n type DecompressionLimitsInput,\n entryInflateCap,\n entryOverflowError,\n recordInflated,\n resolveDecompressionLimits,\n} from './decompression-guard';\nimport type { ZipArchive } from './reader';\n\n/**\n * Chunk size used when feeding compressed bytes into fflate's `Inflate` for\n * streaming reads. 64 KB matches the saxes/SAX consumer's typical batch and\n * keeps peak transient memory bounded.\n */\nconst INFLATE_CHUNK_BYTES = 64 * 1024;\n\nconst singleChunkStream = (bytes: Uint8Array): ReadableStream<Uint8Array> =>\n new ReadableStream<Uint8Array>({\n start(controller) {\n if (bytes.byteLength > 0) controller.enqueue(bytes);\n controller.close();\n },\n });\n\nconst SIG_EOCD = 0x06054b50;\nconst SIG_CD = 0x02014b50;\nconst SIG_LFH = 0x04034b50;\nconst SIG_ZIP64_EOCD = 0x06064b50;\nconst SIG_ZIP64_EOCD_LOCATOR = 0x07064b50;\nconst ZIP32_MAX_U16 = 0xffff;\nconst ZIP32_MAX_U32 = 0xffffffff;\nconst ZIP64_LOCATOR_SIZE = 20;\nconst ZIP64_EXTRA_HEADER_ID = 0x0001;\nconst COMP_STORE = 0;\nconst COMP_DEFLATE = 8;\n\ninterface CdEntry {\n path: string;\n lfhOffset: number;\n compMethod: number;\n compSize: number;\n uncompSize: number;\n /** General-purpose bit flag, used to detect bit-3 (data descriptor) and bit-11 (UTF-8). */\n gpFlag: number;\n}\n\nconst u16 = (b: Uint8Array, off: number): number => (b[off] ?? 0) | ((b[off + 1] ?? 0) << 8);\nconst u32 = (b: Uint8Array, off: number): number => {\n const v0 = b[off] ?? 0;\n const v1 = b[off + 1] ?? 0;\n const v2 = b[off + 2] ?? 0;\n const v3 = b[off + 3] ?? 0;\n return (v0 | (v1 << 8) | (v2 << 16) | (v3 << 24)) >>> 0;\n};\n\n// Read a little-endian 64-bit value as a JS Number. Safe for values up to\n// 2^53-1 (Number.MAX_SAFE_INTEGER ~ 9 PiB), which is well past any realistic\n// xlsx archive — we throw if a parsed value exceeds the safe-integer range.\nconst u64 = (b: Uint8Array, off: number): number => {\n const lo = u32(b, off);\n const hi = u32(b, off + 4);\n if (hi > 0x1fffff) {\n throw new OpenXmlIoError(\n `openZip: ZIP64 field at byte ${off} exceeds the safe-integer range (${hi}*2^32 + ${lo})`,\n );\n }\n return hi * 0x100000000 + lo;\n};\n\n/** Find the End-of-Central-Directory record by scanning backwards from EOF. */\nfunction findEocd(b: Uint8Array): number {\n const minStart = Math.max(0, b.length - 22 - 0xffff);\n for (let i = b.length - 22; i >= minStart; i--) {\n if (u32(b, i) === SIG_EOCD) return i;\n }\n throw new OpenXmlIoError('openZip: no End-of-Central-Directory signature found');\n}\n\ninterface CdSummary {\n totalEntries: number;\n cdSize: number;\n cdOffset: number;\n}\n\n/**\n * Resolve the central-directory totals. When the EOCD carries ZIP64 sentinel\n * values (0xFFFF / 0xFFFFFFFF) the real totals live in a ZIP64 EOCD record\n * located via the ZIP64 EOCD locator just before the regular EOCD. ECMA-376\n * xlsx archives stay in ZIP32 territory; the ZIP64 path exists so the\n * decompression-bomb guards still apply to large external archives instead of\n * falling all the way back to `unzipSync` (which produces every entry's bytes\n * before any cap can fire).\n */\nfunction readCdSummary(b: Uint8Array, eocdOff: number): CdSummary {\n let totalEntries = u16(b, eocdOff + 10);\n let cdSize = u32(b, eocdOff + 12);\n let cdOffset = u32(b, eocdOff + 16);\n\n const usesZip64Eocd =\n totalEntries === ZIP32_MAX_U16 || cdSize === ZIP32_MAX_U32 || cdOffset === ZIP32_MAX_U32;\n if (!usesZip64Eocd) {\n return { totalEntries, cdSize, cdOffset };\n }\n\n // Locate the ZIP64 EOCD locator. It sits immediately before the regular\n // EOCD when one is present.\n const locatorOff = eocdOff - ZIP64_LOCATOR_SIZE;\n if (locatorOff < 0 || u32(b, locatorOff) !== SIG_ZIP64_EOCD_LOCATOR) {\n throw new OpenXmlIoError('openZip: ZIP64 EOCD locator missing despite EOCD sentinel values');\n }\n const zip64EocdOff = u64(b, locatorOff + 8);\n if (zip64EocdOff < 0 || zip64EocdOff + 56 > b.length) {\n throw new OpenXmlIoError(`openZip: ZIP64 EOCD offset ${zip64EocdOff} out of bounds`);\n }\n if (u32(b, zip64EocdOff) !== SIG_ZIP64_EOCD) {\n throw new OpenXmlIoError(`openZip: ZIP64 EOCD signature missing at byte ${zip64EocdOff}`);\n }\n totalEntries = u64(b, zip64EocdOff + 32);\n cdSize = u64(b, zip64EocdOff + 40);\n cdOffset = u64(b, zip64EocdOff + 48);\n return { totalEntries, cdSize, cdOffset };\n}\n\n/** Walk the ZIP64 Extended Information extra field for sentinel-valued sizes/offset. */\nfunction readZip64Extra(\n b: Uint8Array,\n extraStart: number,\n extraLen: number,\n wantsUncompSize: boolean,\n wantsCompSize: boolean,\n wantsLfhOffset: boolean,\n entryPath: string,\n): { uncompSize?: number; compSize?: number; lfhOffset?: number } {\n let p = extraStart;\n const end = extraStart + extraLen;\n while (p + 4 <= end) {\n const id = u16(b, p);\n const size = u16(b, p + 2);\n const dataStart = p + 4;\n const next = dataStart + size;\n if (next > end) break;\n if (id === ZIP64_EXTRA_HEADER_ID) {\n // Sanity-check that the declared extra-field size actually covers every\n // u64 the CD asked us to read. A truncated / mis-sized ZIP64 extra\n // field would otherwise let u64() interpret bytes belonging to the\n // next extra record (or the next CD entry) as a size/offset, producing\n // bogus values that downstream bomb checks then trust. Fail closed.\n const needed = (wantsUncompSize ? 8 : 0) + (wantsCompSize ? 8 : 0) + (wantsLfhOffset ? 8 : 0);\n if (size < needed) {\n throw new OpenXmlIoError(\n `openZip: ZIP64 extended-info field for \"${entryPath}\" declares ${size} bytes` +\n ` but the central directory sentinels require ${needed}`,\n );\n }\n let q = dataStart;\n const result: { uncompSize?: number; compSize?: number; lfhOffset?: number } = {};\n if (wantsUncompSize) {\n result.uncompSize = u64(b, q);\n q += 8;\n }\n if (wantsCompSize) {\n result.compSize = u64(b, q);\n q += 8;\n }\n if (wantsLfhOffset) {\n result.lfhOffset = u64(b, q);\n q += 8;\n }\n return result;\n }\n p = next;\n }\n return {};\n}\n\n/** Parse the central directory into an array of entry descriptors. */\nfunction parseCentralDirectory(b: Uint8Array, cdOffset: number, expectedCount: number): CdEntry[] {\n const entries: CdEntry[] = [];\n let p = cdOffset;\n for (let i = 0; i < expectedCount; i++) {\n if (u32(b, p) !== SIG_CD) {\n throw new OpenXmlIoError(`openZip: malformed central directory at byte ${p}`);\n }\n const gpFlag = u16(b, p + 8);\n const compMethod = u16(b, p + 10);\n let compSize = u32(b, p + 20);\n let uncompSize = u32(b, p + 24);\n const nameLen = u16(b, p + 28);\n const extraLen = u16(b, p + 30);\n const commentLen = u16(b, p + 32);\n let lfhOffset = u32(b, p + 42);\n const nameBytes = b.subarray(p + 46, p + 46 + nameLen);\n // Bit 11 (0x0800) signals UTF-8 filename. xlsx archives are almost always\n // UTF-8 already; treat bit-0 as UTF-8 too since CP437 ⊃ ASCII and xlsx uses\n // ASCII paths.\n const path = new TextDecoder('utf-8').decode(nameBytes);\n\n // ZIP64 Extended Information rewrites whichever of {uncompSize, compSize,\n // lfhOffset} are 0xFFFFFFFF sentinels in the canonical fields. The extra\n // field stores them in the order listed in the spec (and omits any that\n // weren't sentinels), so we have to track which slots to read.\n const wantsUncomp = uncompSize === ZIP32_MAX_U32;\n const wantsComp = compSize === ZIP32_MAX_U32;\n const wantsOffset = lfhOffset === ZIP32_MAX_U32;\n if (wantsUncomp || wantsComp || wantsOffset) {\n const extra = readZip64Extra(b, p + 46 + nameLen, extraLen, wantsUncomp, wantsComp, wantsOffset, path);\n if (extra.uncompSize !== undefined) uncompSize = extra.uncompSize;\n if (extra.compSize !== undefined) compSize = extra.compSize;\n if (extra.lfhOffset !== undefined) lfhOffset = extra.lfhOffset;\n }\n entries.push({ path, lfhOffset, compMethod, compSize, uncompSize, gpFlag });\n p += 46 + nameLen + extraLen + commentLen;\n }\n return entries;\n}\n\n/** Read the compressed bytes for a CD entry by walking its local file header. */\nfunction readCompressedBytes(b: Uint8Array, entry: CdEntry): Uint8Array {\n if (u32(b, entry.lfhOffset) !== SIG_LFH) {\n throw new OpenXmlIoError(`openZip: malformed local file header for \"${entry.path}\"`);\n }\n const nameLen = u16(b, entry.lfhOffset + 26);\n const extraLen = u16(b, entry.lfhOffset + 28);\n const dataStart = entry.lfhOffset + 30 + nameLen + extraLen;\n return b.subarray(dataStart, dataStart + entry.compSize);\n}\n\n/**\n * Open a buffered xlsx archive in random-access mode. The archive bytes stay\n * resident; entries inflate on demand inside `read(path)`.\n *\n * Falls back to `fflate.unzipSync` when the central directory uses ZIP64\n * sentinel values (entry count == 0xFFFF or any size field == 0xFFFFFFFF) so\n * external ZIP64 archives still load. xlsx files in the wild fit comfortably in\n * ZIP32; the fallback exists for safety.\n *\n * `decompressionLimits` opts the archive into the zip-bomb safeguards\n * documented on {@link DecompressionLimits}; pass `false` to disable. Defaults\n * fit any legitimate xlsx.\n */\nexport function openRandomAccessArchive(\n bytes: Uint8Array,\n decompressionLimits?: DecompressionLimitsInput,\n): ZipArchive {\n // Quick sanity on min archive size.\n if (bytes.length < 22) {\n throw new OpenXmlIoError('openZip: archive is shorter than the minimum EOCD size (22 bytes)');\n }\n\n let eocdOff: number;\n try {\n eocdOff = findEocd(bytes);\n } catch (cause) {\n throw new OpenXmlIoError('openZip: archive is not a valid zip', { cause });\n }\n\n const resolvedLimits = resolveDecompressionLimits(decompressionLimits);\n\n // Detect ZIP64 up front so a malformed ZIP64 record fails closed instead of\n // falling back to `unzipSync`. The fallback inflates every entry before any\n // bomb cap runs; the whole point of the ZIP64 path is to keep the streaming\n // caps in play, so we must not silently downgrade when ZIP64 is in use.\n const eocdTotalEntries = u16(bytes, eocdOff + 10);\n const eocdCdSize = u32(bytes, eocdOff + 12);\n const eocdCdOffset = u32(bytes, eocdOff + 16);\n const claimsZip64 =\n eocdTotalEntries === ZIP32_MAX_U16 ||\n eocdCdSize === ZIP32_MAX_U32 ||\n eocdCdOffset === ZIP32_MAX_U32;\n\n let summary: CdSummary;\n try {\n summary = readCdSummary(bytes, eocdOff);\n } catch (cause) {\n if (claimsZip64) {\n // ZIP64 sentinels are set but the matching record / locator is missing\n // or invalid. Fail closed — `unzipSync` would inflate every entry.\n throw cause instanceof OpenXmlIoError\n ? cause\n : new OpenXmlIoError('openZip: ZIP64 central directory is malformed', { cause });\n }\n // Plain ZIP32 with an unparseable EOCD — fflate's `unzipSync` is more\n // tolerant of quirky archives; the post-hoc bomb check still applies.\n return openViaUnzipSync(bytes, resolvedLimits);\n }\n\n let entries: CdEntry[];\n try {\n entries = parseCentralDirectory(bytes, summary.cdOffset, summary.totalEntries);\n } catch (cause) {\n if (claimsZip64) {\n throw cause instanceof OpenXmlIoError\n ? cause\n : new OpenXmlIoError('openZip: ZIP64 central directory is malformed', { cause });\n }\n // Malformed ZIP32 CD — fall back to fflate which is more tolerant.\n return openViaUnzipSync(bytes, resolvedLimits);\n }\n\n const byPath = new Map<string, CdEntry>();\n for (const e of entries) byPath.set(e.path, e);\n\n const budget: DecompressionBudget | null = resolvedLimits ? createBudget(resolvedLimits) : null;\n if (budget) {\n // Declared CD totals are cheap to inspect — reject obvious bombs before\n // wiring up the inflate state machine.\n checkDeclaredTotals(budget, entries);\n }\n\n // Per-entry inflate cache so repeated reads of the same path don't re-inflate\n // — `read(path)` is documented as cheap on the second call (loadWorkbook\n // touches several files multiple times).\n const inflateCache = new Map<string, Uint8Array>();\n let live = true;\n let archiveBytes: Uint8Array | undefined = bytes;\n\n const ensureLive = (): Uint8Array => {\n if (!live || !archiveBytes) {\n throw new OpenXmlIoError('openZip: archive is closed');\n }\n return archiveBytes;\n };\n\n const readEntry = (path: string): Uint8Array => {\n const buf = ensureLive();\n const cached = inflateCache.get(path);\n if (cached) return cached;\n const entry = byPath.get(path);\n if (!entry) {\n throw new OpenXmlIoError(`openZip: no entry at \"${path}\"`);\n }\n const compressed = readCompressedBytes(buf, entry);\n let out: Uint8Array;\n if (entry.compMethod === COMP_STORE) {\n // STORE has ratio 1, so the only thing left to check is the absolute\n // size cap (and the running archive total).\n if (budget) {\n if (compressed.byteLength > budget.limits.maxEntryUncompressedBytes) {\n throw entryOverflowError(path, budget.limits.maxEntryUncompressedBytes);\n }\n recordInflated(budget, path, compressed.byteLength);\n }\n // Copy so callers can safely mutate the returned bytes without perturbing\n // the underlying archive view.\n out = compressed.slice();\n } else if (entry.compMethod === COMP_DEFLATE) {\n out = inflateBounded(path, compressed, budget);\n } else {\n throw new OpenXmlIoError(`openZip: unsupported compression method ${entry.compMethod} for \"${path}\"`);\n }\n inflateCache.set(path, out);\n return out;\n };\n\n const readEntryStream = (path: string): ReadableStream<Uint8Array> => {\n const buf = ensureLive();\n const cached = inflateCache.get(path);\n if (cached) return singleChunkStream(cached);\n const entry = byPath.get(path);\n if (!entry) {\n throw new OpenXmlIoError(`openZip: no entry at \"${path}\"`);\n }\n const compressed = readCompressedBytes(buf, entry);\n if (entry.compMethod === COMP_STORE) {\n // STORE means the bytes on disk are the bytes the caller wants; no need\n // to involve the inflate state machine. Run the same caps the sync\n // `read()` STORE branch applies — otherwise an uncompressed bomb entry\n // reached via `readStream()` would bypass the per-entry / archive-total\n // accounting. Ratio is fixed at 1, so only the absolute bounds apply.\n if (budget) {\n if (compressed.byteLength > budget.limits.maxEntryUncompressedBytes) {\n throw entryOverflowError(path, budget.limits.maxEntryUncompressedBytes);\n }\n recordInflated(budget, path, compressed.byteLength);\n }\n // Copy because callers may mutate the returned bytes.\n return singleChunkStream(compressed.slice());\n }\n if (entry.compMethod !== COMP_DEFLATE) {\n throw new OpenXmlIoError(`openZip: unsupported compression method ${entry.compMethod} for \"${path}\"`);\n }\n // DEFLATE: drive fflate's `Inflate` from `pull()` so the consumer's\n // demand controls how much we inflate. Each `pull()` either emits one\n // already-buffered inflated chunk or pushes one more block of compressed\n // input — never both — so the ReadableStream internal queue stays at\n // depth 1 and the producer can't race ahead of the consumer.\n const entryCap = budget ? entryInflateCap(budget, compressed.byteLength) : Number.POSITIVE_INFINITY;\n let entryEmitted = 0;\n const pending: Uint8Array[] = [];\n let pushedOffset = 0;\n let inflaterFinal = false;\n let inflateError: Error | undefined;\n const inflater = new Inflate((chunk, final) => {\n if (inflateError) return;\n if (chunk.byteLength > 0) {\n entryEmitted += chunk.byteLength;\n if (entryEmitted > entryCap) {\n inflateError = entryOverflowError(path, entryCap);\n return;\n }\n if (budget) {\n try {\n recordInflated(budget, path, chunk.byteLength);\n } catch (err) {\n inflateError = err as Error;\n return;\n }\n }\n pending.push(chunk);\n }\n if (final) inflaterFinal = true;\n });\n return new ReadableStream<Uint8Array>({\n pull(controller) {\n if (inflateError) {\n controller.error(inflateError);\n return;\n }\n // Emit at most one already-buffered inflated chunk per pull; subsequent\n // pulls drain the rest. This caps the stream's internal queue at one\n // chunk regardless of how many ondata callbacks fflate fired off the\n // most recent push.\n const buffered = pending.shift();\n if (buffered) {\n controller.enqueue(buffered);\n if (inflaterFinal && pending.length === 0 && pushedOffset >= compressed.byteLength) {\n controller.close();\n }\n return;\n }\n // No buffered output: push one block of compressed input and let\n // inflate's ondata fill `pending`. We stop pushing the moment we have\n // something to emit so the next pull can return it without racing\n // further inflation. `inflaterFinal` is set inside the ondata callback\n // during the same `push` call that sets `isLast`, so reaching\n // `pushedOffset >= compressed.byteLength` always ends the loop too.\n while (pending.length === 0 && pushedOffset < compressed.byteLength) {\n const end = Math.min(pushedOffset + INFLATE_CHUNK_BYTES, compressed.byteLength);\n const slice = compressed.subarray(pushedOffset, end);\n const isLast = end >= compressed.byteLength;\n try {\n inflater.push(slice, isLast);\n } catch (cause) {\n if (!inflateError) {\n inflateError = new OpenXmlIoError(`openZip: failed to inflate \"${path}\"`, { cause });\n }\n controller.error(inflateError);\n return;\n }\n // The ondata callback may have set inflateError (decompression-bomb\n // guard tripping mid-inflate). Surface it to the consumer before\n // continuing — otherwise we'd loop forever on a CD-lying bomb whose\n // chunks all land beyond the cap and never make it into `pending`.\n if (inflateError) {\n controller.error(inflateError);\n return;\n }\n pushedOffset = end;\n }\n const next = pending.shift();\n if (next) {\n controller.enqueue(next);\n }\n if (inflaterFinal && pending.length === 0 && pushedOffset >= compressed.byteLength) {\n controller.close();\n }\n },\n cancel() {\n // Consumer abandoned the stream early — drop buffered chunks and\n // advance the cursor past the end so the inflater and the compressed\n // slice are eligible for GC. fflate's `Inflate` has no terminate API\n // but losing the only reference is enough.\n pending.length = 0;\n pushedOffset = compressed.byteLength;\n inflaterFinal = true;\n },\n });\n };\n\n return {\n list(): string[] {\n ensureLive();\n return [...byPath.keys()].sort();\n },\n has(path: string): boolean {\n if (!live) return false;\n return byPath.has(path);\n },\n read(path: string): Uint8Array {\n return readEntry(path);\n },\n async readAsync(path: string): Promise<Uint8Array> {\n return readEntry(path);\n },\n readStream(path: string): ReadableStream<Uint8Array> {\n return readEntryStream(path);\n },\n close(): void {\n live = false;\n archiveBytes = undefined;\n inflateCache.clear();\n byPath.clear();\n },\n };\n}\n\n/**\n * Inflate `compressed` into a single `Uint8Array`, abort if it crosses the\n * configured per-entry cap or the archive-wide budget. Uses fflate's streaming\n * `Inflate` so the abort can fire on any internal block boundary rather than\n * after fflate has materialised the entire payload.\n */\nfunction inflateBounded(\n path: string,\n compressed: Uint8Array,\n budget: DecompressionBudget | null,\n): Uint8Array {\n const cap = budget ? entryInflateCap(budget, compressed.byteLength) : Number.POSITIVE_INFINITY;\n const acc: Uint8Array[] = [];\n let emitted = 0;\n let aborted: Error | undefined;\n const inflater = new Inflate((chunk) => {\n if (aborted) return;\n if (chunk.byteLength === 0) return;\n emitted += chunk.byteLength;\n if (emitted > cap) {\n aborted = entryOverflowError(path, cap);\n return;\n }\n if (budget) {\n try {\n recordInflated(budget, path, chunk.byteLength);\n } catch (err) {\n aborted = err as Error;\n return;\n }\n }\n acc.push(chunk);\n });\n let off = 0;\n while (off < compressed.byteLength) {\n const end = Math.min(off + INFLATE_CHUNK_BYTES, compressed.byteLength);\n const isLast = end >= compressed.byteLength;\n try {\n inflater.push(compressed.subarray(off, end), isLast);\n } catch (cause) {\n if (aborted) throw aborted;\n throw new OpenXmlIoError(`openZip: failed to inflate \"${path}\"`, { cause });\n }\n if (aborted) throw aborted;\n off = end;\n }\n const out = new Uint8Array(emitted);\n let cursor = 0;\n for (const chunk of acc) {\n out.set(chunk, cursor);\n cursor += chunk.byteLength;\n }\n return out;\n}\n\n/**\n * Fallback for archives we can't parse ourselves. The random-access reader\n * now understands ZIP64 EOCD + the Zip64 Extended Information extra field, so\n * the only way to reach this path is a malformed central directory. fflate's\n * `unzipSync` is more forgiving but inflates every entry up front; for\n * adversarial archives we still rely on a post-hoc cap check (the only one\n * available without our own streaming decoder).\n */\nfunction openViaUnzipSync(\n bytes: Uint8Array,\n limits: ReturnType<typeof resolveDecompressionLimits>,\n): ZipArchive {\n let entries: Record<string, Uint8Array> | undefined;\n try {\n entries = unzipSync(bytes);\n } catch (cause) {\n throw new OpenXmlIoError('openZip: archive is not a valid zip', { cause });\n }\n // fflate's `unzipSync` returns already-inflated bytes — we can't abort the\n // inflate mid-flight here, but a post-hoc check still rejects a malicious\n // archive *before* any caller-level code touches the bytes. The peak memory\n // spike is bounded by what fflate just produced; the random-access path\n // catches the common cases (ZIP64 + malformed-but-parseable CDs) up front.\n if (limits) {\n const budget = createBudget(limits);\n for (const [path, payload] of Object.entries(entries)) {\n if (payload.byteLength > limits.maxEntryUncompressedBytes) {\n throw new OpenXmlDecompressionBombError(\n `openZip: entry \"${path}\" inflated to ${payload.byteLength} bytes,` +\n ` exceeding the ${limits.maxEntryUncompressedBytes}-byte per-entry limit` +\n ` (decompression-bomb guard).`,\n );\n }\n recordInflated(budget, path, payload.byteLength);\n }\n }\n let live = true;\n return {\n list(): string[] {\n if (!live || !entries) throw new OpenXmlIoError('openZip: archive is closed');\n return Object.keys(entries).sort();\n },\n has(path: string): boolean {\n if (!live || !entries) return false;\n return Object.hasOwn(entries, path);\n },\n read(path: string): Uint8Array {\n if (!live || !entries) throw new OpenXmlIoError('openZip: archive is closed');\n const e = entries[path];\n if (!e) throw new OpenXmlIoError(`openZip: no entry at \"${path}\"`);\n return e;\n },\n async readAsync(path: string): Promise<Uint8Array> {\n return this.read(path);\n },\n readStream(path: string): ReadableStream<Uint8Array> {\n // The unzipSync fallback path already has the entry fully inflated\n // (that's the price of dropping back from random-access). Hand it out as\n // a single-chunk stream so callers using the streaming reader don't have\n // to branch on the implementation.\n const inflated = this.read(path);\n return singleChunkStream(inflated);\n },\n close(): void {\n live = false;\n entries = undefined;\n },\n };\n}\n","// ZIP read layer.\n//\n// `openZip(source)` walks the central directory once and inflates each entry on\n// demand inside `read(path)` (see `./random-access-reader.ts`). That keeps peak\n// memory at compressed-archive size + per-entry inflate scratch, instead of\n// holding every uncompressed entry resident at once the way the old `unzipSync`\n// shortcut did. The fallback path through fflate's `unzipSync` is preserved for\n// ZIP64 / non-standard archives.\n\nimport type { XlsxSource } from '../io/source';\nimport { OpenXmlIoError, OpenXmlNotImplementedError } from '../utils/exceptions';\nimport type { DecompressionLimits } from './decompression-guard';\nimport { openRandomAccessArchive } from './random-access-reader';\n\nconst CFB_MAGIC = [0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1];\n\nconst isCfbCompoundDocument = (bytes: Uint8Array): boolean => {\n if (bytes.length < CFB_MAGIC.length) return false;\n for (let i = 0; i < CFB_MAGIC.length; i++) {\n if (bytes[i] !== CFB_MAGIC[i]) return false;\n }\n return true;\n};\n\nexport interface ZipArchive {\n /** Sorted list of all entry paths in the archive. */\n list(): string[];\n /** Synchronous read; throws OpenXmlIoError when the path is unknown. */\n read(path: string): Uint8Array;\n /** Promise variant for symmetry with the future streaming reader. */\n readAsync(path: string): Promise<Uint8Array>;\n /**\n * Streaming read: returns the entry's inflated bytes as a Web\n * `ReadableStream<Uint8Array>` chunk-by-chunk. Lets callers (the streaming\n * worksheet iterator, in particular) push the inflated payload through a SAX\n * parser without first materialising it in full — peak memory for a sheet\n * walk drops to the inflate window + SAX state instead of the entire\n * uncompressed worksheet body. Throws OpenXmlIoError when the path is\n * unknown.\n */\n readStream(path: string): ReadableStream<Uint8Array>;\n /** Whether the archive holds an entry at the given path. */\n has(path: string): boolean;\n /** Release the in-memory entry table. Subsequent reads throw. */\n close(): void;\n}\n\n/** Options for {@link openZip}. */\nexport interface OpenZipOptions {\n /**\n * Decompression-bomb safeguards applied while inflating archive entries. The\n * default limits admit any legitimate xlsx and reject pathological archives\n * (extreme compression ratios, gigabyte-scale entries). Pass `false` to\n * disable the guard entirely — only safe when the source is fully trusted.\n * See {@link DecompressionLimits} for the individual knobs.\n */\n decompressionLimits?: DecompressionLimits | false;\n}\n\n/**\n * Open a zip archive from any {@link XlsxSource}. The source is fully\n * materialised in memory, the central directory is parsed once, and each\n * entry is inflated on demand by {@link openRandomAccessArchive} — peak memory\n * stays at compressed-archive size plus per-entry inflate scratch rather than\n * holding every uncompressed entry resident. The fflate `unzipSync` fallback\n * is preserved internally for ZIP64 / non-standard archives the random-access\n * reader rejects.\n */\nexport async function openZip(source: XlsxSource, opts: OpenZipOptions = {}): Promise<ZipArchive> {\n let bytes: Uint8Array;\n try {\n bytes = await source.toBytes();\n } catch (cause) {\n throw new OpenXmlIoError('openZip: failed to read source bytes', { cause });\n }\n\n // Encrypted xlsx files (Excel 2007+ password protection) wrap the real\n // package inside an OLE Compound File Binary container with the magic\n // signature `D0 CF 11 E0 A1 B1 1A E1`. Detect that early and surface a clear\n // \"decrypt first\" error rather than letting fflate fail with a generic\n // invalid-zip message.\n if (isCfbCompoundDocument(bytes)) {\n throw new OpenXmlNotImplementedError(\n 'Encrypted xlsx is not supported. Decrypt with msoffcrypto-tool first.',\n );\n }\n\n return openRandomAccessArchive(bytes, opts.decompressionLimits);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"reader-BNaUTVCy.mjs","names":[],"sources":["../src/zip/decompression-guard.ts","../src/zip/random-access-reader.ts","../src/zip/reader.ts"],"sourcesContent":["// Decompression-bomb safeguards for the ZIP reader.\n//\n// Zip-bombs encode pathological compression ratios (e.g. 100 B → 1 GB) so that\n// a naive `inflate` exhausts memory before the caller can react. The guards\n// here apply three orthogonal bounds:\n//\n// 1. Per-entry uncompressed cap — hard ceiling on a single payload.\n// 2. Total-archive uncompressed cap — hard ceiling summed across the archive.\n// 3. Per-entry compression ratio — bounds the *amplification factor* so that\n// even small compressed entries can't decompress into hundreds of MB.\n//\n// Defaults are sized to admit any legitimate xlsx (xml compresses well but\n// rarely exceeds the per-entry cap; office templates with embedded media stay\n// well under the total cap) while rejecting plausible bombs. Trusted callers\n// (e.g. a backend that only ever loads xlsx it generated itself) can pass\n// `false` to disable the guard.\n\nimport { OpenXmlDecompressionBombError, OpenXmlError } from '../utils/exceptions.js';\n\n/** Per-archive limits enforced during {@link openZip}. */\nexport interface DecompressionLimits {\n /**\n * Maximum decompressed bytes for a single archive entry. Default 512 MiB.\n * Legitimate xlsx sheets stay well below this even with millions of cells.\n */\n maxEntryUncompressedBytes?: number;\n /**\n * Maximum decompressed bytes summed across every entry the caller reads.\n * Default 1 GiB.\n */\n maxTotalUncompressedBytes?: number;\n /**\n * Maximum allowed `uncompressed / compressed` ratio for a single entry.\n * Default 1000. xml usually compresses 5–20×, but highly repetitive payloads\n * (long runs of zeros, sparse worksheets) can hit several hundred ×\n * legitimately. Classic zip-bombs run 10 000× and up, so a 1000× ceiling\n * still catches them while admitting realistic content. The implementation\n * treats compressed sizes below 64 B as exempt — there's no amplification\n * budget worth policing on such small entries, and the absolute per-entry /\n * archive limits already cover them.\n */\n maxCompressionRatio?: number;\n}\n\n/** Resolved limits with no `undefined` fields. */\nexport interface ResolvedDecompressionLimits {\n readonly maxEntryUncompressedBytes: number;\n readonly maxTotalUncompressedBytes: number;\n readonly maxCompressionRatio: number;\n}\n\n/** Default safeguards applied when the caller doesn't override them. */\nexport const DEFAULT_DECOMPRESSION_LIMITS: ResolvedDecompressionLimits = {\n maxEntryUncompressedBytes: 512 * 1024 * 1024,\n maxTotalUncompressedBytes: 1024 * 1024 * 1024,\n maxCompressionRatio: 1000,\n};\n\n/** Below this compressed size, the ratio check is skipped — see {@link DecompressionLimits}. */\nconst RATIO_CHECK_MIN_COMPRESSED_BYTES = 64;\n\n/**\n * `DecompressionLimits` plus the sentinel `false` to disable the guard.\n * Anything else is treated as \"use defaults\".\n */\nexport type DecompressionLimitsInput = DecompressionLimits | false | undefined;\n\n// Each limit must be a positive finite number to be meaningful; NaN / Infinity\n// / 0 / negatives silently turn `emitted > cap` and `totalInflated > cap` into\n// no-ops and disable the guard. Reject those at the boundary so the only way\n// the guard is off is the explicit `false` sentinel.\nconst requirePositiveFinite = (field: string, value: number): void => {\n if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) {\n throw new OpenXmlError(\n `resolveDecompressionLimits: ${field} must be a positive finite number; got ${String(value)}`,\n );\n }\n};\n\n/** Returns null when the guard is disabled. */\nexport function resolveDecompressionLimits(\n input: DecompressionLimitsInput,\n): ResolvedDecompressionLimits | null {\n if (input === false) return null;\n if (!input) return DEFAULT_DECOMPRESSION_LIMITS;\n const resolved: ResolvedDecompressionLimits = {\n maxEntryUncompressedBytes:\n input.maxEntryUncompressedBytes ?? DEFAULT_DECOMPRESSION_LIMITS.maxEntryUncompressedBytes,\n maxTotalUncompressedBytes:\n input.maxTotalUncompressedBytes ?? DEFAULT_DECOMPRESSION_LIMITS.maxTotalUncompressedBytes,\n maxCompressionRatio:\n input.maxCompressionRatio ?? DEFAULT_DECOMPRESSION_LIMITS.maxCompressionRatio,\n };\n requirePositiveFinite('maxEntryUncompressedBytes', resolved.maxEntryUncompressedBytes);\n requirePositiveFinite('maxTotalUncompressedBytes', resolved.maxTotalUncompressedBytes);\n requirePositiveFinite('maxCompressionRatio', resolved.maxCompressionRatio);\n return resolved;\n}\n\n/**\n * Per-archive byte accounting shared across every read of an archive opened\n * with the given limits. Tracks total inflated bytes so the cap is enforced\n * even when individual entries stay below the per-entry ceiling.\n */\nexport interface DecompressionBudget {\n readonly limits: ResolvedDecompressionLimits;\n totalInflated: number;\n}\n\nexport function createBudget(limits: ResolvedDecompressionLimits): DecompressionBudget {\n return { limits, totalInflated: 0 };\n}\n\n/**\n * Per-entry inflate cap, accounting for both the absolute per-entry bound and\n * the ratio-based bound. Returns the smaller of the two — whichever fires\n * first stops the inflate loop.\n */\nexport function entryInflateCap(budget: DecompressionBudget, compressedSize: number): number {\n const { maxEntryUncompressedBytes, maxCompressionRatio } = budget.limits;\n if (compressedSize < RATIO_CHECK_MIN_COMPRESSED_BYTES) {\n return maxEntryUncompressedBytes;\n }\n const ratioCap = compressedSize * maxCompressionRatio;\n return Math.min(maxEntryUncompressedBytes, ratioCap);\n}\n\n/** Throw if the declared central-directory totals already exceed the limits. */\nexport function checkDeclaredTotals(\n budget: DecompressionBudget,\n declaredEntries: ReadonlyArray<{ path: string; compSize: number; uncompSize: number }>,\n): void {\n let declaredTotal = 0;\n for (const entry of declaredEntries) {\n declaredTotal += entry.uncompSize;\n if (entry.uncompSize > budget.limits.maxEntryUncompressedBytes) {\n throw new OpenXmlDecompressionBombError(\n `openZip: entry \"${entry.path}\" declares ${entry.uncompSize} uncompressed bytes,` +\n ` exceeding the ${budget.limits.maxEntryUncompressedBytes}-byte per-entry limit` +\n ` (decompression-bomb guard).`,\n );\n }\n if (\n entry.compSize >= RATIO_CHECK_MIN_COMPRESSED_BYTES &&\n entry.uncompSize > entry.compSize * budget.limits.maxCompressionRatio\n ) {\n throw new OpenXmlDecompressionBombError(\n `openZip: entry \"${entry.path}\" declares ratio ${(entry.uncompSize / entry.compSize).toFixed(1)}x` +\n ` (${entry.uncompSize}/${entry.compSize}), exceeding the ${budget.limits.maxCompressionRatio}x` +\n ` per-entry limit (decompression-bomb guard).`,\n );\n }\n }\n if (declaredTotal > budget.limits.maxTotalUncompressedBytes) {\n throw new OpenXmlDecompressionBombError(\n `openZip: declared total uncompressed size ${declaredTotal} bytes exceeds the` +\n ` ${budget.limits.maxTotalUncompressedBytes}-byte archive limit (decompression-bomb guard).`,\n );\n }\n}\n\n/**\n * Record `bytes` against the global budget. Throws when the running total\n * crosses {@link ResolvedDecompressionLimits.maxTotalUncompressedBytes}. Called\n * from both sync and streaming inflate code paths.\n */\nexport function recordInflated(budget: DecompressionBudget, path: string, bytes: number): void {\n budget.totalInflated += bytes;\n if (budget.totalInflated > budget.limits.maxTotalUncompressedBytes) {\n throw new OpenXmlDecompressionBombError(\n `openZip: archive-wide inflated size exceeded ${budget.limits.maxTotalUncompressedBytes} bytes` +\n ` while reading \"${path}\" (decompression-bomb guard).`,\n );\n }\n}\n\n/** Build the message thrown when a single entry exceeds its cap mid-inflate. */\nexport function entryOverflowError(path: string, cap: number): OpenXmlDecompressionBombError {\n return new OpenXmlDecompressionBombError(\n `openZip: inflated size of \"${path}\" exceeded ${cap} bytes (decompression-bomb guard).`,\n );\n}\n","// Random-access ZIP reader. / §2.3 streaming-read residual.\n//\n// The previous reader handed every entry to `fflate.unzipSync` up front, which\n// materialises *every* uncompressed payload into memory at once. For a 100 MB\n// xlsx with ~500 MB of decompressed sheet data the resident set spikes\n// accordingly. The random-access path keeps only the compressed archive bytes\n// resident, parses the central directory once (cheap — ~46 B per entry plus\n// filename), and inflates each entry lazily on `read(path)`.\n//\n// Limitations:\n// - ZIP64 reads only when the standard ZIP32 fields fit. EOCD with\n// sentinel values (0xFFFF / 0xFFFFFFFF) falls back to fflate's `unzipSync` so\n// external ZIP64 archives still load (the writer side has its own ZIP32 cap\n// guard).\n// - Compression methods: STORE (0) and DEFLATE (8). Anything else\n// throws OpenXmlIoError.\n\nimport { Inflate, unzipSync } from 'fflate';\nimport { OpenXmlDecompressionBombError, OpenXmlIoError } from '../utils/exceptions.js';\nimport {\n checkDeclaredTotals,\n createBudget,\n type DecompressionBudget,\n type DecompressionLimitsInput,\n entryInflateCap,\n entryOverflowError,\n recordInflated,\n resolveDecompressionLimits,\n} from './decompression-guard.js';\nimport type { ZipArchive } from './reader.js';\n\n/**\n * Chunk size used when feeding compressed bytes into fflate's `Inflate` for\n * streaming reads. 64 KB matches the saxes/SAX consumer's typical batch and\n * keeps peak transient memory bounded.\n */\nconst INFLATE_CHUNK_BYTES = 64 * 1024;\n\nconst singleChunkStream = (bytes: Uint8Array): ReadableStream<Uint8Array> =>\n new ReadableStream<Uint8Array>({\n start(controller) {\n if (bytes.byteLength > 0) controller.enqueue(bytes);\n controller.close();\n },\n });\n\nconst SIG_EOCD = 0x06054b50;\nconst SIG_CD = 0x02014b50;\nconst SIG_LFH = 0x04034b50;\nconst SIG_ZIP64_EOCD = 0x06064b50;\nconst SIG_ZIP64_EOCD_LOCATOR = 0x07064b50;\nconst ZIP32_MAX_U16 = 0xffff;\nconst ZIP32_MAX_U32 = 0xffffffff;\nconst ZIP64_LOCATOR_SIZE = 20;\nconst ZIP64_EXTRA_HEADER_ID = 0x0001;\nconst COMP_STORE = 0;\nconst COMP_DEFLATE = 8;\n\ninterface CdEntry {\n path: string;\n lfhOffset: number;\n compMethod: number;\n compSize: number;\n uncompSize: number;\n /** General-purpose bit flag, used to detect bit-3 (data descriptor) and bit-11 (UTF-8). */\n gpFlag: number;\n}\n\nconst u16 = (b: Uint8Array, off: number): number => (b[off] ?? 0) | ((b[off + 1] ?? 0) << 8);\nconst u32 = (b: Uint8Array, off: number): number => {\n const v0 = b[off] ?? 0;\n const v1 = b[off + 1] ?? 0;\n const v2 = b[off + 2] ?? 0;\n const v3 = b[off + 3] ?? 0;\n return (v0 | (v1 << 8) | (v2 << 16) | (v3 << 24)) >>> 0;\n};\n\n// Read a little-endian 64-bit value as a JS Number. Safe for values up to\n// 2^53-1 (Number.MAX_SAFE_INTEGER ~ 9 PiB), which is well past any realistic\n// xlsx archive — we throw if a parsed value exceeds the safe-integer range.\nconst u64 = (b: Uint8Array, off: number): number => {\n const lo = u32(b, off);\n const hi = u32(b, off + 4);\n if (hi > 0x1fffff) {\n throw new OpenXmlIoError(\n `openZip: ZIP64 field at byte ${off} exceeds the safe-integer range (${hi}*2^32 + ${lo})`,\n );\n }\n return hi * 0x100000000 + lo;\n};\n\n/** Find the End-of-Central-Directory record by scanning backwards from EOF. */\nfunction findEocd(b: Uint8Array): number {\n const minStart = Math.max(0, b.length - 22 - 0xffff);\n for (let i = b.length - 22; i >= minStart; i--) {\n if (u32(b, i) === SIG_EOCD) return i;\n }\n throw new OpenXmlIoError('openZip: no End-of-Central-Directory signature found');\n}\n\ninterface CdSummary {\n totalEntries: number;\n cdSize: number;\n cdOffset: number;\n}\n\n/**\n * Resolve the central-directory totals. When the EOCD carries ZIP64 sentinel\n * values (0xFFFF / 0xFFFFFFFF) the real totals live in a ZIP64 EOCD record\n * located via the ZIP64 EOCD locator just before the regular EOCD. ECMA-376\n * xlsx archives stay in ZIP32 territory; the ZIP64 path exists so the\n * decompression-bomb guards still apply to large external archives instead of\n * falling all the way back to `unzipSync` (which produces every entry's bytes\n * before any cap can fire).\n */\nfunction readCdSummary(b: Uint8Array, eocdOff: number): CdSummary {\n let totalEntries = u16(b, eocdOff + 10);\n let cdSize = u32(b, eocdOff + 12);\n let cdOffset = u32(b, eocdOff + 16);\n\n const usesZip64Eocd =\n totalEntries === ZIP32_MAX_U16 || cdSize === ZIP32_MAX_U32 || cdOffset === ZIP32_MAX_U32;\n if (!usesZip64Eocd) {\n return { totalEntries, cdSize, cdOffset };\n }\n\n // Locate the ZIP64 EOCD locator. It sits immediately before the regular\n // EOCD when one is present.\n const locatorOff = eocdOff - ZIP64_LOCATOR_SIZE;\n if (locatorOff < 0 || u32(b, locatorOff) !== SIG_ZIP64_EOCD_LOCATOR) {\n throw new OpenXmlIoError('openZip: ZIP64 EOCD locator missing despite EOCD sentinel values');\n }\n const zip64EocdOff = u64(b, locatorOff + 8);\n if (zip64EocdOff < 0 || zip64EocdOff + 56 > b.length) {\n throw new OpenXmlIoError(`openZip: ZIP64 EOCD offset ${zip64EocdOff} out of bounds`);\n }\n if (u32(b, zip64EocdOff) !== SIG_ZIP64_EOCD) {\n throw new OpenXmlIoError(`openZip: ZIP64 EOCD signature missing at byte ${zip64EocdOff}`);\n }\n totalEntries = u64(b, zip64EocdOff + 32);\n cdSize = u64(b, zip64EocdOff + 40);\n cdOffset = u64(b, zip64EocdOff + 48);\n return { totalEntries, cdSize, cdOffset };\n}\n\n/** Walk the ZIP64 Extended Information extra field for sentinel-valued sizes/offset. */\nfunction readZip64Extra(\n b: Uint8Array,\n extraStart: number,\n extraLen: number,\n wantsUncompSize: boolean,\n wantsCompSize: boolean,\n wantsLfhOffset: boolean,\n entryPath: string,\n): { uncompSize?: number; compSize?: number; lfhOffset?: number } {\n let p = extraStart;\n const end = extraStart + extraLen;\n while (p + 4 <= end) {\n const id = u16(b, p);\n const size = u16(b, p + 2);\n const dataStart = p + 4;\n const next = dataStart + size;\n if (next > end) break;\n if (id === ZIP64_EXTRA_HEADER_ID) {\n // Sanity-check that the declared extra-field size actually covers every\n // u64 the CD asked us to read. A truncated / mis-sized ZIP64 extra\n // field would otherwise let u64() interpret bytes belonging to the\n // next extra record (or the next CD entry) as a size/offset, producing\n // bogus values that downstream bomb checks then trust. Fail closed.\n const needed = (wantsUncompSize ? 8 : 0) + (wantsCompSize ? 8 : 0) + (wantsLfhOffset ? 8 : 0);\n if (size < needed) {\n throw new OpenXmlIoError(\n `openZip: ZIP64 extended-info field for \"${entryPath}\" declares ${size} bytes` +\n ` but the central directory sentinels require ${needed}`,\n );\n }\n let q = dataStart;\n const result: { uncompSize?: number; compSize?: number; lfhOffset?: number } = {};\n if (wantsUncompSize) {\n result.uncompSize = u64(b, q);\n q += 8;\n }\n if (wantsCompSize) {\n result.compSize = u64(b, q);\n q += 8;\n }\n if (wantsLfhOffset) {\n result.lfhOffset = u64(b, q);\n q += 8;\n }\n return result;\n }\n p = next;\n }\n return {};\n}\n\n/** Parse the central directory into an array of entry descriptors. */\nfunction parseCentralDirectory(b: Uint8Array, cdOffset: number, expectedCount: number): CdEntry[] {\n const entries: CdEntry[] = [];\n let p = cdOffset;\n for (let i = 0; i < expectedCount; i++) {\n if (u32(b, p) !== SIG_CD) {\n throw new OpenXmlIoError(`openZip: malformed central directory at byte ${p}`);\n }\n const gpFlag = u16(b, p + 8);\n const compMethod = u16(b, p + 10);\n let compSize = u32(b, p + 20);\n let uncompSize = u32(b, p + 24);\n const nameLen = u16(b, p + 28);\n const extraLen = u16(b, p + 30);\n const commentLen = u16(b, p + 32);\n let lfhOffset = u32(b, p + 42);\n const nameBytes = b.subarray(p + 46, p + 46 + nameLen);\n // Bit 11 (0x0800) signals UTF-8 filename. xlsx archives are almost always\n // UTF-8 already; treat bit-0 as UTF-8 too since CP437 ⊃ ASCII and xlsx uses\n // ASCII paths.\n const path = new TextDecoder('utf-8').decode(nameBytes);\n\n // ZIP64 Extended Information rewrites whichever of {uncompSize, compSize,\n // lfhOffset} are 0xFFFFFFFF sentinels in the canonical fields. The extra\n // field stores them in the order listed in the spec (and omits any that\n // weren't sentinels), so we have to track which slots to read.\n const wantsUncomp = uncompSize === ZIP32_MAX_U32;\n const wantsComp = compSize === ZIP32_MAX_U32;\n const wantsOffset = lfhOffset === ZIP32_MAX_U32;\n if (wantsUncomp || wantsComp || wantsOffset) {\n const extra = readZip64Extra(b, p + 46 + nameLen, extraLen, wantsUncomp, wantsComp, wantsOffset, path);\n if (extra.uncompSize !== undefined) uncompSize = extra.uncompSize;\n if (extra.compSize !== undefined) compSize = extra.compSize;\n if (extra.lfhOffset !== undefined) lfhOffset = extra.lfhOffset;\n }\n entries.push({ path, lfhOffset, compMethod, compSize, uncompSize, gpFlag });\n p += 46 + nameLen + extraLen + commentLen;\n }\n return entries;\n}\n\n/** Read the compressed bytes for a CD entry by walking its local file header. */\nfunction readCompressedBytes(b: Uint8Array, entry: CdEntry): Uint8Array {\n if (u32(b, entry.lfhOffset) !== SIG_LFH) {\n throw new OpenXmlIoError(`openZip: malformed local file header for \"${entry.path}\"`);\n }\n const nameLen = u16(b, entry.lfhOffset + 26);\n const extraLen = u16(b, entry.lfhOffset + 28);\n const dataStart = entry.lfhOffset + 30 + nameLen + extraLen;\n return b.subarray(dataStart, dataStart + entry.compSize);\n}\n\n/**\n * Open a buffered xlsx archive in random-access mode. The archive bytes stay\n * resident; entries inflate on demand inside `read(path)`.\n *\n * Falls back to `fflate.unzipSync` when the central directory uses ZIP64\n * sentinel values (entry count == 0xFFFF or any size field == 0xFFFFFFFF) so\n * external ZIP64 archives still load. xlsx files in the wild fit comfortably in\n * ZIP32; the fallback exists for safety.\n *\n * `decompressionLimits` opts the archive into the zip-bomb safeguards\n * documented on {@link DecompressionLimits}; pass `false` to disable. Defaults\n * fit any legitimate xlsx.\n */\nexport function openRandomAccessArchive(\n bytes: Uint8Array,\n decompressionLimits?: DecompressionLimitsInput,\n): ZipArchive {\n // Quick sanity on min archive size.\n if (bytes.length < 22) {\n throw new OpenXmlIoError('openZip: archive is shorter than the minimum EOCD size (22 bytes)');\n }\n\n let eocdOff: number;\n try {\n eocdOff = findEocd(bytes);\n } catch (cause) {\n throw new OpenXmlIoError('openZip: archive is not a valid zip', { cause });\n }\n\n const resolvedLimits = resolveDecompressionLimits(decompressionLimits);\n\n // Detect ZIP64 up front so a malformed ZIP64 record fails closed instead of\n // falling back to `unzipSync`. The fallback inflates every entry before any\n // bomb cap runs; the whole point of the ZIP64 path is to keep the streaming\n // caps in play, so we must not silently downgrade when ZIP64 is in use.\n const eocdTotalEntries = u16(bytes, eocdOff + 10);\n const eocdCdSize = u32(bytes, eocdOff + 12);\n const eocdCdOffset = u32(bytes, eocdOff + 16);\n const claimsZip64 =\n eocdTotalEntries === ZIP32_MAX_U16 ||\n eocdCdSize === ZIP32_MAX_U32 ||\n eocdCdOffset === ZIP32_MAX_U32;\n\n let summary: CdSummary;\n try {\n summary = readCdSummary(bytes, eocdOff);\n } catch (cause) {\n if (claimsZip64) {\n // ZIP64 sentinels are set but the matching record / locator is missing\n // or invalid. Fail closed — `unzipSync` would inflate every entry.\n throw cause instanceof OpenXmlIoError\n ? cause\n : new OpenXmlIoError('openZip: ZIP64 central directory is malformed', { cause });\n }\n // Plain ZIP32 with an unparseable EOCD — fflate's `unzipSync` is more\n // tolerant of quirky archives; the post-hoc bomb check still applies.\n return openViaUnzipSync(bytes, resolvedLimits);\n }\n\n let entries: CdEntry[];\n try {\n entries = parseCentralDirectory(bytes, summary.cdOffset, summary.totalEntries);\n } catch (cause) {\n if (claimsZip64) {\n throw cause instanceof OpenXmlIoError\n ? cause\n : new OpenXmlIoError('openZip: ZIP64 central directory is malformed', { cause });\n }\n // Malformed ZIP32 CD — fall back to fflate which is more tolerant.\n return openViaUnzipSync(bytes, resolvedLimits);\n }\n\n const byPath = new Map<string, CdEntry>();\n for (const e of entries) byPath.set(e.path, e);\n\n const budget: DecompressionBudget | null = resolvedLimits ? createBudget(resolvedLimits) : null;\n if (budget) {\n // Declared CD totals are cheap to inspect — reject obvious bombs before\n // wiring up the inflate state machine.\n checkDeclaredTotals(budget, entries);\n }\n\n // Per-entry inflate cache so repeated reads of the same path don't re-inflate\n // — `read(path)` is documented as cheap on the second call (loadWorkbook\n // touches several files multiple times).\n const inflateCache = new Map<string, Uint8Array>();\n let live = true;\n let archiveBytes: Uint8Array | undefined = bytes;\n\n const ensureLive = (): Uint8Array => {\n if (!live || !archiveBytes) {\n throw new OpenXmlIoError('openZip: archive is closed');\n }\n return archiveBytes;\n };\n\n const readEntry = (path: string): Uint8Array => {\n const buf = ensureLive();\n const cached = inflateCache.get(path);\n if (cached) return cached;\n const entry = byPath.get(path);\n if (!entry) {\n throw new OpenXmlIoError(`openZip: no entry at \"${path}\"`);\n }\n const compressed = readCompressedBytes(buf, entry);\n let out: Uint8Array;\n if (entry.compMethod === COMP_STORE) {\n // STORE has ratio 1, so the only thing left to check is the absolute\n // size cap (and the running archive total).\n if (budget) {\n if (compressed.byteLength > budget.limits.maxEntryUncompressedBytes) {\n throw entryOverflowError(path, budget.limits.maxEntryUncompressedBytes);\n }\n recordInflated(budget, path, compressed.byteLength);\n }\n // Copy so callers can safely mutate the returned bytes without perturbing\n // the underlying archive view.\n out = compressed.slice();\n } else if (entry.compMethod === COMP_DEFLATE) {\n out = inflateBounded(path, compressed, budget);\n } else {\n throw new OpenXmlIoError(`openZip: unsupported compression method ${entry.compMethod} for \"${path}\"`);\n }\n inflateCache.set(path, out);\n return out;\n };\n\n const readEntryStream = (path: string): ReadableStream<Uint8Array> => {\n const buf = ensureLive();\n const cached = inflateCache.get(path);\n if (cached) return singleChunkStream(cached);\n const entry = byPath.get(path);\n if (!entry) {\n throw new OpenXmlIoError(`openZip: no entry at \"${path}\"`);\n }\n const compressed = readCompressedBytes(buf, entry);\n if (entry.compMethod === COMP_STORE) {\n // STORE means the bytes on disk are the bytes the caller wants; no need\n // to involve the inflate state machine. Run the same caps the sync\n // `read()` STORE branch applies — otherwise an uncompressed bomb entry\n // reached via `readStream()` would bypass the per-entry / archive-total\n // accounting. Ratio is fixed at 1, so only the absolute bounds apply.\n if (budget) {\n if (compressed.byteLength > budget.limits.maxEntryUncompressedBytes) {\n throw entryOverflowError(path, budget.limits.maxEntryUncompressedBytes);\n }\n recordInflated(budget, path, compressed.byteLength);\n }\n // Copy because callers may mutate the returned bytes.\n return singleChunkStream(compressed.slice());\n }\n if (entry.compMethod !== COMP_DEFLATE) {\n throw new OpenXmlIoError(`openZip: unsupported compression method ${entry.compMethod} for \"${path}\"`);\n }\n // DEFLATE: drive fflate's `Inflate` from `pull()` so the consumer's\n // demand controls how much we inflate. Each `pull()` either emits one\n // already-buffered inflated chunk or pushes one more block of compressed\n // input — never both — so the ReadableStream internal queue stays at\n // depth 1 and the producer can't race ahead of the consumer.\n const entryCap = budget ? entryInflateCap(budget, compressed.byteLength) : Number.POSITIVE_INFINITY;\n let entryEmitted = 0;\n const pending: Uint8Array[] = [];\n let pushedOffset = 0;\n let inflaterFinal = false;\n let inflateError: Error | undefined;\n const inflater = new Inflate((chunk, final) => {\n if (inflateError) return;\n if (chunk.byteLength > 0) {\n entryEmitted += chunk.byteLength;\n if (entryEmitted > entryCap) {\n inflateError = entryOverflowError(path, entryCap);\n return;\n }\n if (budget) {\n try {\n recordInflated(budget, path, chunk.byteLength);\n } catch (err) {\n inflateError = err as Error;\n return;\n }\n }\n pending.push(chunk);\n }\n if (final) inflaterFinal = true;\n });\n return new ReadableStream<Uint8Array>({\n pull(controller) {\n if (inflateError) {\n controller.error(inflateError);\n return;\n }\n // Emit at most one already-buffered inflated chunk per pull; subsequent\n // pulls drain the rest. This caps the stream's internal queue at one\n // chunk regardless of how many ondata callbacks fflate fired off the\n // most recent push.\n const buffered = pending.shift();\n if (buffered) {\n controller.enqueue(buffered);\n if (inflaterFinal && pending.length === 0 && pushedOffset >= compressed.byteLength) {\n controller.close();\n }\n return;\n }\n // No buffered output: push one block of compressed input and let\n // inflate's ondata fill `pending`. We stop pushing the moment we have\n // something to emit so the next pull can return it without racing\n // further inflation. `inflaterFinal` is set inside the ondata callback\n // during the same `push` call that sets `isLast`, so reaching\n // `pushedOffset >= compressed.byteLength` always ends the loop too.\n while (pending.length === 0 && pushedOffset < compressed.byteLength) {\n const end = Math.min(pushedOffset + INFLATE_CHUNK_BYTES, compressed.byteLength);\n const slice = compressed.subarray(pushedOffset, end);\n const isLast = end >= compressed.byteLength;\n try {\n inflater.push(slice, isLast);\n } catch (cause) {\n if (!inflateError) {\n inflateError = new OpenXmlIoError(`openZip: failed to inflate \"${path}\"`, { cause });\n }\n controller.error(inflateError);\n return;\n }\n // The ondata callback may have set inflateError (decompression-bomb\n // guard tripping mid-inflate). Surface it to the consumer before\n // continuing — otherwise we'd loop forever on a CD-lying bomb whose\n // chunks all land beyond the cap and never make it into `pending`.\n if (inflateError) {\n controller.error(inflateError);\n return;\n }\n pushedOffset = end;\n }\n const next = pending.shift();\n if (next) {\n controller.enqueue(next);\n }\n if (inflaterFinal && pending.length === 0 && pushedOffset >= compressed.byteLength) {\n controller.close();\n }\n },\n cancel() {\n // Consumer abandoned the stream early — drop buffered chunks and\n // advance the cursor past the end so the inflater and the compressed\n // slice are eligible for GC. fflate's `Inflate` has no terminate API\n // but losing the only reference is enough.\n pending.length = 0;\n pushedOffset = compressed.byteLength;\n inflaterFinal = true;\n },\n });\n };\n\n return {\n list(): string[] {\n ensureLive();\n return [...byPath.keys()].sort();\n },\n has(path: string): boolean {\n if (!live) return false;\n return byPath.has(path);\n },\n read(path: string): Uint8Array {\n return readEntry(path);\n },\n async readAsync(path: string): Promise<Uint8Array> {\n return readEntry(path);\n },\n readStream(path: string): ReadableStream<Uint8Array> {\n return readEntryStream(path);\n },\n close(): void {\n live = false;\n archiveBytes = undefined;\n inflateCache.clear();\n byPath.clear();\n },\n };\n}\n\n/**\n * Inflate `compressed` into a single `Uint8Array`, abort if it crosses the\n * configured per-entry cap or the archive-wide budget. Uses fflate's streaming\n * `Inflate` so the abort can fire on any internal block boundary rather than\n * after fflate has materialised the entire payload.\n */\nfunction inflateBounded(\n path: string,\n compressed: Uint8Array,\n budget: DecompressionBudget | null,\n): Uint8Array {\n const cap = budget ? entryInflateCap(budget, compressed.byteLength) : Number.POSITIVE_INFINITY;\n const acc: Uint8Array[] = [];\n let emitted = 0;\n let aborted: Error | undefined;\n const inflater = new Inflate((chunk) => {\n if (aborted) return;\n if (chunk.byteLength === 0) return;\n emitted += chunk.byteLength;\n if (emitted > cap) {\n aborted = entryOverflowError(path, cap);\n return;\n }\n if (budget) {\n try {\n recordInflated(budget, path, chunk.byteLength);\n } catch (err) {\n aborted = err as Error;\n return;\n }\n }\n acc.push(chunk);\n });\n let off = 0;\n while (off < compressed.byteLength) {\n const end = Math.min(off + INFLATE_CHUNK_BYTES, compressed.byteLength);\n const isLast = end >= compressed.byteLength;\n try {\n inflater.push(compressed.subarray(off, end), isLast);\n } catch (cause) {\n if (aborted) throw aborted;\n throw new OpenXmlIoError(`openZip: failed to inflate \"${path}\"`, { cause });\n }\n if (aborted) throw aborted;\n off = end;\n }\n const out = new Uint8Array(emitted);\n let cursor = 0;\n for (const chunk of acc) {\n out.set(chunk, cursor);\n cursor += chunk.byteLength;\n }\n return out;\n}\n\n/**\n * Fallback for archives we can't parse ourselves. The random-access reader\n * now understands ZIP64 EOCD + the Zip64 Extended Information extra field, so\n * the only way to reach this path is a malformed central directory. fflate's\n * `unzipSync` is more forgiving but inflates every entry up front; for\n * adversarial archives we still rely on a post-hoc cap check (the only one\n * available without our own streaming decoder).\n */\nfunction openViaUnzipSync(\n bytes: Uint8Array,\n limits: ReturnType<typeof resolveDecompressionLimits>,\n): ZipArchive {\n let entries: Record<string, Uint8Array> | undefined;\n try {\n entries = unzipSync(bytes);\n } catch (cause) {\n throw new OpenXmlIoError('openZip: archive is not a valid zip', { cause });\n }\n // fflate's `unzipSync` returns already-inflated bytes — we can't abort the\n // inflate mid-flight here, but a post-hoc check still rejects a malicious\n // archive *before* any caller-level code touches the bytes. The peak memory\n // spike is bounded by what fflate just produced; the random-access path\n // catches the common cases (ZIP64 + malformed-but-parseable CDs) up front.\n if (limits) {\n const budget = createBudget(limits);\n for (const [path, payload] of Object.entries(entries)) {\n if (payload.byteLength > limits.maxEntryUncompressedBytes) {\n throw new OpenXmlDecompressionBombError(\n `openZip: entry \"${path}\" inflated to ${payload.byteLength} bytes,` +\n ` exceeding the ${limits.maxEntryUncompressedBytes}-byte per-entry limit` +\n ` (decompression-bomb guard).`,\n );\n }\n recordInflated(budget, path, payload.byteLength);\n }\n }\n let live = true;\n return {\n list(): string[] {\n if (!live || !entries) throw new OpenXmlIoError('openZip: archive is closed');\n return Object.keys(entries).sort();\n },\n has(path: string): boolean {\n if (!live || !entries) return false;\n return Object.hasOwn(entries, path);\n },\n read(path: string): Uint8Array {\n if (!live || !entries) throw new OpenXmlIoError('openZip: archive is closed');\n const e = entries[path];\n if (!e) throw new OpenXmlIoError(`openZip: no entry at \"${path}\"`);\n return e;\n },\n async readAsync(path: string): Promise<Uint8Array> {\n return this.read(path);\n },\n readStream(path: string): ReadableStream<Uint8Array> {\n // The unzipSync fallback path already has the entry fully inflated\n // (that's the price of dropping back from random-access). Hand it out as\n // a single-chunk stream so callers using the streaming reader don't have\n // to branch on the implementation.\n const inflated = this.read(path);\n return singleChunkStream(inflated);\n },\n close(): void {\n live = false;\n entries = undefined;\n },\n };\n}\n","// ZIP read layer.\n//\n// `openZip(source)` walks the central directory once and inflates each entry on\n// demand inside `read(path)` (see `./random-access-reader.ts`). That keeps peak\n// memory at compressed-archive size + per-entry inflate scratch, instead of\n// holding every uncompressed entry resident at once the way the old `unzipSync`\n// shortcut did. The fallback path through fflate's `unzipSync` is preserved for\n// ZIP64 / non-standard archives.\n\nimport type { XlsxSource } from '../io/source.js';\nimport { OpenXmlIoError, OpenXmlNotImplementedError } from '../utils/exceptions.js';\nimport type { DecompressionLimits } from './decompression-guard.js';\nimport { openRandomAccessArchive } from './random-access-reader.js';\n\nconst CFB_MAGIC = [0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1];\n\nconst isCfbCompoundDocument = (bytes: Uint8Array): boolean => {\n if (bytes.length < CFB_MAGIC.length) return false;\n for (let i = 0; i < CFB_MAGIC.length; i++) {\n if (bytes[i] !== CFB_MAGIC[i]) return false;\n }\n return true;\n};\n\nexport interface ZipArchive {\n /** Sorted list of all entry paths in the archive. */\n list(): string[];\n /** Synchronous read; throws OpenXmlIoError when the path is unknown. */\n read(path: string): Uint8Array;\n /** Promise variant for symmetry with the future streaming reader. */\n readAsync(path: string): Promise<Uint8Array>;\n /**\n * Streaming read: returns the entry's inflated bytes as a Web\n * `ReadableStream<Uint8Array>` chunk-by-chunk. Lets callers (the streaming\n * worksheet iterator, in particular) push the inflated payload through a SAX\n * parser without first materialising it in full — peak memory for a sheet\n * walk drops to the inflate window + SAX state instead of the entire\n * uncompressed worksheet body. Throws OpenXmlIoError when the path is\n * unknown.\n */\n readStream(path: string): ReadableStream<Uint8Array>;\n /** Whether the archive holds an entry at the given path. */\n has(path: string): boolean;\n /** Release the in-memory entry table. Subsequent reads throw. */\n close(): void;\n}\n\n/** Options for {@link openZip}. */\nexport interface OpenZipOptions {\n /**\n * Decompression-bomb safeguards applied while inflating archive entries. The\n * default limits admit any legitimate xlsx and reject pathological archives\n * (extreme compression ratios, gigabyte-scale entries). Pass `false` to\n * disable the guard entirely — only safe when the source is fully trusted.\n * See {@link DecompressionLimits} for the individual knobs.\n */\n decompressionLimits?: DecompressionLimits | false;\n}\n\n/**\n * Open a zip archive from any {@link XlsxSource}. The source is fully\n * materialised in memory, the central directory is parsed once, and each\n * entry is inflated on demand by {@link openRandomAccessArchive} — peak memory\n * stays at compressed-archive size plus per-entry inflate scratch rather than\n * holding every uncompressed entry resident. The fflate `unzipSync` fallback\n * is preserved internally for ZIP64 / non-standard archives the random-access\n * reader rejects.\n */\nexport async function openZip(source: XlsxSource, opts: OpenZipOptions = {}): Promise<ZipArchive> {\n let bytes: Uint8Array;\n try {\n bytes = await source.toBytes();\n } catch (cause) {\n throw new OpenXmlIoError('openZip: failed to read source bytes', { cause });\n }\n\n // Encrypted xlsx files (Excel 2007+ password protection) wrap the real\n // package inside an OLE Compound File Binary container with the magic\n // signature `D0 CF 11 E0 A1 B1 1A E1`. Detect that early and surface a clear\n // \"decrypt first\" error rather than letting fflate fail with a generic\n // invalid-zip message.\n if (isCfbCompoundDocument(bytes)) {\n throw new OpenXmlNotImplementedError(\n 'Encrypted xlsx is not supported. Decrypt with msoffcrypto-tool first.',\n );\n }\n\n return openRandomAccessArchive(bytes, opts.decompressionLimits);\n}\n"],"mappings":"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"}
@@ -1,11 +1,11 @@
1
1
  import { i as OpenXmlIoError, o as OpenXmlSchemaError } from "./exceptions-D-CFwxgm.mjs";
2
2
  import { m as IMAGE_FORMAT_MIME, p as IMAGE_FORMAT_EXTENSION } from "./drawing-BxzLuryn.mjs";
3
- import { n as escapeXmlAttr, r as escapeXmlText } from "./escape-DFTE7ZJc.mjs";
3
+ import { n as escapeXmlAttr, r as escapeXmlText } from "./escape-rnWSPpWY.mjs";
4
4
  import { At as parseQName, M as CPROPS_TYPE, _ as ARC_WORKBOOK_RELS, c as ARC_CONTENT_TYPES, ct as PKG_REL_NS, f as ARC_ROOT_RELS, ft as SHEET_MAIN_NS, g as ARC_WORKBOOK, h as ARC_THEME, l as ARC_CORE, lt as REL_NS, m as ARC_STYLE, mt as THEME_TYPE, p as ARC_SHARED_STRINGS, pt as STYLES_TYPE, s as ARC_APP, u as ARC_CUSTOM, ut as SHARED_STRINGS_TYPE, wt as XLSX_TYPE, x as CHARTEX_TYPE, yt as WORKSHEET_TYPE } from "./tree-BX-sRRVp.mjs";
5
- import { C as chartExToBytes, E as chartToBytes, S as userShapesToBytes, g as chartsheetToBytes, i as commentsToBytes, m as drawingToBytes, o as placeholderVmlDrawing, r as tableToBytes, t as stylesheetToBytes, y as worksheetToBytes } from "./stylesheet-writer-BrOw4jMY.mjs";
6
- import { t as serializeXml } from "./serializer-BtO2TrI7.mjs";
7
- import { V as corePropsToBytes, a as makeManifest, l as extendedPropsToBytes, n as addOverride, rt as relsToBytes, s as manifestToBytes, t as addDefault, tt as makeRelationships, v as customPropsToBytes } from "./manifest-Cm9K7D62.mjs";
8
- import { D as makeSharedStrings, S as validateSheetTitle, j as sharedStringsToBytes } from "./workbook-CARZS94U.mjs";
5
+ import { C as chartExToBytes, E as chartToBytes, S as userShapesToBytes, g as chartsheetToBytes, i as commentsToBytes, m as drawingToBytes, o as placeholderVmlDrawing, r as tableToBytes, t as stylesheetToBytes, y as worksheetToBytes } from "./stylesheet-writer-HxaX7bGy.mjs";
6
+ import { t as serializeXml } from "./serializer-C6nm0mRJ.mjs";
7
+ import { V as corePropsToBytes, a as makeManifest, l as extendedPropsToBytes, n as addOverride, rt as relsToBytes, s as manifestToBytes, t as addDefault, tt as makeRelationships, v as customPropsToBytes } from "./manifest-pLmx7KYh.mjs";
8
+ import { D as makeSharedStrings, S as validateSheetTitle, j as sharedStringsToBytes } from "./workbook-BlZVXiyG.mjs";
9
9
  import { t as createZipWriter } from "./writer-IyFccCRJ.mjs";
10
10
  //#region src/io/save.ts
11
11
  const CORE_PROPS_TYPE = "application/vnd.openxmlformats-package.core-properties+xml";
@@ -790,4 +790,4 @@ function serializeChildNode(node, inScopePrefixes) {
790
790
  //#endregion
791
791
  export { workbookToBytes as n, saveWorkbook as t };
792
792
 
793
- //# sourceMappingURL=save-2moCT3Af.mjs.map
793
+ //# sourceMappingURL=save-Bqcf8w3g.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"save-Bqcf8w3g.mjs","names":["serializeXmlNode"],"sources":["../src/io/save.ts"],"sourcesContent":["// Public `saveWorkbook` entry point.\n//\n// **Stage 1 minimum**: emits the bare set of parts a Workbook needs to\n// round-trip through `loadWorkbook`:\n//\n// [Content_Types].xml\n// _rels/.rels\n// xl/workbook.xml\n// xl/_rels/workbook.xml.rels\n// xl/worksheets/sheetN.xml ...\n// xl/styles.xml\n// xl/sharedStrings.xml (only when sst is non-empty)\n//\n// docProps / theme / VBA / drawings / charts are reserved for later iterations\n// — load tolerates their absence.\n\nimport { escapeXmlAttr, escapeXmlText } from '../utils/escape.js';\nimport { chartToBytes } from '../chart/chart-xml.js';\nimport { chartExToBytes } from '../chart/cx/chartex-xml.js';\nimport { userShapesToBytes } from '../chart/user-shapes-xml.js';\nimport { chartsheetToBytes } from '../chartsheet/chartsheet-xml.js';\nimport type { Drawing, DrawingItem } from '../drawing/drawing.js';\nimport { drawingToBytes } from '../drawing/drawing-xml.js';\nimport { IMAGE_FORMAT_EXTENSION, IMAGE_FORMAT_MIME, type XlsxImageFormat } from '../drawing/image.js';\nimport type { XlsxSink } from '../io/sink.js';\nimport { OpenXmlIoError, OpenXmlSchemaError } from '../utils/exceptions.js';\nimport { corePropsToBytes } from '../packaging/core.js';\nimport { customPropsToBytes } from '../packaging/custom.js';\nimport { extendedPropsToBytes } from '../packaging/extended.js';\nimport { addDefault, addOverride, makeManifest, manifestToBytes } from '../packaging/manifest.js';\nimport { makeRelationships, type Relationships, relsToBytes } from '../packaging/relationships.js';\nimport { stylesheetToBytes } from '../styles/stylesheet-writer.js';\nimport { makeSharedStrings, sharedStringsToBytes } from '../workbook/shared-strings.js';\nimport { type Workbook, validateSheetTitle } from '../workbook/workbook.js';\nimport type { LegacyComment } from '../worksheet/comments.js';\nimport { commentsToBytes, placeholderVmlDrawing } from '../worksheet/comments-xml.js';\nimport type { TableDefinition } from '../worksheet/table.js';\nimport { tableToBytes } from '../worksheet/table-xml.js';\nimport { worksheetToBytes } from '../worksheet/writer.js';\nimport { serializeXml as serializeXmlNode } from '../xml/serializer.js';\nimport {\n ARC_APP,\n ARC_CONTENT_TYPES,\n ARC_CORE,\n ARC_CUSTOM,\n ARC_ROOT_RELS,\n ARC_SHARED_STRINGS,\n ARC_STYLE,\n ARC_THEME,\n ARC_WORKBOOK,\n ARC_WORKBOOK_RELS,\n CHARTEX_TYPE,\n CPROPS_TYPE,\n parseQName,\n PKG_REL_NS,\n REL_NS,\n SHARED_STRINGS_TYPE,\n SHEET_MAIN_NS,\n STYLES_TYPE,\n THEME_TYPE,\n WORKSHEET_TYPE,\n XLSX_TYPE,\n} from '../xml/namespaces.js';\nimport { createZipWriter } from '../zip/writer.js';\n\nconst CORE_PROPS_TYPE = 'application/vnd.openxmlformats-package.core-properties+xml';\nconst EXT_PROPS_TYPE = 'application/vnd.openxmlformats-officedocument.extended-properties+xml';\nconst CORE_PROPS_REL = `${PKG_REL_NS}/metadata/core-properties`;\nconst EXT_PROPS_REL = `${REL_NS}/extended-properties`;\nconst CUSTOM_PROPS_REL = `${REL_NS}/custom-properties`;\nconst THEME_REL = `${REL_NS}/theme`;\nconst TABLE_REL = `${REL_NS}/table`;\nconst TABLE_TYPE = 'application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml';\nconst COMMENTS_REL = `${REL_NS}/comments`;\nconst VML_DRAWING_REL = `${REL_NS}/vmlDrawing`;\nconst COMMENTS_TYPE = 'application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml';\nconst VML_DRAWING_TYPE = 'application/vnd.openxmlformats-officedocument.vmlDrawing';\nconst DRAWING_REL = `${REL_NS}/drawing`;\nconst DRAWING_TYPE = 'application/vnd.openxmlformats-officedocument.drawing+xml';\nconst CHART_REL = `${REL_NS}/chart`;\nconst CHARTEX_REL = 'http://schemas.microsoft.com/office/2014/relationships/chartEx';\nconst CHART_TYPE = 'application/vnd.openxmlformats-officedocument.drawingml.chart+xml';\nconst IMAGE_REL = `${REL_NS}/image`;\nconst CHARTSHEET_REL = `${REL_NS}/chartsheet`;\nconst CHARTSHEET_TYPE = 'application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml';\nconst CHART_USER_SHAPES_REL = `${REL_NS}/chartUserShapes`;\n\nexport interface SaveOptions {\n /** Reserved — passes through to the underlying ZIP writer when implemented. */\n compressionLevel?: number;\n}\n\n/** Convenience: serialise a Workbook to an in-memory `Uint8Array` xlsx.\n *\n * Browser-safe: this path uses an in-memory `Uint8Array` sink and never\n * touches Node's `Buffer` global, so it works unchanged in browser bundles.\n */\nexport async function workbookToBytes(wb: Workbook, opts: SaveOptions = {}): Promise<Uint8Array> {\n const sink = toUint8ArraySink();\n await saveWorkbook(wb, sink, opts);\n return sink.result();\n}\n\nconst toUint8ArraySink = (): XlsxSink & { result(): Uint8Array } => {\n const chunks: Uint8Array[] = [];\n let finalised: Uint8Array | undefined;\n const finalise = (): Uint8Array => {\n if (finalised !== undefined) return finalised;\n let total = 0;\n for (const c of chunks) total += c.byteLength;\n const out = new Uint8Array(total);\n let off = 0;\n for (const c of chunks) {\n out.set(c, off);\n off += c.byteLength;\n }\n finalised = out;\n chunks.length = 0;\n return out;\n };\n return {\n toBytes() {\n return {\n write(chunk: Uint8Array): void {\n if (finalised !== undefined) {\n throw new OpenXmlIoError('workbookToBytes sink: write after finish');\n }\n if (!(chunk instanceof Uint8Array)) {\n throw new OpenXmlIoError('workbookToBytes sink: chunk is not a Uint8Array');\n }\n chunks.push(chunk);\n },\n async finish(): Promise<Uint8Array> {\n return finalise();\n },\n abort(): void {\n if (finalised !== undefined) return;\n finalised = new Uint8Array(0);\n chunks.length = 0;\n },\n };\n },\n result(): Uint8Array {\n return finalise();\n },\n };\n};\n\n/**\n * Validate every sheet title against Excel's character + length rules and\n * confirm titles are unique within the workbook. Catches bad state introduced\n * via direct mutation (`ws.title = ...`) that bypassed the public mutators.\n *\n * Without this gate, a workbook with an invalid sheet name would round-trip to\n * an xlsx Excel rejects with a generic \"file is corrupt\" dialog on open —\n * making it hard to diagnose. Raising at save time keeps the bad state\n * recoverable: callers can `renameSheet` and retry.\n */\nconst validateSheetTitles = (wb: Workbook): void => {\n const seen = new Map<string, number>();\n for (let i = 0; i < wb.sheets.length; i++) {\n const ref = wb.sheets[i];\n if (!ref) continue;\n const title = ref.sheet.title;\n const reason = validateSheetTitle(title);\n if (reason) {\n throw new OpenXmlSchemaError(`saveWorkbook: sheet[${i}] title \"${title}\": ${reason}`);\n }\n const lower = title.toLowerCase();\n const prior = seen.get(lower);\n if (prior !== undefined) {\n throw new OpenXmlSchemaError(\n `saveWorkbook: sheet[${i}] title \"${title}\" collides with sheet[${prior}]` +\n ` (Excel treats sheet names case-insensitively for uniqueness).`,\n );\n }\n seen.set(lower, i);\n }\n};\n\n/** Save a workbook through the given sink. Returns once `finalize()` resolves. */\nexport async function saveWorkbook(wb: Workbook, sink: XlsxSink, _opts: SaveOptions = {}): Promise<void> {\n validateSheetTitles(wb);\n const writer = createZipWriter(sink);\n try {\n await saveWorkbookImpl(wb, writer);\n } catch (err) {\n // Release the sink so streaming destinations (`toFile` / `toWritable`)\n // don't leave a half-written file looking valid. abort() is idempotent so\n // a successful finalize() above is a no-op here.\n writer.abort(err);\n throw err;\n }\n}\n\nasync function saveWorkbookImpl(wb: Workbook, writer: ReturnType<typeof createZipWriter>): Promise<void> {\n // ---- 1. assemble the per-sheet rels + serialise each worksheet ----------\n const sst = makeSharedStrings();\n interface SheetEmit {\n id: string;\n target: string;\n bytes: Uint8Array;\n /** OOXML relationship type used in workbook.xml.rels. */\n relType: string;\n /** ZIP archive path the bytes are written to. */\n archivePath: string;\n /** Override content type for [Content_Types].xml. */\n contentType: string;\n /** Per-sheet rels — populated only if the sheet has hyperlinks / drawings / etc. */\n rels?: Relationships;\n }\n const sheetEmits: SheetEmit[] = [];\n // Workbook-global table counter so xl/tables/tableN.xml ids stay unique.\n const tableEmits: Array<{ id: number; bytes: Uint8Array }> = [];\n let nextTableId = 1;\n // Same counter pattern for comments parts. The comments part and the VML\n // drawing share their N because Excel always emits them paired.\n interface CommentEmit {\n id: number;\n commentsBytes: Uint8Array;\n vmlBytes: Uint8Array;\n }\n const commentEmits: CommentEmit[] = [];\n // Passthrough carries form-control / header-footer VML under the same\n // xl/drawings/vmlDrawingN.vml naming, so start past whatever is already\n // taken — the zip writer rejects duplicate entries.\n let nextCommentsId = nextFreeIndex(wb, /^xl\\/drawings\\/vmlDrawing(\\d+)\\.vml$/);\n // Drawings: workbook-global drawingN counter for xl/drawings/drawingN.xml +\n // matching drawing-rels file (when chart items are present).\n interface DrawingEmit {\n id: number;\n bytes: Uint8Array;\n /** Drawing rels — when non-empty we also emit the rels file. */\n rels?: Relationships;\n }\n const drawingEmits: DrawingEmit[] = [];\n let nextDrawingId = 1;\n // Chart parts share a workbook-global counter so xl/charts/chartN.xml ids\n // stay unique.\n const chartEmits: Array<{\n id: number;\n bytes: Uint8Array;\n isCx: boolean;\n /** Per-chart rels file (only emitted when chart.userShapes is set). */\n rels?: Relationships;\n }> = [];\n let nextChartId = 1;\n // Workbook-global counter for chartDrawing parts. Each chart with userShapes\n // set produces one xl/drawings/chartDrawingN.xml entry.\n const userShapeEmits: Array<{ id: number; bytes: Uint8Array }> = [];\n let nextUserShapesId = 1;\n // Workbook-global counter for image media parts. Excel uses\n // xl/media/imageN.{ext} where N is shared across the package.\n interface ImageEmit {\n id: number;\n /** File extension (without the dot) — also drives the manifest Default. */\n ext: string;\n bytes: Uint8Array;\n }\n const imageEmits: ImageEmit[] = [];\n const imageExts = new Set<string>();\n // Same collision guard as for VML: images referenced by passthrough parts\n // keep their original xl/media/imageN.* names.\n let nextImageId = nextFreeIndex(wb, /^xl\\/media\\/image(\\d+)\\./);\n // Track separate counters so worksheet / chartsheet IDs do not collide (Excel\n // uses xl/worksheets/sheetN.xml and xl/chartsheets/sheetM.xml independently\n // of one another).\n let nextWorksheetId = 1;\n let nextChartsheetId = 1;\n // Pre-claim every original rId so freshly allocated ones (sheets without a\n // captured rId, plus modeled non-sheet rels) avoid collision with captured\n // workbookRelsExtras and the modeled non-sheet original ids.\n const claimedRIds = new Set<string>();\n for (const ref of wb.sheets) {\n if (ref.rId !== undefined) claimedRIds.add(ref.rId);\n }\n if (wb.workbookRelOriginalIds) {\n for (const v of Object.values(wb.workbookRelOriginalIds)) {\n if (typeof v === 'string') claimedRIds.add(v);\n }\n }\n if (wb.workbookRelsExtras) {\n for (const e of wb.workbookRelsExtras) claimedRIds.add(e.id);\n }\n let nextRIdCursor = 1;\n const allocateRId = (): string => {\n let id = `rId${nextRIdCursor}`;\n while (claimedRIds.has(id)) {\n nextRIdCursor++;\n id = `rId${nextRIdCursor}`;\n }\n claimedRIds.add(id);\n nextRIdCursor++;\n return id;\n };\n wb.sheets.forEach((ref, _i) => {\n const isChartsheet = ref.kind === 'chartsheet';\n const target = isChartsheet ? `chartsheets/sheet${nextChartsheetId}.xml` : `worksheets/sheet${nextWorksheetId}.xml`;\n const archivePath = isChartsheet\n ? `xl/chartsheets/sheet${nextChartsheetId}.xml`\n : `xl/worksheets/sheet${nextWorksheetId}.xml`;\n if (isChartsheet) nextChartsheetId++;\n else nextWorksheetId++;\n const sheetRels = makeRelationships();\n // Pre-claim every captured relsExtras rId so freshly allocated modeled rels\n // never clash. The extras themselves are appended to sheetRels at the end\n // so any rId already used inside the worksheet body (drawing / hyperlink\n // refs) keeps pointing at our newly emitted rel.\n const sheetRelsClaimed = new Set<string>();\n const sheetRelsExtras = ref.kind === 'worksheet' ? (ref.sheet.relsExtras ?? []) : [];\n for (const e of sheetRelsExtras) sheetRelsClaimed.add(e.id);\n let sheetRIdCursor = 1;\n const allocateSheetRId = (): string => {\n let id = `rId${sheetRIdCursor}`;\n while (sheetRelsClaimed.has(id) || sheetRels.rels.some((r) => r.id === id)) {\n sheetRIdCursor++;\n id = `rId${sheetRIdCursor}`;\n }\n sheetRIdCursor++;\n return id;\n };\n const registerTable = (table: TableDefinition): { rId: string } => {\n const tableId = nextTableId++;\n const rId = allocateSheetRId();\n sheetRels.rels.push({\n id: rId,\n type: TABLE_REL,\n target: `../tables/table${tableId}.xml`,\n });\n // Emit the table part with its workbook-global id baked in.\n const xmlTable: TableDefinition = { ...table, id: tableId };\n tableEmits.push({ id: tableId, bytes: tableToBytes(xmlTable) });\n return { rId };\n };\n const registerComments = (comments: ReadonlyArray<LegacyComment>): { vmlRelId: string } => {\n const id = nextCommentsId++;\n const commentsRelId = allocateSheetRId();\n sheetRels.rels.push({\n id: commentsRelId,\n type: COMMENTS_REL,\n target: `../comments${id}.xml`,\n });\n const vmlRelId = allocateSheetRId();\n sheetRels.rels.push({\n id: vmlRelId,\n type: VML_DRAWING_REL,\n target: `../drawings/vmlDrawing${id}.vml`,\n });\n commentEmits.push({\n id,\n commentsBytes: commentsToBytes(comments),\n vmlBytes: placeholderVmlDrawing(comments),\n });\n return { vmlRelId };\n };\n const registerDrawing = (drawing: Drawing): { rId: string } => {\n const id = nextDrawingId++;\n const rId = allocateSheetRId();\n sheetRels.rels.push({\n id: rId,\n type: DRAWING_REL,\n target: `../drawings/drawing${id}.xml`,\n });\n // Walk drawing items: for each chart with a payload, allocate a\n // workbook-global chartN id and a per-drawing rId. Emit the chart part +\n // collect a drawing-rels entry so drawing.xml's <c:chart r:id> resolves.\n const drawingRels = makeRelationships();\n // Rels the drawing's verbatim nodes reference keep their original ids —\n // those ids are baked into XML we re-emit untouched — so the allocator\n // below has to step over them.\n const usedDrawingRIds = new Set<string>();\n for (const rel of drawing.rawRels?.rels ?? []) {\n drawingRels.rels.push(rel);\n usedDrawingRIds.add(rel.id);\n }\n let nextDrawingRId = 1;\n const allocateDrawingRId = (): string => {\n let candidate = `rId${nextDrawingRId++}`;\n while (usedDrawingRIds.has(candidate)) candidate = `rId${nextDrawingRId++}`;\n usedDrawingRIds.add(candidate);\n return candidate;\n };\n const itemsForXml: DrawingItem[] = [];\n for (const item of drawing.items) {\n if (item.content.kind === 'chart' && (item.content.chart.space || item.content.chart.cxSpace)) {\n const chartId = nextChartId++;\n const chartRId = allocateDrawingRId();\n // chartex parts use a different relationship Type than the legacy\n // ECMA-376 charts. Excel rejects the workbook when the drawing rels\n // claim a `relationships/chart` target that actually contains a\n // `cx:chartSpace` root.\n const isCxChart = item.content.chart.cxSpace !== undefined;\n drawingRels.rels.push({\n id: chartRId,\n type: isCxChart ? CHARTEX_REL : CHART_REL,\n target: `../charts/chart${chartId}.xml`,\n });\n if (item.content.chart.cxSpace) {\n chartEmits.push({\n id: chartId,\n bytes: chartExToBytes(item.content.chart.cxSpace),\n isCx: true,\n });\n } else if (item.content.chart.space) {\n const space = item.content.chart.space;\n // If the chart carries user shapes, allocate the chartDrawingN part\n // + a per-chart rels file referencing it, then bake the resulting\n // r:id into the chart's <c:userShapes> element.\n let userShapesRId: string | undefined;\n let chartRelsFile: Relationships | undefined;\n if (space.userShapes && space.userShapes.shapes.length > 0) {\n const userShapesId = nextUserShapesId++;\n userShapesRId = 'rId1';\n chartRelsFile = makeRelationships();\n chartRelsFile.rels.push({\n id: userShapesRId,\n type: CHART_USER_SHAPES_REL,\n target: `../drawings/chartDrawing${userShapesId}.xml`,\n });\n userShapeEmits.push({\n id: userShapesId,\n bytes: userShapesToBytes(space.userShapes),\n });\n }\n const chartEmit: typeof chartEmits[number] = {\n id: chartId,\n bytes: chartToBytes(space, userShapesRId !== undefined ? { userShapesRId } : {}),\n isCx: false,\n };\n if (chartRelsFile) chartEmit.rels = chartRelsFile;\n chartEmits.push(chartEmit);\n }\n itemsForXml.push({\n anchor: item.anchor,\n content: {\n kind: 'chart',\n chart: {\n rId: chartRId,\n ...(item.content.chart.cxSpace ? { isCx: true } : {}),\n },\n },\n });\n } else if (item.content.kind === 'picture' && item.content.picture.image) {\n const img = item.content.picture.image;\n const ext = IMAGE_FORMAT_EXTENSION[img.format];\n const imageId = nextImageId++;\n const picRId = allocateDrawingRId();\n drawingRels.rels.push({\n id: picRId,\n type: IMAGE_REL,\n target: `../media/image${imageId}.${ext}`,\n });\n imageEmits.push({ id: imageId, ext, bytes: img.bytes });\n imageExts.add(ext);\n itemsForXml.push({\n anchor: item.anchor,\n content: {\n kind: 'picture',\n picture: {\n rId: picRId,\n ...(item.content.picture.name !== undefined ? { name: item.content.picture.name } : {}),\n ...(item.content.picture.descr !== undefined ? { descr: item.content.picture.descr } : {}),\n ...(item.content.picture.hidden !== undefined ? { hidden: item.content.picture.hidden } : {}),\n ...(item.content.picture.spPr ? { spPr: item.content.picture.spPr } : {}),\n },\n },\n });\n } else {\n itemsForXml.push(item);\n }\n }\n const emit: DrawingEmit = {\n id,\n bytes: drawingToBytes({\n items: itemsForXml,\n ...(drawing.rawChildren ? { rawChildren: drawing.rawChildren } : {}),\n }),\n };\n if (drawingRels.rels.length > 0) emit.rels = drawingRels;\n drawingEmits.push(emit);\n return { rId };\n };\n let bytes: Uint8Array;\n if (ref.kind === 'worksheet') {\n bytes = worksheetToBytes(ref.sheet, {\n sharedStrings: sst,\n date1904: wb.date1904,\n rels: sheetRels,\n registerTable,\n registerComments,\n registerDrawing,\n });\n } else {\n // Chartsheet: register the drawing (if any), then emit the chartsheet\n // part with the resulting r:id baked in.\n let drawingRId: string | undefined;\n if (ref.sheet.drawing) drawingRId = registerDrawing(ref.sheet.drawing).rId;\n bytes = chartsheetToBytes(ref.sheet, drawingRId !== undefined ? { drawingRId } : {});\n }\n // Append captured per-sheet rels passthrough (pivotTable / queryTable /\n // printerSettings / oleObject / customProperty / threadedComment …)\n // verbatim. Their original rIds were pre-claimed so the modeled allocations\n // above never collide with them.\n for (const e of sheetRelsExtras) {\n sheetRels.rels.push({ id: e.id, type: e.type, target: e.target });\n }\n const sheetRId = ref.rId ?? allocateRId();\n const emit: SheetEmit = {\n id: sheetRId,\n target,\n bytes,\n relType: isChartsheet ? CHARTSHEET_REL : `${REL_NS}/worksheet`,\n archivePath,\n contentType: isChartsheet ? CHARTSHEET_TYPE : WORKSHEET_TYPE,\n };\n if (sheetRels.rels.length > 0) emit.rels = sheetRels;\n sheetEmits.push(emit);\n });\n\n // ---- 2. workbook rels -- sheets first, then sst (if any), then styles, then\n // theme / vbaProject, then any captured workbookRelsExtras (e.g.\n // pivotCacheDefinition rels referenced by `<pivotCaches>`). Modeled non-sheet\n // rels prefer the rId captured at load time so any captured extras XML using\n // that Id still resolves after the round-trip.\n const wbRels = makeRelationships();\n wbRels.rels = sheetEmits.map((e) => ({\n id: e.id,\n type: e.relType,\n target: e.target,\n }));\n const orig = wb.workbookRelOriginalIds;\n if (sst.entries.length > 0) {\n wbRels.rels.push({\n id: orig?.sharedStrings ?? allocateRId(),\n type: `${REL_NS}/sharedStrings`,\n target: 'sharedStrings.xml',\n });\n }\n wbRels.rels.push({\n id: orig?.styles ?? allocateRId(),\n type: `${REL_NS}/styles`,\n target: 'styles.xml',\n });\n if (wb.themeXml) {\n wbRels.rels.push({\n id: orig?.theme ?? allocateRId(),\n type: THEME_REL,\n target: 'theme/theme1.xml',\n });\n }\n if (wb.vbaProject) {\n wbRels.rels.push({\n id: orig?.vbaProject ?? allocateRId(),\n type: `${REL_NS}/vbaProject`,\n target: 'vbaProject.bin',\n });\n }\n if (wb.workbookRelsExtras) {\n for (const e of wb.workbookRelsExtras) {\n wbRels.rels.push({ id: e.id, type: e.type, target: e.target });\n }\n }\n\n // ---- 3. workbook.xml ----------------------------------------------------\n const workbookXml = serializeWorkbookXml(\n wb,\n sheetEmits.map((e) => e.id),\n );\n await writer.addEntry(ARC_WORKBOOK, new TextEncoder().encode(workbookXml));\n await writer.addEntry(ARC_WORKBOOK_RELS, relsToBytes(wbRels));\n\n // ---- 4. each worksheet / chartsheet (and its rels file when present) --\n for (const e of sheetEmits) {\n await writer.addEntry(e.archivePath, e.bytes);\n if (e.rels) {\n // The rels file sits alongside its part: `xl/<dir>/_rels/<file>.rels`.\n const slash = e.archivePath.lastIndexOf('/');\n const dir = e.archivePath.slice(0, slash);\n const file = e.archivePath.slice(slash + 1);\n await writer.addEntry(`${dir}/_rels/${file}.rels`, relsToBytes(e.rels));\n }\n }\n\n // ---- 4b. table parts ----------------------------------------------------\n for (const t of tableEmits) {\n await writer.addEntry(`xl/tables/table${t.id}.xml`, t.bytes);\n }\n\n // ---- 4c. comments parts + matching VML drawings ------------------------\n for (const c of commentEmits) {\n await writer.addEntry(`xl/comments${c.id}.xml`, c.commentsBytes);\n await writer.addEntry(`xl/drawings/vmlDrawing${c.id}.vml`, c.vmlBytes);\n }\n\n // ---- 4d. drawings + their rels (when charts are embedded) -------------\n for (const d of drawingEmits) {\n await writer.addEntry(`xl/drawings/drawing${d.id}.xml`, d.bytes);\n if (d.rels) {\n await writer.addEntry(`xl/drawings/_rels/drawing${d.id}.xml.rels`, relsToBytes(d.rels));\n }\n }\n\n // ---- 4e. chart parts (and per-chart rels when user shapes attach) -----\n for (const c of chartEmits) {\n await writer.addEntry(`xl/charts/chart${c.id}.xml`, c.bytes);\n if (c.rels) {\n await writer.addEntry(`xl/charts/_rels/chart${c.id}.xml.rels`, relsToBytes(c.rels));\n }\n }\n\n // ---- 4f. user-shape drawings (xl/drawings/chartDrawingN.xml) ----------\n for (const us of userShapeEmits) {\n await writer.addEntry(`xl/drawings/chartDrawing${us.id}.xml`, us.bytes);\n }\n\n // ---- 4g. embedded images (xl/media/imageN.{ext}) ----------------------\n for (const img of imageEmits) {\n await writer.addEntry(`xl/media/image${img.id}.${img.ext}`, img.bytes);\n }\n\n // ---- 5. styles.xml + sharedStrings.xml (if any) -------------------------\n await writer.addEntry(ARC_STYLE, stylesheetToBytes(wb.styles));\n if (sst.entries.length > 0) {\n await writer.addEntry(ARC_SHARED_STRINGS, sharedStringsToBytes(sst));\n }\n\n // ---- 5b. theme1.xml (passthrough) — only when wb carries one. The theme rel\n // was already added to wbRels above so workbook.xml.rels references it.\n if (wb.themeXml) await writer.addEntry(ARC_THEME, wb.themeXml);\n\n // ---- 5c. docProps/{core,app,custom}.xml (when present) ------------------\n if (wb.properties) await writer.addEntry(ARC_CORE, corePropsToBytes(wb.properties));\n if (wb.appProperties) await writer.addEntry(ARC_APP, extendedPropsToBytes(wb.appProperties));\n if (wb.customProperties) await writer.addEntry(ARC_CUSTOM, customPropsToBytes(wb.customProperties));\n\n // ---- 5d. VBA project + signature (when present) -------------------------\n if (wb.vbaProject) await writer.addEntry('xl/vbaProject.bin', wb.vbaProject);\n if (wb.vbaSignature) await writer.addEntry('xl/vbaProjectSignature.bin', wb.vbaSignature);\n\n // ---- 5e. Pass-through bytes (pivot / activeX / customXml / etc.) -------\n if (wb.passthrough) {\n for (const [path, bytes] of wb.passthrough) await writer.addEntry(path, bytes);\n }\n\n // ---- 6. root rels -------------------------------------------------------\n const rootRels: Relationships = { rels: [] };\n rootRels.rels.push({\n id: 'rId1',\n type: `${REL_NS}/officeDocument`,\n target: 'xl/workbook.xml',\n });\n if (wb.properties) {\n rootRels.rels.push({\n id: `rId${rootRels.rels.length + 1}`,\n type: CORE_PROPS_REL,\n target: 'docProps/core.xml',\n });\n }\n if (wb.appProperties) {\n rootRels.rels.push({\n id: `rId${rootRels.rels.length + 1}`,\n type: EXT_PROPS_REL,\n target: 'docProps/app.xml',\n });\n }\n if (wb.customProperties) {\n rootRels.rels.push({\n id: `rId${rootRels.rels.length + 1}`,\n type: CUSTOM_PROPS_REL,\n target: 'docProps/custom.xml',\n });\n }\n await writer.addEntry(ARC_ROOT_RELS, relsToBytes(rootRels));\n\n // ---- 7. [Content_Types].xml --------------------------------------------\n const manifest = makeManifest();\n addDefault(manifest, 'rels', 'application/vnd.openxmlformats-package.relationships+xml');\n addDefault(manifest, 'xml', 'application/xml');\n // Defaults carried over for passthrough parts go first: addDefault is\n // last-wins, so the writer's own registrations below (vml for comments, bin\n // for VBA, image formats) still take precedence on a shared extension.\n if (wb.passthroughDefaults) {\n for (const [ext, ct] of wb.passthroughDefaults) addDefault(manifest, ext, ct);\n }\n // VBA-bearing workbooks promote the workbook content type to xlsm.\n const workbookContentType = wb.vbaProject\n ? 'application/vnd.ms-excel.sheet.macroEnabled.main+xml'\n : XLSX_TYPE;\n addOverride(manifest, `/${ARC_WORKBOOK}`, workbookContentType);\n for (const e of sheetEmits) {\n addOverride(manifest, `/${e.archivePath}`, e.contentType);\n }\n addOverride(manifest, `/${ARC_STYLE}`, STYLES_TYPE);\n if (sst.entries.length > 0) {\n addOverride(manifest, `/${ARC_SHARED_STRINGS}`, SHARED_STRINGS_TYPE);\n }\n if (wb.themeXml) addOverride(manifest, `/${ARC_THEME}`, THEME_TYPE);\n for (const t of tableEmits) {\n addOverride(manifest, `/xl/tables/table${t.id}.xml`, TABLE_TYPE);\n }\n if (commentEmits.length > 0) {\n addDefault(manifest, 'vml', VML_DRAWING_TYPE);\n }\n for (const c of commentEmits) {\n addOverride(manifest, `/xl/comments${c.id}.xml`, COMMENTS_TYPE);\n }\n for (const d of drawingEmits) {\n addOverride(manifest, `/xl/drawings/drawing${d.id}.xml`, DRAWING_TYPE);\n }\n for (const c of chartEmits) {\n addOverride(manifest, `/xl/charts/chart${c.id}.xml`, c.isCx ? CHARTEX_TYPE : CHART_TYPE);\n }\n for (const us of userShapeEmits) {\n addOverride(manifest, `/xl/drawings/chartDrawing${us.id}.xml`, DRAWING_TYPE);\n }\n if (wb.vbaProject) {\n addDefault(manifest, 'bin', 'application/vnd.ms-office.vbaProject');\n }\n if (wb.passthrough) {\n for (const path of wb.passthrough.keys()) {\n const ct = wb.passthroughContentTypes?.get(path);\n if (ct !== undefined) addOverride(manifest, `/${path}`, ct);\n }\n }\n // Each unique image extension gets a Default entry (`<Default Extension=\"png\"\n // ContentType=\"image/png\"/>`).\n for (const ext of imageExts) {\n const fmt = (Object.entries(IMAGE_FORMAT_EXTENSION).find(([, e]) => e === ext) ?? [])[0] as\n | XlsxImageFormat\n | undefined;\n if (fmt) addDefault(manifest, ext, IMAGE_FORMAT_MIME[fmt]);\n }\n if (wb.properties) addOverride(manifest, `/${ARC_CORE}`, CORE_PROPS_TYPE);\n if (wb.appProperties) addOverride(manifest, `/${ARC_APP}`, EXT_PROPS_TYPE);\n if (wb.customProperties) addOverride(manifest, `/${ARC_CUSTOM}`, CPROPS_TYPE);\n await writer.addEntry(ARC_CONTENT_TYPES, manifestToBytes(manifest));\n\n // ---- 8. close ----------------------------------------------------------\n await writer.finalize();\n}\n\n/**\n * 1 + the highest N among passthrough paths matching `pattern` (whose first\n * capture group is N), so freshly numbered modeled parts never collide with a\n * carried-over one.\n */\nfunction nextFreeIndex(wb: Workbook, pattern: RegExp): number {\n let max = 0;\n if (wb.passthrough) {\n for (const path of wb.passthrough.keys()) {\n const m = pattern.exec(path);\n if (m?.[1] !== undefined) max = Math.max(max, Number.parseInt(m[1], 10));\n }\n }\n return max + 1;\n}\n\n/** Serialise the minimum `<workbook><sheets/></workbook>` Excel needs to load a sheet list. */\nfunction serializeWorkbookXml(wb: Workbook, sheetRIds: ReadonlyArray<string>): string {\n // Excel's markup-compatibility header: the extra prefixes it declares on the\n // root and the `mc:Ignorable` list naming them. Both are re-emitted exactly\n // as written, and the same prefix map is handed to every captured child so\n // `xr:revisionPtr` doesn't come back as `x16:revisionPtr`.\n const rootNs = wb.workbookXmlRoot?.namespaces ?? [];\n const nsDecls = rootNs.map((d) => ` xmlns:${d.prefix}=\"${escapeAttr(d.ns)}\"`).join('');\n const ignorable = wb.workbookXmlRoot?.ignorable;\n const ignorableAttr = ignorable !== undefined ? ` mc:Ignorable=\"${escapeAttr(ignorable)}\"` : '';\n const inScopePrefixes: Record<string, string> = { [SHEET_MAIN_NS]: '', [REL_NS]: 'r' };\n for (const d of rootNs) inScopePrefixes[d.ns] ??= d.prefix;\n const emitExtra = (node: import('../xml/tree.js').XmlNode): string =>\n serializeChildNode(node, inScopePrefixes);\n\n const parts: string[] = [\n '<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>',\n `<workbook xmlns=\"${SHEET_MAIN_NS}\" xmlns:r=\"${REL_NS}\"${nsDecls}${ignorableAttr}>`,\n ];\n if (wb.fileVersion) {\n const fv = serializeFileVersion(wb.fileVersion);\n if (fv) parts.push(fv);\n }\n if (wb.fileSharing) {\n const fs = serializeFileSharing(wb.fileSharing);\n if (fs) parts.push(fs);\n }\n // Emit <workbookPr> from the typed model; fall back to a minimal {date1904:\n // true} synthesis so a fresh workbook (no load history) still round-trips\n // through Excel with the right epoch.\n const effectiveWp = effectiveWorkbookProperties(wb);\n if (effectiveWp) {\n const wp = serializeWorkbookProperties(effectiveWp);\n if (wp) parts.push(wp);\n }\n if (wb.workbookProtection) {\n const wp = serializeWorkbookProtection(wb.workbookProtection);\n if (wp) parts.push(wp);\n }\n // The unmodeled head — `mc:AlternateContent`, `xr:revisionPtr` — sits after\n // `<workbookPr>` in what Excel writes, and CT_Workbook has no slot for it,\n // so it goes here rather than ahead of the modeled elements.\n if (wb.workbookXmlExtras?.beforeSheets) {\n for (const node of wb.workbookXmlExtras.beforeSheets) parts.push(emitExtra(node));\n }\n if (wb.bookViews && wb.bookViews.length > 0) parts.push(serializeBookViews(wb.bookViews, inScopePrefixes));\n parts.push('<sheets>');\n wb.sheets.forEach((ref, i) => {\n const stateAttr = ref.state === 'visible' ? '' : ` state=\"${ref.state}\"`;\n const rId = sheetRIds[i] ?? `rId${i + 1}`;\n parts.push(`<sheet name=\"${escapeAttr(ref.sheet.title)}\" sheetId=\"${ref.sheetId}\"${stateAttr} r:id=\"${rId}\"/>`);\n });\n parts.push('</sheets>');\n // ECMA-376 §18.2.27 makes CT_Workbook an xsd:sequence, so the order below is\n // normative: functionGroups, externalReferences, definedNames, calcPr,\n // oleSize, customWorkbookViews, pivotCaches, smartTagPr, smartTagTypes,\n // webPublishing, fileRecoveryPr, webPublishObjects, extLst. Excel rejects a\n // workbook whose children are out of sequence. `afterSheets` is the\n // unmodeled tail (webPublishing / webPublishObjects / extLst /\n // mc:AlternateContent), so it goes last.\n if (wb.functionGroups) {\n const fg = serializeFunctionGroups(wb.functionGroups);\n if (fg) parts.push(fg);\n }\n if (wb.externalReferences && wb.externalReferences.length > 0) {\n const inner: string[] = ['<externalReferences>'];\n for (const er of wb.externalReferences) {\n inner.push(`<externalReference r:id=\"${escapeAttr(er.rId)}\"/>`);\n }\n inner.push('</externalReferences>');\n parts.push(inner.join(''));\n }\n if (wb.definedNames.length > 0) {\n parts.push('<definedNames>');\n for (const dn of wb.definedNames) {\n let attrs = ` name=\"${escapeAttr(dn.name)}\"`;\n if (dn.scope !== undefined) attrs += ` localSheetId=\"${dn.scope}\"`;\n if (dn.hidden) attrs += ' hidden=\"1\"';\n if (dn.comment !== undefined) attrs += ` comment=\"${escapeAttr(dn.comment)}\"`;\n parts.push(`<definedName${attrs}>${escapeText(dn.value)}</definedName>`);\n }\n parts.push('</definedNames>');\n }\n if (wb.calcProperties) {\n const cp = serializeCalcProperties(wb.calcProperties);\n if (cp) parts.push(cp);\n }\n // No default `<calcPr>` — Excel handles its absence by inserting one on save.\n // Hand-rolled `calcId` values (especially the modern 191029) made Excel\n // reject workbooks whose future-function formulas (LET / LAMBDA / FILTER…)\n // couldn't be evaluated against the declared engine.\n if (wb.oleSize !== undefined) {\n parts.push(`<oleSize ref=\"${escapeAttr(wb.oleSize)}\"/>`);\n }\n if (wb.customWorkbookViews && wb.customWorkbookViews.length > 0) {\n parts.push(serializeCustomWorkbookViews(wb.customWorkbookViews));\n }\n if (wb.pivotCaches && wb.pivotCaches.length > 0) {\n const inner: string[] = ['<pivotCaches>'];\n for (const pc of wb.pivotCaches) {\n inner.push(`<pivotCache cacheId=\"${pc.cacheId}\" r:id=\"${escapeAttr(pc.rId)}\"/>`);\n }\n inner.push('</pivotCaches>');\n parts.push(inner.join(''));\n }\n if (wb.smartTagPr) {\n const stp = serializeSmartTagPr(wb.smartTagPr);\n if (stp) parts.push(stp);\n }\n if (wb.smartTagTypes && wb.smartTagTypes.length > 0) {\n parts.push(serializeSmartTagTypes(wb.smartTagTypes));\n }\n if (wb.fileRecoveryPr) {\n const fp = serializeFileRecoveryPr(wb.fileRecoveryPr);\n if (fp) parts.push(fp);\n }\n if (wb.workbookXmlExtras?.afterSheets) {\n for (const node of wb.workbookXmlExtras.afterSheets) parts.push(emitExtra(node));\n }\n parts.push('</workbook>');\n return parts.join('');\n}\n\nfunction serializeCalcProperties(\n cp: import('../workbook/calc-properties.js').CalcProperties,\n): string | undefined {\n let attrs = '';\n if (cp.calcId !== undefined) attrs += ` calcId=\"${cp.calcId}\"`;\n if (cp.calcMode !== undefined) attrs += ` calcMode=\"${cp.calcMode}\"`;\n if (cp.fullCalcOnLoad !== undefined) attrs += ` fullCalcOnLoad=\"${cp.fullCalcOnLoad ? '1' : '0'}\"`;\n if (cp.refMode !== undefined) attrs += ` refMode=\"${cp.refMode}\"`;\n if (cp.iterate !== undefined) attrs += ` iterate=\"${cp.iterate ? '1' : '0'}\"`;\n if (cp.iterateCount !== undefined) attrs += ` iterateCount=\"${cp.iterateCount}\"`;\n if (cp.iterateDelta !== undefined) attrs += ` iterateDelta=\"${cp.iterateDelta}\"`;\n if (cp.fullPrecision !== undefined) attrs += ` fullPrecision=\"${cp.fullPrecision ? '1' : '0'}\"`;\n if (cp.calcCompleted !== undefined) attrs += ` calcCompleted=\"${cp.calcCompleted ? '1' : '0'}\"`;\n if (cp.calcOnSave !== undefined) attrs += ` calcOnSave=\"${cp.calcOnSave ? '1' : '0'}\"`;\n if (cp.concurrentCalc !== undefined) attrs += ` concurrentCalc=\"${cp.concurrentCalc ? '1' : '0'}\"`;\n if (cp.concurrentManualCount !== undefined) attrs += ` concurrentManualCount=\"${cp.concurrentManualCount}\"`;\n if (cp.forceFullCalc !== undefined) attrs += ` forceFullCalc=\"${cp.forceFullCalc ? '1' : '0'}\"`;\n if (attrs.length === 0) return undefined;\n return `<calcPr${attrs}/>`;\n}\n\nfunction serializeFunctionGroups(\n fg: import('../workbook/function-groups.js').FunctionGroups,\n): string | undefined {\n let attrs = '';\n if (fg.builtInGroupCount !== undefined) attrs += ` builtInGroupCount=\"${fg.builtInGroupCount}\"`;\n if (fg.groups.length === 0 && attrs.length === 0) return undefined;\n if (fg.groups.length === 0) return `<functionGroups${attrs}/>`;\n const inner: string[] = [`<functionGroups${attrs}>`];\n for (const g of fg.groups) inner.push(`<functionGroup name=\"${escapeAttr(g.name)}\"/>`);\n inner.push('</functionGroups>');\n return inner.join('');\n}\n\nfunction serializeSmartTagPr(\n stp: import('../workbook/smart-tags.js').SmartTagProperties,\n): string | undefined {\n let attrs = '';\n if (stp.embed !== undefined) attrs += ` embed=\"${stp.embed ? '1' : '0'}\"`;\n if (stp.show !== undefined) attrs += ` show=\"${stp.show}\"`;\n if (attrs.length === 0) return undefined;\n return `<smartTagPr${attrs}/>`;\n}\n\nfunction serializeSmartTagTypes(\n tags: ReadonlyArray<import('../workbook/smart-tags.js').SmartTagType>,\n): string {\n const inner: string[] = ['<smartTagTypes>'];\n for (const t of tags) {\n let attrs = '';\n if (t.namespaceUri !== undefined) attrs += ` namespaceUri=\"${escapeAttr(t.namespaceUri)}\"`;\n if (t.name !== undefined) attrs += ` name=\"${escapeAttr(t.name)}\"`;\n if (t.url !== undefined) attrs += ` url=\"${escapeAttr(t.url)}\"`;\n inner.push(`<smartTagType${attrs}/>`);\n }\n inner.push('</smartTagTypes>');\n return inner.join('');\n}\n\nfunction serializeFileRecoveryPr(\n fp: import('../workbook/file-recovery.js').FileRecoveryProperties,\n): string | undefined {\n let attrs = '';\n if (fp.autoRecover !== undefined) attrs += ` autoRecover=\"${fp.autoRecover ? '1' : '0'}\"`;\n if (fp.crashSave !== undefined) attrs += ` crashSave=\"${fp.crashSave ? '1' : '0'}\"`;\n if (fp.dataExtractLoad !== undefined) attrs += ` dataExtractLoad=\"${fp.dataExtractLoad ? '1' : '0'}\"`;\n if (fp.repairLoad !== undefined) attrs += ` repairLoad=\"${fp.repairLoad ? '1' : '0'}\"`;\n if (attrs.length === 0) return undefined;\n return `<fileRecoveryPr${attrs}/>`;\n}\n\nfunction serializeFileSharing(\n fs: import('../workbook/file-sharing.js').FileSharing,\n): string | undefined {\n let attrs = '';\n if (fs.readOnlyRecommended !== undefined)\n attrs += ` readOnlyRecommended=\"${fs.readOnlyRecommended ? '1' : '0'}\"`;\n if (fs.userName !== undefined) attrs += ` userName=\"${escapeAttr(fs.userName)}\"`;\n if (fs.reservationPassword !== undefined)\n attrs += ` reservationPassword=\"${escapeAttr(fs.reservationPassword)}\"`;\n if (fs.algorithmName !== undefined) attrs += ` algorithmName=\"${escapeAttr(fs.algorithmName)}\"`;\n if (fs.hashValue !== undefined) attrs += ` hashValue=\"${escapeAttr(fs.hashValue)}\"`;\n if (fs.saltValue !== undefined) attrs += ` saltValue=\"${escapeAttr(fs.saltValue)}\"`;\n if (fs.spinCount !== undefined) attrs += ` spinCount=\"${fs.spinCount}\"`;\n if (attrs.length === 0) return undefined;\n return `<fileSharing${attrs}/>`;\n}\n\nfunction serializeFileVersion(\n fv: import('../workbook/file-version.js').FileVersion,\n): string | undefined {\n let attrs = '';\n if (fv.appName !== undefined) attrs += ` appName=\"${escapeAttr(fv.appName)}\"`;\n if (fv.lastEdited !== undefined) attrs += ` lastEdited=\"${escapeAttr(fv.lastEdited)}\"`;\n if (fv.lowestEdited !== undefined) attrs += ` lowestEdited=\"${escapeAttr(fv.lowestEdited)}\"`;\n if (fv.rupBuild !== undefined) attrs += ` rupBuild=\"${escapeAttr(fv.rupBuild)}\"`;\n if (fv.codeName !== undefined) attrs += ` codeName=\"${escapeAttr(fv.codeName)}\"`;\n if (attrs.length === 0) return undefined;\n return `<fileVersion${attrs}/>`;\n}\n\nfunction effectiveWorkbookProperties(\n wb: Workbook,\n): import('../workbook/workbook-properties.js').WorkbookProperties | undefined {\n const explicit = wb.workbookProperties;\n if (explicit) {\n // Mirror the canonical date1904 flag if the typed model omits it.\n if (wb.date1904 && explicit.date1904 === undefined) {\n return { ...explicit, date1904: true };\n }\n return explicit;\n }\n if (wb.date1904) return { date1904: true };\n return undefined;\n}\n\nfunction serializeWorkbookProperties(\n wp: import('../workbook/workbook-properties.js').WorkbookProperties,\n): string | undefined {\n let attrs = '';\n const boolKeys: ReadonlyArray<keyof import('../workbook/workbook-properties.js').WorkbookProperties> = [\n 'date1904',\n 'dateCompatibility',\n 'showBorderUnselectedTables',\n 'filterPrivacy',\n 'promptedSolutions',\n 'showInkAnnotation',\n 'backupFile',\n 'saveExternalLinkValues',\n 'hidePivotFieldList',\n 'showPivotChartFilter',\n 'allowRefreshQuery',\n 'publishItems',\n 'checkCompatibility',\n 'autoCompressPictures',\n 'refreshAllConnections',\n ];\n for (const k of boolKeys) {\n const v = wp[k];\n if (v !== undefined) attrs += ` ${k}=\"${v ? '1' : '0'}\"`;\n }\n if (wp.showObjects !== undefined) attrs += ` showObjects=\"${wp.showObjects}\"`;\n if (wp.updateLinks !== undefined) attrs += ` updateLinks=\"${wp.updateLinks}\"`;\n if (wp.codeName !== undefined) attrs += ` codeName=\"${escapeAttr(wp.codeName)}\"`;\n if (wp.defaultThemeVersion !== undefined) attrs += ` defaultThemeVersion=\"${wp.defaultThemeVersion}\"`;\n if (attrs.length === 0) return undefined;\n return `<workbookPr${attrs}/>`;\n}\n\nfunction serializeCustomWorkbookViews(\n views: ReadonlyArray<import('../workbook/views.js').CustomWorkbookView>,\n): string {\n const parts: string[] = ['<customWorkbookViews>'];\n for (const v of views) {\n let attrs = ` name=\"${escapeAttr(v.name)}\" guid=\"${escapeAttr(v.guid)}\"`;\n if (v.autoUpdate !== undefined) attrs += ` autoUpdate=\"${v.autoUpdate ? '1' : '0'}\"`;\n if (v.mergeInterval !== undefined) attrs += ` mergeInterval=\"${v.mergeInterval}\"`;\n if (v.changesSavedWin !== undefined) attrs += ` changesSavedWin=\"${v.changesSavedWin ? '1' : '0'}\"`;\n if (v.onlySync !== undefined) attrs += ` onlySync=\"${v.onlySync ? '1' : '0'}\"`;\n if (v.personalView !== undefined) attrs += ` personalView=\"${v.personalView ? '1' : '0'}\"`;\n if (v.includePrintSettings !== undefined)\n attrs += ` includePrintSettings=\"${v.includePrintSettings ? '1' : '0'}\"`;\n if (v.includeHiddenRowCol !== undefined)\n attrs += ` includeHiddenRowCol=\"${v.includeHiddenRowCol ? '1' : '0'}\"`;\n if (v.maximized !== undefined) attrs += ` maximized=\"${v.maximized ? '1' : '0'}\"`;\n if (v.minimized !== undefined) attrs += ` minimized=\"${v.minimized ? '1' : '0'}\"`;\n if (v.showHorizontalScroll !== undefined)\n attrs += ` showHorizontalScroll=\"${v.showHorizontalScroll ? '1' : '0'}\"`;\n if (v.showVerticalScroll !== undefined)\n attrs += ` showVerticalScroll=\"${v.showVerticalScroll ? '1' : '0'}\"`;\n if (v.showSheetTabs !== undefined) attrs += ` showSheetTabs=\"${v.showSheetTabs ? '1' : '0'}\"`;\n if (v.xWindow !== undefined) attrs += ` xWindow=\"${v.xWindow}\"`;\n if (v.yWindow !== undefined) attrs += ` yWindow=\"${v.yWindow}\"`;\n attrs += ` windowWidth=\"${v.windowWidth}\" windowHeight=\"${v.windowHeight}\"`;\n if (v.tabRatio !== undefined) attrs += ` tabRatio=\"${v.tabRatio}\"`;\n attrs += ` activeSheetId=\"${v.activeSheetId}\"`;\n if (v.showFormulaBar !== undefined) attrs += ` showFormulaBar=\"${v.showFormulaBar ? '1' : '0'}\"`;\n if (v.showStatusbar !== undefined) attrs += ` showStatusbar=\"${v.showStatusbar ? '1' : '0'}\"`;\n if (v.showComments !== undefined) attrs += ` showComments=\"${v.showComments}\"`;\n if (v.showObjects !== undefined) attrs += ` showObjects=\"${v.showObjects}\"`;\n parts.push(`<customWorkbookView${attrs}/>`);\n }\n parts.push('</customWorkbookViews>');\n return parts.join('');\n}\n\nfunction serializeBookViews(\n views: ReadonlyArray<import('../workbook/views.js').WorkbookView>,\n prefixOf: Readonly<Record<string, string>>,\n): string {\n const parts: string[] = ['<bookViews>'];\n for (const v of views) {\n let attrs = '';\n if (v.visibility !== undefined) attrs += ` visibility=\"${v.visibility}\"`;\n if (v.minimized !== undefined) attrs += ` minimized=\"${v.minimized ? '1' : '0'}\"`;\n if (v.showHorizontalScroll !== undefined)\n attrs += ` showHorizontalScroll=\"${v.showHorizontalScroll ? '1' : '0'}\"`;\n if (v.showVerticalScroll !== undefined)\n attrs += ` showVerticalScroll=\"${v.showVerticalScroll ? '1' : '0'}\"`;\n if (v.showSheetTabs !== undefined) attrs += ` showSheetTabs=\"${v.showSheetTabs ? '1' : '0'}\"`;\n if (v.xWindow !== undefined) attrs += ` xWindow=\"${v.xWindow}\"`;\n if (v.yWindow !== undefined) attrs += ` yWindow=\"${v.yWindow}\"`;\n if (v.windowWidth !== undefined) attrs += ` windowWidth=\"${v.windowWidth}\"`;\n if (v.windowHeight !== undefined) attrs += ` windowHeight=\"${v.windowHeight}\"`;\n if (v.tabRatio !== undefined) attrs += ` tabRatio=\"${v.tabRatio}\"`;\n if (v.firstSheet !== undefined) attrs += ` firstSheet=\"${v.firstSheet}\"`;\n if (v.activeTab !== undefined) attrs += ` activeTab=\"${v.activeTab}\"`;\n if (v.autoFilterDateGrouping !== undefined)\n attrs += ` autoFilterDateGrouping=\"${v.autoFilterDateGrouping ? '1' : '0'}\"`;\n // Namespaced extras (`xr2:uid`) only make sense with a prefix the root\n // declares; without one the attribute would be unreadable, so skip it.\n for (const [clark, value] of Object.entries(v.extAttrs ?? {})) {\n const { ns, local } = parseQName(clark);\n const prefix = prefixOf[ns];\n if (prefix === undefined) continue;\n attrs += ` ${prefix}:${local}=\"${escapeAttr(value)}\"`;\n }\n parts.push(`<workbookView${attrs}/>`);\n }\n parts.push('</bookViews>');\n return parts.join('');\n}\n\nfunction serializeWorkbookProtection(\n wp: import('../workbook/protection.js').WorkbookProtection,\n): string | undefined {\n let attrs = '';\n const strAttrs = [\n 'workbookPassword',\n 'workbookPasswordCharacterSet',\n 'workbookAlgorithmName',\n 'workbookHashValue',\n 'workbookSaltValue',\n 'revisionsPassword',\n 'revisionsPasswordCharacterSet',\n 'revisionsAlgorithmName',\n 'revisionsHashValue',\n 'revisionsSaltValue',\n ] as const;\n for (const k of strAttrs) {\n const v = wp[k];\n if (v !== undefined) attrs += ` ${k}=\"${escapeAttr(v)}\"`;\n }\n if (wp.workbookSpinCount !== undefined) attrs += ` workbookSpinCount=\"${wp.workbookSpinCount}\"`;\n if (wp.revisionsSpinCount !== undefined) attrs += ` revisionsSpinCount=\"${wp.revisionsSpinCount}\"`;\n if (wp.lockStructure !== undefined) attrs += ` lockStructure=\"${wp.lockStructure ? '1' : '0'}\"`;\n if (wp.lockWindows !== undefined) attrs += ` lockWindows=\"${wp.lockWindows ? '1' : '0'}\"`;\n if (wp.lockRevision !== undefined) attrs += ` lockRevision=\"${wp.lockRevision ? '1' : '0'}\"`;\n if (attrs.length === 0) return undefined;\n return `<workbookProtection${attrs}/>`;\n}\n\n// Local aliases keep the dense call sites below readable. Canonical\n// implementations live in utils/escape.ts so all three writers (this file,\n// xml/serializer, xml/stream-writer) agree on how `>` / whitespace are\n// handled.\nconst escapeText = escapeXmlText;\nconst escapeAttr = escapeXmlAttr;\n\n/**\n * Serialise an XmlNode child of `<workbook>` for inline injection back into the\n * workbook XML stream. Reuses serializeXml then strips the declaration.\n * Captured nodes carry Clark-notation names, so `inScopePrefixes` is what keeps\n * a child on the prefix the root declared for it instead of a freshly\n * allocated one — which matters when `mc:Ignorable` names those prefixes.\n */\nfunction serializeChildNode(\n node: import('../xml/tree.js').XmlNode,\n inScopePrefixes?: Readonly<Record<string, string>>,\n): string {\n const bytes = serializeXmlNode(node, {\n xmlDeclaration: false,\n ...(inScopePrefixes ? { inScopePrefixes } : {}),\n });\n return new TextDecoder().decode(bytes);\n}\n"],"mappings":"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"}
@@ -122,7 +122,7 @@ export interface Schema<T> {
122
122
  /** Child elements in declaration / emission order. */
123
123
  elements: ReadonlyArray<ElementDef>;
124
124
  /** Hook to normalise the parsed object (e.g., infer derived fields). */
125
- postParse?: (value: T, node: import('../xml/tree').XmlNode) => T;
125
+ postParse?: (value: T, node: import('../xml/tree.js').XmlNode) => T;
126
126
  /** Hook to normalise the value before serialisation. */
127
127
  preSerialize?: (value: T) => T;
128
128
  }