@cj-tech-master/excelts 9.2.1 → 9.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (383) hide show
  1. package/README.md +25 -2
  2. package/README_zh.md +29 -6
  3. package/dist/browser/index.browser.d.ts +1 -1
  4. package/dist/browser/index.browser.js +4 -0
  5. package/dist/browser/index.d.ts +1 -1
  6. package/dist/browser/index.js +4 -0
  7. package/dist/browser/modules/excel/cell.d.ts +17 -3
  8. package/dist/browser/modules/excel/cell.js +170 -22
  9. package/dist/browser/modules/excel/defined-names.d.ts +96 -1
  10. package/dist/browser/modules/excel/defined-names.js +411 -21
  11. package/dist/browser/modules/excel/image.d.ts +11 -0
  12. package/dist/browser/modules/excel/image.js +24 -1
  13. package/dist/browser/modules/excel/stream/workbook-reader.browser.d.ts +9 -3
  14. package/dist/browser/modules/excel/stream/workbook-reader.browser.js +14 -0
  15. package/dist/browser/modules/excel/stream/workbook-reader.d.ts +2 -1
  16. package/dist/browser/modules/excel/stream/workbook-writer.browser.d.ts +39 -5
  17. package/dist/browser/modules/excel/stream/workbook-writer.browser.js +48 -1
  18. package/dist/browser/modules/excel/stream/workbook-writer.d.ts +3 -2
  19. package/dist/browser/modules/excel/stream/worksheet-reader.js +17 -1
  20. package/dist/browser/modules/excel/stream/worksheet-writer.d.ts +39 -6
  21. package/dist/browser/modules/excel/stream/worksheet-writer.js +45 -5
  22. package/dist/browser/modules/excel/table.js +15 -2
  23. package/dist/browser/modules/excel/types.d.ts +133 -2
  24. package/dist/browser/modules/excel/utils/col-cache.d.ts +1 -0
  25. package/dist/browser/modules/excel/utils/col-cache.js +15 -0
  26. package/dist/browser/modules/excel/utils/drawing-utils.d.ts +3 -3
  27. package/dist/browser/modules/excel/utils/drawing-utils.js +4 -0
  28. package/dist/browser/modules/excel/utils/external-link-formula.d.ts +76 -0
  29. package/dist/browser/modules/excel/utils/external-link-formula.js +208 -0
  30. package/dist/browser/modules/excel/utils/iterate-stream.d.ts +9 -3
  31. package/dist/browser/modules/excel/utils/iterate-stream.js +3 -1
  32. package/dist/browser/modules/excel/utils/ooxml-paths.d.ts +19 -0
  33. package/dist/browser/modules/excel/utils/ooxml-paths.js +37 -2
  34. package/dist/browser/modules/excel/utils/shared-strings.d.ts +8 -3
  35. package/dist/browser/modules/excel/utils/shared-strings.js +21 -2
  36. package/dist/browser/modules/excel/utils/workbook-protection.d.ts +30 -0
  37. package/dist/browser/modules/excel/utils/workbook-protection.js +30 -0
  38. package/dist/browser/modules/excel/workbook.browser.d.ts +257 -6
  39. package/dist/browser/modules/excel/workbook.browser.js +318 -34
  40. package/dist/browser/modules/excel/workbook.d.ts +1 -1
  41. package/dist/browser/modules/excel/worksheet.d.ts +3 -1
  42. package/dist/browser/modules/excel/worksheet.js +21 -2
  43. package/dist/browser/modules/excel/xlsx/rel-type.d.ts +15 -0
  44. package/dist/browser/modules/excel/xlsx/rel-type.js +16 -1
  45. package/dist/browser/modules/excel/xlsx/xform/book/defined-name-xform.d.ts +6 -5
  46. package/dist/browser/modules/excel/xlsx/xform/book/defined-name-xform.js +21 -86
  47. package/dist/browser/modules/excel/xlsx/xform/book/external-link-xform.d.ts +84 -0
  48. package/dist/browser/modules/excel/xlsx/xform/book/external-link-xform.js +330 -0
  49. package/dist/browser/modules/excel/xlsx/xform/book/external-reference-xform.d.ts +17 -0
  50. package/dist/browser/modules/excel/xlsx/xform/book/external-reference-xform.js +24 -0
  51. package/dist/browser/modules/excel/xlsx/xform/book/workbook-calc-properties-xform.d.ts +3 -0
  52. package/dist/browser/modules/excel/xlsx/xform/book/workbook-calc-properties-xform.js +11 -2
  53. package/dist/browser/modules/excel/xlsx/xform/book/workbook-protection-xform.d.ts +20 -0
  54. package/dist/browser/modules/excel/xlsx/xform/book/workbook-protection-xform.js +66 -0
  55. package/dist/browser/modules/excel/xlsx/xform/book/workbook-xform.js +38 -5
  56. package/dist/browser/modules/excel/xlsx/xform/core/content-types-xform.js +19 -1
  57. package/dist/browser/modules/excel/xlsx/xform/core/metadata-xform.d.ts +56 -0
  58. package/dist/browser/modules/excel/xlsx/xform/core/metadata-xform.js +158 -0
  59. package/dist/browser/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.d.ts +26 -0
  60. package/dist/browser/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +105 -0
  61. package/dist/browser/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +3 -0
  62. package/dist/browser/modules/excel/xlsx/xform/drawing/drawing-xform.js +10 -2
  63. package/dist/browser/modules/excel/xlsx/xform/sheet/cell-xform.d.ts +1 -1
  64. package/dist/browser/modules/excel/xlsx/xform/sheet/cell-xform.js +166 -8
  65. package/dist/browser/modules/excel/xlsx/xform/sheet/data-validations-xform.js +1 -1
  66. package/dist/browser/modules/excel/xlsx/xform/sheet/ignored-errors-xform.d.ts +21 -0
  67. package/dist/browser/modules/excel/xlsx/xform/sheet/ignored-errors-xform.js +80 -0
  68. package/dist/browser/modules/excel/xlsx/xform/sheet/worksheet-xform.js +9 -4
  69. package/dist/browser/modules/excel/xlsx/xform/style/border-xform.js +4 -1
  70. package/dist/browser/modules/excel/xlsx/xlsx.browser.d.ts +172 -13
  71. package/dist/browser/modules/excel/xlsx/xlsx.browser.js +410 -20
  72. package/dist/browser/modules/excel/xlsx/xlsx.d.ts +7 -4
  73. package/dist/browser/modules/excel/xlsx/xlsx.js +4 -5
  74. package/dist/browser/modules/formula/compile/address-utils.d.ts +62 -0
  75. package/dist/browser/modules/formula/compile/address-utils.js +83 -0
  76. package/dist/browser/modules/formula/compile/binder.d.ts +42 -0
  77. package/dist/browser/modules/formula/compile/binder.js +487 -0
  78. package/dist/browser/modules/formula/compile/bound-ast.d.ts +230 -0
  79. package/dist/browser/modules/formula/compile/bound-ast.js +80 -0
  80. package/dist/browser/modules/formula/compile/compiled-formula.d.ts +137 -0
  81. package/dist/browser/modules/formula/compile/compiled-formula.js +383 -0
  82. package/dist/browser/modules/formula/compile/dependency-analysis.d.ts +93 -0
  83. package/dist/browser/modules/formula/compile/dependency-analysis.js +432 -0
  84. package/dist/browser/modules/formula/compile/structured-ref-utils.d.ts +93 -0
  85. package/dist/browser/modules/formula/compile/structured-ref-utils.js +136 -0
  86. package/dist/browser/modules/formula/default-syntax-probe.d.ts +79 -0
  87. package/dist/browser/modules/formula/default-syntax-probe.js +83 -0
  88. package/dist/browser/modules/formula/functions/_date-context.d.ts +4 -0
  89. package/dist/browser/modules/formula/functions/_date-context.js +29 -0
  90. package/dist/browser/modules/formula/functions/_shared.d.ts +121 -0
  91. package/dist/browser/modules/formula/functions/_shared.js +381 -0
  92. package/dist/browser/modules/formula/functions/conditional.d.ts +27 -0
  93. package/dist/browser/modules/formula/functions/conditional.js +343 -0
  94. package/dist/browser/modules/formula/functions/database.d.ts +37 -0
  95. package/dist/browser/modules/formula/functions/database.js +274 -0
  96. package/dist/browser/modules/formula/functions/date.d.ts +61 -0
  97. package/dist/browser/modules/formula/functions/date.js +855 -0
  98. package/dist/browser/modules/formula/functions/dynamic-array.d.ts +23 -0
  99. package/dist/browser/modules/formula/functions/dynamic-array.js +860 -0
  100. package/dist/browser/modules/formula/functions/engineering.d.ts +57 -0
  101. package/dist/browser/modules/formula/functions/engineering.js +1128 -0
  102. package/dist/browser/modules/formula/functions/financial.d.ts +202 -0
  103. package/dist/browser/modules/formula/functions/financial.js +2296 -0
  104. package/dist/browser/modules/formula/functions/lookup.d.ts +18 -0
  105. package/dist/browser/modules/formula/functions/lookup.js +886 -0
  106. package/dist/browser/modules/formula/functions/math.d.ts +114 -0
  107. package/dist/browser/modules/formula/functions/math.js +1406 -0
  108. package/dist/browser/modules/formula/functions/statistical.d.ts +193 -0
  109. package/dist/browser/modules/formula/functions/statistical.js +3390 -0
  110. package/dist/browser/modules/formula/functions/text.d.ts +86 -0
  111. package/dist/browser/modules/formula/functions/text.js +1845 -0
  112. package/dist/browser/modules/formula/host-registry.d.ts +53 -0
  113. package/dist/browser/modules/formula/host-registry.js +69 -0
  114. package/dist/browser/modules/formula/index.d.ts +39 -0
  115. package/dist/browser/modules/formula/index.js +49 -0
  116. package/dist/browser/modules/formula/install.d.ts +62 -0
  117. package/dist/browser/modules/formula/install.js +88 -0
  118. package/dist/browser/modules/formula/integration/apply-writeback-plan.d.ts +26 -0
  119. package/dist/browser/modules/formula/integration/apply-writeback-plan.js +210 -0
  120. package/dist/browser/modules/formula/integration/calculate-formulas-impl.d.ts +30 -0
  121. package/dist/browser/modules/formula/integration/calculate-formulas-impl.js +616 -0
  122. package/dist/browser/modules/formula/integration/calculate-formulas.d.ts +67 -0
  123. package/dist/browser/modules/formula/integration/calculate-formulas.js +68 -0
  124. package/dist/browser/modules/formula/integration/formula-instance.d.ts +64 -0
  125. package/dist/browser/modules/formula/integration/formula-instance.js +79 -0
  126. package/dist/browser/modules/formula/integration/workbook-adapter.d.ts +26 -0
  127. package/dist/browser/modules/formula/integration/workbook-adapter.js +324 -0
  128. package/dist/browser/modules/formula/integration/workbook-snapshot.d.ts +267 -0
  129. package/dist/browser/modules/formula/integration/workbook-snapshot.js +77 -0
  130. package/dist/browser/modules/formula/materialize/build-writeback-plan.d.ts +34 -0
  131. package/dist/browser/modules/formula/materialize/build-writeback-plan.js +473 -0
  132. package/dist/browser/modules/formula/materialize/spill-engine.d.ts +9 -0
  133. package/dist/browser/modules/formula/materialize/spill-engine.js +38 -0
  134. package/dist/browser/modules/formula/materialize/types.d.ts +179 -0
  135. package/dist/browser/modules/formula/materialize/types.js +29 -0
  136. package/dist/browser/modules/formula/materialize/writeback-plan.d.ts +167 -0
  137. package/dist/browser/modules/formula/materialize/writeback-plan.js +27 -0
  138. package/dist/browser/modules/formula/runtime/evaluator.d.ts +151 -0
  139. package/dist/browser/modules/formula/runtime/evaluator.js +2291 -0
  140. package/dist/browser/modules/formula/runtime/function-registry.d.ts +47 -0
  141. package/dist/browser/modules/formula/runtime/function-registry.js +840 -0
  142. package/dist/browser/modules/formula/runtime/values.d.ts +211 -0
  143. package/dist/browser/modules/formula/runtime/values.js +385 -0
  144. package/dist/browser/modules/formula/syntax/ast.d.ts +129 -0
  145. package/dist/browser/modules/formula/syntax/ast.js +28 -0
  146. package/dist/browser/modules/formula/syntax/parser.d.ts +18 -0
  147. package/dist/browser/modules/formula/syntax/parser.js +439 -0
  148. package/dist/browser/modules/formula/syntax/token-types.d.ts +153 -0
  149. package/dist/browser/modules/formula/syntax/token-types.js +59 -0
  150. package/dist/browser/modules/formula/syntax/tokenizer.d.ts +10 -0
  151. package/dist/browser/modules/formula/syntax/tokenizer.js +1074 -0
  152. package/dist/browser/modules/pdf/excel-bridge.js +9 -0
  153. package/dist/cjs/index.js +4 -0
  154. package/dist/cjs/modules/excel/cell.js +170 -22
  155. package/dist/cjs/modules/excel/defined-names.js +411 -21
  156. package/dist/cjs/modules/excel/image.js +24 -1
  157. package/dist/cjs/modules/excel/stream/workbook-reader.browser.js +14 -0
  158. package/dist/cjs/modules/excel/stream/workbook-writer.browser.js +48 -1
  159. package/dist/cjs/modules/excel/stream/worksheet-reader.js +17 -1
  160. package/dist/cjs/modules/excel/stream/worksheet-writer.js +45 -5
  161. package/dist/cjs/modules/excel/table.js +15 -2
  162. package/dist/cjs/modules/excel/utils/col-cache.js +15 -0
  163. package/dist/cjs/modules/excel/utils/drawing-utils.js +4 -0
  164. package/dist/cjs/modules/excel/utils/external-link-formula.js +212 -0
  165. package/dist/cjs/modules/excel/utils/iterate-stream.js +3 -1
  166. package/dist/cjs/modules/excel/utils/ooxml-paths.js +42 -2
  167. package/dist/cjs/modules/excel/utils/shared-strings.js +21 -2
  168. package/dist/cjs/modules/excel/utils/workbook-protection.js +33 -0
  169. package/dist/cjs/modules/excel/workbook.browser.js +318 -34
  170. package/dist/cjs/modules/excel/worksheet.js +20 -1
  171. package/dist/cjs/modules/excel/xlsx/rel-type.js +16 -1
  172. package/dist/cjs/modules/excel/xlsx/xform/book/defined-name-xform.js +21 -86
  173. package/dist/cjs/modules/excel/xlsx/xform/book/external-link-xform.js +333 -0
  174. package/dist/cjs/modules/excel/xlsx/xform/book/external-reference-xform.js +27 -0
  175. package/dist/cjs/modules/excel/xlsx/xform/book/workbook-calc-properties-xform.js +11 -2
  176. package/dist/cjs/modules/excel/xlsx/xform/book/workbook-protection-xform.js +69 -0
  177. package/dist/cjs/modules/excel/xlsx/xform/book/workbook-xform.js +38 -5
  178. package/dist/cjs/modules/excel/xlsx/xform/core/content-types-xform.js +18 -0
  179. package/dist/cjs/modules/excel/xlsx/xform/core/metadata-xform.js +161 -0
  180. package/dist/cjs/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +108 -0
  181. package/dist/cjs/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +3 -0
  182. package/dist/cjs/modules/excel/xlsx/xform/drawing/drawing-xform.js +10 -2
  183. package/dist/cjs/modules/excel/xlsx/xform/sheet/cell-xform.js +166 -8
  184. package/dist/cjs/modules/excel/xlsx/xform/sheet/data-validations-xform.js +1 -1
  185. package/dist/cjs/modules/excel/xlsx/xform/sheet/ignored-errors-xform.js +83 -0
  186. package/dist/cjs/modules/excel/xlsx/xform/sheet/worksheet-xform.js +9 -4
  187. package/dist/cjs/modules/excel/xlsx/xform/style/border-xform.js +4 -1
  188. package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +408 -18
  189. package/dist/cjs/modules/excel/xlsx/xlsx.js +4 -5
  190. package/dist/cjs/modules/formula/compile/address-utils.js +89 -0
  191. package/dist/cjs/modules/formula/compile/binder.js +489 -0
  192. package/dist/cjs/modules/formula/compile/bound-ast.js +68 -0
  193. package/dist/cjs/modules/formula/compile/compiled-formula.js +387 -0
  194. package/dist/cjs/modules/formula/compile/dependency-analysis.js +437 -0
  195. package/dist/cjs/modules/formula/compile/structured-ref-utils.js +141 -0
  196. package/dist/cjs/modules/formula/default-syntax-probe.js +87 -0
  197. package/dist/cjs/modules/formula/functions/_date-context.js +33 -0
  198. package/dist/cjs/modules/formula/functions/_shared.js +396 -0
  199. package/dist/cjs/modules/formula/functions/conditional.js +354 -0
  200. package/dist/cjs/modules/formula/functions/database.js +288 -0
  201. package/dist/cjs/modules/formula/functions/date.js +883 -0
  202. package/dist/cjs/modules/formula/functions/dynamic-array.js +881 -0
  203. package/dist/cjs/modules/formula/functions/engineering.js +1183 -0
  204. package/dist/cjs/modules/formula/functions/financial.js +2348 -0
  205. package/dist/cjs/modules/formula/functions/lookup.js +902 -0
  206. package/dist/cjs/modules/formula/functions/math.js +1487 -0
  207. package/dist/cjs/modules/formula/functions/statistical.js +3488 -0
  208. package/dist/cjs/modules/formula/functions/text.js +1889 -0
  209. package/dist/cjs/modules/formula/host-registry.js +75 -0
  210. package/dist/cjs/modules/formula/index.js +58 -0
  211. package/dist/cjs/modules/formula/install.js +93 -0
  212. package/dist/cjs/modules/formula/integration/apply-writeback-plan.js +213 -0
  213. package/dist/cjs/modules/formula/integration/calculate-formulas-impl.js +619 -0
  214. package/dist/cjs/modules/formula/integration/calculate-formulas.js +71 -0
  215. package/dist/cjs/modules/formula/integration/formula-instance.js +82 -0
  216. package/dist/cjs/modules/formula/integration/workbook-adapter.js +327 -0
  217. package/dist/cjs/modules/formula/integration/workbook-snapshot.js +84 -0
  218. package/dist/cjs/modules/formula/materialize/build-writeback-plan.js +475 -0
  219. package/dist/cjs/modules/formula/materialize/spill-engine.js +42 -0
  220. package/dist/cjs/modules/formula/materialize/types.js +32 -0
  221. package/dist/cjs/modules/formula/materialize/writeback-plan.js +28 -0
  222. package/dist/cjs/modules/formula/runtime/evaluator.js +2298 -0
  223. package/dist/cjs/modules/formula/runtime/function-registry.js +846 -0
  224. package/dist/cjs/modules/formula/runtime/values.js +385 -0
  225. package/dist/cjs/modules/formula/syntax/ast.js +8 -0
  226. package/dist/cjs/modules/formula/syntax/parser.js +440 -0
  227. package/dist/cjs/modules/formula/syntax/token-types.js +32 -0
  228. package/dist/cjs/modules/formula/syntax/tokenizer.js +1076 -0
  229. package/dist/cjs/modules/pdf/excel-bridge.js +9 -0
  230. package/dist/esm/index.browser.js +4 -0
  231. package/dist/esm/index.js +4 -0
  232. package/dist/esm/modules/excel/cell.js +170 -22
  233. package/dist/esm/modules/excel/defined-names.js +411 -21
  234. package/dist/esm/modules/excel/image.js +24 -1
  235. package/dist/esm/modules/excel/stream/workbook-reader.browser.js +14 -0
  236. package/dist/esm/modules/excel/stream/workbook-writer.browser.js +48 -1
  237. package/dist/esm/modules/excel/stream/worksheet-reader.js +17 -1
  238. package/dist/esm/modules/excel/stream/worksheet-writer.js +45 -5
  239. package/dist/esm/modules/excel/table.js +15 -2
  240. package/dist/esm/modules/excel/utils/col-cache.js +15 -0
  241. package/dist/esm/modules/excel/utils/drawing-utils.js +4 -0
  242. package/dist/esm/modules/excel/utils/external-link-formula.js +208 -0
  243. package/dist/esm/modules/excel/utils/iterate-stream.js +3 -1
  244. package/dist/esm/modules/excel/utils/ooxml-paths.js +37 -2
  245. package/dist/esm/modules/excel/utils/shared-strings.js +21 -2
  246. package/dist/esm/modules/excel/utils/workbook-protection.js +30 -0
  247. package/dist/esm/modules/excel/workbook.browser.js +318 -34
  248. package/dist/esm/modules/excel/worksheet.js +21 -2
  249. package/dist/esm/modules/excel/xlsx/rel-type.js +16 -1
  250. package/dist/esm/modules/excel/xlsx/xform/book/defined-name-xform.js +21 -86
  251. package/dist/esm/modules/excel/xlsx/xform/book/external-link-xform.js +330 -0
  252. package/dist/esm/modules/excel/xlsx/xform/book/external-reference-xform.js +24 -0
  253. package/dist/esm/modules/excel/xlsx/xform/book/workbook-calc-properties-xform.js +11 -2
  254. package/dist/esm/modules/excel/xlsx/xform/book/workbook-protection-xform.js +66 -0
  255. package/dist/esm/modules/excel/xlsx/xform/book/workbook-xform.js +38 -5
  256. package/dist/esm/modules/excel/xlsx/xform/core/content-types-xform.js +19 -1
  257. package/dist/esm/modules/excel/xlsx/xform/core/metadata-xform.js +158 -0
  258. package/dist/esm/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +105 -0
  259. package/dist/esm/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +3 -0
  260. package/dist/esm/modules/excel/xlsx/xform/drawing/drawing-xform.js +10 -2
  261. package/dist/esm/modules/excel/xlsx/xform/sheet/cell-xform.js +166 -8
  262. package/dist/esm/modules/excel/xlsx/xform/sheet/data-validations-xform.js +1 -1
  263. package/dist/esm/modules/excel/xlsx/xform/sheet/ignored-errors-xform.js +80 -0
  264. package/dist/esm/modules/excel/xlsx/xform/sheet/worksheet-xform.js +9 -4
  265. package/dist/esm/modules/excel/xlsx/xform/style/border-xform.js +4 -1
  266. package/dist/esm/modules/excel/xlsx/xlsx.browser.js +410 -20
  267. package/dist/esm/modules/excel/xlsx/xlsx.js +4 -5
  268. package/dist/esm/modules/formula/compile/address-utils.js +83 -0
  269. package/dist/esm/modules/formula/compile/binder.js +487 -0
  270. package/dist/esm/modules/formula/compile/bound-ast.js +80 -0
  271. package/dist/esm/modules/formula/compile/compiled-formula.js +383 -0
  272. package/dist/esm/modules/formula/compile/dependency-analysis.js +432 -0
  273. package/dist/esm/modules/formula/compile/structured-ref-utils.js +136 -0
  274. package/dist/esm/modules/formula/default-syntax-probe.js +83 -0
  275. package/dist/esm/modules/formula/functions/_date-context.js +29 -0
  276. package/dist/esm/modules/formula/functions/_shared.js +381 -0
  277. package/dist/esm/modules/formula/functions/conditional.js +343 -0
  278. package/dist/esm/modules/formula/functions/database.js +274 -0
  279. package/dist/esm/modules/formula/functions/date.js +855 -0
  280. package/dist/esm/modules/formula/functions/dynamic-array.js +860 -0
  281. package/dist/esm/modules/formula/functions/engineering.js +1128 -0
  282. package/dist/esm/modules/formula/functions/financial.js +2296 -0
  283. package/dist/esm/modules/formula/functions/lookup.js +886 -0
  284. package/dist/esm/modules/formula/functions/math.js +1406 -0
  285. package/dist/esm/modules/formula/functions/statistical.js +3390 -0
  286. package/dist/esm/modules/formula/functions/text.js +1845 -0
  287. package/dist/esm/modules/formula/host-registry.js +69 -0
  288. package/dist/esm/modules/formula/index.js +49 -0
  289. package/dist/esm/modules/formula/install.js +88 -0
  290. package/dist/esm/modules/formula/integration/apply-writeback-plan.js +210 -0
  291. package/dist/esm/modules/formula/integration/calculate-formulas-impl.js +616 -0
  292. package/dist/esm/modules/formula/integration/calculate-formulas.js +68 -0
  293. package/dist/esm/modules/formula/integration/formula-instance.js +79 -0
  294. package/dist/esm/modules/formula/integration/workbook-adapter.js +324 -0
  295. package/dist/esm/modules/formula/integration/workbook-snapshot.js +77 -0
  296. package/dist/esm/modules/formula/materialize/build-writeback-plan.js +473 -0
  297. package/dist/esm/modules/formula/materialize/spill-engine.js +38 -0
  298. package/dist/esm/modules/formula/materialize/types.js +29 -0
  299. package/dist/esm/modules/formula/materialize/writeback-plan.js +27 -0
  300. package/dist/esm/modules/formula/runtime/evaluator.js +2291 -0
  301. package/dist/esm/modules/formula/runtime/function-registry.js +840 -0
  302. package/dist/esm/modules/formula/runtime/values.js +385 -0
  303. package/dist/esm/modules/formula/syntax/ast.js +28 -0
  304. package/dist/esm/modules/formula/syntax/parser.js +439 -0
  305. package/dist/esm/modules/formula/syntax/token-types.js +59 -0
  306. package/dist/esm/modules/formula/syntax/tokenizer.js +1074 -0
  307. package/dist/esm/modules/pdf/excel-bridge.js +9 -0
  308. package/dist/iife/excelts.iife.js +2302 -373
  309. package/dist/iife/excelts.iife.js.map +1 -1
  310. package/dist/iife/excelts.iife.min.js +34 -34
  311. package/dist/types/index.browser.d.ts +1 -1
  312. package/dist/types/index.d.ts +1 -1
  313. package/dist/types/modules/excel/cell.d.ts +17 -3
  314. package/dist/types/modules/excel/defined-names.d.ts +96 -1
  315. package/dist/types/modules/excel/image.d.ts +11 -0
  316. package/dist/types/modules/excel/stream/workbook-reader.browser.d.ts +9 -3
  317. package/dist/types/modules/excel/stream/workbook-reader.d.ts +2 -1
  318. package/dist/types/modules/excel/stream/workbook-writer.browser.d.ts +39 -5
  319. package/dist/types/modules/excel/stream/workbook-writer.d.ts +3 -2
  320. package/dist/types/modules/excel/stream/worksheet-writer.d.ts +39 -6
  321. package/dist/types/modules/excel/types.d.ts +133 -2
  322. package/dist/types/modules/excel/utils/col-cache.d.ts +1 -0
  323. package/dist/types/modules/excel/utils/drawing-utils.d.ts +3 -3
  324. package/dist/types/modules/excel/utils/external-link-formula.d.ts +76 -0
  325. package/dist/types/modules/excel/utils/iterate-stream.d.ts +9 -3
  326. package/dist/types/modules/excel/utils/ooxml-paths.d.ts +19 -0
  327. package/dist/types/modules/excel/utils/shared-strings.d.ts +8 -3
  328. package/dist/types/modules/excel/utils/workbook-protection.d.ts +30 -0
  329. package/dist/types/modules/excel/workbook.browser.d.ts +257 -6
  330. package/dist/types/modules/excel/workbook.d.ts +1 -1
  331. package/dist/types/modules/excel/worksheet.d.ts +3 -1
  332. package/dist/types/modules/excel/xlsx/rel-type.d.ts +15 -0
  333. package/dist/types/modules/excel/xlsx/xform/book/defined-name-xform.d.ts +6 -5
  334. package/dist/types/modules/excel/xlsx/xform/book/external-link-xform.d.ts +84 -0
  335. package/dist/types/modules/excel/xlsx/xform/book/external-reference-xform.d.ts +17 -0
  336. package/dist/types/modules/excel/xlsx/xform/book/workbook-calc-properties-xform.d.ts +3 -0
  337. package/dist/types/modules/excel/xlsx/xform/book/workbook-protection-xform.d.ts +20 -0
  338. package/dist/types/modules/excel/xlsx/xform/core/metadata-xform.d.ts +56 -0
  339. package/dist/types/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.d.ts +26 -0
  340. package/dist/types/modules/excel/xlsx/xform/sheet/cell-xform.d.ts +1 -1
  341. package/dist/types/modules/excel/xlsx/xform/sheet/ignored-errors-xform.d.ts +21 -0
  342. package/dist/types/modules/excel/xlsx/xlsx.browser.d.ts +172 -13
  343. package/dist/types/modules/excel/xlsx/xlsx.d.ts +7 -4
  344. package/dist/types/modules/formula/compile/address-utils.d.ts +62 -0
  345. package/dist/types/modules/formula/compile/binder.d.ts +42 -0
  346. package/dist/types/modules/formula/compile/bound-ast.d.ts +230 -0
  347. package/dist/types/modules/formula/compile/compiled-formula.d.ts +137 -0
  348. package/dist/types/modules/formula/compile/dependency-analysis.d.ts +93 -0
  349. package/dist/types/modules/formula/compile/structured-ref-utils.d.ts +93 -0
  350. package/dist/types/modules/formula/default-syntax-probe.d.ts +79 -0
  351. package/dist/types/modules/formula/functions/_date-context.d.ts +4 -0
  352. package/dist/types/modules/formula/functions/_shared.d.ts +121 -0
  353. package/dist/types/modules/formula/functions/conditional.d.ts +27 -0
  354. package/dist/types/modules/formula/functions/database.d.ts +37 -0
  355. package/dist/types/modules/formula/functions/date.d.ts +61 -0
  356. package/dist/types/modules/formula/functions/dynamic-array.d.ts +23 -0
  357. package/dist/types/modules/formula/functions/engineering.d.ts +57 -0
  358. package/dist/types/modules/formula/functions/financial.d.ts +202 -0
  359. package/dist/types/modules/formula/functions/lookup.d.ts +18 -0
  360. package/dist/types/modules/formula/functions/math.d.ts +114 -0
  361. package/dist/types/modules/formula/functions/statistical.d.ts +193 -0
  362. package/dist/types/modules/formula/functions/text.d.ts +86 -0
  363. package/dist/types/modules/formula/host-registry.d.ts +53 -0
  364. package/dist/types/modules/formula/index.d.ts +39 -0
  365. package/dist/types/modules/formula/install.d.ts +62 -0
  366. package/dist/types/modules/formula/integration/apply-writeback-plan.d.ts +26 -0
  367. package/dist/types/modules/formula/integration/calculate-formulas-impl.d.ts +30 -0
  368. package/dist/types/modules/formula/integration/calculate-formulas.d.ts +67 -0
  369. package/dist/types/modules/formula/integration/formula-instance.d.ts +64 -0
  370. package/dist/types/modules/formula/integration/workbook-adapter.d.ts +26 -0
  371. package/dist/types/modules/formula/integration/workbook-snapshot.d.ts +267 -0
  372. package/dist/types/modules/formula/materialize/build-writeback-plan.d.ts +34 -0
  373. package/dist/types/modules/formula/materialize/spill-engine.d.ts +9 -0
  374. package/dist/types/modules/formula/materialize/types.d.ts +179 -0
  375. package/dist/types/modules/formula/materialize/writeback-plan.d.ts +167 -0
  376. package/dist/types/modules/formula/runtime/evaluator.d.ts +151 -0
  377. package/dist/types/modules/formula/runtime/function-registry.d.ts +47 -0
  378. package/dist/types/modules/formula/runtime/values.d.ts +211 -0
  379. package/dist/types/modules/formula/syntax/ast.d.ts +129 -0
  380. package/dist/types/modules/formula/syntax/parser.d.ts +18 -0
  381. package/dist/types/modules/formula/syntax/token-types.d.ts +153 -0
  382. package/dist/types/modules/formula/syntax/tokenizer.d.ts +10 -0
  383. package/package.json +28 -28
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @cj-tech-master/excelts v9.2.1
2
+ * @cj-tech-master/excelts v9.3.0
3
3
  * Zero-dependency TypeScript toolkit — Excel (XLSX), PDF, CSV, Markdown, XML, ZIP/TAR, and streaming.
4
4
  * (c) 2026 cjnoname
5
5
  * Released under the MIT License
@@ -7364,7 +7364,8 @@ var ExcelTS = (function(exports) {
7364
7364
  this._isSubclassTransform = false;
7365
7365
  return false;
7366
7366
  }
7367
- this._isSubclassTransform = Object.getPrototypeOf(this)._transform !== Transform.prototype._transform;
7367
+ const proto = Object.getPrototypeOf(this);
7368
+ this._isSubclassTransform = proto._transform !== Transform.prototype._transform;
7368
7369
  return this._isSubclassTransform;
7369
7370
  }
7370
7371
  _hasSubclassFlush() {
@@ -7739,34 +7740,36 @@ var ExcelTS = (function(exports) {
7739
7740
  _getWebStream() {
7740
7741
  if (this._webStream) return this._webStream;
7741
7742
  const iterator = this[Symbol.asyncIterator]();
7742
- this._webStream = {
7743
- readable: new ReadableStream({
7744
- pull: async (controller) => {
7745
- const { done, value } = await iterator.next();
7746
- if (done) {
7747
- controller.close();
7748
- return;
7749
- }
7750
- controller.enqueue(value);
7751
- },
7752
- cancel: (reason) => {
7753
- this.destroy(reason instanceof Error ? reason : new Error(String(reason)));
7743
+ const readable = new ReadableStream({
7744
+ pull: async (controller) => {
7745
+ const { done, value } = await iterator.next();
7746
+ if (done) {
7747
+ controller.close();
7748
+ return;
7754
7749
  }
7750
+ controller.enqueue(value);
7751
+ },
7752
+ cancel: (reason) => {
7753
+ this.destroy(reason instanceof Error ? reason : new Error(String(reason)));
7754
+ }
7755
+ });
7756
+ const writable = new WritableStream({
7757
+ write: (chunk) => new Promise((resolve, reject) => {
7758
+ this.write(chunk, (err) => {
7759
+ if (err) reject(err);
7760
+ else resolve();
7761
+ });
7755
7762
  }),
7756
- writable: new WritableStream({
7757
- write: (chunk) => new Promise((resolve, reject) => {
7758
- this.write(chunk, (err) => {
7759
- if (err) reject(err);
7760
- else resolve();
7761
- });
7762
- }),
7763
- close: () => new Promise((resolve) => {
7764
- this.end(() => resolve());
7765
- }),
7766
- abort: (reason) => {
7767
- this.destroy(reason instanceof Error ? reason : new Error(String(reason)));
7768
- }
7769
- })
7763
+ close: () => new Promise((resolve) => {
7764
+ this.end(() => resolve());
7765
+ }),
7766
+ abort: (reason) => {
7767
+ this.destroy(reason instanceof Error ? reason : new Error(String(reason)));
7768
+ }
7769
+ });
7770
+ this._webStream = {
7771
+ readable,
7772
+ writable
7770
7773
  };
7771
7774
  return this._webStream;
7772
7775
  }
@@ -8461,7 +8464,8 @@ var ExcelTS = (function(exports) {
8461
8464
  this.options = options;
8462
8465
  this.chunkSize = options.chunkSize ?? 1e3;
8463
8466
  this.decoder = new TextDecoder(options.encoding || "utf-8");
8464
- this.autoDetectDelimiter = (options.delimiter ?? ",") === "";
8467
+ const delimiterOption = options.delimiter ?? ",";
8468
+ this.autoDetectDelimiter = delimiterOption === "";
8465
8469
  const { config } = createParseConfig({ options });
8466
8470
  this.parseConfig = config;
8467
8471
  this.parseState = createParseState(config);
@@ -9611,6 +9615,11 @@ var ExcelTS = (function(exports) {
9611
9615
  default: throw new InvalidAddressError(String(args.length), "Can only encode with 2 or 4 arguments");
9612
9616
  }
9613
9617
  },
9618
+ compareAddress(a, b) {
9619
+ const addrA = colCache.decodeAddress(a);
9620
+ const addrB = colCache.decodeAddress(b);
9621
+ return addrA.col - addrB.col || addrA.row - addrB.row;
9622
+ },
9614
9623
  inRange(range, address) {
9615
9624
  const [left, top, , right, bottom] = range;
9616
9625
  const [col, row] = address;
@@ -9937,11 +9946,179 @@ var ExcelTS = (function(exports) {
9937
9946
  }
9938
9947
  };
9939
9948
  //#endregion
9949
+ //#region src/modules/formula/default-syntax-probe.ts
9950
+ let defaultProbe = null;
9951
+ /**
9952
+ * Retrieve the currently-installed default probe, or `null` if none is
9953
+ * installed. Consumers (chiefly `DefinedNames`) should treat `null` as
9954
+ * a signal to use conservative (opaque) classification rather than
9955
+ * guessing.
9956
+ */
9957
+ function getDefaultSyntaxProbe() {
9958
+ return defaultProbe;
9959
+ }
9960
+ //#endregion
9940
9961
  //#region src/modules/excel/defined-names.ts
9941
9962
  const rangeRegexp = /[$](\w+)[$](\d+)(:[$](\w+)[$](\d+))?/;
9963
+ const cellRangeRegexp = /^[$]?[A-Za-z]{1,3}[$]?\d+(:[$]?[A-Za-z]{1,3}[$]?\d+)?$/;
9964
+ const rowRangeRegexp = /^[$]?\d+:[$]?\d+$/;
9965
+ const colRangeRegexp = /^[$]?[A-Za-z]{1,3}:[$]?[A-Za-z]{1,3}$/;
9966
+ function isValidRange(range) {
9967
+ if (range.startsWith("{") || range.endsWith("}")) return false;
9968
+ const cellRef = range.split("!").pop() ?? "";
9969
+ if (!cellRangeRegexp.test(cellRef) && !rowRangeRegexp.test(cellRef) && !colRangeRegexp.test(cellRef)) return false;
9970
+ try {
9971
+ const decoded = colCache.decodeEx(range);
9972
+ if ("row" in decoded && typeof decoded.row === "number" || "top" in decoded && typeof decoded.top === "number" || "left" in decoded && typeof decoded.left === "number") return true;
9973
+ return false;
9974
+ } catch {
9975
+ return false;
9976
+ }
9977
+ }
9978
+ /**
9979
+ * Extract valid cell/range references from a raw defined name text string.
9980
+ * Handles comma-separated ranges and quoted sheet names that may contain commas.
9981
+ */
9982
+ function extractRanges(parsedText) {
9983
+ const trimmed = parsedText.trim();
9984
+ if (trimmed.startsWith("{") && trimmed.endsWith("}")) return [];
9985
+ const ranges = [];
9986
+ let quotesOpened = false;
9987
+ let last = "";
9988
+ parsedText.split(",").forEach((item) => {
9989
+ if (!item) return;
9990
+ const quotes = (item.match(/'/g) ?? []).length;
9991
+ if (!quotes) {
9992
+ if (quotesOpened) last += `${item},`;
9993
+ else if (isValidRange(item)) ranges.push(item);
9994
+ return;
9995
+ }
9996
+ const quotesEven = quotes % 2 === 0;
9997
+ if (!quotesOpened && quotesEven && isValidRange(item)) ranges.push(item);
9998
+ else if (quotesOpened && !quotesEven) {
9999
+ quotesOpened = false;
10000
+ if (isValidRange(last + item)) ranges.push(last + item);
10001
+ last = "";
10002
+ } else {
10003
+ quotesOpened = true;
10004
+ last += `${item},`;
10005
+ }
10006
+ });
10007
+ return ranges;
10008
+ }
10009
+ /**
10010
+ * Check whether a string contains a '(' character outside of single-quoted
10011
+ * sheet name segments. Sheet names in cell references use single quotes:
10012
+ * e.g. `'Sheet (1)'!$A$1` — the '(' is inside quotes and does NOT indicate
10013
+ * a function call. A genuine formula like `OFFSET(Sheet1!$A$1,0,0,3,1)`
10014
+ * has '(' outside of any quotes.
10015
+ */
10016
+ function hasUnquotedParen(s) {
10017
+ let inQuote = false;
10018
+ for (let i = 0; i < s.length; i++) {
10019
+ const ch = s[i];
10020
+ if (ch === "'") inQuote = !inQuote;
10021
+ else if (ch === "(" && !inQuote) return true;
10022
+ }
10023
+ return false;
10024
+ }
10025
+ /**
10026
+ * Classify a raw defined name text into one of three categories:
10027
+ * 1. **reference** — pure cell/range union (goes into matrixMap)
10028
+ * 2. **formula** — parseable expression (goes into formulaMap)
10029
+ * 3. **opaque** — unrecognised content preserved for round-trip
10030
+ *
10031
+ * Classification order matters:
10032
+ * - If the text contains an unquoted `(`, it is likely a formula — try the
10033
+ * formula parser first (this prevents `extractRanges` from misinterpreting
10034
+ * function arguments as partial range references).
10035
+ * - Otherwise try to extract ranges (pure cell references).
10036
+ * - If neither works, fall back to opaque.
10037
+ *
10038
+ * **Probe semantics:** `probe` is the formula tokenizer+parser oracle. It
10039
+ * is the *only* authority for deciding whether a non-range, non-wrapper
10040
+ * string is a formula. When `probe` is `null` (no formula engine
10041
+ * installed and no probe injected), any such string is classified as
10042
+ * **opaque** — we have no evidence it is a formula, and leaving it
10043
+ * opaque preserves round-trip bytes via `rawText`.
10044
+ *
10045
+ * This function is pure: the classification of a given input depends
10046
+ * entirely on (rawText, ranges, probe) and no global state. Two calls
10047
+ * with the same arguments always produce the same result.
10048
+ */
10049
+ function classifyDefinedName(rawText, ranges, probe) {
10050
+ if (rawText === void 0) {
10051
+ if (ranges.length > 0) return {
10052
+ kind: "reference",
10053
+ ranges
10054
+ };
10055
+ return {
10056
+ kind: "opaque",
10057
+ ranges: []
10058
+ };
10059
+ }
10060
+ const trimmed = rawText.trim();
10061
+ if (trimmed.length === 0) return {
10062
+ kind: "opaque",
10063
+ ranges: []
10064
+ };
10065
+ const isArrayConst = trimmed.startsWith("{") && trimmed.endsWith("}");
10066
+ const isStringLit = trimmed.startsWith("\"") && trimmed.endsWith("\"");
10067
+ const isErrorVal = trimmed.startsWith("#");
10068
+ const isOpaqueWrapper = isArrayConst || isStringLit || isErrorVal;
10069
+ if (hasUnquotedParen(trimmed) && !isOpaqueWrapper) {
10070
+ if (probe && probe(trimmed)) return {
10071
+ kind: "formula",
10072
+ ranges: [trimmed],
10073
+ formulaExpression: trimmed
10074
+ };
10075
+ return {
10076
+ kind: "opaque",
10077
+ ranges: []
10078
+ };
10079
+ }
10080
+ const extracted = extractRanges(rawText);
10081
+ if (extracted.length > 0) return {
10082
+ kind: "reference",
10083
+ ranges: extracted
10084
+ };
10085
+ if (isOpaqueWrapper) return {
10086
+ kind: "opaque",
10087
+ ranges: []
10088
+ };
10089
+ if (probe && probe(trimmed)) return {
10090
+ kind: "formula",
10091
+ ranges: [trimmed],
10092
+ formulaExpression: trimmed
10093
+ };
10094
+ return {
10095
+ kind: "opaque",
10096
+ ranges: []
10097
+ };
10098
+ }
10099
+ /**
10100
+ * Build the internal storage key for a defined name entry.
10101
+ * Workbook-scoped: just the bare name.
10102
+ * Sheet-scoped: `"name\0sheetId"` (null char separator avoids collisions).
10103
+ */
10104
+ function storageKey(name, localSheetId) {
10105
+ return localSheetId !== void 0 ? `${name}\0${localSheetId}` : name;
10106
+ }
9942
10107
  var DefinedNames = class {
9943
- constructor() {
10108
+ /**
10109
+ * @param probe Optional formula-syntax probe used when classifying
10110
+ * defined-name text. Injecting a probe here makes classification
10111
+ * deterministic for this instance regardless of process-global
10112
+ * `installFormulaEngine()` state. When omitted, the instance defers
10113
+ * to the default probe at classification time (see `set model`).
10114
+ */
10115
+ constructor(probe) {
9944
10116
  this.matrixMap = {};
10117
+ this.formulaMap = {};
10118
+ this.localSheetIdMap = {};
10119
+ this.opaqueMap = {};
10120
+ this.nameForKey = {};
10121
+ this._explicitProbe = probe ?? null;
9945
10122
  }
9946
10123
  getMatrix(name) {
9947
10124
  return this.matrixMap[name] || (this.matrixMap[name] = new CellMatrix());
@@ -9949,6 +10126,9 @@ var ExcelTS = (function(exports) {
9949
10126
  add(locStr, name) {
9950
10127
  const location = colCache.decodeEx(locStr);
9951
10128
  if ("error" in location) return;
10129
+ delete this.formulaMap[name];
10130
+ delete this.opaqueMap[name];
10131
+ this.nameForKey[name] = name;
9952
10132
  this.addEx(location, name);
9953
10133
  }
9954
10134
  addEx(location, name) {
@@ -9964,6 +10144,22 @@ var ExcelTS = (function(exports) {
9964
10144
  }
9965
10145
  else matrix.addCellEx(location);
9966
10146
  }
10147
+ /**
10148
+ * Register a formula-based defined name.
10149
+ *
10150
+ * Unlike `add()` which binds a name to a cell/range reference, this binds
10151
+ * a name to an arbitrary formula expression that will be evaluated at
10152
+ * calculation time.
10153
+ *
10154
+ * @param name - The defined name (e.g. "MyArray")
10155
+ * @param expression - The formula expression (e.g. "{1,2;3,4}", "LAMBDA(x,y,x+y)")
10156
+ */
10157
+ addFormula(name, expression) {
10158
+ delete this.matrixMap[name];
10159
+ delete this.opaqueMap[name];
10160
+ this.nameForKey[name] = name;
10161
+ this.formulaMap[name] = expression;
10162
+ }
9967
10163
  remove(locStr, name) {
9968
10164
  const location = colCache.decodeEx(locStr);
9969
10165
  if ("error" in location) return;
@@ -9991,9 +10187,10 @@ var ExcelTS = (function(exports) {
9991
10187
  });
9992
10188
  }
9993
10189
  forEach(callback) {
9994
- Object.entries(this.matrixMap).forEach(([name, matrix]) => {
10190
+ Object.entries(this.matrixMap).forEach(([sKey, matrix]) => {
10191
+ const bareName = this.nameForKey[sKey] ?? sKey;
9995
10192
  matrix.forEach((cell) => {
9996
- callback(name, cell);
10193
+ callback(bareName, cell);
9997
10194
  });
9998
10195
  });
9999
10196
  }
@@ -10003,7 +10200,63 @@ var ExcelTS = (function(exports) {
10003
10200
  return this.getNamesEx(location);
10004
10201
  }
10005
10202
  getNamesEx(address) {
10006
- return Object.entries(this.matrixMap).map(([name, matrix]) => matrix.findCellEx(address, false) && name).filter((name) => Boolean(name));
10203
+ return Object.entries(this.matrixMap).map(([sKey, matrix]) => matrix.findCellEx(address, false) && (this.nameForKey[sKey] ?? sKey)).filter((name) => Boolean(name));
10204
+ }
10205
+ /**
10206
+ * Return all defined name entries in this collection, including scope info.
10207
+ * Each entry has the bare name and optional localSheetId.
10208
+ * Same bare name may appear multiple times with different scopes.
10209
+ */
10210
+ getAllNames() {
10211
+ return this.getAllEntries().map((e) => e.localSheetId !== void 0 ? {
10212
+ name: e.name,
10213
+ localSheetId: e.localSheetId
10214
+ } : { name: e.name });
10215
+ }
10216
+ /**
10217
+ * Return all defined name entries with full details (name, ranges, scope).
10218
+ *
10219
+ * This is the primary enumeration API. Each entry is self-contained —
10220
+ * no second lookup is needed. Same bare name may appear multiple times
10221
+ * with different scopes.
10222
+ */
10223
+ getAllEntries() {
10224
+ const result = [];
10225
+ const seen = /* @__PURE__ */ new Set();
10226
+ for (const sKey of Object.keys(this.matrixMap)) {
10227
+ if (seen.has(sKey)) continue;
10228
+ seen.add(sKey);
10229
+ const model = this.getRanges(sKey);
10230
+ const localSheetId = this.localSheetIdMap[sKey];
10231
+ result.push(localSheetId !== void 0 ? {
10232
+ ...model,
10233
+ localSheetId
10234
+ } : model);
10235
+ }
10236
+ for (const sKey of Object.keys(this.formulaMap)) {
10237
+ if (seen.has(sKey)) continue;
10238
+ seen.add(sKey);
10239
+ const model = this.getRanges(sKey);
10240
+ const localSheetId = this.localSheetIdMap[sKey];
10241
+ result.push(localSheetId !== void 0 ? {
10242
+ ...model,
10243
+ localSheetId
10244
+ } : model);
10245
+ }
10246
+ for (const sKey of Object.keys(this.opaqueMap)) {
10247
+ if (seen.has(sKey)) continue;
10248
+ seen.add(sKey);
10249
+ const bareName = this.nameForKey[sKey] ?? sKey;
10250
+ const entry = this.opaqueMap[sKey];
10251
+ result.push({
10252
+ name: bareName,
10253
+ ranges: [],
10254
+ rawText: entry.rawText,
10255
+ localSheetId: entry.localSheetId,
10256
+ kind: "opaque"
10257
+ });
10258
+ }
10259
+ return result;
10007
10260
  }
10008
10261
  _explore(matrix, cell) {
10009
10262
  cell.mark = false;
@@ -10038,17 +10291,37 @@ var ExcelTS = (function(exports) {
10038
10291
  for (x = cell.col + 1; hGrow(x, "right"); x++);
10039
10292
  return range;
10040
10293
  }
10294
+ /**
10295
+ * Get ranges for a specific scoped entry.
10296
+ *
10297
+ * Unlike `getRanges(name)` which uses the bare name (and may hit the
10298
+ * wrong scope when the same name exists both globally and locally),
10299
+ * this method uses the internal `storageKey` to look up the exact entry.
10300
+ */
10301
+ getRangesScoped(name, localSheetId) {
10302
+ const sKey = storageKey(name, localSheetId);
10303
+ return this.getRanges(sKey);
10304
+ }
10041
10305
  getRanges(name, matrix) {
10306
+ const formula = this.formulaMap[name];
10042
10307
  matrix = matrix || this.matrixMap[name];
10043
- if (!matrix) return {
10044
- name,
10045
- ranges: []
10046
- };
10308
+ const bareName = this.nameForKey[name] ?? name;
10309
+ if (!matrix) {
10310
+ if (formula) return {
10311
+ name: bareName,
10312
+ ranges: [formula],
10313
+ formulaExpression: formula
10314
+ };
10315
+ return {
10316
+ name: bareName,
10317
+ ranges: []
10318
+ };
10319
+ }
10047
10320
  matrix.forEach((cell) => {
10048
10321
  cell.mark = true;
10049
10322
  });
10050
10323
  return {
10051
- name,
10324
+ name: bareName,
10052
10325
  ranges: matrix.map((cell) => cell.mark && this._explore(matrix, cell)).filter((range) => Boolean(range)).map((range) => range.$shortRange)
10053
10326
  };
10054
10327
  }
@@ -10076,16 +10349,84 @@ var ExcelTS = (function(exports) {
10076
10349
  });
10077
10350
  }
10078
10351
  get model() {
10079
- return Object.entries(this.matrixMap).map(([name, matrix]) => this.getRanges(name, matrix)).filter((definedName) => definedName.ranges.length);
10352
+ const cellNames = Object.entries(this.matrixMap).map(([sKey, matrix]) => {
10353
+ const result = this.getRanges(sKey, matrix);
10354
+ const localSheetId = this.localSheetIdMap[sKey];
10355
+ if (localSheetId !== void 0) return {
10356
+ ...result,
10357
+ localSheetId
10358
+ };
10359
+ return result;
10360
+ }).filter((definedName) => definedName.ranges.length);
10361
+ const formulaNames = Object.entries(this.formulaMap).filter(([sKey]) => !this.matrixMap[sKey]).map(([sKey, expression]) => {
10362
+ const result = {
10363
+ name: this.nameForKey[sKey] ?? sKey,
10364
+ ranges: [expression],
10365
+ formulaExpression: expression
10366
+ };
10367
+ const localSheetId = this.localSheetIdMap[sKey];
10368
+ if (localSheetId !== void 0) return {
10369
+ ...result,
10370
+ localSheetId
10371
+ };
10372
+ return result;
10373
+ });
10374
+ const opaqueNames = Object.entries(this.opaqueMap).map(([sKey, entry]) => {
10375
+ return {
10376
+ name: this.nameForKey[sKey] ?? sKey,
10377
+ ranges: [],
10378
+ rawText: entry.rawText,
10379
+ localSheetId: entry.localSheetId,
10380
+ kind: "opaque"
10381
+ };
10382
+ });
10383
+ return [
10384
+ ...cellNames,
10385
+ ...formulaNames,
10386
+ ...opaqueNames
10387
+ ];
10080
10388
  }
10389
+ /**
10390
+ * Deserialise an array of `DefinedNameModel` entries (typically from XLSX parsing).
10391
+ *
10392
+ * Stage 2 of the two-phase design: each entry's `rawText` is classified
10393
+ * into reference / formula / opaque by `classifyDefinedName()`. Entries
10394
+ * that arrive without `rawText` (programmatic API) fall back to inspecting
10395
+ * the existing `ranges` and `formulaExpression` fields for compatibility.
10396
+ */
10081
10397
  set model(value) {
10082
10398
  const matrixMap = this.matrixMap = {};
10083
- value.forEach((definedName) => {
10084
- const matrix = matrixMap[definedName.name] = new CellMatrix();
10085
- definedName.ranges.forEach((rangeStr) => {
10086
- if (rangeRegexp.test(rangeStr.split("!").pop() ?? "")) matrix.addCell(rangeStr);
10087
- });
10088
- });
10399
+ const formulaMap = this.formulaMap = {};
10400
+ const localSheetIdMap = this.localSheetIdMap = {};
10401
+ const opaqueMap = this.opaqueMap = {};
10402
+ const nameForKeyMap = this.nameForKey = {};
10403
+ const probe = this._explicitProbe ?? getDefaultSyntaxProbe();
10404
+ for (const definedName of value) {
10405
+ const sKey = storageKey(definedName.name, definedName.localSheetId);
10406
+ nameForKeyMap[sKey] = definedName.name;
10407
+ if (definedName.localSheetId !== void 0) localSheetIdMap[sKey] = definedName.localSheetId;
10408
+ if (definedName.formulaExpression && definedName.rawText === void 0) {
10409
+ formulaMap[sKey] = definedName.formulaExpression;
10410
+ continue;
10411
+ }
10412
+ const classified = classifyDefinedName(definedName.rawText, definedName.ranges, probe);
10413
+ switch (classified.kind) {
10414
+ case "reference": {
10415
+ const matrix = matrixMap[sKey] = new CellMatrix();
10416
+ for (const rangeStr of classified.ranges) if (rangeRegexp.test(rangeStr.split("!").pop() ?? "")) matrix.addCell(rangeStr);
10417
+ break;
10418
+ }
10419
+ case "formula":
10420
+ formulaMap[sKey] = classified.formulaExpression;
10421
+ break;
10422
+ case "opaque":
10423
+ if (definedName.rawText) opaqueMap[sKey] = {
10424
+ rawText: definedName.rawText,
10425
+ localSheetId: definedName.localSheetId
10426
+ };
10427
+ break;
10428
+ }
10429
+ }
10089
10430
  }
10090
10431
  };
10091
10432
  //#endregion
@@ -15225,7 +15566,10 @@ onmessage = async (ev) => {
15225
15566
  PivotCacheRecords: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheRecords",
15226
15567
  PivotTable: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotTable",
15227
15568
  FeaturePropertyBag: "http://schemas.microsoft.com/office/2022/11/relationships/FeaturePropertyBag",
15228
- CtrlProp: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/ctrlProp"
15569
+ CtrlProp: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/ctrlProp",
15570
+ SheetMetadata: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sheetMetadata",
15571
+ ExternalLink: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/externalLink",
15572
+ ExternalLinkPath: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/externalLinkPath"
15229
15573
  };
15230
15574
  //#endregion
15231
15575
  //#region src/modules/xml/errors.ts
@@ -16643,7 +16987,7 @@ onmessage = async (ev) => {
16643
16987
  const attributes = node.attributes;
16644
16988
  if (attributes.Type !== RelType.Hyperlink) return;
16645
16989
  const relationship = {
16646
- type: Enums.RelationshipType.Hyperlink,
16990
+ type: 7,
16647
16991
  rId: attributes.Id,
16648
16992
  target: attributes.Target,
16649
16993
  targetMode: attributes.TargetMode
@@ -16939,7 +17283,7 @@ onmessage = async (ev) => {
16939
17283
  get values() {
16940
17284
  const v = [];
16941
17285
  this.eachCell((cell, rowNumber) => {
16942
- if (cell && cell.type !== Enums.ValueType.Null) v[rowNumber] = cell.value;
17286
+ if (cell && cell.type !== 0) v[rowNumber] = cell.value;
16943
17287
  });
16944
17288
  return v;
16945
17289
  }
@@ -17151,6 +17495,40 @@ onmessage = async (ev) => {
17151
17495
  //#endregion
17152
17496
  //#region src/modules/excel/cell.ts
17153
17497
  const hasOwnKeys = (v) => !!v && (typeof v !== "object" || Object.keys(v).length > 0);
17498
+ /**
17499
+ * Flatten a rich-text array into its plain-text representation by
17500
+ * concatenating each run's `text`. Missing/null runs contribute "".
17501
+ */
17502
+ function flattenRichText(runs) {
17503
+ let out = "";
17504
+ for (const run of runs) if (run && typeof run.text === "string") out += run.text;
17505
+ return out;
17506
+ }
17507
+ /**
17508
+ * Normalize a CellHyperlinkValue so the {@link NormalizedHyperlink} invariants
17509
+ * hold.
17510
+ *
17511
+ * - If the caller supplied `richText` but no `text`, text is derived.
17512
+ * - If the caller supplied `text` but no `richText`, richText stays absent.
17513
+ * - If both are supplied, `richText` wins and `text` is regenerated
17514
+ * (to keep `text === flatten(richText)`).
17515
+ * - An empty `richText: []` is dropped (treated as "no rich text").
17516
+ */
17517
+ function normalizeHyperlinkValue(value) {
17518
+ let text;
17519
+ let richText;
17520
+ if (Array.isArray(value.richText) && value.richText.length > 0) {
17521
+ richText = value.richText;
17522
+ text = flattenRichText(richText);
17523
+ } else text = typeof value.text === "string" ? value.text : "";
17524
+ const out = {
17525
+ text,
17526
+ hyperlink: typeof value.hyperlink === "string" ? value.hyperlink : ""
17527
+ };
17528
+ if (richText) out.richText = richText;
17529
+ if (typeof value.tooltip === "string" && value.tooltip.length > 0) out.tooltip = value.tooltip;
17530
+ return out;
17531
+ }
17154
17532
  var Cell = class Cell {
17155
17533
  static {
17156
17534
  this.Types = Enums.ValueType;
@@ -17322,10 +17700,25 @@ onmessage = async (ev) => {
17322
17700
  }
17323
17701
  /** @internal */
17324
17702
  _upgradeToHyperlink(hyperlink) {
17325
- if (this.type === Cell.Types.String) this._value = Value.create(Cell.Types.Hyperlink, this, {
17326
- text: String(this._value.value),
17327
- hyperlink
17328
- });
17703
+ switch (this.type) {
17704
+ case Cell.Types.String:
17705
+ this._value = Value.create(Cell.Types.Hyperlink, this, {
17706
+ text: String(this._value.value),
17707
+ hyperlink
17708
+ });
17709
+ break;
17710
+ case Cell.Types.RichText: {
17711
+ const current = this._value.value;
17712
+ const runs = current && Array.isArray(current.richText) ? current.richText : [];
17713
+ this._value = Value.create(Cell.Types.Hyperlink, this, {
17714
+ text: flattenRichText(runs),
17715
+ richText: runs.length > 0 ? runs : void 0,
17716
+ hyperlink
17717
+ });
17718
+ break;
17719
+ }
17720
+ default: break;
17721
+ }
17329
17722
  }
17330
17723
  get formula() {
17331
17724
  return this._value.formula;
@@ -17333,8 +17726,11 @@ onmessage = async (ev) => {
17333
17726
  get result() {
17334
17727
  return this._value.result;
17335
17728
  }
17729
+ set result(value) {
17730
+ if (this.type === Cell.Types.Formula) this._value.result = value;
17731
+ }
17336
17732
  get formulaType() {
17337
- return this._value.formulaType ?? Enums.FormulaType.None;
17733
+ return this._value.formulaType ?? 0;
17338
17734
  }
17339
17735
  get fullAddress() {
17340
17736
  const { worksheet } = this._row;
@@ -17572,30 +17968,60 @@ onmessage = async (ev) => {
17572
17968
  constructor(cell, value) {
17573
17969
  this.model = {
17574
17970
  address: cell.address,
17575
- type: Cell.Types.Hyperlink,
17576
- text: value ? value.text : void 0,
17577
- hyperlink: value ? value.hyperlink : void 0
17971
+ type: Cell.Types.Hyperlink
17578
17972
  };
17579
- if (value && value.tooltip) this.model.tooltip = value.tooltip;
17973
+ if (value) {
17974
+ if ("formula" in value && typeof value.formula === "string") {
17975
+ const fh = value;
17976
+ const display = fh.result === void 0 || fh.result === null ? "" : String(fh.result);
17977
+ this.model.text = display;
17978
+ this.model.hyperlink = fh.hyperlink ?? "";
17979
+ if (fh.tooltip !== void 0) this.model.tooltip = fh.tooltip;
17980
+ this.model.formula = fh.formula;
17981
+ if (fh.result !== void 0) this.model.result = fh.result;
17982
+ return;
17983
+ }
17984
+ const normalized = normalizeHyperlinkValue(value);
17985
+ this.model.text = normalized.text;
17986
+ this.model.hyperlink = normalized.hyperlink;
17987
+ if (normalized.richText) this.model.richText = normalized.richText;
17988
+ if (normalized.tooltip !== void 0) this.model.tooltip = normalized.tooltip;
17989
+ }
17580
17990
  }
17581
17991
  get value() {
17582
- return {
17992
+ const out = {
17583
17993
  text: this.model.text ?? "",
17584
- hyperlink: this.model.hyperlink ?? "",
17585
- tooltip: this.model.tooltip
17994
+ hyperlink: this.model.hyperlink ?? ""
17586
17995
  };
17996
+ if (this.model.richText && this.model.richText.length > 0) out.richText = this.model.richText;
17997
+ if (this.model.tooltip !== void 0) out.tooltip = this.model.tooltip;
17998
+ return out;
17587
17999
  }
17588
18000
  set value(value) {
17589
- this.model.text = value.text;
17590
- this.model.hyperlink = value.hyperlink;
17591
- if (value.tooltip) this.model.tooltip = value.tooltip;
18001
+ const normalized = normalizeHyperlinkValue(value);
18002
+ this.model.text = normalized.text;
18003
+ this.model.hyperlink = normalized.hyperlink;
18004
+ if (normalized.richText) this.model.richText = normalized.richText;
18005
+ else delete this.model.richText;
18006
+ if (normalized.tooltip !== void 0) this.model.tooltip = normalized.tooltip;
18007
+ else delete this.model.tooltip;
17592
18008
  }
17593
18009
  get text() {
17594
18010
  return this.model.text;
17595
18011
  }
17596
18012
  set text(value) {
18013
+ if (this.model.richText) delete this.model.richText;
17597
18014
  this.model.text = value;
17598
18015
  }
18016
+ get richText() {
18017
+ return this.model.richText;
18018
+ }
18019
+ set richText(value) {
18020
+ if (Array.isArray(value) && value.length > 0) {
18021
+ this.model.richText = value;
18022
+ this.model.text = flattenRichText(value);
18023
+ } else delete this.model.richText;
18024
+ }
17599
18025
  get hyperlink() {
17600
18026
  return this.model.hyperlink;
17601
18027
  }
@@ -17680,7 +18106,8 @@ onmessage = async (ev) => {
17680
18106
  ref: value ? value.ref : void 0,
17681
18107
  formula: value ? value.formula : void 0,
17682
18108
  sharedFormula: value ? value.sharedFormula : void 0,
17683
- result: value ? value.result : void 0
18109
+ result: value ? value.result : void 0,
18110
+ isDynamicArray: value ? value.isDynamicArray : void 0
17684
18111
  };
17685
18112
  }
17686
18113
  _copyModel(model) {
@@ -17690,6 +18117,7 @@ onmessage = async (ev) => {
17690
18117
  if (model.ref) copy.ref = model.ref;
17691
18118
  if (model.shareType) copy.shareType = model.shareType;
17692
18119
  if (model.sharedFormula) copy.sharedFormula = model.sharedFormula;
18120
+ if (model.isDynamicArray) copy.isDynamicArray = model.isDynamicArray;
17693
18121
  return copy;
17694
18122
  }
17695
18123
  get value() {
@@ -17701,6 +18129,7 @@ onmessage = async (ev) => {
17701
18129
  if (value.ref) this.model.ref = value.ref;
17702
18130
  if (value.shareType) this.model.shareType = value.shareType;
17703
18131
  if (value.sharedFormula) this.model.sharedFormula = value.sharedFormula;
18132
+ if (value.isDynamicArray !== void 0) this.model.isDynamicArray = value.isDynamicArray;
17704
18133
  }
17705
18134
  validate(value) {
17706
18135
  switch (Value.getType(value)) {
@@ -17726,9 +18155,9 @@ onmessage = async (ev) => {
17726
18155
  this.model.formula = value;
17727
18156
  }
17728
18157
  get formulaType() {
17729
- if (this.model.formula) return Enums.FormulaType.Master;
17730
- if (this.model.sharedFormula) return Enums.FormulaType.Shared;
17731
- return Enums.FormulaType.None;
18158
+ if (this.model.formula) return 1;
18159
+ if (this.model.sharedFormula) return 2;
18160
+ return 0;
17732
18161
  }
17733
18162
  get result() {
17734
18163
  return this.model.result;
@@ -17741,12 +18170,12 @@ onmessage = async (ev) => {
17741
18170
  }
17742
18171
  get effectiveType() {
17743
18172
  const v = this.model.result;
17744
- if (v === null || v === void 0) return Enums.ValueType.Null;
17745
- if (typeof v === "string") return Enums.ValueType.String;
17746
- if (typeof v === "number") return Enums.ValueType.Number;
17747
- if (v instanceof Date) return Enums.ValueType.Date;
17748
- if (typeof v === "object" && "error" in v) return Enums.ValueType.Error;
17749
- return Enums.ValueType.Null;
18173
+ if (v === null || v === void 0) return 0;
18174
+ if (typeof v === "string") return 3;
18175
+ if (typeof v === "number") return 2;
18176
+ if (v instanceof Date) return 4;
18177
+ if (typeof v === "object" && "error" in v) return 10;
18178
+ return 0;
17750
18179
  }
17751
18180
  get address() {
17752
18181
  return this.model.address;
@@ -17951,9 +18380,14 @@ onmessage = async (ev) => {
17951
18380
  if (value instanceof Date) return Cell.Types.Date;
17952
18381
  if (typeof value === "object") {
17953
18382
  if ("checkbox" in value && typeof value.checkbox === "boolean") return Cell.Types.Checkbox;
17954
- if ("text" in value && value.text && "hyperlink" in value && value.hyperlink) return Cell.Types.Hyperlink;
18383
+ if ("hyperlink" in value && typeof value.hyperlink === "string" && value.hyperlink) {
18384
+ const hasText = "text" in value && typeof value.text === "string" && value.text.length > 0;
18385
+ const hasRichText = "richText" in value && Array.isArray(value.richText) && value.richText.length > 0;
18386
+ const hasFormula = "formula" in value && typeof value.formula === "string";
18387
+ if (hasText || hasRichText || hasFormula) return Cell.Types.Hyperlink;
18388
+ }
17955
18389
  if ("formula" in value && value.formula || "sharedFormula" in value && value.sharedFormula) return Cell.Types.Formula;
17956
- if ("richText" in value && value.richText) return Cell.Types.RichText;
18390
+ if ("richText" in value && Array.isArray(value.richText) && value.richText.length > 0) return Cell.Types.RichText;
17957
18391
  if ("sharedString" in value && value.sharedString) return Cell.Types.SharedString;
17958
18392
  if ("error" in value && value.error) return Cell.Types.Error;
17959
18393
  }
@@ -18142,7 +18576,7 @@ onmessage = async (ev) => {
18142
18576
  const n = this._cells.length;
18143
18577
  for (let i = 1; i <= n; i++) callback(this.getCell(i), i);
18144
18578
  } else this._cells.forEach((cell, index) => {
18145
- if (cell && cell.type !== Enums.ValueType.Null) callback(cell, index + 1);
18579
+ if (cell && cell.type !== 0) callback(cell, index + 1);
18146
18580
  });
18147
18581
  }
18148
18582
  addPageBreak(lft, rght) {
@@ -18163,7 +18597,7 @@ onmessage = async (ev) => {
18163
18597
  get values() {
18164
18598
  const values = [];
18165
18599
  this._cells.forEach((cell) => {
18166
- if (cell && cell.type !== Enums.ValueType.Null) values[cell.col] = cell.value;
18600
+ if (cell && cell.type !== 0) values[cell.col] = cell.value;
18167
18601
  });
18168
18602
  return values;
18169
18603
  }
@@ -18199,7 +18633,7 @@ onmessage = async (ev) => {
18199
18633
  getValues() {
18200
18634
  const values = [];
18201
18635
  this._cells.forEach((cell) => {
18202
- if (cell && cell.type !== Enums.ValueType.Null) values[cell.col - 1] = cell.value;
18636
+ if (cell && cell.type !== 0) values[cell.col - 1] = cell.value;
18203
18637
  });
18204
18638
  return values;
18205
18639
  }
@@ -18215,7 +18649,7 @@ onmessage = async (ev) => {
18215
18649
  * Returns true if the row includes at least one cell with a value
18216
18650
  */
18217
18651
  get hasValues() {
18218
- return this._cells.some((cell) => cell && cell.type !== Enums.ValueType.Null);
18652
+ return this._cells.some((cell) => cell && cell.type !== 0);
18219
18653
  }
18220
18654
  /**
18221
18655
  * Number of cells including empty ones
@@ -18240,7 +18674,7 @@ onmessage = async (ev) => {
18240
18674
  let min = 0;
18241
18675
  let max = 0;
18242
18676
  this._cells.forEach((cell) => {
18243
- if (cell && cell.type !== Enums.ValueType.Null) {
18677
+ if (cell && cell.type !== 0) {
18244
18678
  if (!min || min > cell.col) min = cell.col;
18245
18679
  if (max < cell.col) max = cell.col;
18246
18680
  }
@@ -18944,10 +19378,12 @@ onmessage = async (ev) => {
18944
19378
  case "c":
18945
19379
  if (row) {
18946
19380
  const styleAttr = node.attributes.s;
19381
+ const cmAttr = node.attributes.cm;
18947
19382
  c = {
18948
19383
  ref: node.attributes.r,
18949
19384
  s: styleAttr !== void 0 ? parseInt(styleAttr, 10) : void 0,
18950
- t: node.attributes.t
19385
+ t: node.attributes.t,
19386
+ cm: cmAttr !== void 0 ? parseInt(cmAttr, 10) : void 0
18951
19387
  };
18952
19388
  }
18953
19389
  break;
@@ -19025,6 +19461,12 @@ onmessage = async (ev) => {
19025
19461
  const cellValue = { formula: c.f.text };
19026
19462
  if (c.v) if (c.t === "str") cellValue.result = c.v.text;
19027
19463
  else cellValue.result = parseFloat(c.v.text);
19464
+ if (c.cm !== void 0) {
19465
+ const { workbook: wb } = this;
19466
+ if (wb.dynamicArrayCmIndices) {
19467
+ if (wb.dynamicArrayCmIndices.has(c.cm)) cellValue.isDynamicArray = true;
19468
+ } else if (wb.hasDynamicArrayMetadata) cellValue.isDynamicArray = true;
19469
+ }
19028
19470
  cell.value = cellValue;
19029
19471
  } else if (c.v) switch (c.t) {
19030
19472
  case "s": {
@@ -19130,7 +19572,8 @@ onmessage = async (ev) => {
19130
19572
  });
19131
19573
  } else {
19132
19574
  stream.pause();
19133
- yield contents.shift();
19575
+ const data = contents.shift();
19576
+ if (data !== void 0) yield data;
19134
19577
  }
19135
19578
  if (error) throw toError(error);
19136
19579
  }
@@ -19152,7 +19595,8 @@ onmessage = async (ev) => {
19152
19595
  xlSharedStrings: "xl/sharedStrings.xml",
19153
19596
  xlStyles: "xl/styles.xml",
19154
19597
  xlTheme1: "xl/theme/theme1.xml",
19155
- xlFeaturePropertyBag: "xl/featurePropertyBag/featurePropertyBag.xml"
19598
+ xlFeaturePropertyBag: "xl/featurePropertyBag/featurePropertyBag.xml",
19599
+ xlMetadata: "xl/metadata.xml"
19156
19600
  };
19157
19601
  const worksheetXmlRegex = /^xl\/worksheets\/sheet(\d+)[.]xml$/;
19158
19602
  const worksheetRelsXmlRegex = /^xl\/worksheets\/_rels\/sheet(\d+)[.]xml[.]rels$/;
@@ -19169,6 +19613,8 @@ onmessage = async (ev) => {
19169
19613
  const pivotCacheDefinitionXmlRegex = /^xl\/pivotCache\/(pivotCacheDefinition\d+)[.]xml$/;
19170
19614
  const pivotCacheDefinitionRelsXmlRegex = /^xl\/pivotCache\/_rels\/(pivotCacheDefinition\d+)[.]xml[.]rels$/;
19171
19615
  const pivotCacheRecordsXmlRegex = /^xl\/pivotCache\/(pivotCacheRecords\d+)[.]xml$/;
19616
+ const externalLinkXmlRegex = /^xl\/externalLinks\/externalLink(\d+)[.]xml$/;
19617
+ const externalLinkRelsXmlRegex = /^xl\/externalLinks\/_rels\/externalLink(\d+)[.]xml[.]rels$/;
19172
19618
  function normalizeZipPath$1(path) {
19173
19619
  return path.startsWith("/") ? path.slice(1) : path;
19174
19620
  }
@@ -19243,6 +19689,22 @@ onmessage = async (ev) => {
19243
19689
  const match = pivotCacheRecordsXmlRegex.exec(path);
19244
19690
  return match ? match[1] : void 0;
19245
19691
  }
19692
+ /**
19693
+ * Extract the 1-based index `N` from `xl/externalLinks/externalLink{N}.xml`.
19694
+ * Returns the raw integer (e.g. `1` for externalLink1.xml) or undefined.
19695
+ */
19696
+ function getExternalLinkIndexFromPath(path) {
19697
+ const match = externalLinkXmlRegex.exec(path);
19698
+ return match ? parseInt(match[1], 10) : void 0;
19699
+ }
19700
+ /**
19701
+ * Extract the 1-based index `N` from
19702
+ * `xl/externalLinks/_rels/externalLink{N}.xml.rels`.
19703
+ */
19704
+ function getExternalLinkIndexFromRelsPath(path) {
19705
+ const match = externalLinkRelsXmlRegex.exec(path);
19706
+ return match ? parseInt(match[1], 10) : void 0;
19707
+ }
19246
19708
  function toContentTypesPartName(zipPath) {
19247
19709
  return zipPath.startsWith("/") ? zipPath : `/${zipPath}`;
19248
19710
  }
@@ -19303,6 +19765,19 @@ onmessage = async (ev) => {
19303
19765
  function pivotTableRelsPath(n) {
19304
19766
  return `xl/pivotTables/_rels/pivotTable${n}.xml.rels`;
19305
19767
  }
19768
+ function externalLinkPath(n) {
19769
+ return `xl/externalLinks/externalLink${n}.xml`;
19770
+ }
19771
+ function externalLinkRelsPath(n) {
19772
+ return `xl/externalLinks/_rels/externalLink${n}.xml.rels`;
19773
+ }
19774
+ /**
19775
+ * Build the `Target` value for an externalLink relationship inside
19776
+ * `xl/_rels/workbook.xml.rels` (base: `xl/`).
19777
+ */
19778
+ function externalLinkRelTargetFromWorkbook(n) {
19779
+ return `externalLinks/externalLink${n}.xml`;
19780
+ }
19306
19781
  function pivotCacheDefinitionRelTargetFromPivotTable(n) {
19307
19782
  return `../pivotCache/pivotCacheDefinition${n}.xml`;
19308
19783
  }
@@ -19310,7 +19785,8 @@ onmessage = async (ev) => {
19310
19785
  workbookStyles: "styles.xml",
19311
19786
  workbookSharedStrings: "sharedStrings.xml",
19312
19787
  workbookTheme1: "theme/theme1.xml",
19313
- workbookFeaturePropertyBag: "featurePropertyBag/featurePropertyBag.xml"
19788
+ workbookFeaturePropertyBag: "featurePropertyBag/featurePropertyBag.xml",
19789
+ workbookMetadata: "metadata.xml"
19314
19790
  };
19315
19791
  function pivotCacheDefinitionRelTargetFromWorkbook(n) {
19316
19792
  return `pivotCache/pivotCacheDefinition${n}.xml`;
@@ -19788,7 +20264,8 @@ onmessage = async (ev) => {
19788
20264
  name: model.name,
19789
20265
  localSheetId: model.localSheetId
19790
20266
  });
19791
- xmlStream.writeText(model.ranges.join(","));
20267
+ if (model.kind === "opaque" && model.rawText) xmlStream.writeText(model.rawText);
20268
+ else xmlStream.writeText(model.ranges.join(","));
19792
20269
  xmlStream.closeNode();
19793
20270
  }
19794
20271
  parseOpen(node) {
@@ -19804,57 +20281,47 @@ onmessage = async (ev) => {
19804
20281
  parseText(text) {
19805
20282
  this._parsedText.push(text);
19806
20283
  }
20284
+ /**
20285
+ * Stage 1 of the two-phase defined name design: the XLSX layer only
20286
+ * preserves the raw XML text. Semantic classification (reference vs
20287
+ * formula vs opaque) is deferred to `DefinedNames.set model()`.
20288
+ */
19807
20289
  parseClose() {
19808
- this.model = {
20290
+ const rawText = this._parsedText.join("");
20291
+ const model = {
19809
20292
  name: this._parsedName,
19810
- ranges: extractRanges(this._parsedText.join(""))
20293
+ ranges: [],
20294
+ rawText: rawText.trim() || void 0
19811
20295
  };
19812
- if (this._parsedLocalSheetId !== void 0) this.model.localSheetId = parseInt(this._parsedLocalSheetId, 10);
20296
+ if (this._parsedLocalSheetId !== void 0) model.localSheetId = parseInt(this._parsedLocalSheetId, 10);
20297
+ this.model = model;
19813
20298
  return false;
19814
20299
  }
19815
20300
  };
19816
- const cellRangeRegexp = /^[$]?[A-Za-z]{1,3}[$]?\d+(:[$]?[A-Za-z]{1,3}[$]?\d+)?$/;
19817
- const rowRangeRegexp = /^[$]?\d+:[$]?\d+$/;
19818
- const colRangeRegexp = /^[$]?[A-Za-z]{1,3}:[$]?[A-Za-z]{1,3}$/;
19819
- function isValidRange(range) {
19820
- if (range.startsWith("{") || range.endsWith("}")) return false;
19821
- const cellRef = range.split("!").pop() ?? "";
19822
- if (!cellRangeRegexp.test(cellRef) && !rowRangeRegexp.test(cellRef) && !colRangeRegexp.test(cellRef)) return false;
19823
- try {
19824
- const decoded = colCache.decodeEx(range);
19825
- if ("row" in decoded && typeof decoded.row === "number" || "top" in decoded && typeof decoded.top === "number" || "left" in decoded && typeof decoded.left === "number") return true;
20301
+ //#endregion
20302
+ //#region src/modules/excel/xlsx/xform/book/external-reference-xform.ts
20303
+ /**
20304
+ * Xform for a single `<externalReference r:id="..."/>` element inside
20305
+ * `<externalReferences>` in `xl/workbook.xml`. Each `<externalReference>`
20306
+ * maps positionally (in document order, 1-based) to an `[N]Sheet!Ref`
20307
+ * prefix in formula strings.
20308
+ */
20309
+ var ExternalReferenceXform = class extends BaseXform {
20310
+ render(xmlStream, model) {
20311
+ xmlStream.leafNode("externalReference", { "r:id": model.rId });
20312
+ }
20313
+ parseOpen(node) {
20314
+ if (node.name === "externalReference") {
20315
+ this.model = { rId: node.attributes["r:id"] ?? "" };
20316
+ return true;
20317
+ }
19826
20318
  return false;
19827
- } catch {
20319
+ }
20320
+ parseText() {}
20321
+ parseClose() {
19828
20322
  return false;
19829
20323
  }
19830
- }
19831
- function extractRanges(parsedText) {
19832
- const trimmed = parsedText.trim();
19833
- if (trimmed.startsWith("{") && trimmed.endsWith("}")) return [];
19834
- const ranges = [];
19835
- let quotesOpened = false;
19836
- let last = "";
19837
- parsedText.split(",").forEach((item) => {
19838
- if (!item) return;
19839
- const quotes = (item.match(/'/g) ?? []).length;
19840
- if (!quotes) {
19841
- if (quotesOpened) last += `${item},`;
19842
- else if (isValidRange(item)) ranges.push(item);
19843
- return;
19844
- }
19845
- const quotesEven = quotes % 2 === 0;
19846
- if (!quotesOpened && quotesEven && isValidRange(item)) ranges.push(item);
19847
- else if (quotesOpened && !quotesEven) {
19848
- quotesOpened = false;
19849
- if (isValidRange(last + item)) ranges.push(last + item);
19850
- last = "";
19851
- } else {
19852
- quotesOpened = true;
19853
- last += `${item},`;
19854
- }
19855
- });
19856
- return ranges;
19857
- }
20324
+ };
19858
20325
  //#endregion
19859
20326
  //#region src/modules/excel/xlsx/xform/book/sheet-xform.ts
19860
20327
  const VALID_STATES = new Set([
@@ -19898,12 +20365,21 @@ onmessage = async (ev) => {
19898
20365
  render(xmlStream, model) {
19899
20366
  xmlStream.leafNode("calcPr", {
19900
20367
  calcId: 171027,
19901
- fullCalcOnLoad: model.fullCalcOnLoad ? 1 : void 0
20368
+ fullCalcOnLoad: model.fullCalcOnLoad ? 1 : void 0,
20369
+ iterate: model.iterate ? 1 : void 0,
20370
+ iterateCount: model.iterateCount !== void 0 ? model.iterateCount : void 0,
20371
+ iterateDelta: model.iterateDelta !== void 0 ? model.iterateDelta : void 0
19902
20372
  });
19903
20373
  }
19904
20374
  parseOpen(node) {
19905
20375
  if (node.name === "calcPr") {
19906
- this.model = {};
20376
+ const attrs = node.attributes ?? {};
20377
+ this.model = {
20378
+ fullCalcOnLoad: attrs.fullCalcOnLoad === "1",
20379
+ iterate: attrs.iterate === "1" ? true : void 0,
20380
+ iterateCount: attrs.iterateCount !== void 0 ? parseInt(attrs.iterateCount, 10) : void 0,
20381
+ iterateDelta: attrs.iterateDelta !== void 0 ? parseFloat(attrs.iterateDelta) : void 0
20382
+ };
19907
20383
  return true;
19908
20384
  }
19909
20385
  return false;
@@ -19959,6 +20435,55 @@ onmessage = async (ev) => {
19959
20435
  }
19960
20436
  };
19961
20437
  //#endregion
20438
+ //#region src/modules/excel/xlsx/xform/book/workbook-protection-xform.ts
20439
+ var WorkbookProtectionXform = class extends BaseXform {
20440
+ get tag() {
20441
+ return "workbookProtection";
20442
+ }
20443
+ render(xmlStream, model) {
20444
+ if (!model) return;
20445
+ const attributes = {};
20446
+ if (model.lockStructure) attributes.lockStructure = "1";
20447
+ if (model.lockWindows) attributes.lockWindows = "1";
20448
+ if (model.lockRevision) attributes.lockRevision = "1";
20449
+ if (model.workbookPassword) attributes.workbookPassword = model.workbookPassword;
20450
+ if (model.revisionsPassword) attributes.revisionsPassword = model.revisionsPassword;
20451
+ if (model.algorithmName) {
20452
+ attributes.workbookAlgorithmName = model.algorithmName;
20453
+ attributes.workbookHashValue = model.hashValue;
20454
+ attributes.workbookSaltValue = model.saltValue;
20455
+ attributes.workbookSpinCount = model.spinCount?.toString();
20456
+ }
20457
+ if (Object.values(attributes).some((v) => v !== void 0)) xmlStream.leafNode(this.tag, attributes);
20458
+ }
20459
+ parseOpen(node) {
20460
+ switch (node.name) {
20461
+ case this.tag: {
20462
+ const a = node.attributes;
20463
+ this.model = {
20464
+ lockStructure: a.lockStructure === "1" || void 0,
20465
+ lockWindows: a.lockWindows === "1" || void 0,
20466
+ lockRevision: a.lockRevision === "1" || void 0,
20467
+ workbookPassword: a.workbookPassword || void 0,
20468
+ revisionsPassword: a.revisionsPassword || void 0
20469
+ };
20470
+ if (a.workbookAlgorithmName) {
20471
+ this.model.algorithmName = a.workbookAlgorithmName;
20472
+ this.model.hashValue = a.workbookHashValue;
20473
+ this.model.saltValue = a.workbookSaltValue;
20474
+ this.model.spinCount = a.workbookSpinCount ? parseInt(a.workbookSpinCount, 10) : void 0;
20475
+ }
20476
+ return true;
20477
+ }
20478
+ default: return false;
20479
+ }
20480
+ }
20481
+ parseText() {}
20482
+ parseClose() {
20483
+ return false;
20484
+ }
20485
+ };
20486
+ //#endregion
19962
20487
  //#region src/modules/excel/xlsx/xform/book/workbook-view-xform.ts
19963
20488
  var WorkbookViewXform = class extends BaseXform {
19964
20489
  render(xmlStream, model) {
@@ -20115,6 +20640,7 @@ onmessage = async (ev) => {
20115
20640
  this.map = {
20116
20641
  fileVersion: WorkbookXform.STATIC_XFORMS.fileVersion,
20117
20642
  workbookPr: new WorkbookPropertiesXform(),
20643
+ workbookProtection: new WorkbookProtectionXform(),
20118
20644
  bookViews: new ListXform({
20119
20645
  tag: "bookViews",
20120
20646
  count: false,
@@ -20135,6 +20661,11 @@ onmessage = async (ev) => {
20135
20661
  tag: "pivotCaches",
20136
20662
  count: false,
20137
20663
  childXform: new WorkbookPivotCacheXform()
20664
+ }),
20665
+ externalReferences: new ListXform({
20666
+ tag: "externalReferences",
20667
+ count: false,
20668
+ childXform: new ExternalReferenceXform()
20138
20669
  })
20139
20670
  };
20140
20671
  }
@@ -20187,6 +20718,7 @@ onmessage = async (ev) => {
20187
20718
  xmlStream.openNode("workbook", WorkbookXform.WORKBOOK_ATTRIBUTES);
20188
20719
  this.map.fileVersion.render(xmlStream);
20189
20720
  this.map.workbookPr.render(xmlStream, model.properties);
20721
+ this.map.workbookProtection.render(xmlStream, model.protection);
20190
20722
  this.map.bookViews.render(xmlStream, model.views);
20191
20723
  this.map.sheets.render(xmlStream, model.sheets);
20192
20724
  this.map.definedNames.render(xmlStream, model.definedNames);
@@ -20199,6 +20731,11 @@ onmessage = async (ev) => {
20199
20731
  return true;
20200
20732
  });
20201
20733
  this.map.pivotCaches.render(xmlStream, uniquePivotCaches);
20734
+ const externalLinks = model.externalLinks ?? [];
20735
+ if (externalLinks.length > 0) {
20736
+ const externalReferenceModels = externalLinks.map((link) => ({ rId: link.rId }));
20737
+ this.map.externalReferences.render(xmlStream, externalReferenceModels);
20738
+ }
20202
20739
  xmlStream.closeNode();
20203
20740
  }
20204
20741
  parseOpen(node) {
@@ -20227,11 +20764,13 @@ onmessage = async (ev) => {
20227
20764
  this.model = {
20228
20765
  sheets: this.map.sheets.model,
20229
20766
  properties: this.map.workbookPr.model || {},
20767
+ protection: this.map.workbookProtection.model,
20230
20768
  views: this.map.bookViews.model,
20231
- calcProperties: {}
20769
+ calcProperties: this.map.calcPr.model || {}
20232
20770
  };
20233
20771
  if (this.map.definedNames.model) this.model.definedNames = this.map.definedNames.model;
20234
20772
  if (this.map.pivotCaches.model && this.map.pivotCaches.model.length > 0) this.model.pivotCaches = this.map.pivotCaches.model;
20773
+ if (this.map.externalReferences.model && this.map.externalReferences.model.length > 0) this.model.externalReferences = this.map.externalReferences.model;
20235
20774
  return false;
20236
20775
  default: return true;
20237
20776
  }
@@ -20257,18 +20796,19 @@ onmessage = async (ev) => {
20257
20796
  });
20258
20797
  const definedNames = [];
20259
20798
  if (model.definedNames) model.definedNames.forEach((definedName) => {
20799
+ const effectiveRanges = definedName.ranges?.length > 0 ? definedName.ranges : definedName.rawText ? [definedName.rawText] : [];
20260
20800
  if (definedName.name === "_xlnm.Print_Area") {
20261
20801
  worksheet = worksheets[definedName.localSheetId];
20262
- if (worksheet && definedName.ranges?.length > 0) {
20802
+ if (worksheet && effectiveRanges.length > 0) {
20263
20803
  if (!worksheet.pageSetup) worksheet.pageSetup = {};
20264
- const range = colCache.decodeEx(definedName.ranges[0]);
20804
+ const range = colCache.decodeEx(effectiveRanges[0]);
20265
20805
  worksheet.pageSetup.printArea = worksheet.pageSetup.printArea ? `${worksheet.pageSetup.printArea}&&${range.dimensions}` : range.dimensions;
20266
20806
  }
20267
20807
  } else if (definedName.name === "_xlnm.Print_Titles") {
20268
20808
  worksheet = worksheets[definedName.localSheetId];
20269
- if (worksheet && definedName.ranges?.length > 0) {
20809
+ if (worksheet && effectiveRanges.length > 0) {
20270
20810
  if (!worksheet.pageSetup) worksheet.pageSetup = {};
20271
- const rangeString = definedName.ranges.join(",");
20811
+ const rangeString = effectiveRanges.join(",");
20272
20812
  const dollarRegex = /\$/g;
20273
20813
  const rowRangeMatches = rangeString.match(/\$\d+:\$\d+/);
20274
20814
  if (rowRangeMatches && rowRangeMatches.length) {
@@ -20310,6 +20850,135 @@ onmessage = async (ev) => {
20310
20850
  }
20311
20851
  };
20312
20852
  //#endregion
20853
+ //#region src/modules/excel/xlsx/xform/core/metadata-xform.ts
20854
+ const NS_SPREADSHEETML = "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
20855
+ const NS_DYNAMIC_ARRAY = "http://schemas.microsoft.com/office/spreadsheetml/2017/dynamicarray";
20856
+ const XLDAPR_EXT_URI = "{bdbb8cdc-fa1e-496e-a857-3c3f30c029c3}";
20857
+ var MetadataXform = class extends BaseXform {
20858
+ constructor(..._args) {
20859
+ super(..._args);
20860
+ this._metadataTypeNames = [];
20861
+ this._cellMetadataTypeRefs = [];
20862
+ this._inCellMetadata = false;
20863
+ this._currentRcType = void 0;
20864
+ }
20865
+ get tag() {
20866
+ return "metadata";
20867
+ }
20868
+ /**
20869
+ * Render xl/metadata.xml for the given model.
20870
+ * Only emits content when dynamicArrayCount > 0.
20871
+ */
20872
+ render(xmlStream, model) {
20873
+ if (!model || model.dynamicArrayCount <= 0) return;
20874
+ xmlStream.openXml(StdDocAttributes);
20875
+ xmlStream.openNode("metadata", {
20876
+ xmlns: NS_SPREADSHEETML,
20877
+ "xmlns:xda": NS_DYNAMIC_ARRAY
20878
+ });
20879
+ xmlStream.openNode("metadataTypes", { count: "1" });
20880
+ xmlStream.leafNode("metadataType", {
20881
+ name: "XLDAPR",
20882
+ minSupportedVersion: "120000",
20883
+ copy: "1",
20884
+ pasteAll: "1",
20885
+ pasteValues: "1",
20886
+ merge: "1",
20887
+ splitFirst: "1",
20888
+ rowColShift: "1",
20889
+ clearFormats: "1",
20890
+ clearComments: "1",
20891
+ assign: "1",
20892
+ coerce: "1",
20893
+ adjust: "1",
20894
+ cellMeta: "1"
20895
+ });
20896
+ xmlStream.closeNode();
20897
+ xmlStream.openNode("futureMetadata", {
20898
+ name: "XLDAPR",
20899
+ count: "1"
20900
+ });
20901
+ xmlStream.openNode("bk");
20902
+ xmlStream.openNode("extLst");
20903
+ xmlStream.openNode("ext", { uri: XLDAPR_EXT_URI });
20904
+ xmlStream.leafNode("xda:dynamicArrayProperties", {
20905
+ fDynamic: "1",
20906
+ fCollapsed: "0"
20907
+ });
20908
+ xmlStream.closeNode();
20909
+ xmlStream.closeNode();
20910
+ xmlStream.closeNode();
20911
+ xmlStream.closeNode();
20912
+ xmlStream.openNode("cellMetadata", { count: "1" });
20913
+ xmlStream.openNode("bk");
20914
+ xmlStream.leafNode("rc", {
20915
+ t: "1",
20916
+ v: "0"
20917
+ });
20918
+ xmlStream.closeNode();
20919
+ xmlStream.closeNode();
20920
+ xmlStream.closeNode();
20921
+ }
20922
+ reset() {
20923
+ this._metadataTypeNames = [];
20924
+ this._cellMetadataTypeRefs = [];
20925
+ this._inCellMetadata = false;
20926
+ this._currentRcType = void 0;
20927
+ }
20928
+ parseOpen(node) {
20929
+ switch (node.name) {
20930
+ case "metadata":
20931
+ this.reset();
20932
+ return true;
20933
+ case "metadataType":
20934
+ this._metadataTypeNames.push(node.attributes.name || "");
20935
+ return true;
20936
+ case "cellMetadata":
20937
+ this._inCellMetadata = true;
20938
+ return true;
20939
+ case "bk":
20940
+ if (this._inCellMetadata) this._currentRcType = void 0;
20941
+ return true;
20942
+ case "rc":
20943
+ if (this._inCellMetadata && node.attributes.t !== void 0) this._currentRcType = parseInt(node.attributes.t, 10);
20944
+ return true;
20945
+ case "metadataTypes":
20946
+ case "futureMetadata":
20947
+ case "extLst":
20948
+ case "ext":
20949
+ case "xda:dynamicArrayProperties": return true;
20950
+ default: return false;
20951
+ }
20952
+ }
20953
+ parseText() {}
20954
+ parseClose(name) {
20955
+ switch (name) {
20956
+ case "bk":
20957
+ if (this._inCellMetadata) {
20958
+ this._cellMetadataTypeRefs.push(this._currentRcType ?? 0);
20959
+ this._currentRcType = void 0;
20960
+ }
20961
+ return true;
20962
+ case "cellMetadata":
20963
+ this._inCellMetadata = false;
20964
+ return true;
20965
+ case "metadata": {
20966
+ const dynamicArrayCmIndices = /* @__PURE__ */ new Set();
20967
+ for (let i = 0; i < this._cellMetadataTypeRefs.length; i++) {
20968
+ const typeIndex = this._cellMetadataTypeRefs[i];
20969
+ if (this._metadataTypeNames[typeIndex - 1] === "XLDAPR") dynamicArrayCmIndices.add(i + 1);
20970
+ }
20971
+ this.model = {
20972
+ hasDynamicArrays: dynamicArrayCmIndices.size > 0,
20973
+ dynamicArrayCmIndices
20974
+ };
20975
+ return false;
20976
+ }
20977
+ default: return true;
20978
+ }
20979
+ }
20980
+ };
20981
+ //#endregion
20313
20982
  //#region src/modules/excel/xlsx/xform/core/relationship-xform.ts
20314
20983
  var RelationshipXform = class extends BaseXform {
20315
20984
  render(xmlStream, model) {
@@ -20564,11 +21233,13 @@ onmessage = async (ev) => {
20564
21233
  return true;
20565
21234
  }
20566
21235
  if (name === "border") {
20567
- const model = this.model = {};
21236
+ const model = {};
21237
+ let hasContent = false;
20568
21238
  const add = (key, edgeModel, extensions) => {
20569
21239
  if (edgeModel) {
20570
21240
  if (extensions) Object.assign(edgeModel, extensions);
20571
21241
  model[key] = edgeModel;
21242
+ hasContent = true;
20572
21243
  }
20573
21244
  };
20574
21245
  add("left", this.map.left.model);
@@ -20579,6 +21250,7 @@ onmessage = async (ev) => {
20579
21250
  up: this.diagonalUp,
20580
21251
  down: this.diagonalDown
20581
21252
  });
21253
+ this.model = hasContent ? model : void 0;
20582
21254
  }
20583
21255
  return false;
20584
21256
  }
@@ -21507,7 +22179,8 @@ onmessage = async (ev) => {
21507
22179
  locked: !(node.attributes.locked === "0"),
21508
22180
  hidden: node.attributes.hidden === "1"
21509
22181
  };
21510
- this.model = !model.locked || model.hidden ? model : null;
22182
+ const isSignificant = !model.locked || model.hidden;
22183
+ this.model = isSignificant ? model : null;
21511
22184
  }
21512
22185
  parseText() {}
21513
22186
  parseClose() {
@@ -21911,15 +22584,15 @@ onmessage = async (ev) => {
21911
22584
  family: 2,
21912
22585
  scheme: "minor"
21913
22586
  });
21914
- const type = cellType || Enums.ValueType.Number;
21915
- if (type !== Enums.ValueType.Checkbox && this.weakMap && this.weakMap.has(model)) return this.weakMap.get(model);
22587
+ const type = cellType || 2;
22588
+ if (type !== 12 && this.weakMap && this.weakMap.has(model)) return this.weakMap.get(model);
21916
22589
  const style = {};
21917
22590
  if (model.numFmt) style.numFmtId = this._addNumFmtStr(model.numFmt);
21918
22591
  else switch (type) {
21919
- case Enums.ValueType.Number:
22592
+ case 2:
21920
22593
  style.numFmtId = this._addNumFmtStr("General");
21921
22594
  break;
21922
- case Enums.ValueType.Date:
22595
+ case 4:
21923
22596
  style.numFmtId = this._addNumFmtStr("mm-dd-yy");
21924
22597
  break;
21925
22598
  default: break;
@@ -21938,14 +22611,14 @@ onmessage = async (ev) => {
21938
22611
  "applyAlignment",
21939
22612
  "applyProtection"
21940
22613
  ]) if (model[flag]) style[flag] = true;
21941
- if (type === Enums.ValueType.Checkbox) {
22614
+ if (type === 12) {
21942
22615
  this._hasCheckboxes = true;
21943
22616
  style.alignment = style.alignment || {};
21944
22617
  style.checkbox = true;
21945
22618
  style.xfComplementIndex = 0;
21946
22619
  }
21947
22620
  const styleId = this._addStyle(style);
21948
- if (type !== Enums.ValueType.Checkbox && this.weakMap) this.weakMap.set(model, styleId);
22621
+ if (type !== 12 && this.weakMap) this.weakMap.set(model, styleId);
21949
22622
  return styleId;
21950
22623
  }
21951
22624
  getStyleModel(id) {
@@ -22137,8 +22810,8 @@ onmessage = async (ev) => {
22137
22810
  }
22138
22811
  addStyleModel(model, cellType) {
22139
22812
  switch (cellType) {
22140
- case Enums.ValueType.Checkbox: throw new ExcelNotSupportedError("Checkbox cells", "require styles to be enabled (useStyles: true)");
22141
- case Enums.ValueType.Date: return this.dateStyleId;
22813
+ case 12: throw new ExcelNotSupportedError("Checkbox cells", "require styles to be enabled (useStyles: true)");
22814
+ case 4: return this.dateStyleId;
22142
22815
  default: return 0;
22143
22816
  }
22144
22817
  }
@@ -22182,6 +22855,7 @@ onmessage = async (ev) => {
22182
22855
  }
22183
22856
  constructor(input, options, WorksheetReaderClass, HyperlinkReaderClass) {
22184
22857
  super();
22858
+ this.hasDynamicArrayMetadata = false;
22185
22859
  this._totalBufferedBytes = 0;
22186
22860
  this.input = input;
22187
22861
  this.WorksheetReaderClass = WorksheetReaderClass;
@@ -22275,7 +22949,8 @@ onmessage = async (ev) => {
22275
22949
  if (this.options.entries === "emit") this.emit("entry", payload);
22276
22950
  }
22277
22951
  async _parseRels(entry) {
22278
- this.workbookRels = await new RelationshipsXform().parseStream(iterateStream(entry));
22952
+ const xform = new RelationshipsXform();
22953
+ this.workbookRels = await xform.parseStream(iterateStream(entry));
22279
22954
  this._workbookRelIdByTarget = Object.create(null);
22280
22955
  for (const rel of this.workbookRels ?? []) if (rel?.Target && rel?.Id) this._workbookRelIdByTarget[rel.Target] = rel.Id;
22281
22956
  }
@@ -22516,6 +23191,13 @@ onmessage = async (ev) => {
22516
23191
  await this.styles.parseStream(iterateStream(entry));
22517
23192
  }
22518
23193
  }
23194
+ async _parseMetadata(entry) {
23195
+ const result = await new MetadataXform().parseStream(iterateStream(entry));
23196
+ if (result) {
23197
+ this.hasDynamicArrayMetadata = !!result.hasDynamicArrays;
23198
+ this.dynamicArrayCmIndices = result.dynamicArrayCmIndices;
23199
+ }
23200
+ }
22519
23201
  *_parseWorksheet(iterator, sheetNo) {
22520
23202
  this._emitEntry({
22521
23203
  type: "worksheet",
@@ -22586,6 +23268,9 @@ onmessage = async (ev) => {
22586
23268
  case OOXML_PATHS.xlStyles:
22587
23269
  await this._parseStyles(entry);
22588
23270
  break;
23271
+ case OOXML_PATHS.xlMetadata:
23272
+ await this._parseMetadata(entry);
23273
+ break;
22589
23274
  default:
22590
23275
  sheetNo = getWorksheetNoFromWorksheetPath(normalizedPath)?.toString();
22591
23276
  if (sheetNo) if (!!this.workbookRels && !!this.model && (this.options.sharedStrings !== "cache" || !!this.sharedStrings)) {
@@ -24638,13 +25323,14 @@ self.onmessage = async function(event) {
24638
25323
  this._localHeader = null;
24639
25324
  return;
24640
25325
  }
24641
- this._deflateWanted = !isProbablyIncompressibleChunks((function* (pending, current) {
25326
+ const store = isProbablyIncompressibleChunks((function* (pending, current) {
24642
25327
  for (const c of pending) if (c.length) yield c;
24643
25328
  if (current.length) yield current;
24644
25329
  })(this._pendingChunks, dataForDecision), {
24645
25330
  sampleBytes: SMART_STORE_DECIDE_BYTES,
24646
25331
  minDecisionBytes: SMART_STORE_DECIDE_BYTES
24647
25332
  });
25333
+ this._deflateWanted = !store;
24648
25334
  this._sampleLen = 0;
24649
25335
  this._compressionMethod = this._buildCompressionMethod(this._deflateWanted);
24650
25336
  this._localHeader = null;
@@ -24892,7 +25578,8 @@ self.onmessage = async function(event) {
24892
25578
  if (this._inputPos > 0) {
24893
25579
  const flushData = this._inputBuf.slice(0, this._inputPos);
24894
25580
  this._inputPos = 0;
24895
- const promise = this._pushChain = this._pushAsync(flushData, false).then(() => this._pushUnchained(data, final, callback), () => this._pushUnchained(data, final, callback));
25581
+ const flushPromise = this._pushAsync(flushData, false);
25582
+ const promise = this._pushChain = flushPromise.then(() => this._pushUnchained(data, final, callback), () => this._pushUnchained(data, final, callback));
24896
25583
  promise.catch(() => {});
24897
25584
  return promise;
24898
25585
  }
@@ -26049,6 +26736,7 @@ self.onmessage = async function(event) {
26049
26736
  function filterDrawingAnchors(anchors) {
26050
26737
  return anchors.filter((a) => {
26051
26738
  if (a == null) return false;
26739
+ if (a.range?.pos !== void 0) return !!a.picture;
26052
26740
  if (a.range?.br && a.shape) return true;
26053
26741
  if (!a.range?.br && !a.picture) return false;
26054
26742
  if (a.range?.br && !a.picture && !a.shape) return false;
@@ -26984,7 +27672,7 @@ self.onmessage = async function(event) {
26984
27672
  address,
26985
27673
  dataValidation,
26986
27674
  marked: false
26987
- })).sort((a, b) => a.address.localeCompare(b.address));
27675
+ })).sort((a, b) => colCache.compareAddress(a.address, b.address));
26988
27676
  const dvMap = Object.fromEntries(dvList.map((dv) => [dv.address, dv]));
26989
27677
  const matchCol = (addr, height, col) => {
26990
27678
  for (let i = 0; i < height; i++) {
@@ -27712,6 +28400,69 @@ self.onmessage = async function(event) {
27712
28400
  }
27713
28401
  };
27714
28402
  //#endregion
28403
+ //#region src/modules/excel/xlsx/xform/sheet/ignored-errors-xform.ts
28404
+ /**
28405
+ * Boolean attribute names supported on <ignoredError> elements.
28406
+ * These correspond to the OOXML spec CT_IgnoredError attributes.
28407
+ */
28408
+ const BOOL_ATTRS = [
28409
+ "numberStoredAsText",
28410
+ "formula",
28411
+ "formulaRange",
28412
+ "unlockedFormula",
28413
+ "emptyCellReference",
28414
+ "listDataValidation",
28415
+ "calculatedColumn",
28416
+ "evalError",
28417
+ "twoDigitTextYear"
28418
+ ];
28419
+ /**
28420
+ * Xform for the <ignoredErrors> element in a worksheet.
28421
+ *
28422
+ * Renders:
28423
+ * ```xml
28424
+ * <ignoredErrors>
28425
+ * <ignoredError sqref="A1:B10" numberStoredAsText="1" />
28426
+ * </ignoredErrors>
28427
+ * ```
28428
+ */
28429
+ var IgnoredErrorsXform = class extends BaseXform {
28430
+ get tag() {
28431
+ return "ignoredErrors";
28432
+ }
28433
+ render(xmlStream, model) {
28434
+ if (!model || model.length === 0) return;
28435
+ xmlStream.openNode("ignoredErrors");
28436
+ for (const entry of model) {
28437
+ const attrs = { sqref: entry.ref };
28438
+ for (const attr of BOOL_ATTRS) if (entry[attr]) attrs[attr] = 1;
28439
+ xmlStream.leafNode("ignoredError", attrs);
28440
+ }
28441
+ xmlStream.closeNode();
28442
+ }
28443
+ parseOpen(node) {
28444
+ switch (node.name) {
28445
+ case "ignoredErrors":
28446
+ this.model = [];
28447
+ return true;
28448
+ case "ignoredError": {
28449
+ const entry = { ref: node.attributes.sqref ?? "" };
28450
+ for (const attr of BOOL_ATTRS) if (node.attributes[attr] === "1" || node.attributes[attr] === "true") entry[attr] = true;
28451
+ this.model.push(entry);
28452
+ return true;
28453
+ }
28454
+ default: return true;
28455
+ }
28456
+ }
28457
+ parseText() {}
28458
+ parseClose(name) {
28459
+ switch (name) {
28460
+ case "ignoredErrors": return false;
28461
+ default: return true;
28462
+ }
28463
+ }
28464
+ };
28465
+ //#endregion
27715
28466
  //#region src/modules/excel/xlsx/xform/sheet/page-margins-xform.ts
27716
28467
  var PageMarginsXform = class extends BaseXform {
27717
28468
  get tag() {
@@ -27892,22 +28643,62 @@ self.onmessage = async function(event) {
27892
28643
  //#endregion
27893
28644
  //#region src/modules/excel/xlsx/xform/sheet/cell-xform.ts
27894
28645
  function getValueType(v) {
27895
- if (v === null || v === void 0) return Enums.ValueType.Null;
27896
- if (v instanceof String || typeof v === "string") return Enums.ValueType.String;
27897
- if (typeof v === "number") return Enums.ValueType.Number;
27898
- if (typeof v === "boolean") return Enums.ValueType.Boolean;
27899
- if (v instanceof Date) return Enums.ValueType.Date;
27900
- if (v.text && v.hyperlink) return Enums.ValueType.Hyperlink;
27901
- if (v.formula) return Enums.ValueType.Formula;
27902
- if (v.error) return Enums.ValueType.Error;
28646
+ if (v === null || v === void 0) return 0;
28647
+ if (v instanceof String || typeof v === "string") return 3;
28648
+ if (typeof v === "number") return 2;
28649
+ if (typeof v === "boolean") return 9;
28650
+ if (v instanceof Date) return 4;
28651
+ if (v.text && v.hyperlink) return 5;
28652
+ if (v.formula) return 6;
28653
+ if (v.error) return 10;
27903
28654
  throw new InvalidValueTypeError(typeof v, "Could not understand type of value");
27904
28655
  }
27905
28656
  function getEffectiveCellType(cell) {
27906
28657
  switch (cell.type) {
27907
- case Enums.ValueType.Formula: return getValueType(cell.result);
28658
+ case 6: return getValueType(cell.result);
27908
28659
  default: return cell.type;
27909
28660
  }
27910
28661
  }
28662
+ /**
28663
+ * Extract the display form of a hyperlink cell value that came either from
28664
+ * a shared-string rich-text payload (`{ richText: [...] }`) or from a
28665
+ * plain scalar.
28666
+ *
28667
+ * Input comes from the XML parser (`raw: unknown`), so every nested value is
28668
+ * treated defensively — the public `RichText` shape is only produced after
28669
+ * runtime validation, never asserted.
28670
+ *
28671
+ * Returns:
28672
+ * - `text`: always a string (flattened rich-text or `String(raw)`)
28673
+ * - `richText`: preserved if the source was a rich-text payload, else undefined
28674
+ *
28675
+ * This keeps the CellHyperlinkValue.text: string public contract intact while
28676
+ * also letting the Hyperlink value class retain the formatted runs
28677
+ * (see https://github.com/cjnoname/excelts/issues/142).
28678
+ */
28679
+ function extractHyperlinkDisplay(raw) {
28680
+ if (raw === null || raw === void 0) return { text: "" };
28681
+ if (typeof raw === "string") return { text: raw };
28682
+ if (typeof raw === "number" || typeof raw === "boolean") return { text: String(raw) };
28683
+ if (typeof raw === "object") {
28684
+ const obj = raw;
28685
+ if (Array.isArray(obj.richText)) {
28686
+ if (obj.richText.length === 0) return { text: "" };
28687
+ const runs = obj.richText.map((rawRun) => {
28688
+ const run = rawRun;
28689
+ const normalized = { text: typeof run?.text === "string" ? run.text : "" };
28690
+ if (run?.font !== null && typeof run?.font === "object") normalized.font = run.font;
28691
+ return normalized;
28692
+ });
28693
+ return {
28694
+ text: runs.map((r) => r.text).join(""),
28695
+ richText: runs
28696
+ };
28697
+ }
28698
+ if (typeof obj.error === "string") return { text: obj.error };
28699
+ }
28700
+ return { text: String(raw) };
28701
+ }
27911
28702
  var CellXform = class extends BaseXform {
27912
28703
  constructor() {
27913
28704
  super();
@@ -27924,26 +28715,35 @@ self.onmessage = async function(event) {
27924
28715
  ref: model.address
27925
28716
  });
27926
28717
  switch (model.type) {
27927
- case Enums.ValueType.String:
27928
- case Enums.ValueType.RichText:
28718
+ case 3:
28719
+ case 8:
27929
28720
  if (options.sharedStrings) model.ssId = options.sharedStrings.add(model.value);
27930
28721
  break;
27931
- case Enums.ValueType.Date:
28722
+ case 4:
27932
28723
  if (options.date1904) model.date1904 = true;
27933
28724
  break;
27934
- case Enums.ValueType.Hyperlink:
27935
- if (options.sharedStrings && model.text !== void 0 && model.text !== null) model.ssId = options.sharedStrings.add(model.text);
28725
+ case 5:
28726
+ if (options.sharedStrings) {
28727
+ if (Array.isArray(model.richText) && model.richText.length > 0) model.ssId = options.sharedStrings.add({ richText: model.richText });
28728
+ else if (model.text !== void 0 && model.text !== null) model.ssId = options.sharedStrings.add(model.text);
28729
+ }
27936
28730
  options.hyperlinks.push({
27937
28731
  address: model.address,
27938
28732
  target: model.hyperlink,
27939
28733
  tooltip: model.tooltip
27940
28734
  });
27941
28735
  break;
27942
- case Enums.ValueType.Merge:
28736
+ case 1:
27943
28737
  options.merges.add(model);
27944
28738
  break;
27945
- case Enums.ValueType.Formula:
28739
+ case 6:
27946
28740
  if (options.date1904) model.date1904 = true;
28741
+ if (model.isDynamicArray) model.cm = 1;
28742
+ if (model.hyperlink) options.hyperlinks.push({
28743
+ address: model.address,
28744
+ target: model.hyperlink,
28745
+ tooltip: model.tooltip
28746
+ });
27947
28747
  if (model.shareType === "shared") model.si = options.siFormulae++;
27948
28748
  if (model.formula) options.formulae[model.address] = model;
27949
28749
  else if (model.sharedFormula) {
@@ -27984,29 +28784,29 @@ self.onmessage = async function(event) {
27984
28784
  break;
27985
28785
  }
27986
28786
  switch (getValueType(model.result)) {
27987
- case Enums.ValueType.Null:
28787
+ case 0:
27988
28788
  xmlStream.leafNode("f", attrs, model.formula);
27989
28789
  break;
27990
- case Enums.ValueType.String:
28790
+ case 3:
27991
28791
  xmlStream.addAttribute("t", "str");
27992
28792
  xmlStream.leafNode("f", attrs, model.formula);
27993
28793
  xmlStream.leafNode("v", null, model.result);
27994
28794
  break;
27995
- case Enums.ValueType.Number:
28795
+ case 2:
27996
28796
  xmlStream.leafNode("f", attrs, model.formula);
27997
28797
  xmlStream.leafNode("v", null, model.result);
27998
28798
  break;
27999
- case Enums.ValueType.Boolean:
28799
+ case 9:
28000
28800
  xmlStream.addAttribute("t", "b");
28001
28801
  xmlStream.leafNode("f", attrs, model.formula);
28002
28802
  xmlStream.leafNode("v", null, model.result ? 1 : 0);
28003
28803
  break;
28004
- case Enums.ValueType.Error:
28804
+ case 10:
28005
28805
  xmlStream.addAttribute("t", "e");
28006
28806
  xmlStream.leafNode("f", attrs, model.formula);
28007
28807
  xmlStream.leafNode("v", null, model.result.error);
28008
28808
  break;
28009
- case Enums.ValueType.Date:
28809
+ case 4:
28010
28810
  xmlStream.leafNode("f", attrs, model.formula);
28011
28811
  xmlStream.leafNode("v", null, dateToExcel(model.result, model.date1904));
28012
28812
  break;
@@ -28014,29 +28814,30 @@ self.onmessage = async function(event) {
28014
28814
  }
28015
28815
  }
28016
28816
  render(xmlStream, model) {
28017
- if (model.type === Enums.ValueType.Null && !model.styleId) return;
28817
+ if (model.type === 0 && !model.styleId) return;
28018
28818
  xmlStream.openNode("c");
28019
28819
  xmlStream.addAttribute("r", model.address);
28020
28820
  if (model.styleId) xmlStream.addAttribute("s", model.styleId);
28821
+ if (model.cm) xmlStream.addAttribute("cm", model.cm);
28021
28822
  switch (model.type) {
28022
- case Enums.ValueType.Null: break;
28023
- case Enums.ValueType.Number:
28823
+ case 0: break;
28824
+ case 2:
28024
28825
  xmlStream.leafNode("v", null, model.value);
28025
28826
  break;
28026
- case Enums.ValueType.Boolean:
28827
+ case 9:
28027
28828
  xmlStream.addAttribute("t", "b");
28028
28829
  xmlStream.leafNode("v", null, model.value ? "1" : "0");
28029
28830
  break;
28030
- case Enums.ValueType.Checkbox:
28831
+ case 12:
28031
28832
  xmlStream.addAttribute("t", "b");
28032
28833
  xmlStream.leafNode("v", null, model.value ? "1" : "0");
28033
28834
  break;
28034
- case Enums.ValueType.Error:
28835
+ case 10:
28035
28836
  xmlStream.addAttribute("t", "e");
28036
28837
  xmlStream.leafNode("v", null, model.value.error);
28037
28838
  break;
28038
- case Enums.ValueType.String:
28039
- case Enums.ValueType.RichText:
28839
+ case 3:
28840
+ case 8:
28040
28841
  if (model.ssId !== void 0) {
28041
28842
  xmlStream.addAttribute("t", "s");
28042
28843
  xmlStream.leafNode("v", null, model.ssId);
@@ -28052,22 +28853,30 @@ self.onmessage = async function(event) {
28052
28853
  xmlStream.leafNode("v", null, model.value);
28053
28854
  }
28054
28855
  break;
28055
- case Enums.ValueType.Date:
28856
+ case 4:
28056
28857
  xmlStream.leafNode("v", null, dateToExcel(model.value, model.date1904));
28057
28858
  break;
28058
- case Enums.ValueType.Hyperlink:
28059
- if (model.ssId !== void 0) {
28859
+ case 5:
28860
+ if (model.formula || model.sharedFormula) this.renderFormula(xmlStream, model);
28861
+ else if (model.ssId !== void 0) {
28060
28862
  xmlStream.addAttribute("t", "s");
28061
28863
  xmlStream.leafNode("v", null, model.ssId);
28864
+ } else if (Array.isArray(model.richText) && model.richText.length > 0) {
28865
+ xmlStream.addAttribute("t", "inlineStr");
28866
+ xmlStream.openNode("is");
28867
+ model.richText.forEach((text) => {
28868
+ this.richTextXform.render(xmlStream, text);
28869
+ });
28870
+ xmlStream.closeNode("is");
28062
28871
  } else {
28063
28872
  xmlStream.addAttribute("t", "str");
28064
28873
  xmlStream.leafNode("v", null, model.text);
28065
28874
  }
28066
28875
  break;
28067
- case Enums.ValueType.Formula:
28876
+ case 6:
28068
28877
  this.renderFormula(xmlStream, model);
28069
28878
  break;
28070
- case Enums.ValueType.Merge: break;
28879
+ case 1: break;
28071
28880
  default: break;
28072
28881
  }
28073
28882
  xmlStream.closeNode();
@@ -28082,6 +28891,7 @@ self.onmessage = async function(event) {
28082
28891
  this.model = { address: node.attributes.r };
28083
28892
  this.t = node.attributes.t;
28084
28893
  if (node.attributes.s) this.model.styleId = parseInt(node.attributes.s, 10);
28894
+ if (node.attributes.cm) this.model.cm = parseInt(node.attributes.cm, 10);
28085
28895
  return true;
28086
28896
  case "f":
28087
28897
  this.currentNode = "f";
@@ -28124,7 +28934,7 @@ self.onmessage = async function(event) {
28124
28934
  case "c": {
28125
28935
  const { model } = this;
28126
28936
  if (model.formula || model.shareType) {
28127
- model.type = Enums.ValueType.Formula;
28937
+ model.type = 6;
28128
28938
  if (model.value) {
28129
28939
  if (this.t === "str") model.result = model.value;
28130
28940
  else if (this.t === "b") model.result = parseInt(model.value, 10) !== 0;
@@ -28134,35 +28944,35 @@ self.onmessage = async function(event) {
28134
28944
  }
28135
28945
  } else if (model.value !== void 0) switch (this.t) {
28136
28946
  case "s":
28137
- model.type = Enums.ValueType.String;
28947
+ model.type = 3;
28138
28948
  model.value = parseInt(model.value, 10);
28139
28949
  break;
28140
28950
  case "str":
28141
- model.type = Enums.ValueType.String;
28951
+ model.type = 3;
28142
28952
  break;
28143
28953
  case "inlineStr":
28144
- model.type = Enums.ValueType.String;
28954
+ model.type = 3;
28145
28955
  if (typeof model.value === "string" && model.value.includes("_x")) model.value = decodeOoxmlEscape(model.value);
28146
28956
  break;
28147
28957
  case "b":
28148
- model.type = Enums.ValueType.Boolean;
28958
+ model.type = 9;
28149
28959
  model.value = parseInt(model.value, 10) !== 0;
28150
28960
  break;
28151
28961
  case "e":
28152
- model.type = Enums.ValueType.Error;
28962
+ model.type = 10;
28153
28963
  model.value = { error: model.value };
28154
28964
  break;
28155
28965
  case "d":
28156
- model.type = Enums.ValueType.Date;
28966
+ model.type = 4;
28157
28967
  model.value = new Date(model.value);
28158
28968
  break;
28159
28969
  default:
28160
- model.type = Enums.ValueType.Number;
28970
+ model.type = 2;
28161
28971
  model.value = parseFloat(model.value);
28162
28972
  break;
28163
28973
  }
28164
- else if (model.styleId) model.type = Enums.ValueType.Null;
28165
- else model.type = Enums.ValueType.Merge;
28974
+ else if (model.styleId) model.type = 0;
28975
+ else model.type = 1;
28166
28976
  return false;
28167
28977
  }
28168
28978
  case "f":
@@ -28197,19 +29007,23 @@ self.onmessage = async function(event) {
28197
29007
  if (style) model.style = style;
28198
29008
  if (model.styleId !== void 0) model.styleId = void 0;
28199
29009
  switch (model.type) {
28200
- case Enums.ValueType.String:
29010
+ case 3:
28201
29011
  if (typeof model.value === "number") {
28202
- if (options.sharedStrings) model.value = options.sharedStrings.getString(model.value);
29012
+ if (options.sharedStrings) {
29013
+ const ssIndex = model.value;
29014
+ model.value = options.sharedStrings.getString(ssIndex);
29015
+ if (model.value === void 0) throw new ExcelError(`Invalid shared string index ${ssIndex} in cell ${model.address}: the xlsx file appears to be corrupted`);
29016
+ }
28203
29017
  }
28204
- if (model.value.richText) model.type = Enums.ValueType.RichText;
29018
+ if (model.value && model.value.richText) model.type = 8;
28205
29019
  break;
28206
- case Enums.ValueType.Number:
29020
+ case 2:
28207
29021
  if (style && isDateFmt(style.numFmt)) {
28208
- model.type = Enums.ValueType.Date;
29022
+ model.type = 4;
28209
29023
  model.value = excelToDate(model.value, options.date1904);
28210
29024
  }
28211
29025
  break;
28212
- case Enums.ValueType.Formula:
29026
+ case 6:
28213
29027
  if (model.result !== void 0 && typeof model.result === "number" && style && isDateFmt(style.numFmt)) model.result = excelToDate(model.result, options.date1904);
28214
29028
  if (model.shareType === "shared") {
28215
29029
  if (model.ref) options.formulae[model.si] = model.address;
@@ -28219,19 +29033,28 @@ self.onmessage = async function(event) {
28219
29033
  }
28220
29034
  delete model.si;
28221
29035
  }
29036
+ if (model.cm) {
29037
+ if (options.dynamicArrayCmIndices) {
29038
+ if (options.dynamicArrayCmIndices.has(model.cm)) model.isDynamicArray = true;
29039
+ } else if (options.hasDynamicArrayMetadata) model.isDynamicArray = true;
29040
+ }
29041
+ delete model.cm;
28222
29042
  break;
28223
29043
  default: break;
28224
29044
  }
28225
29045
  const hyperlink = options.hyperlinkMap[model.address];
28226
29046
  if (hyperlink) {
28227
- if (model.type === Enums.ValueType.Formula) {
28228
- model.text = model.result;
28229
- model.result = void 0;
28230
- } else {
28231
- model.text = model.value;
29047
+ let source;
29048
+ if (model.type === 6) source = model.result;
29049
+ else {
29050
+ source = model.value;
28232
29051
  model.value = void 0;
28233
29052
  }
28234
- model.type = Enums.ValueType.Hyperlink;
29053
+ const display = extractHyperlinkDisplay(source);
29054
+ model.text = display.text;
29055
+ if (display.richText) model.richText = display.richText;
29056
+ else delete model.richText;
29057
+ model.type = 5;
28235
29058
  model.hyperlink = hyperlink;
28236
29059
  }
28237
29060
  const comment = options.commentsMap && options.commentsMap[model.address];
@@ -28301,8 +29124,9 @@ self.onmessage = async function(event) {
28301
29124
  spanMin = parseInt(spans, 10);
28302
29125
  spanMax = colonIdx > -1 ? parseInt(spans.substring(colonIdx + 1), 10) : void 0;
28303
29126
  }
29127
+ const rowNumber = node.attributes.r ? parseInt(node.attributes.r, 10) : this.numRowsSeen;
28304
29128
  const model = this.model = {
28305
- number: node.attributes.r ? parseInt(node.attributes.r, 10) : this.numRowsSeen,
29129
+ number: rowNumber,
28306
29130
  min: spanMin,
28307
29131
  max: spanMax,
28308
29132
  cells: []
@@ -28786,6 +29610,7 @@ self.onmessage = async function(event) {
28786
29610
  drawing: new DrawingXform$1(),
28787
29611
  conditionalFormattings: new ConditionalFormattingsXform(),
28788
29612
  extLst: new ExtLstXform$1(),
29613
+ ignoredErrors: new IgnoredErrorsXform(),
28789
29614
  headerFooter: new HeaderFooterXform(),
28790
29615
  rowBreaks: new RowBreaksXform(),
28791
29616
  colBreaks: new ColBreaksXform()
@@ -28810,6 +29635,7 @@ self.onmessage = async function(event) {
28810
29635
  this._formulae = {};
28811
29636
  this._siFormulae = 0;
28812
29637
  this.conditionalFormatting = [];
29638
+ this.ignoredErrors = [];
28813
29639
  this.rowBreaks = [];
28814
29640
  this.colBreaks = [];
28815
29641
  this.properties = Object.assign({}, {
@@ -28900,6 +29726,7 @@ self.onmessage = async function(event) {
28900
29726
  this._writeDrawing();
28901
29727
  this._writeBackground();
28902
29728
  this._writeLegacyData();
29729
+ this._writeIgnoredErrors();
28903
29730
  this._writeExtLst();
28904
29731
  this._writeCloseWorksheet();
28905
29732
  this.stream.end();
@@ -29146,17 +29973,33 @@ self.onmessage = async function(event) {
29146
29973
  };
29147
29974
  throw new Error(`Invalid image range: "${range}". Expected a range like "A1:C3".`);
29148
29975
  }
29149
- return {
29976
+ if ("pos" in range && range.pos) return {
29150
29977
  type: "image",
29151
29978
  imageId,
29152
29979
  range: {
29153
- tl: new Anchor(this, range.tl, 0).model,
29154
- br: range.br ? new Anchor(this, range.br, 0).model : void 0,
29980
+ tl: {
29981
+ nativeCol: 0,
29982
+ nativeColOff: 0,
29983
+ nativeRow: 0,
29984
+ nativeRowOff: 0
29985
+ },
29155
29986
  ext: range.ext,
29156
- editAs: range.editAs
29987
+ pos: range.pos
29157
29988
  },
29158
29989
  hyperlinks: range.hyperlinks
29159
29990
  };
29991
+ const cellRange = range;
29992
+ return {
29993
+ type: "image",
29994
+ imageId,
29995
+ range: {
29996
+ tl: new Anchor(this, cellRange.tl, 0).model,
29997
+ br: cellRange.br ? new Anchor(this, cellRange.br, 0).model : void 0,
29998
+ ext: cellRange.ext,
29999
+ editAs: cellRange.editAs
30000
+ },
30001
+ hyperlinks: cellRange.hyperlinks
30002
+ };
29160
30003
  }
29161
30004
  async protect(password, options) {
29162
30005
  this.sheetProtection = { sheet: true };
@@ -29215,6 +30058,7 @@ self.onmessage = async function(event) {
29215
30058
  }
29216
30059
  if (row.hasValues || row.height != null) {
29217
30060
  const { model } = row;
30061
+ if (!model) return;
29218
30062
  const options = {
29219
30063
  styles: this._workbook.styles,
29220
30064
  sharedStrings: this.useSharedStrings ? this._workbook.sharedStrings : void 0,
@@ -29226,6 +30070,9 @@ self.onmessage = async function(event) {
29226
30070
  };
29227
30071
  xform.row.prepare(model, options);
29228
30072
  this.stream.write(xform.row.toXml(model));
30073
+ if (model.cells) {
30074
+ for (const cell of model.cells) if (cell && cell.isDynamicArray) this._workbook.dynamicArrayCount++;
30075
+ }
29229
30076
  if (options.comments.length) {
29230
30077
  this.hasComments = true;
29231
30078
  this._sheetCommentsWriter.addComments(options.comments);
@@ -29268,6 +30115,9 @@ self.onmessage = async function(event) {
29268
30115
  const model = { conditionalFormattings: this.conditionalFormatting };
29269
30116
  this.stream.write(xform.extLst.toXml(model));
29270
30117
  }
30118
+ _writeIgnoredErrors() {
30119
+ if (this.ignoredErrors.length > 0) this.stream.write(xform.ignoredErrors.toXml(this.ignoredErrors));
30120
+ }
29271
30121
  _writeRowBreaks() {
29272
30122
  this.stream.write(xform.rowBreaks.toXml(this.rowBreaks));
29273
30123
  }
@@ -29330,6 +30180,7 @@ self.onmessage = async function(event) {
29330
30180
  if (this._background) {
29331
30181
  if (this._background.imageId !== void 0) {
29332
30182
  const image = this._workbook.getImage(this._background.imageId);
30183
+ if (!image) return;
29333
30184
  const pictureId = this._sheetRelsWriter.addMedia({
29334
30185
  Target: mediaRelTargetFromRels(image.name),
29335
30186
  Type: RelType.Image
@@ -29356,6 +30207,20 @@ self.onmessage = async function(event) {
29356
30207
  };
29357
30208
  //#endregion
29358
30209
  //#region src/modules/excel/utils/shared-strings.ts
30210
+ /**
30211
+ * Canonical JSON serializer with sorted object keys.
30212
+ *
30213
+ * Used to derive an insertion-order-independent dedupe key for rich-text
30214
+ * shared-string entries. Two semantically identical run/font objects must
30215
+ * map to the same key regardless of the order their properties were assigned.
30216
+ */
30217
+ function canonicalStringify(value) {
30218
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
30219
+ if (Array.isArray(value)) return `[${value.map(canonicalStringify).join(",")}]`;
30220
+ const keys = Object.keys(value).sort();
30221
+ const obj = value;
30222
+ return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalStringify(obj[k])}`).join(",")}}`;
30223
+ }
29359
30224
  var SharedStrings = class {
29360
30225
  constructor() {
29361
30226
  this._values = [];
@@ -29375,9 +30240,10 @@ self.onmessage = async function(event) {
29375
30240
  return this._values[index];
29376
30241
  }
29377
30242
  add(value) {
29378
- let index = this._hash[value];
30243
+ const key = typeof value === "string" ? `s:${value}` : `r:${canonicalStringify(value.richText)}`;
30244
+ let index = this._hash[key];
29379
30245
  if (index === void 0) {
29380
- index = this._hash[value] = this._values.length;
30246
+ index = this._hash[key] = this._values.length;
29381
30247
  this._values.push(value);
29382
30248
  }
29383
30249
  this._totalRefs++;
@@ -29773,6 +30639,33 @@ self.onmessage = async function(event) {
29773
30639
  return result;
29774
30640
  }
29775
30641
  //#endregion
30642
+ //#region src/modules/excel/utils/workbook-protection.ts
30643
+ /**
30644
+ * Build a workbook-protection object with optional password hashing.
30645
+ *
30646
+ * This is the shared implementation used by both `Workbook.protect()` and
30647
+ * `WorkbookWriter.protect()`. The caller is responsible for assigning the
30648
+ * result to its own `protection` field.
30649
+ *
30650
+ * @param password - Optional password to hash
30651
+ * @param options - Optional protection flags (lockStructure, lockWindows, lockRevision, spinCount)
30652
+ * @returns A fully-populated workbook-protection object
30653
+ */
30654
+ async function buildWorkbookProtection(password, options) {
30655
+ const protection = {
30656
+ lockStructure: options?.lockStructure ?? true,
30657
+ lockWindows: options?.lockWindows,
30658
+ lockRevision: options?.lockRevision
30659
+ };
30660
+ if (password) {
30661
+ protection.algorithmName = "SHA-512";
30662
+ protection.saltValue = uint8ArrayToBase64(Encryptor.randomBytes(16));
30663
+ protection.spinCount = options?.spinCount != null && Number.isFinite(options.spinCount) ? Math.round(Math.max(0, options.spinCount)) : 1e5;
30664
+ protection.hashValue = await Encryptor.convertPasswordToHash(password, "SHA-512", protection.saltValue, protection.spinCount);
30665
+ }
30666
+ return protection;
30667
+ }
30668
+ //#endregion
29776
30669
  //#region src/modules/excel/xlsx/xform/core/app-heading-pairs-xform.ts
29777
30670
  var AppHeadingPairsXform = class extends BaseXform {
29778
30671
  render(xmlStream, model) {
@@ -29966,10 +30859,18 @@ self.onmessage = async function(event) {
29966
30859
  PartName: toContentTypesPartName(OOXML_PATHS.xlStyles),
29967
30860
  ContentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"
29968
30861
  });
30862
+ if (model.externalLinks && model.externalLinks.length > 0) for (const link of model.externalLinks) xmlStream.leafNode("Override", {
30863
+ PartName: toContentTypesPartName(externalLinkPath(link.index)),
30864
+ ContentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.externalLink+xml"
30865
+ });
29969
30866
  if (model.hasCheckboxes) xmlStream.leafNode("Override", {
29970
30867
  PartName: toContentTypesPartName(OOXML_PATHS.xlFeaturePropertyBag),
29971
30868
  ContentType: "application/vnd.ms-excel.featurepropertybag+xml"
29972
30869
  });
30870
+ if (model.hasDynamicArrayFormulas) xmlStream.leafNode("Override", {
30871
+ PartName: toContentTypesPartName(OOXML_PATHS.xlMetadata),
30872
+ ContentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheetMetadata+xml"
30873
+ });
29973
30874
  if (model.sharedStrings && model.sharedStrings.count) xmlStream.leafNode("Override", {
29974
30875
  PartName: toContentTypesPartName(OOXML_PATHS.xlSharedStrings),
29975
30876
  ContentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"
@@ -30236,7 +31137,9 @@ self.onmessage = async function(event) {
30236
31137
  }
30237
31138
  reconcilePicture(model, options) {
30238
31139
  if (model && model.rId) {
30239
- const match = options.rels[model.rId].Target.match(/.*\/media\/(.+[.][a-zA-Z]{3,4})/);
31140
+ const rel = options.rels[model.rId];
31141
+ if (!rel) return;
31142
+ const match = rel.Target.match(/.*\/media\/(.+[.][a-zA-Z]{3,4})/);
30240
31143
  if (match) {
30241
31144
  const name = match[1];
30242
31145
  const mediaId = options.mediaIndex[name];
@@ -30251,85 +31154,9 @@ self.onmessage = async function(event) {
30251
31154
  }
30252
31155
  };
30253
31156
  //#endregion
30254
- //#region src/modules/excel/xlsx/xform/drawing/cell-position-xform.ts
30255
- var CellPositionXform = class extends BaseXform {
30256
- constructor(options) {
30257
- super();
30258
- this.tag = options.tag;
30259
- this.map = {
30260
- "xdr:col": new IntegerXform({
30261
- tag: "xdr:col",
30262
- zero: true
30263
- }),
30264
- "xdr:colOff": new IntegerXform({
30265
- tag: "xdr:colOff",
30266
- zero: true
30267
- }),
30268
- "xdr:row": new IntegerXform({
30269
- tag: "xdr:row",
30270
- zero: true
30271
- }),
30272
- "xdr:rowOff": new IntegerXform({
30273
- tag: "xdr:rowOff",
30274
- zero: true
30275
- })
30276
- };
30277
- this.model = {
30278
- nativeCol: 0,
30279
- nativeColOff: 0,
30280
- nativeRow: 0,
30281
- nativeRowOff: 0
30282
- };
30283
- }
30284
- render(xmlStream, model) {
30285
- xmlStream.openNode(this.tag);
30286
- this.map["xdr:col"].render(xmlStream, model.nativeCol);
30287
- this.map["xdr:colOff"].render(xmlStream, model.nativeColOff);
30288
- this.map["xdr:row"].render(xmlStream, model.nativeRow);
30289
- this.map["xdr:rowOff"].render(xmlStream, model.nativeRowOff);
30290
- xmlStream.closeNode();
30291
- }
30292
- parseOpen(node) {
30293
- if (this.parser) {
30294
- this.parser.parseOpen(node);
30295
- return true;
30296
- }
30297
- switch (node.name) {
30298
- case this.tag:
30299
- this.reset();
30300
- break;
30301
- default:
30302
- this.parser = this.map[node.name];
30303
- if (this.parser) this.parser.parseOpen(node);
30304
- break;
30305
- }
30306
- return true;
30307
- }
30308
- parseText(text) {
30309
- if (this.parser) this.parser.parseText(text);
30310
- }
30311
- parseClose(name) {
30312
- if (this.parser) {
30313
- if (!this.parser.parseClose(name)) this.parser = void 0;
30314
- return true;
30315
- }
30316
- switch (name) {
30317
- case this.tag:
30318
- this.model = {
30319
- nativeCol: this.map["xdr:col"].model,
30320
- nativeColOff: this.map["xdr:colOff"].model,
30321
- nativeRow: this.map["xdr:row"].model,
30322
- nativeRowOff: this.map["xdr:rowOff"].model
30323
- };
30324
- return false;
30325
- default: return true;
30326
- }
30327
- }
30328
- };
30329
- //#endregion
30330
31157
  //#region src/modules/excel/xlsx/xform/drawing/ext-xform.ts
30331
31158
  /** https://en.wikipedia.org/wiki/Office_Open_XML_file_formats#DrawingML */
30332
- const EMU_PER_PIXEL_AT_96_DPI = 9525;
31159
+ const EMU_PER_PIXEL_AT_96_DPI$1 = 9525;
30333
31160
  var ExtXform = class extends BaseXform {
30334
31161
  constructor(options) {
30335
31162
  super();
@@ -30342,8 +31169,8 @@ self.onmessage = async function(event) {
30342
31169
  }
30343
31170
  render(xmlStream, model) {
30344
31171
  xmlStream.openNode(this.tag);
30345
- const width = Math.floor(model.width * EMU_PER_PIXEL_AT_96_DPI);
30346
- const height = Math.floor(model.height * EMU_PER_PIXEL_AT_96_DPI);
31172
+ const width = Math.floor(model.width * EMU_PER_PIXEL_AT_96_DPI$1);
31173
+ const height = Math.floor(model.height * EMU_PER_PIXEL_AT_96_DPI$1);
30347
31174
  xmlStream.addAttribute("cx", width);
30348
31175
  xmlStream.addAttribute("cy", height);
30349
31176
  xmlStream.closeNode();
@@ -30351,8 +31178,8 @@ self.onmessage = async function(event) {
30351
31178
  parseOpen(node) {
30352
31179
  if (node.name === this.tag) {
30353
31180
  this.model = {
30354
- width: parseInt(node.attributes.cx ?? "0", 10) / EMU_PER_PIXEL_AT_96_DPI,
30355
- height: parseInt(node.attributes.cy ?? "0", 10) / EMU_PER_PIXEL_AT_96_DPI
31181
+ width: parseInt(node.attributes.cx ?? "0", 10) / EMU_PER_PIXEL_AT_96_DPI$1,
31182
+ height: parseInt(node.attributes.cy ?? "0", 10) / EMU_PER_PIXEL_AT_96_DPI$1
30356
31183
  };
30357
31184
  return true;
30358
31185
  }
@@ -30729,6 +31556,182 @@ self.onmessage = async function(event) {
30729
31556
  }
30730
31557
  };
30731
31558
  //#endregion
31559
+ //#region src/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.ts
31560
+ /** https://en.wikipedia.org/wiki/Office_Open_XML_file_formats#DrawingML */
31561
+ const EMU_PER_PIXEL_AT_96_DPI = 9525;
31562
+ /**
31563
+ * Xform for the <xdr:pos> element (absolute position in EMU).
31564
+ * Converts between EMU (in XML) and pixels (in model).
31565
+ */
31566
+ var PosXform = class extends BaseXform {
31567
+ constructor() {
31568
+ super();
31569
+ this.map = {};
31570
+ this.model = {
31571
+ x: 0,
31572
+ y: 0
31573
+ };
31574
+ }
31575
+ get tag() {
31576
+ return "xdr:pos";
31577
+ }
31578
+ render(xmlStream, model) {
31579
+ xmlStream.leafNode(this.tag, {
31580
+ x: Math.floor(model.x * EMU_PER_PIXEL_AT_96_DPI),
31581
+ y: Math.floor(model.y * EMU_PER_PIXEL_AT_96_DPI)
31582
+ });
31583
+ }
31584
+ parseOpen(node) {
31585
+ if (node.name === this.tag) {
31586
+ this.model = {
31587
+ x: parseInt(node.attributes.x ?? "0", 10) / EMU_PER_PIXEL_AT_96_DPI,
31588
+ y: parseInt(node.attributes.y ?? "0", 10) / EMU_PER_PIXEL_AT_96_DPI
31589
+ };
31590
+ return true;
31591
+ }
31592
+ return false;
31593
+ }
31594
+ parseText() {}
31595
+ parseClose() {
31596
+ return false;
31597
+ }
31598
+ };
31599
+ /**
31600
+ * Xform for <xdr:absoluteAnchor> — images positioned by absolute coordinates
31601
+ * rather than cell references.
31602
+ *
31603
+ * Structure:
31604
+ * ```xml
31605
+ * <xdr:absoluteAnchor>
31606
+ * <xdr:pos x="0" y="0"/>
31607
+ * <xdr:ext cx="1000000" cy="1000000"/>
31608
+ * <xdr:pic>...</xdr:pic>
31609
+ * <xdr:clientData/>
31610
+ * </xdr:absoluteAnchor>
31611
+ * ```
31612
+ */
31613
+ var AbsoluteAnchorXform = class extends BaseCellAnchorXform {
31614
+ constructor() {
31615
+ super();
31616
+ this.map = {
31617
+ "xdr:pos": new PosXform(),
31618
+ "xdr:ext": new ExtXform({ tag: "xdr:ext" }),
31619
+ "xdr:pic": new PicXform(),
31620
+ "xdr:clientData": new StaticXform({ tag: "xdr:clientData" })
31621
+ };
31622
+ }
31623
+ get tag() {
31624
+ return "xdr:absoluteAnchor";
31625
+ }
31626
+ prepare(model, options) {
31627
+ if (model.picture) this.map["xdr:pic"].prepare(model.picture, options);
31628
+ }
31629
+ render(xmlStream, model) {
31630
+ xmlStream.openNode(this.tag);
31631
+ this.map["xdr:pos"].render(xmlStream, model.range.pos ?? {
31632
+ x: 0,
31633
+ y: 0
31634
+ });
31635
+ this.map["xdr:ext"].render(xmlStream, model.range.ext);
31636
+ if (model.picture) this.map["xdr:pic"].render(xmlStream, model.picture);
31637
+ this.map["xdr:clientData"].render(xmlStream, {});
31638
+ xmlStream.closeNode();
31639
+ }
31640
+ parseClose(name) {
31641
+ if (this.parser) {
31642
+ if (!this.parser.parseClose(name)) this.parser = void 0;
31643
+ return true;
31644
+ }
31645
+ switch (name) {
31646
+ case this.tag:
31647
+ this.model.range.pos = this.map["xdr:pos"].model;
31648
+ this.model.range.ext = this.map["xdr:ext"].model;
31649
+ this.model.picture = this.map["xdr:pic"].model;
31650
+ return false;
31651
+ default: return true;
31652
+ }
31653
+ }
31654
+ reconcile(model, options) {
31655
+ model.medium = this.reconcilePicture(model.picture, options);
31656
+ }
31657
+ };
31658
+ //#endregion
31659
+ //#region src/modules/excel/xlsx/xform/drawing/cell-position-xform.ts
31660
+ var CellPositionXform = class extends BaseXform {
31661
+ constructor(options) {
31662
+ super();
31663
+ this.tag = options.tag;
31664
+ this.map = {
31665
+ "xdr:col": new IntegerXform({
31666
+ tag: "xdr:col",
31667
+ zero: true
31668
+ }),
31669
+ "xdr:colOff": new IntegerXform({
31670
+ tag: "xdr:colOff",
31671
+ zero: true
31672
+ }),
31673
+ "xdr:row": new IntegerXform({
31674
+ tag: "xdr:row",
31675
+ zero: true
31676
+ }),
31677
+ "xdr:rowOff": new IntegerXform({
31678
+ tag: "xdr:rowOff",
31679
+ zero: true
31680
+ })
31681
+ };
31682
+ this.model = {
31683
+ nativeCol: 0,
31684
+ nativeColOff: 0,
31685
+ nativeRow: 0,
31686
+ nativeRowOff: 0
31687
+ };
31688
+ }
31689
+ render(xmlStream, model) {
31690
+ xmlStream.openNode(this.tag);
31691
+ this.map["xdr:col"].render(xmlStream, model.nativeCol);
31692
+ this.map["xdr:colOff"].render(xmlStream, model.nativeColOff);
31693
+ this.map["xdr:row"].render(xmlStream, model.nativeRow);
31694
+ this.map["xdr:rowOff"].render(xmlStream, model.nativeRowOff);
31695
+ xmlStream.closeNode();
31696
+ }
31697
+ parseOpen(node) {
31698
+ if (this.parser) {
31699
+ this.parser.parseOpen(node);
31700
+ return true;
31701
+ }
31702
+ switch (node.name) {
31703
+ case this.tag:
31704
+ this.reset();
31705
+ break;
31706
+ default:
31707
+ this.parser = this.map[node.name];
31708
+ if (this.parser) this.parser.parseOpen(node);
31709
+ break;
31710
+ }
31711
+ return true;
31712
+ }
31713
+ parseText(text) {
31714
+ if (this.parser) this.parser.parseText(text);
31715
+ }
31716
+ parseClose(name) {
31717
+ if (this.parser) {
31718
+ if (!this.parser.parseClose(name)) this.parser = void 0;
31719
+ return true;
31720
+ }
31721
+ switch (name) {
31722
+ case this.tag:
31723
+ this.model = {
31724
+ nativeCol: this.map["xdr:col"].model,
31725
+ nativeColOff: this.map["xdr:colOff"].model,
31726
+ nativeRow: this.map["xdr:row"].model,
31727
+ nativeRowOff: this.map["xdr:rowOff"].model
31728
+ };
31729
+ return false;
31730
+ default: return true;
31731
+ }
31732
+ }
31733
+ };
31734
+ //#endregion
30732
31735
  //#region src/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.ts
30733
31736
  var OneCellAnchorXform = class extends BaseCellAnchorXform {
30734
31737
  constructor() {
@@ -30961,14 +31964,17 @@ self.onmessage = async function(event) {
30961
31964
  //#endregion
30962
31965
  //#region src/modules/excel/xlsx/xform/drawing/drawing-xform.ts
30963
31966
  function getAnchorType(model) {
30964
- return (typeof model.range === "string" ? colCache.decode(model.range) : model.range).br ? "xdr:twoCellAnchor" : "xdr:oneCellAnchor";
31967
+ const range = typeof model.range === "string" ? colCache.decode(model.range) : model.range;
31968
+ if (range.pos !== void 0) return "xdr:absoluteAnchor";
31969
+ return range.br ? "xdr:twoCellAnchor" : "xdr:oneCellAnchor";
30965
31970
  }
30966
31971
  var DrawingXform = class DrawingXform extends BaseXform {
30967
31972
  constructor() {
30968
31973
  super();
30969
31974
  this.map = {
30970
31975
  "xdr:twoCellAnchor": new TwoCellAnchorXform(),
30971
- "xdr:oneCellAnchor": new OneCellAnchorXform()
31976
+ "xdr:oneCellAnchor": new OneCellAnchorXform(),
31977
+ "xdr:absoluteAnchor": new AbsoluteAnchorXform()
30972
31978
  };
30973
31979
  this.model = { anchors: [] };
30974
31980
  }
@@ -31025,7 +32031,8 @@ self.onmessage = async function(event) {
31025
32031
  }
31026
32032
  reconcile(model, options) {
31027
32033
  model.anchors.forEach((anchor) => {
31028
- if (anchor.br) this.map["xdr:twoCellAnchor"].reconcile(anchor, options);
32034
+ if (anchor.range?.pos !== void 0) this.map["xdr:absoluteAnchor"].reconcile(anchor, options);
32035
+ else if (anchor.br) this.map["xdr:twoCellAnchor"].reconcile(anchor, options);
31029
32036
  else this.map["xdr:oneCellAnchor"].reconcile(anchor, options);
31030
32037
  });
31031
32038
  }
@@ -31308,6 +32315,7 @@ self.onmessage = async function(event) {
31308
32315
  this.compressionLevel = Math.max(0, Math.min(9, level));
31309
32316
  this.media = [];
31310
32317
  this.commentRefs = [];
32318
+ this.dynamicArrayCount = 0;
31311
32319
  this._trueStreaming = options.trueStreaming ?? false;
31312
32320
  this.zip = new StreamingZip((err, data, final) => {
31313
32321
  if (err) this.stream.emit("error", err);
@@ -31329,6 +32337,16 @@ self.onmessage = async function(event) {
31329
32337
  get definedNames() {
31330
32338
  return this._definedNames;
31331
32339
  }
32340
+ /**
32341
+ * The default font for the workbook (fontId=0 / "Normal" style).
32342
+ * Must be set before any worksheet rows are committed.
32343
+ */
32344
+ get defaultFont() {
32345
+ return this.styles.defaultFont;
32346
+ }
32347
+ set defaultFont(font) {
32348
+ if (this.styles.setDefaultFont) this.styles.setDefaultFont(font);
32349
+ }
31332
32350
  /** @internal */
31333
32351
  _openStream(path) {
31334
32352
  const stream = new StreamBuf({
@@ -31380,6 +32398,7 @@ self.onmessage = async function(event) {
31380
32398
  this.addSharedStrings(),
31381
32399
  this.addStyles(),
31382
32400
  this.addFeaturePropertyBag(),
32401
+ this.addMetadata(),
31383
32402
  this.addWorkbookRels()
31384
32403
  ]);
31385
32404
  await this.addWorkbook();
@@ -31402,6 +32421,19 @@ self.onmessage = async function(event) {
31402
32421
  getImage(id) {
31403
32422
  return this.media[id];
31404
32423
  }
32424
+ /**
32425
+ * Protect the workbook structure with an optional password.
32426
+ * Prevents users from adding, deleting, renaming, moving, or copying worksheets.
32427
+ */
32428
+ async protect(password, options) {
32429
+ this.protection = await buildWorkbookProtection(password, options);
32430
+ }
32431
+ /**
32432
+ * Remove workbook structure protection.
32433
+ */
32434
+ unprotect() {
32435
+ this.protection = void 0;
32436
+ }
31405
32437
  addWorksheet(name, options) {
31406
32438
  const opts = options || {};
31407
32439
  const useSharedStrings = opts.useSharedStrings !== void 0 ? opts.useSharedStrings : this.useSharedStrings;
@@ -31485,7 +32517,8 @@ self.onmessage = async function(event) {
31485
32517
  commentRefs: this.commentRefs,
31486
32518
  media: this.media,
31487
32519
  drawings,
31488
- hasCheckboxes: this.styles.hasCheckboxes
32520
+ hasCheckboxes: this.styles.hasCheckboxes,
32521
+ hasDynamicArrayFormulas: this.dynamicArrayCount > 0
31489
32522
  };
31490
32523
  const xform = new ContentTypesXform();
31491
32524
  this._addFile(xform.toXml(model), OOXML_PATHS.contentTypes);
@@ -31565,6 +32598,12 @@ self.onmessage = async function(event) {
31565
32598
  }
31566
32599
  return Promise.resolve();
31567
32600
  }
32601
+ addMetadata() {
32602
+ if (this.dynamicArrayCount <= 0) return Promise.resolve();
32603
+ const xform = new MetadataXform();
32604
+ this._addFile(xform.toXml({ dynamicArrayCount: this.dynamicArrayCount }), OOXML_PATHS.xlMetadata);
32605
+ return Promise.resolve();
32606
+ }
31568
32607
  addWorkbookRels() {
31569
32608
  let count = 1;
31570
32609
  const relationships = [{
@@ -31586,6 +32625,11 @@ self.onmessage = async function(event) {
31586
32625
  Type: RelType.FeaturePropertyBag,
31587
32626
  Target: OOXML_REL_TARGETS.workbookFeaturePropertyBag
31588
32627
  });
32628
+ if (this.dynamicArrayCount > 0) relationships.push({
32629
+ Id: `rId${count++}`,
32630
+ Type: RelType.SheetMetadata,
32631
+ Target: OOXML_REL_TARGETS.workbookMetadata
32632
+ });
31589
32633
  this._worksheets.forEach((ws) => {
31590
32634
  if (ws) {
31591
32635
  ws.rId = `rId${count++}`;
@@ -31608,6 +32652,7 @@ self.onmessage = async function(event) {
31608
32652
  definedNames: this._definedNames.model,
31609
32653
  views: this.views,
31610
32654
  properties: {},
32655
+ protection: this.protection,
31611
32656
  calcProperties: {}
31612
32657
  };
31613
32658
  return new Promise((resolve) => {
@@ -31922,6 +32967,21 @@ self.onmessage = async function(event) {
31922
32967
  case "image": {
31923
32968
  const range = this.range;
31924
32969
  if (!range) throw new ImageError("Image has no range");
32970
+ if (range.pos) return {
32971
+ type: this.type,
32972
+ imageId: this.imageId ?? "",
32973
+ hyperlinks: range.hyperlinks,
32974
+ range: {
32975
+ tl: {
32976
+ nativeCol: 0,
32977
+ nativeColOff: 0,
32978
+ nativeRow: 0,
32979
+ nativeRowOff: 0
32980
+ },
32981
+ ext: range.ext,
32982
+ pos: range.pos
32983
+ }
32984
+ };
31925
32985
  return {
31926
32986
  type: this.type,
31927
32987
  imageId: this.imageId ?? "",
@@ -31958,7 +33018,13 @@ self.onmessage = async function(event) {
31958
33018
  }, 0),
31959
33019
  editAs: "oneCell"
31960
33020
  };
31961
- } else if (range) this.range = {
33021
+ } else if (range && "pos" in range && range.pos) this.range = {
33022
+ tl: new Anchor(this.worksheet, null, 0),
33023
+ ext: range.ext,
33024
+ hyperlinks: hyperlinks || ("hyperlinks" in range ? range.hyperlinks : void 0),
33025
+ pos: range.pos
33026
+ };
33027
+ else if (range) this.range = {
31962
33028
  tl: new Anchor(this.worksheet, range.tl, 0),
31963
33029
  br: range.br ? new Anchor(this.worksheet, range.br, 0) : void 0,
31964
33030
  ext: range.ext,
@@ -31981,7 +33047,8 @@ self.onmessage = async function(event) {
31981
33047
  br: this.range.br ? this.range.br.clone(target) : void 0,
31982
33048
  ext: this.range.ext ? { ...this.range.ext } : void 0,
31983
33049
  editAs: this.range.editAs,
31984
- hyperlinks: this.range.hyperlinks ? { ...this.range.hyperlinks } : void 0
33050
+ hyperlinks: this.range.hyperlinks ? { ...this.range.hyperlinks } : void 0,
33051
+ pos: this.range.pos ? { ...this.range.pos } : void 0
31985
33052
  };
31986
33053
  return cloned;
31987
33054
  }
@@ -32436,8 +33503,8 @@ self.onmessage = async function(event) {
32436
33503
  count: 103,
32437
33504
  max: 104,
32438
33505
  min: 105,
32439
- stdDev: 106,
32440
- var: 107,
33506
+ stdDev: 107,
33507
+ var: 110,
32441
33508
  sum: 109
32442
33509
  };
32443
33510
  }
@@ -37779,7 +38846,7 @@ self.onmessage = async function(event) {
37779
38846
  function getCellTextWidthPx(cell) {
37780
38847
  const cellType = cell.effectiveType;
37781
38848
  const font = cell.font;
37782
- if (cellType === ValueType.RichText) {
38849
+ if (cellType === 8) {
37783
38850
  const value = cell.value;
37784
38851
  if (value && typeof value === "object" && "richText" in value) return measureRichTextWidthPx(value.richText, font);
37785
38852
  }
@@ -37799,7 +38866,7 @@ self.onmessage = async function(event) {
37799
38866
  function getCellHeightPt(cell, mdw, columnWidthPx) {
37800
38867
  const font = cell.font;
37801
38868
  const alignment = cell.alignment;
37802
- if (cell.effectiveType === ValueType.RichText) {
38869
+ if (cell.effectiveType === 8) {
37803
38870
  const value = cell.value;
37804
38871
  if (value && typeof value === "object" && "richText" in value) return calculateRichTextAutoFitHeight(value.richText, font, alignment, columnWidthPx);
37805
38872
  }
@@ -37879,6 +38946,7 @@ self.onmessage = async function(event) {
37879
38946
  this.pivotTables = [];
37880
38947
  this.conditionalFormattings = [];
37881
38948
  this.formControls = [];
38949
+ this.ignoredErrors = [];
37882
38950
  this._watermark = null;
37883
38951
  }
37884
38952
  get name() {
@@ -37992,7 +39060,7 @@ self.onmessage = async function(event) {
37992
39060
  spliceColumns(start, count, ...inserts) {
37993
39061
  for (const merge of Object.values(this._merges)) for (let r = merge.top; r <= merge.bottom; r++) for (let c = merge.left; c <= merge.right; c++) {
37994
39062
  const cell = this.findCell(r, c);
37995
- if (cell && cell.type === Enums.ValueType.Merge) cell.unmerge();
39063
+ if (cell && cell.type === 1) cell.unmerge();
37996
39064
  }
37997
39065
  const nRows = this._rows.length;
37998
39066
  if (inserts.length > 0) for (let i = 0; i < nRows; i++) {
@@ -38215,7 +39283,7 @@ self.onmessage = async function(event) {
38215
39283
  spliceRows(start, count, ...inserts) {
38216
39284
  for (const merge of Object.values(this._merges)) for (let r = merge.top; r <= merge.bottom; r++) for (let c = merge.left; c <= merge.right; c++) {
38217
39285
  const cell = this.findCell(r, c);
38218
- if (cell && cell.type === Enums.ValueType.Merge) cell.unmerge();
39286
+ if (cell && cell.type === 1) cell.unmerge();
38219
39287
  }
38220
39288
  const nKeep = start + count;
38221
39289
  const nInserts = inserts.length;
@@ -38403,7 +39471,7 @@ self.onmessage = async function(event) {
38403
39471
  for (let i = dimensions.top; i <= dimensions.bottom; i++) for (let j = dimensions.left; j <= dimensions.right; j++) {
38404
39472
  const cell = this.findCell(i, j);
38405
39473
  if (cell) {
38406
- if (cell.type === Enums.ValueType.Merge) this._unMergeMaster(cell.master);
39474
+ if (cell.type === 1) this._unMergeMaster(cell.master);
38407
39475
  else if (this._merges[cell.address]) this._unMergeMaster(cell);
38408
39476
  }
38409
39477
  }
@@ -38578,7 +39646,11 @@ self.onmessage = async function(event) {
38578
39646
  */
38579
39647
  addTable(model) {
38580
39648
  const table = new Table(this, model);
39649
+ const nameKey = table.name.toLowerCase();
39650
+ if (this.tables[table.name]) throw new TableError(`Table name "${table.name}" already exists in worksheet "${this.name}".`);
39651
+ if (this.workbook._tableNames.has(nameKey)) throw new TableError(`Table name "${table.name}" already exists in another worksheet. Table names must be unique across the entire workbook (case-insensitive).`);
38581
39652
  this.tables[table.name] = table;
39653
+ this.workbook._tableNames.add(nameKey);
38582
39654
  return table;
38583
39655
  }
38584
39656
  /**
@@ -38591,6 +39663,7 @@ self.onmessage = async function(event) {
38591
39663
  * Delete table by name
38592
39664
  */
38593
39665
  removeTable(name) {
39666
+ if (this.tables[name]) this.workbook._tableNames.delete(name.toLowerCase());
38594
39667
  delete this.tables[name];
38595
39668
  }
38596
39669
  /**
@@ -38684,7 +39757,7 @@ self.onmessage = async function(event) {
38684
39757
  if (row.hidden) return;
38685
39758
  const cell = row.findCell(colNum);
38686
39759
  if (!cell) return;
38687
- if (cell.type === Enums.ValueType.Merge) return;
39760
+ if (cell.type === 1) return;
38688
39761
  if (cell.isMerged) {
38689
39762
  const mergeRange = this._merges[cell.address];
38690
39763
  if (mergeRange && mergeRange.left !== mergeRange.right) return;
@@ -38711,7 +39784,7 @@ self.onmessage = async function(event) {
38711
39784
  const mdw = getMaxDigitWidth();
38712
39785
  let maxHeightPt = 0;
38713
39786
  row.eachCell((cell) => {
38714
- if (cell.type === Enums.ValueType.Merge) return;
39787
+ if (cell.type === 1) return;
38715
39788
  if (cell.isMerged) {
38716
39789
  const mergeRange = this._merges[cell.address];
38717
39790
  if (mergeRange && mergeRange.top !== mergeRange.bottom) return;
@@ -38765,6 +39838,7 @@ self.onmessage = async function(event) {
38765
39838
  pivotTables: this.pivotTables,
38766
39839
  conditionalFormattings: this.conditionalFormattings,
38767
39840
  formControls: this.formControls.map((fc) => fc.model),
39841
+ ignoredErrors: this.ignoredErrors,
38768
39842
  watermark: this._watermark,
38769
39843
  drawing: this._drawing
38770
39844
  };
@@ -38833,10 +39907,12 @@ self.onmessage = async function(event) {
38833
39907
  const t = new Table(this, table);
38834
39908
  t.model = table;
38835
39909
  tables[table.name] = t;
39910
+ this.workbook._tableNames.add(table.name.toLowerCase());
38836
39911
  return tables;
38837
39912
  }, {});
38838
39913
  this.pivotTables = value.pivotTables;
38839
39914
  this.conditionalFormattings = value.conditionalFormattings;
39915
+ this.ignoredErrors = value.ignoredErrors ?? [];
38840
39916
  this.formControls = [];
38841
39917
  this._drawing = value.drawing;
38842
39918
  }
@@ -39673,6 +40749,144 @@ self.onmessage = async function(event) {
39673
40749
  }
39674
40750
  };
39675
40751
  //#endregion
40752
+ //#region src/modules/excel/utils/external-link-formula.ts
40753
+ /**
40754
+ * Utilities for manipulating the external-workbook prefix in formula strings.
40755
+ *
40756
+ * Excel formula strings containing external workbook references have the
40757
+ * shape `[<workbook>]Sheet!Ref` where `<workbook>` is either
40758
+ *
40759
+ * - a 1-based numeric index — `[1]Sheet1!A1` (the canonical on-disk form
40760
+ * stored inside `<f>` elements of worksheet XML), or
40761
+ * - a filename / relative path — `[测试.xlsx]Sheet1!A1` (what Excel
40762
+ * displays in the formula bar; not part of the OOXML storage contract,
40763
+ * but produced by hand-written formulas and some older tools).
40764
+ *
40765
+ * When writing, excelts always emits the numeric form — indices map
40766
+ * positionally into the workbook's `<externalReferences>` list. When a
40767
+ * formula arrives with the filename form, the writer assigns (or reuses) an
40768
+ * ExternalLinkModel with that filename as its `target` and rewrites the
40769
+ * formula to the numeric form. This matches how Excel itself stores formulas
40770
+ * and makes them round-trippable.
40771
+ *
40772
+ * The quoted variant `'[file.xlsx]Sheet with space'!A1` is handled too — Excel
40773
+ * wraps the `[name]Sheet` segment in single quotes when the sheet name needs
40774
+ * quoting. The matching logic here recognises both the unquoted and quoted
40775
+ * forms, rewriting inside the quotes when needed.
40776
+ *
40777
+ * Edge cases we explicitly *do not* treat as external refs:
40778
+ * - `[@Column]`, `[#Headers]`, `[Column Name]` — table structured refs
40779
+ * (no `]Sheet!` tail). The regex requires the `]<sheet>!` follow-up,
40780
+ * which structured refs never have.
40781
+ * - Array literals `{1,2;3,4}` use `{}`, not `[]`.
40782
+ * - String literals `"[Book]Sheet!A1"` — handled by scanning only outside
40783
+ * string literal regions.
40784
+ */
40785
+ const UNQUOTED_EXTERNAL_REF = /\[([^\]\\/:*?"<>|\s]+)\]([A-Za-z_\u00A1-\uFFFF][A-Za-z0-9_\u00A1-\uFFFF.]*)!/g;
40786
+ const QUOTED_EXTERNAL_REF = /'\[([^\]]+)\]((?:''|[^'])+)'!/g;
40787
+ /**
40788
+ * Scan a formula string for all external-workbook references. String
40789
+ * literals (inside `"..."`) are skipped so that a string value like
40790
+ * `"[Book]Sheet!A1"` is not misidentified as a ref.
40791
+ *
40792
+ * The returned matches are in source order. If a formula contains no
40793
+ * external refs, the array is empty.
40794
+ */
40795
+ function findExternalRefs(formula) {
40796
+ if (formula.indexOf("[") === -1) return [];
40797
+ const matches = [];
40798
+ const safeRegions = stringLiteralRegions(formula);
40799
+ const addMatch = (start, end, workbook, sheet, quoted, match) => {
40800
+ if (insideAnyRegion(start, safeRegions)) return;
40801
+ const numeric = /^\d+$/.test(workbook);
40802
+ matches.push({
40803
+ match,
40804
+ workbook,
40805
+ numeric,
40806
+ index: numeric ? parseInt(workbook, 10) : null,
40807
+ sheet: unquoteSheetName(sheet),
40808
+ quoted,
40809
+ start,
40810
+ end
40811
+ });
40812
+ };
40813
+ UNQUOTED_EXTERNAL_REF.lastIndex = 0;
40814
+ let m;
40815
+ while ((m = UNQUOTED_EXTERNAL_REF.exec(formula)) !== null) addMatch(m.index, m.index + m[0].length, m[1], m[2], false, m[0]);
40816
+ QUOTED_EXTERNAL_REF.lastIndex = 0;
40817
+ while ((m = QUOTED_EXTERNAL_REF.exec(formula)) !== null) addMatch(m.index, m.index + m[0].length, m[1], m[2], true, m[0]);
40818
+ matches.sort((a, b) => a.start - b.start);
40819
+ const out = [];
40820
+ let lastEnd = -1;
40821
+ for (const ref of matches) if (ref.start >= lastEnd) {
40822
+ out.push(ref);
40823
+ lastEnd = ref.end;
40824
+ }
40825
+ return out;
40826
+ }
40827
+ /**
40828
+ * Replace every external-workbook token in `formula` using the supplied
40829
+ * resolver. The resolver is called once per match and returns the numeric
40830
+ * index to substitute; returning `null` leaves the match unchanged (useful
40831
+ * when the caller cannot resolve a particular filename).
40832
+ *
40833
+ * Returns the rewritten formula. Offsets inside the original formula are
40834
+ * adjusted correctly even when multiple rewrites change the total length.
40835
+ */
40836
+ function rewriteExternalRefs(formula, resolve) {
40837
+ const refs = findExternalRefs(formula);
40838
+ if (refs.length === 0) return formula;
40839
+ let out = "";
40840
+ let cursor = 0;
40841
+ for (const ref of refs) {
40842
+ const index = resolve(ref);
40843
+ if (index === null) continue;
40844
+ out += formula.slice(cursor, ref.start);
40845
+ if (ref.quoted) {
40846
+ const inner = formula.slice(ref.start + 1, ref.end - 2);
40847
+ out += "'" + inner.replace(/^\[[^\]]*\]/, `[${index}]`) + "'!";
40848
+ } else out += `[${index}]${ref.match.slice(ref.match.indexOf("]") + 1)}`;
40849
+ cursor = ref.end;
40850
+ }
40851
+ out += formula.slice(cursor);
40852
+ return out;
40853
+ }
40854
+ /**
40855
+ * Return the spans of string literal regions in a formula, as half-open
40856
+ * [start, end) intervals (exclusive of the surrounding quotes themselves).
40857
+ * Used to skip external-ref matches that fall inside a string value.
40858
+ */
40859
+ function stringLiteralRegions(formula) {
40860
+ if (formula.indexOf("\"") === -1) return [];
40861
+ const regions = [];
40862
+ const len = formula.length;
40863
+ let i = 0;
40864
+ while (i < len) if (formula[i] === "\"") {
40865
+ const start = i;
40866
+ i++;
40867
+ while (i < len) {
40868
+ if (formula[i] === "\"") {
40869
+ if (i + 1 < len && formula[i + 1] === "\"") {
40870
+ i += 2;
40871
+ continue;
40872
+ }
40873
+ i++;
40874
+ break;
40875
+ }
40876
+ i++;
40877
+ }
40878
+ regions.push([start, i]);
40879
+ } else i++;
40880
+ return regions;
40881
+ }
40882
+ function insideAnyRegion(pos, regions) {
40883
+ for (const [a, b] of regions) if (pos >= a && pos < b) return true;
40884
+ return false;
40885
+ }
40886
+ function unquoteSheetName(sheet) {
40887
+ return sheet.replace(/''/g, "'");
40888
+ }
40889
+ //#endregion
39676
40890
  //#region src/modules/excel/utils/passthrough-manager.ts
39677
40891
  /**
39678
40892
  * Content type definitions for passthrough files
@@ -39781,6 +40995,232 @@ self.onmessage = async function(event) {
39781
40995
  }
39782
40996
  };
39783
40997
  //#endregion
40998
+ //#region src/modules/excel/xlsx/xform/book/external-link-xform.ts
40999
+ /** Pretty attribute constant used on the root element. */
41000
+ const ROOT_ATTRIBUTES = {
41001
+ xmlns: "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
41002
+ "xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships"
41003
+ };
41004
+ var ExternalLinkXform = class extends BaseXform {
41005
+ constructor(..._args) {
41006
+ super(..._args);
41007
+ this.sheetNamesByIndex = [];
41008
+ this.currentSheetCells = null;
41009
+ this.currentSheetName = null;
41010
+ this.currentCellAddress = null;
41011
+ this.currentCellType = "n";
41012
+ this.currentCellValue = null;
41013
+ this.inV = false;
41014
+ }
41015
+ get tag() {
41016
+ return "externalLink";
41017
+ }
41018
+ reset() {
41019
+ super.reset();
41020
+ this.sheetNamesByIndex = [];
41021
+ this.currentSheetCells = null;
41022
+ this.currentSheetName = null;
41023
+ this.currentCellAddress = null;
41024
+ this.currentCellType = "n";
41025
+ this.currentCellValue = null;
41026
+ this.inV = false;
41027
+ }
41028
+ render(xmlStream, model) {
41029
+ xmlStream.openXml(StdDocAttributes);
41030
+ xmlStream.openNode("externalLink", ROOT_ATTRIBUTES);
41031
+ xmlStream.openNode("externalBook", { "r:id": "rId1" });
41032
+ if (model.sheetNames.length > 0) {
41033
+ xmlStream.openNode("sheetNames");
41034
+ for (const name of model.sheetNames) xmlStream.leafNode("sheetName", { val: name });
41035
+ xmlStream.closeNode();
41036
+ }
41037
+ const cache = model.cachedValues ?? {};
41038
+ if (Object.keys(cache).some((name) => {
41039
+ const sheet = cache[name];
41040
+ return sheet && Object.keys(sheet).length > 0;
41041
+ })) {
41042
+ xmlStream.openNode("sheetDataSet");
41043
+ for (let i = 0; i < model.sheetNames.length; i++) {
41044
+ const cells = cache[model.sheetNames[i]];
41045
+ if (!cells || Object.keys(cells).length === 0) continue;
41046
+ renderSheetData(xmlStream, i, cells);
41047
+ }
41048
+ xmlStream.closeNode();
41049
+ }
41050
+ xmlStream.closeNode();
41051
+ xmlStream.closeNode();
41052
+ }
41053
+ parseOpen(node) {
41054
+ switch (node.name) {
41055
+ case "externalLink":
41056
+ this.model = {
41057
+ externalBookRId: "",
41058
+ sheetNames: [],
41059
+ cachedValues: {}
41060
+ };
41061
+ this.sheetNamesByIndex = [];
41062
+ return true;
41063
+ case "externalBook":
41064
+ if (this.model) this.model.externalBookRId = node.attributes["r:id"] ?? "";
41065
+ return true;
41066
+ case "sheetName":
41067
+ if (this.model) {
41068
+ const val = node.attributes.val ?? "";
41069
+ this.model.sheetNames.push(val);
41070
+ this.sheetNamesByIndex.push(val);
41071
+ }
41072
+ return true;
41073
+ case "sheetData": {
41074
+ const sheetIdRaw = node.attributes.sheetId;
41075
+ const sheetId = sheetIdRaw !== void 0 ? parseInt(sheetIdRaw, 10) : NaN;
41076
+ this.currentSheetName = Number.isFinite(sheetId) ? this.sheetNamesByIndex[sheetId] ?? null : null;
41077
+ this.currentSheetCells = this.currentSheetName ? {} : null;
41078
+ return true;
41079
+ }
41080
+ case "cell":
41081
+ this.currentCellAddress = node.attributes.r ?? null;
41082
+ this.currentCellType = node.attributes.t ?? "n";
41083
+ this.currentCellValue = null;
41084
+ return true;
41085
+ case "v":
41086
+ this.inV = true;
41087
+ this.currentCellValue = "";
41088
+ return true;
41089
+ default: return false;
41090
+ }
41091
+ }
41092
+ parseText(text) {
41093
+ if (this.inV) this.currentCellValue = (this.currentCellValue ?? "") + text;
41094
+ }
41095
+ parseClose(name) {
41096
+ switch (name) {
41097
+ case "v":
41098
+ this.inV = false;
41099
+ return true;
41100
+ case "cell":
41101
+ if (this.model && this.currentSheetCells && this.currentCellAddress) this.currentSheetCells[this.currentCellAddress.toUpperCase()] = decodePrimitive(this.currentCellType, this.currentCellValue);
41102
+ this.currentCellAddress = null;
41103
+ this.currentCellType = "n";
41104
+ this.currentCellValue = null;
41105
+ return true;
41106
+ case "sheetData":
41107
+ if (this.model && this.currentSheetName && this.currentSheetCells && Object.keys(this.currentSheetCells).length > 0) this.model.cachedValues[this.currentSheetName] = this.currentSheetCells;
41108
+ this.currentSheetName = null;
41109
+ this.currentSheetCells = null;
41110
+ return true;
41111
+ case "externalLink": return false;
41112
+ default: return true;
41113
+ }
41114
+ }
41115
+ };
41116
+ function renderSheetData(xmlStream, sheetIdx, cells) {
41117
+ const rows = groupByRow(cells);
41118
+ xmlStream.openNode("sheetData", { sheetId: sheetIdx });
41119
+ for (const row of rows) {
41120
+ xmlStream.openNode("row", { r: row.rowNum });
41121
+ for (const { address, value } of row.cells) renderCell(xmlStream, address, value);
41122
+ xmlStream.closeNode();
41123
+ }
41124
+ xmlStream.closeNode();
41125
+ }
41126
+ function groupByRow(cells) {
41127
+ const byRow = /* @__PURE__ */ new Map();
41128
+ for (const rawAddress of Object.keys(cells)) {
41129
+ const address = rawAddress.toUpperCase();
41130
+ const rowNum = extractRowNumber(address);
41131
+ if (rowNum === void 0) continue;
41132
+ let bucket = byRow.get(rowNum);
41133
+ if (!bucket) {
41134
+ bucket = {
41135
+ rowNum,
41136
+ cells: []
41137
+ };
41138
+ byRow.set(rowNum, bucket);
41139
+ }
41140
+ bucket.cells.push({
41141
+ address: stripAbsoluteMarkers(address),
41142
+ value: cells[rawAddress]
41143
+ });
41144
+ }
41145
+ return [...byRow.values()].sort((a, b) => a.rowNum - b.rowNum);
41146
+ }
41147
+ function extractRowNumber(address) {
41148
+ const match = /^\$?[A-Z]+\$?(\d+)$/.exec(address);
41149
+ return match ? parseInt(match[1], 10) : void 0;
41150
+ }
41151
+ function stripAbsoluteMarkers(address) {
41152
+ return address.replace(/\$/g, "");
41153
+ }
41154
+ function renderCell(xmlStream, address, value) {
41155
+ if (value === null || value === void 0) {
41156
+ xmlStream.leafNode("cell", { r: address });
41157
+ return;
41158
+ }
41159
+ if (typeof value === "number") {
41160
+ xmlStream.openNode("cell", { r: address });
41161
+ xmlStream.leafNode("v", void 0, String(value));
41162
+ xmlStream.closeNode();
41163
+ return;
41164
+ }
41165
+ if (typeof value === "boolean") {
41166
+ xmlStream.openNode("cell", {
41167
+ r: address,
41168
+ t: "b"
41169
+ });
41170
+ xmlStream.leafNode("v", void 0, value ? "1" : "0");
41171
+ xmlStream.closeNode();
41172
+ return;
41173
+ }
41174
+ if (isErrorLiteral(value)) {
41175
+ xmlStream.openNode("cell", {
41176
+ r: address,
41177
+ t: "e"
41178
+ });
41179
+ xmlStream.leafNode("v", void 0, value);
41180
+ xmlStream.closeNode();
41181
+ return;
41182
+ }
41183
+ xmlStream.openNode("cell", {
41184
+ r: address,
41185
+ t: "str"
41186
+ });
41187
+ xmlStream.leafNode("v", void 0, value);
41188
+ xmlStream.closeNode();
41189
+ }
41190
+ /** Excel error literals that should be written with `t="e"`. */
41191
+ const ERROR_LITERALS = new Set([
41192
+ "#NULL!",
41193
+ "#DIV/0!",
41194
+ "#VALUE!",
41195
+ "#REF!",
41196
+ "#NAME?",
41197
+ "#NUM!",
41198
+ "#N/A",
41199
+ "#GETTING_DATA",
41200
+ "#SPILL!",
41201
+ "#CALC!",
41202
+ "#CONNECT!",
41203
+ "#BLOCKED!",
41204
+ "#UNKNOWN!",
41205
+ "#FIELD!",
41206
+ "#PYTHON!"
41207
+ ]);
41208
+ function isErrorLiteral(value) {
41209
+ return ERROR_LITERALS.has(value);
41210
+ }
41211
+ function decodePrimitive(type, raw) {
41212
+ if (raw === null || raw === "") return null;
41213
+ switch (type) {
41214
+ case "b": return raw !== "0" && raw.toLowerCase() !== "false";
41215
+ case "e": return raw;
41216
+ case "str": return raw;
41217
+ default: {
41218
+ const n = Number(raw);
41219
+ return Number.isFinite(n) ? n : raw;
41220
+ }
41221
+ }
41222
+ }
41223
+ //#endregion
39784
41224
  //#region src/modules/excel/xlsx/xform/comment/comments-xform.ts
39785
41225
  var CommentsXform = class CommentsXform extends BaseXform {
39786
41226
  constructor() {
@@ -41897,10 +43337,10 @@ self.onmessage = async function(event) {
41897
43337
  for (let j = dimensions.left; j <= dimensions.right; j++) {
41898
43338
  const cell = row.cells[j - 1];
41899
43339
  if (!cell) row.cells[j] = {
41900
- type: Enums.ValueType.Null,
43340
+ type: 0,
41901
43341
  address: colCache.encodeAddress(i, j)
41902
43342
  };
41903
- else if (cell.type === Enums.ValueType.Merge) cell.master = dimensions.tl;
43343
+ else if (cell.type === 1) cell.master = dimensions.tl;
41904
43344
  }
41905
43345
  }
41906
43346
  });
@@ -42066,7 +43506,8 @@ self.onmessage = async function(event) {
42066
43506
  childXform: new TablePartXform()
42067
43507
  }),
42068
43508
  conditionalFormatting: new ConditionalFormattingsXform(),
42069
- extLst: new ExtLstXform$1()
43509
+ extLst: new ExtLstXform$1(),
43510
+ ignoredErrors: new IgnoredErrorsXform()
42070
43511
  };
42071
43512
  }
42072
43513
  prepare(model, options) {
@@ -42449,6 +43890,7 @@ self.onmessage = async function(event) {
42449
43890
  xmlStream.leafNode("mc:Fallback");
42450
43891
  xmlStream.closeNode();
42451
43892
  }
43893
+ this.map.ignoredErrors.render(xmlStream, model.ignoredErrors);
42452
43894
  this.map.tableParts.render(xmlStream, model.tables);
42453
43895
  this.map.extLst.render(xmlStream, model);
42454
43896
  xmlStream.closeNode();
@@ -42505,7 +43947,8 @@ self.onmessage = async function(event) {
42505
43947
  tables: this.map.tableParts.model,
42506
43948
  conditionalFormattings,
42507
43949
  rowBreaks: this.map.rowBreaks.model ?? [],
42508
- colBreaks: this.map.colBreaks.model ?? []
43950
+ colBreaks: this.map.colBreaks.model ?? [],
43951
+ ignoredErrors: this.map.ignoredErrors.model ?? []
42509
43952
  };
42510
43953
  if (this.map.autoFilter.model) this.model.autoFilter = this.map.autoFilter.model;
42511
43954
  if (this.map.sheetProtection.model) this.model.sheetProtection = this.map.sheetProtection.model;
@@ -43335,6 +44778,27 @@ self.onmessage = async function(event) {
43335
44778
  }
43336
44779
  };
43337
44780
  /**
44781
+ * Extract the trailing integer from a workbook-rels Target like
44782
+ * `"externalLinks/externalLink12.xml"` (a path relative to `xl/`). Mirror
44783
+ * of {@link getExternalLinkIndexFromPath} which takes the full
44784
+ * `xl/externalLinks/…` form. Used during reconcile to bridge the
44785
+ * workbook.xml.rels entry to the parsed externalLinkN.xml part.
44786
+ */
44787
+ function externalLinkIndexFromRelTarget(target) {
44788
+ const match = /(?:^|\/)externalLink(\d+)[.]xml$/.exec(target);
44789
+ return match ? parseInt(match[1], 10) : void 0;
44790
+ }
44791
+ /**
44792
+ * Add `sheetName` to an ExternalLinkModel's `sheetNames` list if it isn't
44793
+ * already present. Ordering is preserved — the first-seen sheet wins
44794
+ * position 0, which matches what Excel does when writing externalLinks
44795
+ * itself.
44796
+ */
44797
+ function upsertSheet(link, sheetName) {
44798
+ if (!sheetName) return;
44799
+ if (!link.sheetNames.includes(sheetName)) link.sheetNames.push(sheetName);
44800
+ }
44801
+ /**
43338
44802
  * XLSX class - handles Excel file operations
43339
44803
  * Works in both Node.js and Browser environments
43340
44804
  */
@@ -43396,10 +44860,12 @@ self.onmessage = async function(event) {
43396
44860
  await this.addDrawings(zip, model);
43397
44861
  await this.addTables(zip, model);
43398
44862
  await this.addPivotTables(zip, model);
44863
+ await this.addExternalLinks(zip, model);
43399
44864
  this.addPassthrough(zip, model);
43400
44865
  await this.addThemes(zip, model);
43401
44866
  await this.addStyles(zip, model);
43402
44867
  await this.addFeaturePropertyBag(zip, model);
44868
+ await this.addMetadata(zip, model);
43403
44869
  await this.addMedia(zip, model);
43404
44870
  await this.addApp(zip, model);
43405
44871
  await this.addCore(zip, model);
@@ -43444,16 +44910,27 @@ self.onmessage = async function(event) {
43444
44910
  return this._finalize(zip);
43445
44911
  }
43446
44912
  /**
43447
- * Load workbook from buffer/ArrayBuffer/Uint8Array
44913
+ * Load a workbook from binary data.
44914
+ *
44915
+ * Accepted inputs:
44916
+ * - `Uint8Array` (and `Buffer`, which is a Uint8Array at runtime)
44917
+ * - `ArrayBuffer` / `SharedArrayBuffer`
44918
+ * - Any `ArrayBufferView` (DataView, Int8Array, Float32Array, …) — the
44919
+ * underlying bytes are reinterpreted as a zip archive
44920
+ * - `string` — treated as base64-encoded data when `options.base64 === true`;
44921
+ * raw binary cannot be round-tripped through a JS string and is rejected
44922
+ * to prevent silent corruption.
43448
44923
  */
43449
44924
  async load(data, options) {
44925
+ if (data === null || data === void 0) throw new ExcelFileError("<input>", "read", "Can't read the data of 'the loaded zip file'. Is it in a supported JavaScript type (String, Blob, ArrayBuffer, etc) ?");
43450
44926
  let buffer;
43451
- const isBuffer = typeof Buffer !== "undefined" ? Buffer.isBuffer(data) : false;
43452
- if (!data || typeof data === "object" && !isBuffer && !(data instanceof Uint8Array) && !(data instanceof ArrayBuffer)) throw new ExcelFileError("<input>", "read", "Can't read the data of 'the loaded zip file'. Is it in a supported JavaScript type (String, Blob, ArrayBuffer, etc) ?");
43453
- if (options && options.base64) buffer = base64ToUint8Array(data.toString());
44927
+ if (typeof data === "string") {
44928
+ if (!options?.base64) throw new ExcelFileError("<input>", "read", "Can't read the data of 'the loaded zip file'. Is it in a supported JavaScript type (String, Blob, ArrayBuffer, etc) ? String input requires options.base64 === true (base64-encoded zip archive).");
44929
+ buffer = base64ToUint8Array(data);
44930
+ } else if (data instanceof Uint8Array) buffer = data;
43454
44931
  else if (data instanceof ArrayBuffer) buffer = new Uint8Array(data);
43455
- else if (data instanceof Uint8Array) buffer = data;
43456
- else buffer = new Uint8Array(data);
44932
+ else if (ArrayBuffer.isView(data)) buffer = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
44933
+ else throw new ExcelFileError("<input>", "read", "Can't read the data of 'the loaded zip file'. Is it in a supported JavaScript type (String, Blob, ArrayBuffer, etc) ?");
43457
44934
  return this.loadBuffer(buffer, options);
43458
44935
  }
43459
44936
  /**
@@ -43493,7 +44970,9 @@ self.onmessage = async function(event) {
43493
44970
  pivotTableRels: {},
43494
44971
  pivotCacheDefinitions: {},
43495
44972
  pivotCacheRecords: {},
43496
- passthrough: {}
44973
+ passthrough: {},
44974
+ externalLinksByIndex: {},
44975
+ externalLinkRelsByIndex: {}
43497
44976
  };
43498
44977
  }
43499
44978
  /**
@@ -43546,8 +45025,10 @@ self.onmessage = async function(event) {
43546
45025
  model.definedNames = workbook.definedNames;
43547
45026
  model.views = workbook.views;
43548
45027
  model.properties = workbook.properties;
45028
+ model.protection = workbook.protection;
43549
45029
  model.calcProperties = workbook.calcProperties;
43550
45030
  model.pivotCaches = workbook.pivotCaches;
45031
+ model.externalReferences = workbook.externalReferences;
43551
45032
  return true;
43552
45033
  }
43553
45034
  case OOXML_PATHS.xlSharedStrings:
@@ -43574,6 +45055,11 @@ self.onmessage = async function(event) {
43574
45055
  model.styles = new StylesXform();
43575
45056
  await model.styles.parseStream(stream);
43576
45057
  return true;
45058
+ case OOXML_PATHS.xlMetadata: {
45059
+ const metadataResult = await new MetadataXform().parseStream(stream);
45060
+ if (metadataResult) model.metadata = metadataResult;
45061
+ return true;
45062
+ }
43577
45063
  default: return false;
43578
45064
  }
43579
45065
  }
@@ -43703,12 +45189,15 @@ self.onmessage = async function(event) {
43703
45189
  comments: model.comments,
43704
45190
  tables: model.tables,
43705
45191
  vmlDrawings: model.vmlDrawings,
43706
- pivotTables: model.pivotTablesIndexed
45192
+ pivotTables: model.pivotTablesIndexed,
45193
+ hasDynamicArrayMetadata: !!model.metadata?.hasDynamicArrays,
45194
+ dynamicArrayCmIndices: model.metadata?.dynamicArrayCmIndices
43707
45195
  };
43708
45196
  model.worksheets.forEach((worksheet) => {
43709
45197
  worksheet.relationships = model.worksheetRels[worksheet.sheetNo];
43710
45198
  worksheetXform.reconcile(worksheet, sheetOptions);
43711
45199
  });
45200
+ this._reconcileExternalLinks(model);
43712
45201
  delete model.worksheetHash;
43713
45202
  delete model.worksheetRels;
43714
45203
  delete model.globalRels;
@@ -43722,6 +45211,127 @@ self.onmessage = async function(event) {
43722
45211
  delete model.drawingRels;
43723
45212
  delete model.vmlDrawings;
43724
45213
  delete model.pivotTableRels;
45214
+ delete model.metadata;
45215
+ delete model.externalReferences;
45216
+ delete model.externalLinksByIndex;
45217
+ delete model.externalLinkRelsByIndex;
45218
+ }
45219
+ /**
45220
+ * Join the three on-disk sources that together describe external workbook
45221
+ * references into a single dense `model.externalLinks: ExternalLinkModel[]`.
45222
+ *
45223
+ * Sources:
45224
+ * - `<externalReferences>` list in workbook.xml (declaration order)
45225
+ * - `xl/_rels/workbook.xml.rels` (rId → internal path)
45226
+ * - `xl/externalLinks/externalLink{N}.xml` (sheet names, cached values)
45227
+ * - `xl/externalLinks/_rels/externalLink{N}.xml.rels` (target, TargetMode)
45228
+ *
45229
+ * The 1-based index of each resulting ExternalLinkModel matches the `[N]`
45230
+ * used in formula strings — this is the single source of truth formula
45231
+ * code should rely on.
45232
+ */
45233
+ _reconcileExternalLinks(model) {
45234
+ const refs = model.externalReferences;
45235
+ if (!refs || refs.length === 0) {
45236
+ if (!model.externalLinks) model.externalLinks = [];
45237
+ return;
45238
+ }
45239
+ const rels = model.workbookRels ?? [];
45240
+ const relById = /* @__PURE__ */ new Map();
45241
+ for (const rel of rels) if (rel.Type === RelType.ExternalLink) relById.set(rel.Id, rel);
45242
+ const externalLinks = [];
45243
+ for (let i = 0; i < refs.length; i++) {
45244
+ const ref = refs[i];
45245
+ const rel = relById.get(ref.rId);
45246
+ if (!rel) continue;
45247
+ const partIndex = externalLinkIndexFromRelTarget(rel.Target);
45248
+ if (partIndex === void 0) continue;
45249
+ const parsed = model.externalLinksByIndex[partIndex];
45250
+ const partRels = model.externalLinkRelsByIndex[partIndex] ?? [];
45251
+ const pathRel = partRels.find((r) => r.Type === RelType.ExternalLinkPath) ?? partRels.find((r) => r.TargetMode === "External");
45252
+ externalLinks.push({
45253
+ index: i + 1,
45254
+ rId: ref.rId,
45255
+ target: pathRel?.Target ?? "",
45256
+ targetMode: pathRel?.TargetMode ?? "External",
45257
+ sheetNames: parsed?.sheetNames ?? [],
45258
+ cachedValues: parsed?.cachedValues ?? {}
45259
+ });
45260
+ }
45261
+ model.externalLinks = externalLinks;
45262
+ }
45263
+ /**
45264
+ * Write-time pass that brings the workbook model into a shape the writer
45265
+ * can serialise cleanly. Two concerns:
45266
+ *
45267
+ * 1. Build the final external-link list for this write, combining
45268
+ * user-declared links (`wb.externalLinks`) with auto-discovered
45269
+ * ones from previous writes (cached on the Workbook). The result
45270
+ * is assigned to `model.externalLinks` and consumed by the writer;
45271
+ * `wb.externalLinks` is **not** modified.
45272
+ *
45273
+ * 2. Scan every formula cell for `[Book]Sheet!` prefixes. Filename-form
45274
+ * references that don't match an existing link trigger
45275
+ * `_recordAutoExternalLink()` on the Workbook, which adds the target
45276
+ * to the private writer cache (so subsequent writes are fixed-point
45277
+ * stable) but leaves `wb.externalLinks` untouched.
45278
+ *
45279
+ * 3. Rewrite every external-ref formula so it uses the numeric `[N]`
45280
+ * form, the canonical OOXML storage form. This mutation lands on
45281
+ * the cell's model object — matching the library's existing
45282
+ * write-time pattern for `ssId`, `styleId`, `si`, and `cm`.
45283
+ * Subsequent writes see the `[N]` form directly and resolve it
45284
+ * against `model.externalLinks`, giving idempotent output.
45285
+ */
45286
+ _normaliseExternalLinks(model) {
45287
+ const links = this.workbook._collectExternalLinksForWrite();
45288
+ const byTarget = /* @__PURE__ */ new Map();
45289
+ for (const link of links) if (link.target) byTarget.set(link.target.toLowerCase(), link);
45290
+ const scratch = {
45291
+ links,
45292
+ byTarget,
45293
+ workbook: this.workbook
45294
+ };
45295
+ for (const worksheet of model.worksheets ?? []) for (const row of worksheet.rows ?? []) for (const cell of row.cells ?? []) {
45296
+ if (typeof cell?.formula === "string" && cell.formula.length > 0) cell.formula = this._normaliseFormulaExternalRefs(cell.formula, scratch);
45297
+ if (typeof cell?.sharedFormula === "string" && cell.sharedFormula.length > 0) cell.sharedFormula = this._normaliseFormulaExternalRefs(cell.sharedFormula, scratch);
45298
+ }
45299
+ model.externalLinks = links;
45300
+ }
45301
+ /**
45302
+ * Rewrite a single formula so every external-ref prefix uses the numeric
45303
+ * `[N]` form. When an unknown filename-form reference is found we record
45304
+ * it on the workbook's private writer cache (so the next write can still
45305
+ * resolve it) and append a local link to `scratch.links` so subsequent
45306
+ * refs in the same formula see the freshly-assigned index.
45307
+ */
45308
+ _normaliseFormulaExternalRefs(formula, scratch) {
45309
+ return rewriteExternalRefs(formula, (ref) => {
45310
+ if (ref.numeric) {
45311
+ if (ref.index !== null && ref.index >= 1 && ref.index <= scratch.links.length) {
45312
+ upsertSheet(scratch.links[ref.index - 1], ref.sheet);
45313
+ return ref.index;
45314
+ }
45315
+ return null;
45316
+ }
45317
+ const key = ref.workbook.toLowerCase();
45318
+ let link = scratch.byTarget.get(key);
45319
+ if (!link) {
45320
+ link = {
45321
+ index: scratch.workbook._recordAutoExternalLink(ref.workbook, ref.sheet),
45322
+ target: ref.workbook,
45323
+ targetMode: "External",
45324
+ sheetNames: ref.sheet ? [ref.sheet] : [],
45325
+ cachedValues: {}
45326
+ };
45327
+ scratch.links.push(link);
45328
+ scratch.byTarget.set(key, link);
45329
+ } else {
45330
+ upsertSheet(link, ref.sheet);
45331
+ if (ref.sheet) scratch.workbook._recordAutoExternalLink(ref.workbook, ref.sheet);
45332
+ }
45333
+ return link.index;
45334
+ });
43725
45335
  }
43726
45336
  /**
43727
45337
  * Reconcile pivot tables by linking them to worksheets and their cache data.
@@ -43931,6 +45541,26 @@ self.onmessage = async function(event) {
43931
45541
  const cacheRecords = await new PivotCacheRecordsXform().parseStream(stream);
43932
45542
  if (cacheRecords) model.pivotCacheRecords[name] = cacheRecords;
43933
45543
  }
45544
+ /**
45545
+ * Parse `xl/externalLinks/externalLink{N}.xml` into the intermediate
45546
+ * ParsedExternalLink shape. Reconciliation (joining with the rels file
45547
+ * and the workbook's `<externalReferences>` list) happens later in
45548
+ * {@link reconcile}.
45549
+ */
45550
+ async _processExternalLinkEntry(stream, model, index) {
45551
+ const parsed = await new ExternalLinkXform().parseStream(stream);
45552
+ if (parsed) model.externalLinksByIndex[index] = parsed;
45553
+ }
45554
+ /**
45555
+ * Parse `xl/externalLinks/_rels/externalLink{N}.xml.rels`. The Target /
45556
+ * TargetMode carried here is what Excel uses to locate the actual external
45557
+ * file at open time, so we must preserve it verbatim (including relative
45558
+ * paths like `"测试.xlsx"`).
45559
+ */
45560
+ async _processExternalLinkRelsEntry(stream, model, index) {
45561
+ const relationships = await this.parseRels(stream);
45562
+ model.externalLinkRelsByIndex[index] = relationships ?? [];
45563
+ }
43934
45564
  async loadFromFiles(zipData, options) {
43935
45565
  const model = this.createEmptyModel();
43936
45566
  const entries = Object.keys(zipData).map((name) => ({
@@ -44018,6 +45648,16 @@ self.onmessage = async function(event) {
44018
45648
  await this._processPivotCacheRecordsEntry(stream, model, pivotCacheRecordsName);
44019
45649
  return true;
44020
45650
  }
45651
+ const externalLinkIndex = getExternalLinkIndexFromPath(entryName);
45652
+ if (externalLinkIndex !== void 0) {
45653
+ await this._processExternalLinkEntry(stream, model, externalLinkIndex);
45654
+ return true;
45655
+ }
45656
+ const externalLinkRelsIndex = getExternalLinkIndexFromRelsPath(entryName);
45657
+ if (externalLinkRelsIndex !== void 0) {
45658
+ await this._processExternalLinkRelsEntry(stream, model, externalLinkRelsIndex);
45659
+ return true;
45660
+ }
44021
45661
  if (PassthroughManager.isPassthroughPath(entryName)) {
44022
45662
  if (rawData) model.passthrough[entryName] = rawData;
44023
45663
  else await this._processPassthroughEntry(stream, model, entryName);
@@ -44101,6 +45741,11 @@ self.onmessage = async function(event) {
44101
45741
  Type: XLSX.RelType.FeaturePropertyBag,
44102
45742
  Target: OOXML_REL_TARGETS.workbookFeaturePropertyBag
44103
45743
  });
45744
+ if (model.hasDynamicArrayFormulas) relationships.push({
45745
+ Id: `rId${count++}`,
45746
+ Type: XLSX.RelType.SheetMetadata,
45747
+ Target: OOXML_REL_TARGETS.workbookMetadata
45748
+ });
44104
45749
  const seenCacheIds = /* @__PURE__ */ new Map();
44105
45750
  (model.pivotTables ?? []).forEach((pivotTable) => {
44106
45751
  const existing = seenCacheIds.get(pivotTable.cacheId);
@@ -44123,6 +45768,15 @@ self.onmessage = async function(event) {
44123
45768
  Target: worksheetRelTarget(worksheet.fileIndex)
44124
45769
  });
44125
45770
  });
45771
+ const externalLinks = model.externalLinks ?? [];
45772
+ for (const link of externalLinks) {
45773
+ link.rId = `rId${count++}`;
45774
+ relationships.push({
45775
+ Id: link.rId,
45776
+ Type: XLSX.RelType.ExternalLink,
45777
+ Target: externalLinkRelTargetFromWorkbook(link.index)
45778
+ });
45779
+ }
44126
45780
  const xform = new RelationshipsXform();
44127
45781
  await this._renderToZip(zip, OOXML_PATHS.xlWorkbookRels, xform, relationships);
44128
45782
  }
@@ -44130,6 +45784,10 @@ self.onmessage = async function(event) {
44130
45784
  if (!model.hasCheckboxes) return;
44131
45785
  await this._renderToZip(zip, OOXML_PATHS.xlFeaturePropertyBag, new FeaturePropertyBagXform(), {});
44132
45786
  }
45787
+ async addMetadata(zip, model) {
45788
+ if (!model.hasDynamicArrayFormulas) return;
45789
+ await this._renderToZip(zip, OOXML_PATHS.xlMetadata, new MetadataXform(), { dynamicArrayCount: model.dynamicArrayCount });
45790
+ }
44133
45791
  async addSharedStrings(zip, model) {
44134
45792
  if (model.sharedStrings && model.sharedStrings.count) await this._renderToZip(zip, OOXML_PATHS.xlSharedStrings, model.sharedStrings, model.sharedStrings.model);
44135
45793
  }
@@ -44211,6 +45869,34 @@ self.onmessage = async function(event) {
44211
45869
  }
44212
45870
  }
44213
45871
  /**
45872
+ * Write every external workbook reference into the archive. For each
45873
+ * {@link ExternalLinkModel} in `model.externalLinks` we emit two files:
45874
+ *
45875
+ * xl/externalLinks/externalLink{index}.xml — sheet names + cache
45876
+ * xl/externalLinks/_rels/externalLink{index}.xml.rels — target path
45877
+ *
45878
+ * The target-path rel carries `TargetMode="External"` with a **bare
45879
+ * relative** `Target` whenever the user supplied one. This is the single
45880
+ * line that makes Office / WPS resolve the referenced workbook relative
45881
+ * to the current file's directory (not the `%USERPROFILE%\Documents`
45882
+ * fallback) — the root of the behaviour reported in exceljs#3039.
45883
+ */
45884
+ async addExternalLinks(zip, model) {
45885
+ const externalLinks = model.externalLinks ?? [];
45886
+ if (externalLinks.length === 0) return;
45887
+ const externalLinkXform = new ExternalLinkXform();
45888
+ const relsXform = new RelationshipsXform();
45889
+ for (const link of externalLinks) {
45890
+ await this._renderToZip(zip, externalLinkPath(link.index), externalLinkXform, link);
45891
+ await this._renderToZip(zip, externalLinkRelsPath(link.index), relsXform, [{
45892
+ Id: "rId1",
45893
+ Type: XLSX.RelType.ExternalLinkPath,
45894
+ Target: link.target,
45895
+ TargetMode: link.targetMode ?? "External"
45896
+ }]);
45897
+ }
45898
+ }
45899
+ /**
44214
45900
  * Write passthrough files (charts, etc.) that were preserved during read.
44215
45901
  * These files are written back unchanged to preserve unsupported features.
44216
45902
  */
@@ -44280,6 +45966,7 @@ self.onmessage = async function(event) {
44280
45966
  const workbookXform = new WorkbookXform();
44281
45967
  const worksheetXform = new WorkSheetXform();
44282
45968
  workbookXform.prepare(model);
45969
+ this._normaliseExternalLinks(model);
44283
45970
  const worksheetOptions = {
44284
45971
  sharedStrings: model.sharedStrings,
44285
45972
  styles: model.styles,
@@ -44293,9 +45980,14 @@ self.onmessage = async function(event) {
44293
45980
  model.hasHeaderWatermark = false;
44294
45981
  let tableCount = 0;
44295
45982
  model.tables = [];
45983
+ const tableNameMap = /* @__PURE__ */ new Map();
44296
45984
  model.worksheets.forEach((worksheet, index) => {
44297
45985
  worksheet.fileIndex = index + 1;
44298
45986
  worksheet.tables.forEach((table) => {
45987
+ const nameKey = table.name.toLowerCase();
45988
+ const existingSheet = tableNameMap.get(nameKey);
45989
+ if (existingSheet !== void 0) throw new TableError(`Duplicate table name "${table.name}": already used in worksheet "${existingSheet}". Table names must be unique across the entire workbook (case-insensitive).`);
45990
+ tableNameMap.set(nameKey, worksheet.name);
44299
45991
  tableCount++;
44300
45992
  table.target = `table${tableCount}.xml`;
44301
45993
  table.id = tableCount;
@@ -44304,6 +45996,16 @@ self.onmessage = async function(event) {
44304
45996
  worksheetXform.prepare(worksheet, worksheetOptions);
44305
45997
  });
44306
45998
  model.hasCheckboxes = model.styles.hasCheckboxes;
45999
+ let dynamicArrayCount = 0;
46000
+ model.worksheets.forEach((worksheet) => {
46001
+ (worksheet.rows ?? []).forEach((row) => {
46002
+ (row.cells ?? []).forEach((cell) => {
46003
+ if (cell.isDynamicArray) dynamicArrayCount++;
46004
+ });
46005
+ });
46006
+ });
46007
+ model.hasDynamicArrayFormulas = dynamicArrayCount > 0;
46008
+ model.dynamicArrayCount = dynamicArrayCount;
44307
46009
  if (worksheetOptions.hasHeaderWatermark) model.hasHeaderWatermark = true;
44308
46010
  const passthrough = model.passthrough || {};
44309
46011
  const passthroughManager = new PassthroughManager();
@@ -44311,6 +46013,22 @@ self.onmessage = async function(event) {
44311
46013
  model.passthroughContentTypes = passthroughManager.getContentTypes();
44312
46014
  }
44313
46015
  };
46016
+ /**
46017
+ * Invoke the registered engine on `workbook`. Throws a descriptive error
46018
+ * if no engine has been installed.
46019
+ */
46020
+ function invokeFormulaEngine(workbook) {
46021
+ throw new Error("No formula engine is installed. Call `installFormulaEngine()` from `@cj-tech-master/excelts/formula` once at startup to enable `Workbook.calculateFormulas()` and automatic recalculation during `excelToPdf()`.");
46022
+ }
46023
+ /**
46024
+ * Like {@link invokeFormulaEngine} but returns silently when no engine is
46025
+ * installed. Used by `excelToPdf()` so that PDF export still works for
46026
+ * workbooks whose cached formula results are already up to date (the
46027
+ * common case when the XLSX was saved by Excel itself).
46028
+ */
46029
+ function tryInvokeFormulaEngine(workbook) {
46030
+ return false;
46031
+ }
44314
46032
  //#endregion
44315
46033
  //#region src/modules/markdown/constants.ts
44316
46034
  /**
@@ -44950,29 +46668,48 @@ self.onmessage = async function(event) {
44950
46668
  const datumNumber = Number(datum);
44951
46669
  if (!Number.isNaN(datumNumber) && datumNumber !== Infinity) return datumNumber;
44952
46670
  }
44953
- const date = dateParser.parse(datum);
44954
- if (date) return date;
44955
- const special = SpecialValues[datum];
44956
- if (special !== void 0) return special;
46671
+ if (typeof datum === "string") {
46672
+ const date = dateParser.parse(datum);
46673
+ if (date) return date;
46674
+ const special = SpecialValues[datum];
46675
+ if (special !== void 0) return special;
46676
+ }
44957
46677
  return datum;
44958
46678
  };
44959
46679
  }
44960
46680
  /**
44961
46681
  * Create a value mapper for writing Excel values to CSV format.
44962
- * Handles hyperlinks, formulas, rich text, dates, errors, and objects.
46682
+ *
46683
+ * Branches below correspond to each member of the `CellValue` union, in an
46684
+ * order that prefers the most specific shape:
46685
+ * 1. Hyperlink — emit the URL (falling back to display text)
46686
+ * 2. Formula / SharedFormula / ArrayFormula — emit the evaluated `result`
46687
+ * 3. RichText — flatten runs to plain text
46688
+ * 4. Checkbox — emit the boolean state
46689
+ * 5. Error — emit the Excel error token (e.g. "#N/A")
46690
+ * 6. Date — format via the configured DateFormatter
46691
+ * 7. Primitive — pass through (`string | number | boolean | null | undefined`)
46692
+ *
46693
+ * An object that does not match any of the above is stringified as JSON so
46694
+ * the CSV remains round-trippable rather than producing `[object Object]`.
44963
46695
  */
44964
46696
  function createDefaultWriteMapper(dateFormat, dateUTC) {
44965
46697
  const formatter = dateFormat ? DateFormatter.create(dateFormat, { utc: dateUTC }) : DateFormatter.iso(dateUTC);
44966
46698
  return function mapValue(value) {
44967
- if (value) {
44968
- if (value.text || value.hyperlink) return value.hyperlink || value.text || "";
44969
- if (value.formula || value.result) return value.result || "";
44970
- if (value.richText && Array.isArray(value.richText)) return value.richText.map((r) => r.text).join("");
44971
- if (value instanceof Date) return formatter.format(value);
44972
- if (value.error) return value.error;
44973
- if (typeof value === "object") return JSON.stringify(value);
44974
- }
44975
- return value;
46699
+ if (value === null || value === void 0) return value;
46700
+ if (value instanceof Date) return formatter.format(value);
46701
+ if (typeof value !== "object") return value;
46702
+ const maybeLink = value;
46703
+ if (typeof maybeLink.hyperlink === "string" || typeof maybeLink.text === "string") {
46704
+ const url = typeof maybeLink.hyperlink === "string" ? maybeLink.hyperlink : "";
46705
+ const text = typeof maybeLink.text === "string" ? maybeLink.text : "";
46706
+ return url || text || "";
46707
+ }
46708
+ if ("formula" in value || "sharedFormula" in value) return value.result ?? "";
46709
+ if ("richText" in value && Array.isArray(value.richText)) return value.richText.map((r) => r.text).join("");
46710
+ if ("checkbox" in value && typeof value.checkbox === "boolean") return value.checkbox;
46711
+ if ("error" in value && typeof value.error === "string") return value.error;
46712
+ return JSON.stringify(value);
44976
46713
  };
44977
46714
  }
44978
46715
  /**
@@ -45064,7 +46801,17 @@ self.onmessage = async function(event) {
45064
46801
  static {
45065
46802
  this.Reader = WorkbookReader;
45066
46803
  }
45067
- constructor() {
46804
+ /**
46805
+ * @param options Optional construction options.
46806
+ * - `formulaSyntaxProbe`: An explicit tokenizer+parser probe used to
46807
+ * classify defined-name text during XLSX load. Providing this makes
46808
+ * classification deterministic for *this* workbook regardless of
46809
+ * whether `installFormulaEngine()` has been called. Most callers
46810
+ * don't need it — `installFormulaEngine()` registers a
46811
+ * process-wide default probe that is picked up automatically.
46812
+ */
46813
+ constructor(options) {
46814
+ this._tableNames = /* @__PURE__ */ new Set();
45068
46815
  this.category = "";
45069
46816
  this.company = "";
45070
46817
  this.created = /* @__PURE__ */ new Date();
@@ -45080,9 +46827,30 @@ self.onmessage = async function(event) {
45080
46827
  this.views = [];
45081
46828
  this.media = [];
45082
46829
  this.pivotTables = [];
46830
+ this.externalLinks = [];
45083
46831
  this._passthrough = {};
45084
46832
  this._rawDrawings = {};
45085
- this._definedNames = new DefinedNames();
46833
+ this._writerExternalLinkCache = /* @__PURE__ */ new Map();
46834
+ this._definedNames = new DefinedNames(options?.formulaSyntaxProbe);
46835
+ }
46836
+ /**
46837
+ * The default font for the workbook (fontId=0 / "Normal" style).
46838
+ * Cells without explicit font styles will inherit this font in Excel.
46839
+ *
46840
+ * @example
46841
+ * ```ts
46842
+ * wb.defaultFont = { name: "Arial", size: 12 };
46843
+ * ```
46844
+ *
46845
+ * When reading an existing XLSX file, this preserves the original default font
46846
+ * for round-trip fidelity. Setting it on a new workbook changes the default
46847
+ * from Calibri 11 to your chosen font.
46848
+ */
46849
+ get defaultFont() {
46850
+ return this._defaultFont;
46851
+ }
46852
+ set defaultFont(font) {
46853
+ this._defaultFont = font;
45086
46854
  }
45087
46855
  /**
45088
46856
  * Import a worksheet from another workbook (or a standalone worksheet).
@@ -45105,6 +46873,22 @@ self.onmessage = async function(event) {
45105
46873
  return newWs;
45106
46874
  }
45107
46875
  /**
46876
+ * Protect the workbook structure with an optional password.
46877
+ * Prevents users from adding, deleting, renaming, moving, or copying worksheets.
46878
+ *
46879
+ * @param password - Optional password to protect the structure
46880
+ * @param options - Optional protection flags (lockStructure, lockWindows, lockRevision)
46881
+ */
46882
+ async protect(password, options) {
46883
+ this.protection = await buildWorkbookProtection(password, options);
46884
+ }
46885
+ /**
46886
+ * Remove workbook structure protection.
46887
+ */
46888
+ unprotect() {
46889
+ this.protection = void 0;
46890
+ }
46891
+ /**
45108
46892
  * xlsx file format operations
45109
46893
  * Node.js: readFile, writeFile, read (stream), write (stream), load (buffer), writeBuffer
45110
46894
  * Browser: load (buffer), writeBuffer
@@ -45615,6 +47399,47 @@ self.onmessage = async function(event) {
45615
47399
  get definedNames() {
45616
47400
  return this._definedNames;
45617
47401
  }
47402
+ /**
47403
+ * Recalculate all formula cells in this workbook.
47404
+ *
47405
+ * Evaluates every formula cell using the built-in calculation engine and updates
47406
+ * each cell's cached `result` value in-place. Formulas are evaluated with
47407
+ * recursive dependency resolution, memoization, and circular reference detection.
47408
+ *
47409
+ * Call this after programmatically modifying cell values that are referenced by
47410
+ * formulas, to ensure formula results reflect the latest data.
47411
+ *
47412
+ * Unsupported functions preserve their original cached result if one exists.
47413
+ *
47414
+ * ## Tree-shaking note
47415
+ *
47416
+ * The formula engine ships ~200KB of code (433 Excel functions, parser,
47417
+ * evaluator, dependency graph, spill materialiser). To keep it out of
47418
+ * bundles that don't need it, the engine is registered at runtime
47419
+ * rather than imported by the core `Workbook` module. Call
47420
+ * {@link installFormulaEngine} once at startup before the first call
47421
+ * to this method, or a clear error will be thrown explaining what to do.
47422
+ *
47423
+ * ```ts
47424
+ * import { installFormulaEngine } from "@cj-tech-master/excelts/formula";
47425
+ *
47426
+ * installFormulaEngine(); // once, at startup
47427
+ *
47428
+ * sheet.getCell("A1").value = 100;
47429
+ * workbook.calculateFormulas(); // now works
47430
+ * ```
47431
+ *
47432
+ * Callers who prefer a zero-side-effect, tree-shakeable surface can
47433
+ * use the functional equivalent directly:
47434
+ *
47435
+ * ```ts
47436
+ * import { calculateFormulas } from "@cj-tech-master/excelts/formula";
47437
+ * calculateFormulas(workbook);
47438
+ * ```
47439
+ */
47440
+ calculateFormulas() {
47441
+ invokeFormulaEngine(this);
47442
+ }
45618
47443
  clearThemes() {
45619
47444
  this._themes = void 0;
45620
47445
  }
@@ -45632,6 +47457,119 @@ self.onmessage = async function(event) {
45632
47457
  getImage(id) {
45633
47458
  return this.media[Number(id)];
45634
47459
  }
47460
+ /**
47461
+ * Declare that formulas in this workbook may reference an external
47462
+ * workbook. Registers the target so the output file contains the required
47463
+ * `xl/externalLinks/externalLink{N}.xml` part plus its `.rels` sibling
47464
+ * and Office/WPS can resolve the reference correctly.
47465
+ *
47466
+ * When Office opens the file, it resolves a relative `target` like
47467
+ * `"测试.xlsx"` **relative to the current workbook's directory** — which
47468
+ * is the exact behaviour the user expects when they write
47469
+ * `=[测试.xlsx]Sheet1!A1`. Absolute `file:///…` or `http(s)://…` URIs
47470
+ * are accepted and written through unchanged.
47471
+ *
47472
+ * @returns the registered {@link ExternalLinkModel}. Its `index` field is
47473
+ * the 1-based number used inside the `[N]` prefix of on-disk formula
47474
+ * strings (the library rewrites `[target]` forms to `[index]` at write
47475
+ * time automatically).
47476
+ *
47477
+ * @example
47478
+ * ```ts
47479
+ * const wb = new Workbook();
47480
+ * const ws = wb.addWorksheet("Main");
47481
+ *
47482
+ * // Declare the link once — sheet names and cached values are optional
47483
+ * // but improve interoperability (Excel displays cached values when the
47484
+ * // external file is unavailable).
47485
+ * wb.addExternalLink({
47486
+ * target: "测试.xlsx",
47487
+ * sheetNames: ["Sheet1"],
47488
+ * cachedValues: { Sheet1: { A1: 42 } }
47489
+ * });
47490
+ *
47491
+ * // Write the formula using either the target name OR the numeric index;
47492
+ * // the library normalises both to the on-disk `[N]` form.
47493
+ * ws.getCell("A1").value = { formula: "=[测试.xlsx]Sheet1!A1", result: 42 };
47494
+ * ```
47495
+ */
47496
+ addExternalLink(input) {
47497
+ const link = {
47498
+ index: this.externalLinks.length + 1,
47499
+ target: input.target,
47500
+ targetMode: input.targetMode ?? "External",
47501
+ sheetNames: input.sheetNames ? [...input.sheetNames] : [],
47502
+ cachedValues: input.cachedValues ? { ...input.cachedValues } : {}
47503
+ };
47504
+ this.externalLinks.push(link);
47505
+ return link;
47506
+ }
47507
+ /**
47508
+ * Retrieve an external link by its 1-based on-disk index (the number
47509
+ * inside the `[N]` formula prefix) or by matching target path.
47510
+ */
47511
+ getExternalLink(indexOrTarget) {
47512
+ if (typeof indexOrTarget === "number") return this.externalLinks[indexOrTarget - 1];
47513
+ const lower = indexOrTarget.toLowerCase();
47514
+ return this.externalLinks.find((link) => link.target.toLowerCase() === lower);
47515
+ }
47516
+ /**
47517
+ * @internal — used by the writer to obtain the full list of external
47518
+ * links to serialise, including entries auto-discovered from formula
47519
+ * strings during earlier writes. User-visible `externalLinks` always
47520
+ * comes first (in declaration order) so explicit `addExternalLink()`
47521
+ * indices are stable across writes.
47522
+ */
47523
+ _collectExternalLinksForWrite() {
47524
+ const userLower = new Set(this.externalLinks.map((l) => l.target.toLowerCase()));
47525
+ const combined = this.externalLinks.map((link, i) => ({
47526
+ ...link,
47527
+ index: i + 1,
47528
+ sheetNames: [...link.sheetNames ?? []],
47529
+ cachedValues: { ...link.cachedValues ?? {} },
47530
+ targetMode: link.targetMode ?? "External"
47531
+ }));
47532
+ for (const cached of this._writerExternalLinkCache.values()) {
47533
+ if (userLower.has(cached.target.toLowerCase())) continue;
47534
+ combined.push({
47535
+ ...cached,
47536
+ index: combined.length + 1,
47537
+ sheetNames: [...cached.sheetNames],
47538
+ cachedValues: { ...cached.cachedValues }
47539
+ });
47540
+ }
47541
+ return combined;
47542
+ }
47543
+ /**
47544
+ * @internal — record an auto-discovered external link (seen in a
47545
+ * formula but not explicitly declared). Idempotent by target; the
47546
+ * sheet name is upserted onto the existing cached entry when present.
47547
+ * Returns the 1-based index the link will carry in the output file.
47548
+ */
47549
+ _recordAutoExternalLink(target, sheetName) {
47550
+ const lower = target.toLowerCase();
47551
+ const existingUserIdx = this.externalLinks.findIndex((l) => l.target.toLowerCase() === lower);
47552
+ if (existingUserIdx !== -1) return existingUserIdx + 1;
47553
+ let cached = this._writerExternalLinkCache.get(lower);
47554
+ if (!cached) {
47555
+ cached = {
47556
+ index: 0,
47557
+ target,
47558
+ targetMode: "External",
47559
+ sheetNames: [],
47560
+ cachedValues: {}
47561
+ };
47562
+ this._writerExternalLinkCache.set(lower, cached);
47563
+ }
47564
+ if (sheetName && !cached.sheetNames.includes(sheetName)) cached.sheetNames.push(sheetName);
47565
+ const userCount = this.externalLinks.length;
47566
+ let cacheIdx = 0;
47567
+ for (const key of this._writerExternalLinkCache.keys()) {
47568
+ cacheIdx++;
47569
+ if (key === lower) return userCount + cacheIdx;
47570
+ }
47571
+ return userCount + this._writerExternalLinkCache.size;
47572
+ }
45635
47573
  get model() {
45636
47574
  return {
45637
47575
  creator: this.creator || "Unknown",
@@ -45640,6 +47578,7 @@ self.onmessage = async function(event) {
45640
47578
  created: this.created,
45641
47579
  modified: this.modified,
45642
47580
  properties: this.properties,
47581
+ protection: this.protection,
45643
47582
  worksheets: this.worksheets.map((worksheet) => worksheet.model),
45644
47583
  sheets: this.worksheets.map((ws) => ws.model).filter(Boolean),
45645
47584
  definedNames: this._definedNames.model,
@@ -45660,7 +47599,8 @@ self.onmessage = async function(event) {
45660
47599
  calcProperties: this.calcProperties,
45661
47600
  passthrough: this._passthrough,
45662
47601
  rawDrawings: this._rawDrawings,
45663
- defaultFont: this._defaultFont
47602
+ defaultFont: this._defaultFont,
47603
+ externalLinks: this.externalLinks
45664
47604
  };
45665
47605
  }
45666
47606
  set model(value) {
@@ -45680,8 +47620,10 @@ self.onmessage = async function(event) {
45680
47620
  this.revision = value.revision;
45681
47621
  this.contentStatus = value.contentStatus;
45682
47622
  this.properties = value.properties;
47623
+ this.protection = value.protection;
45683
47624
  this.calcProperties = value.calcProperties;
45684
47625
  this._worksheets = [];
47626
+ this._tableNames.clear();
45685
47627
  value.worksheets.forEach((worksheetModel) => {
45686
47628
  const { id, name, state } = worksheetModel;
45687
47629
  const orderNo = value.sheets && value.sheets.findIndex((ws) => ws.id === id);
@@ -45702,6 +47644,8 @@ self.onmessage = async function(event) {
45702
47644
  this._passthrough = value.passthrough || {};
45703
47645
  this._rawDrawings = value.rawDrawings || {};
45704
47646
  this._defaultFont = value.defaultFont;
47647
+ this.externalLinks = value.externalLinks ? [...value.externalLinks] : [];
47648
+ this._writerExternalLinkCache = /* @__PURE__ */ new Map();
45705
47649
  }
45706
47650
  };
45707
47651
  //#endregion
@@ -52434,22 +54378,6 @@ self.onmessage = async function(event) {
52434
54378
  //#endregion
52435
54379
  //#region src/modules/pdf/excel-bridge.ts
52436
54380
  /**
52437
- * Excel-to-PDF Bridge
52438
- *
52439
- * Converts Excel Workbook data into the PDF module's independent data model.
52440
- * This is the ONLY file in the PDF module that imports from @excel.
52441
- *
52442
- * @example
52443
- * ```typescript
52444
- * import { Workbook } from "excelts";
52445
- * import { excelToPdf } from "excelts/pdf";
52446
- *
52447
- * const workbook = new Workbook();
52448
- * // ... build workbook ...
52449
- * const pdf = await excelToPdf(workbook);
52450
- * ```
52451
- */
52452
- /**
52453
54381
  * Export an Excel Workbook directly to PDF.
52454
54382
  *
52455
54383
  * This is a convenience function that converts the Workbook to the PDF module's
@@ -52461,6 +54389,7 @@ self.onmessage = async function(event) {
52461
54389
  * @returns Promise of PDF file as a Uint8Array
52462
54390
  */
52463
54391
  async function excelToPdf(workbook, options) {
54392
+ tryInvokeFormulaEngine(workbook);
52464
54393
  return exportPdf(excelWorkbookToPdf(workbook), options);
52465
54394
  }
52466
54395
  /**
@@ -52502,7 +54431,7 @@ self.onmessage = async function(event) {
52502
54431
  if (!row) continue;
52503
54432
  const cells = /* @__PURE__ */ new Map();
52504
54433
  row.eachCell({ includeEmpty: true }, (cell) => {
52505
- const hasValue = cell.type !== ValueType.Null && cell.type !== ValueType.Merge;
54434
+ const hasValue = cell.type !== 0 && cell.type !== 1;
52506
54435
  const hasStyle = cell.style && (cell.style.border && (cell.style.border.top || cell.style.border.right || cell.style.border.bottom || cell.style.border.left) || cell.style.fill || cell.style.font);
52507
54436
  if (hasValue || hasStyle) cells.set(cell.col, convertCell(cell));
52508
54437
  });
@@ -52589,17 +54518,17 @@ self.onmessage = async function(event) {
52589
54518
  }
52590
54519
  function mapValueType(vt) {
52591
54520
  switch (vt) {
52592
- case ValueType.Null: return PdfCellType.Empty;
52593
- case ValueType.Merge: return PdfCellType.Merge;
52594
- case ValueType.Number: return PdfCellType.Number;
52595
- case ValueType.String:
52596
- case ValueType.SharedString: return PdfCellType.String;
52597
- case ValueType.Date: return PdfCellType.Date;
52598
- case ValueType.Hyperlink: return PdfCellType.Hyperlink;
52599
- case ValueType.Formula: return PdfCellType.Formula;
52600
- case ValueType.RichText: return PdfCellType.RichText;
52601
- case ValueType.Boolean: return PdfCellType.Boolean;
52602
- case ValueType.Error: return PdfCellType.Error;
54521
+ case 0: return PdfCellType.Empty;
54522
+ case 1: return PdfCellType.Merge;
54523
+ case 2: return PdfCellType.Number;
54524
+ case 3:
54525
+ case 7: return PdfCellType.String;
54526
+ case 4: return PdfCellType.Date;
54527
+ case 5: return PdfCellType.Hyperlink;
54528
+ case 6: return PdfCellType.Formula;
54529
+ case 8: return PdfCellType.RichText;
54530
+ case 9: return PdfCellType.Boolean;
54531
+ case 10: return PdfCellType.Error;
52603
54532
  default: return PdfCellType.String;
52604
54533
  }
52605
54534
  }
@@ -52609,12 +54538,12 @@ self.onmessage = async function(event) {
52609
54538
  function getCellDisplayText(cell) {
52610
54539
  if (!cell) return "";
52611
54540
  switch (cell.type) {
52612
- case ValueType.Null:
52613
- case ValueType.Merge: return "";
52614
- case ValueType.RichText:
52615
- case ValueType.Hyperlink: return cell.text ?? "";
52616
- case ValueType.Error: return cell.value?.error ?? cell.text ?? "";
52617
- case ValueType.Formula: {
54541
+ case 0:
54542
+ case 1: return "";
54543
+ case 8:
54544
+ case 5: return cell.text ?? "";
54545
+ case 10: return cell.value?.error ?? cell.text ?? "";
54546
+ case 6: {
52618
54547
  const result = cell.result;
52619
54548
  if (result !== void 0 && result !== null) {
52620
54549
  if (typeof result === "object" && "error" in result) return result.error;
@@ -52638,7 +54567,7 @@ self.onmessage = async function(event) {
52638
54567
  return String(value);
52639
54568
  }
52640
54569
  function convertCellValue(cell) {
52641
- if (cell.type === ValueType.RichText) {
54570
+ if (cell.type === 8) {
52642
54571
  const rtValue = cell.value;
52643
54572
  if (rtValue?.richText) return { richText: rtValue.richText.map((run) => ({
52644
54573
  text: run.text,