@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
@@ -10,18 +10,21 @@
10
10
  */
11
11
  import { ZipParser } from "../../archive/unzip/zip-parser.js";
12
12
  import { StreamingZip, ZipDeflateFile } from "../../archive/zip/stream.js";
13
- import { ExcelStreamStateError, ExcelFileError, ImageError, ExcelNotSupportedError, XmlParseError } from "../errors.js";
13
+ import { ExcelStreamStateError, ExcelFileError, ImageError, ExcelNotSupportedError, XmlParseError, TableError } from "../errors.js";
14
14
  import { filterDrawingAnchors } from "../utils/drawing-utils.js";
15
- import { commentsPath, commentsRelTargetFromWorksheetName, ctrlPropPath, drawingPath, drawingRelsPath, OOXML_REL_TARGETS, pivotTableRelTargetFromWorksheetName, pivotCacheDefinitionRelTargetFromWorkbook, getCommentsIndexFromPath, getDrawingNameFromPath, getDrawingNameFromRelsPath, getMediaFilenameFromPath, mediaPath, getPivotCacheDefinitionNameFromPath, getPivotCacheDefinitionNameFromRelsPath, getPivotCacheRecordsNameFromPath, getPivotTableNameFromPath, getPivotTableNameFromRelsPath, pivotCacheDefinitionPath, pivotCacheDefinitionRelsPath, pivotCacheDefinitionRelTargetFromPivotTable, pivotCacheRecordsPath, pivotCacheRecordsRelTarget, pivotTablePath, pivotTableRelsPath, getTableNameFromPath, tablePath, tableRelTargetFromWorksheetName, themePath, getThemeNameFromPath, getVmlDrawingNameFromPath, getVmlDrawingHFNameFromPath, getWorksheetNoFromWorksheetPath, getWorksheetNoFromWorksheetRelsPath, isBinaryEntryPath, normalizeZipPath, OOXML_PATHS, vmlDrawingRelTargetFromWorksheetName, vmlDrawingPath, vmlDrawingHFPath, vmlDrawingHFRelsPath, worksheetPath, worksheetRelsPath, worksheetRelTarget } from "../utils/ooxml-paths.js";
15
+ import { rewriteExternalRefs } from "../utils/external-link-formula.js";
16
+ import { commentsPath, commentsRelTargetFromWorksheetName, ctrlPropPath, drawingPath, drawingRelsPath, externalLinkPath, externalLinkRelsPath, externalLinkRelTargetFromWorkbook, OOXML_REL_TARGETS, pivotTableRelTargetFromWorksheetName, pivotCacheDefinitionRelTargetFromWorkbook, getCommentsIndexFromPath, getDrawingNameFromPath, getDrawingNameFromRelsPath, getExternalLinkIndexFromPath, getExternalLinkIndexFromRelsPath, getMediaFilenameFromPath, mediaPath, getPivotCacheDefinitionNameFromPath, getPivotCacheDefinitionNameFromRelsPath, getPivotCacheRecordsNameFromPath, getPivotTableNameFromPath, getPivotTableNameFromRelsPath, pivotCacheDefinitionPath, pivotCacheDefinitionRelsPath, pivotCacheDefinitionRelTargetFromPivotTable, pivotCacheRecordsPath, pivotCacheRecordsRelTarget, pivotTablePath, pivotTableRelsPath, getTableNameFromPath, tablePath, tableRelTargetFromWorksheetName, themePath, getThemeNameFromPath, getVmlDrawingNameFromPath, getVmlDrawingHFNameFromPath, getWorksheetNoFromWorksheetPath, getWorksheetNoFromWorksheetRelsPath, isBinaryEntryPath, normalizeZipPath, OOXML_PATHS, vmlDrawingRelTargetFromWorksheetName, vmlDrawingPath, vmlDrawingHFPath, vmlDrawingHFRelsPath, worksheetPath, worksheetRelsPath, worksheetRelTarget } from "../utils/ooxml-paths.js";
16
17
  import { PassthroughManager } from "../utils/passthrough-manager.js";
17
18
  import { StreamBuf } from "../utils/stream-buf.js";
18
19
  import { RelType } from "./rel-type.js";
20
+ import { ExternalLinkXform } from "./xform/book/external-link-xform.js";
19
21
  import { WorkbookXform } from "./xform/book/workbook-xform.js";
20
22
  import { CommentsXform } from "./xform/comment/comments-xform.js";
21
23
  import { AppXform } from "./xform/core/app-xform.js";
22
24
  import { ContentTypesXform } from "./xform/core/content-types-xform.js";
23
25
  import { CoreXform } from "./xform/core/core-xform.js";
24
26
  import { FeaturePropertyBagXform } from "./xform/core/feature-property-bag-xform.js";
27
+ import { MetadataXform } from "./xform/core/metadata-xform.js";
25
28
  import { RelationshipsXform } from "./xform/core/relationships-xform.js";
26
29
  import { CtrlPropXform } from "./xform/drawing/ctrl-prop-xform.js";
27
30
  import { DrawingXform } from "./xform/drawing/drawing-xform.js";
@@ -242,6 +245,31 @@ class StreamingZipWriterAdapter {
242
245
  }
243
246
  }
244
247
  StreamingZipWriterAdapter.textEncoder = new TextEncoder();
248
+ /**
249
+ * Extract the trailing integer from a workbook-rels Target like
250
+ * `"externalLinks/externalLink12.xml"` (a path relative to `xl/`). Mirror
251
+ * of {@link getExternalLinkIndexFromPath} which takes the full
252
+ * `xl/externalLinks/…` form. Used during reconcile to bridge the
253
+ * workbook.xml.rels entry to the parsed externalLinkN.xml part.
254
+ */
255
+ function externalLinkIndexFromRelTarget(target) {
256
+ const match = /(?:^|\/)externalLink(\d+)[.]xml$/.exec(target);
257
+ return match ? parseInt(match[1], 10) : undefined;
258
+ }
259
+ /**
260
+ * Add `sheetName` to an ExternalLinkModel's `sheetNames` list if it isn't
261
+ * already present. Ordering is preserved — the first-seen sheet wins
262
+ * position 0, which matches what Excel does when writing externalLinks
263
+ * itself.
264
+ */
265
+ function upsertSheet(link, sheetName) {
266
+ if (!sheetName) {
267
+ return;
268
+ }
269
+ if (!link.sheetNames.includes(sheetName)) {
270
+ link.sheetNames.push(sheetName);
271
+ }
272
+ }
245
273
  /**
246
274
  * XLSX class - handles Excel file operations
247
275
  * Works in both Node.js and Browser environments
@@ -310,10 +338,12 @@ class XLSX {
310
338
  await this.addDrawings(zip, model);
311
339
  await this.addTables(zip, model);
312
340
  await this.addPivotTables(zip, model);
341
+ await this.addExternalLinks(zip, model);
313
342
  this.addPassthrough(zip, model);
314
343
  await this.addThemes(zip, model);
315
344
  await this.addStyles(zip, model);
316
345
  await this.addFeaturePropertyBag(zip, model);
346
+ await this.addMetadata(zip, model);
317
347
  await this.addMedia(zip, model);
318
348
  await this.addApp(zip, model);
319
349
  await this.addCore(zip, model);
@@ -363,32 +393,44 @@ class XLSX {
363
393
  return this._finalize(zip);
364
394
  }
365
395
  /**
366
- * Load workbook from buffer/ArrayBuffer/Uint8Array
396
+ * Load a workbook from binary data.
397
+ *
398
+ * Accepted inputs:
399
+ * - `Uint8Array` (and `Buffer`, which is a Uint8Array at runtime)
400
+ * - `ArrayBuffer` / `SharedArrayBuffer`
401
+ * - Any `ArrayBufferView` (DataView, Int8Array, Float32Array, …) — the
402
+ * underlying bytes are reinterpreted as a zip archive
403
+ * - `string` — treated as base64-encoded data when `options.base64 === true`;
404
+ * raw binary cannot be round-tripped through a JS string and is rejected
405
+ * to prevent silent corruption.
367
406
  */
368
407
  async load(data, options) {
369
- let buffer;
370
- // Validate input
371
- const isBuffer = typeof Buffer !== "undefined" ? Buffer.isBuffer(data) : false;
372
- if (!data ||
373
- (typeof data === "object" &&
374
- !isBuffer &&
375
- !(data instanceof Uint8Array) &&
376
- !(data instanceof ArrayBuffer))) {
408
+ if (data === null || data === undefined) {
377
409
  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) ?");
378
410
  }
379
- // Handle base64 input
380
- if (options && options.base64) {
381
- buffer = base64ToUint8Array(data.toString());
411
+ let buffer;
412
+ if (typeof data === "string") {
413
+ // Strings must be base64-encoded — binary zip bytes cannot be round-tripped
414
+ // through a JS string without corruption. Require the explicit opt-in.
415
+ if (!options?.base64) {
416
+ 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) ? " +
417
+ "String input requires options.base64 === true (base64-encoded zip archive).");
418
+ }
419
+ buffer = base64ToUint8Array(data);
420
+ }
421
+ else if (data instanceof Uint8Array) {
422
+ // Covers Buffer (Node) and any typed-array view whose element size is 1.
423
+ buffer = data;
382
424
  }
383
425
  else if (data instanceof ArrayBuffer) {
384
426
  buffer = new Uint8Array(data);
385
427
  }
386
- else if (data instanceof Uint8Array) {
387
- buffer = data;
428
+ else if (ArrayBuffer.isView(data)) {
429
+ // DataView, Int8Array, Float32Array, … — view onto an underlying buffer.
430
+ buffer = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
388
431
  }
389
432
  else {
390
- // Node.js Buffer or other array-like
391
- buffer = new Uint8Array(data);
433
+ 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) ?");
392
434
  }
393
435
  return this.loadBuffer(buffer, options);
394
436
  }
@@ -435,7 +477,15 @@ class XLSX {
435
477
  pivotCacheDefinitions: {},
436
478
  pivotCacheRecords: {},
437
479
  // Passthrough storage for unknown/unsupported files (charts, etc.)
438
- passthrough: {}
480
+ passthrough: {},
481
+ // External workbook links — parsed from xl/externalLinks/externalLinkN.xml
482
+ // during _processDefaultEntry, then reconciled into a dense
483
+ // ExternalLinkModel[] by reconcile() using workbookRels + <externalReferences>.
484
+ externalLinksByIndex: {},
485
+ // Raw rels from each externalLinkN.rels file, keyed by index.
486
+ // Contains the actual Target path (e.g. "测试.xlsx", "file:///...")
487
+ // and TargetMode ("External" / "Internal").
488
+ externalLinkRelsByIndex: {}
439
489
  };
440
490
  }
441
491
  /**
@@ -495,8 +545,12 @@ class XLSX {
495
545
  model.definedNames = workbook.definedNames;
496
546
  model.views = workbook.views;
497
547
  model.properties = workbook.properties;
548
+ model.protection = workbook.protection;
498
549
  model.calcProperties = workbook.calcProperties;
499
550
  model.pivotCaches = workbook.pivotCaches;
551
+ // Pass-through the ordered list of <externalReference> rIds. These
552
+ // get resolved into a dense externalLinks[] during reconcile().
553
+ model.externalReferences = workbook.externalReferences;
500
554
  return true;
501
555
  }
502
556
  case OOXML_PATHS.xlSharedStrings:
@@ -525,6 +579,14 @@ class XLSX {
525
579
  model.styles = new StylesXform();
526
580
  await model.styles.parseStream(stream);
527
581
  return true;
582
+ case OOXML_PATHS.xlMetadata: {
583
+ const metadataXform = new MetadataXform();
584
+ const metadataResult = await metadataXform.parseStream(stream);
585
+ if (metadataResult) {
586
+ model.metadata = metadataResult;
587
+ }
588
+ return true;
589
+ }
528
590
  default:
529
591
  return false;
530
592
  }
@@ -695,12 +757,21 @@ class XLSX {
695
757
  comments: model.comments,
696
758
  tables: model.tables,
697
759
  vmlDrawings: model.vmlDrawings,
698
- pivotTables: model.pivotTablesIndexed
760
+ pivotTables: model.pivotTablesIndexed,
761
+ hasDynamicArrayMetadata: !!model.metadata?.hasDynamicArrays,
762
+ dynamicArrayCmIndices: model.metadata?.dynamicArrayCmIndices
699
763
  };
700
764
  model.worksheets.forEach((worksheet) => {
701
765
  worksheet.relationships = model.worksheetRels[worksheet.sheetNo];
702
766
  worksheetXform.reconcile(worksheet, sheetOptions);
703
767
  });
768
+ // Reconcile external workbook links before workbookRels / externalReferences
769
+ // are dropped. Joins 3 sources:
770
+ // 1. model.externalReferences — ordered list of { rId } from workbook.xml
771
+ // 2. model.workbookRels — maps rId → target path (inside xl/)
772
+ // 3. model.externalLinksByIndex — parsed externalLinkN.xml parts
773
+ // 4. model.externalLinkRelsByIndex — parsed externalLinkN.xml.rels parts
774
+ this._reconcileExternalLinks(model);
704
775
  // delete unnecessary parts
705
776
  delete model.worksheetHash;
706
777
  delete model.worksheetRels;
@@ -716,6 +787,180 @@ class XLSX {
716
787
  delete model.drawingRels;
717
788
  delete model.vmlDrawings;
718
789
  delete model.pivotTableRels;
790
+ delete model.metadata;
791
+ // Internal-only scratch fields consumed by _reconcileExternalLinks.
792
+ delete model.externalReferences;
793
+ delete model.externalLinksByIndex;
794
+ delete model.externalLinkRelsByIndex;
795
+ }
796
+ /**
797
+ * Join the three on-disk sources that together describe external workbook
798
+ * references into a single dense `model.externalLinks: ExternalLinkModel[]`.
799
+ *
800
+ * Sources:
801
+ * - `<externalReferences>` list in workbook.xml (declaration order)
802
+ * - `xl/_rels/workbook.xml.rels` (rId → internal path)
803
+ * - `xl/externalLinks/externalLink{N}.xml` (sheet names, cached values)
804
+ * - `xl/externalLinks/_rels/externalLink{N}.xml.rels` (target, TargetMode)
805
+ *
806
+ * The 1-based index of each resulting ExternalLinkModel matches the `[N]`
807
+ * used in formula strings — this is the single source of truth formula
808
+ * code should rely on.
809
+ */
810
+ _reconcileExternalLinks(model) {
811
+ const refs = model.externalReferences;
812
+ if (!refs || refs.length === 0) {
813
+ // Even when workbook.xml has no <externalReferences>, we may still
814
+ // have parsed externalLink parts (e.g. orphaned files); those are
815
+ // dropped silently rather than generating synthesised indices.
816
+ if (!model.externalLinks) {
817
+ model.externalLinks = [];
818
+ }
819
+ return;
820
+ }
821
+ const rels = (model.workbookRels ?? []);
822
+ const relById = new Map();
823
+ for (const rel of rels) {
824
+ if (rel.Type === RelType.ExternalLink) {
825
+ relById.set(rel.Id, rel);
826
+ }
827
+ }
828
+ const externalLinks = [];
829
+ for (let i = 0; i < refs.length; i++) {
830
+ const ref = refs[i];
831
+ const rel = relById.get(ref.rId);
832
+ if (!rel) {
833
+ // Broken reference — <externalReference> points at an rId that is
834
+ // not of type ExternalLink. We skip silently; the formula engine
835
+ // will see the now-missing index and fall back to #REF! as before.
836
+ continue;
837
+ }
838
+ // The rel Target is a path inside xl/ like "externalLinks/externalLink1.xml".
839
+ // Extract the trailing index to look up the parsed part.
840
+ const partIndex = externalLinkIndexFromRelTarget(rel.Target);
841
+ if (partIndex === undefined) {
842
+ continue;
843
+ }
844
+ const parsed = model.externalLinksByIndex[partIndex];
845
+ const partRels = (model.externalLinkRelsByIndex[partIndex] ?? []);
846
+ // Locate the externalLinkPath rel (should be unique within a part).
847
+ const pathRel = partRels.find(r => r.Type === RelType.ExternalLinkPath) ??
848
+ partRels.find(r => r.TargetMode === "External");
849
+ externalLinks.push({
850
+ index: i + 1,
851
+ rId: ref.rId,
852
+ target: pathRel?.Target ?? "",
853
+ targetMode: pathRel?.TargetMode ?? "External",
854
+ sheetNames: parsed?.sheetNames ?? [],
855
+ cachedValues: parsed?.cachedValues ?? {}
856
+ });
857
+ }
858
+ model.externalLinks = externalLinks;
859
+ }
860
+ /**
861
+ * Write-time pass that brings the workbook model into a shape the writer
862
+ * can serialise cleanly. Two concerns:
863
+ *
864
+ * 1. Build the final external-link list for this write, combining
865
+ * user-declared links (`wb.externalLinks`) with auto-discovered
866
+ * ones from previous writes (cached on the Workbook). The result
867
+ * is assigned to `model.externalLinks` and consumed by the writer;
868
+ * `wb.externalLinks` is **not** modified.
869
+ *
870
+ * 2. Scan every formula cell for `[Book]Sheet!` prefixes. Filename-form
871
+ * references that don't match an existing link trigger
872
+ * `_recordAutoExternalLink()` on the Workbook, which adds the target
873
+ * to the private writer cache (so subsequent writes are fixed-point
874
+ * stable) but leaves `wb.externalLinks` untouched.
875
+ *
876
+ * 3. Rewrite every external-ref formula so it uses the numeric `[N]`
877
+ * form, the canonical OOXML storage form. This mutation lands on
878
+ * the cell's model object — matching the library's existing
879
+ * write-time pattern for `ssId`, `styleId`, `si`, and `cm`.
880
+ * Subsequent writes see the `[N]` form directly and resolve it
881
+ * against `model.externalLinks`, giving idempotent output.
882
+ */
883
+ _normaliseExternalLinks(model) {
884
+ // Start from user-declared links, honouring their declaration order.
885
+ const links = this.workbook._collectExternalLinksForWrite();
886
+ // Fast lookup: case-insensitive target → link object in `links`.
887
+ const byTarget = new Map();
888
+ for (const link of links) {
889
+ if (link.target) {
890
+ byTarget.set(link.target.toLowerCase(), link);
891
+ }
892
+ }
893
+ const scratch = { links, byTarget, workbook: this.workbook };
894
+ for (const worksheet of model.worksheets ?? []) {
895
+ for (const row of worksheet.rows ?? []) {
896
+ for (const cell of row.cells ?? []) {
897
+ if (typeof cell?.formula === "string" && cell.formula.length > 0) {
898
+ cell.formula = this._normaliseFormulaExternalRefs(cell.formula, scratch);
899
+ }
900
+ if (typeof cell?.sharedFormula === "string" && cell.sharedFormula.length > 0) {
901
+ // Shared-formula clones typically carry the master's *address*
902
+ // here, not a formula body — they won't match the ref regex.
903
+ // Masters carry the formula on `.formula` (handled above).
904
+ // We rewrite defensively in case a caller stored an actual
905
+ // formula string here.
906
+ cell.sharedFormula = this._normaliseFormulaExternalRefs(cell.sharedFormula, scratch);
907
+ }
908
+ }
909
+ }
910
+ }
911
+ model.externalLinks = links;
912
+ }
913
+ /**
914
+ * Rewrite a single formula so every external-ref prefix uses the numeric
915
+ * `[N]` form. When an unknown filename-form reference is found we record
916
+ * it on the workbook's private writer cache (so the next write can still
917
+ * resolve it) and append a local link to `scratch.links` so subsequent
918
+ * refs in the same formula see the freshly-assigned index.
919
+ */
920
+ _normaliseFormulaExternalRefs(formula, scratch) {
921
+ // rewriteExternalRefs internally calls findExternalRefs and returns
922
+ // the original string unchanged when there are no matches — no need
923
+ // for a separate guard scan here.
924
+ return rewriteExternalRefs(formula, ref => {
925
+ // Numeric ref: accept if it resolves, otherwise preserve verbatim so
926
+ // Excel surfaces `#REF!` at load time — same as the old behaviour
927
+ // for truly broken references.
928
+ if (ref.numeric) {
929
+ if (ref.index !== null && ref.index >= 1 && ref.index <= scratch.links.length) {
930
+ upsertSheet(scratch.links[ref.index - 1], ref.sheet);
931
+ return ref.index;
932
+ }
933
+ return null;
934
+ }
935
+ // Filename form — look up or auto-register.
936
+ const key = ref.workbook.toLowerCase();
937
+ let link = scratch.byTarget.get(key);
938
+ if (!link) {
939
+ const index = scratch.workbook._recordAutoExternalLink(ref.workbook, ref.sheet);
940
+ link = {
941
+ index,
942
+ target: ref.workbook,
943
+ targetMode: "External",
944
+ sheetNames: ref.sheet ? [ref.sheet] : [],
945
+ cachedValues: {}
946
+ };
947
+ // Keep the local writer list dense: insert at its future position.
948
+ // `_recordAutoExternalLink` guarantees `index` is user.length + cache.size
949
+ // at the time of insertion, which equals `scratch.links.length + 1`
950
+ // whenever we walk formulas sequentially.
951
+ scratch.links.push(link);
952
+ scratch.byTarget.set(key, link);
953
+ }
954
+ else {
955
+ upsertSheet(link, ref.sheet);
956
+ // Keep the workbook cache's sheetNames in sync so subsequent
957
+ // writes see the accumulated set.
958
+ if (ref.sheet) {
959
+ scratch.workbook._recordAutoExternalLink(ref.workbook, ref.sheet);
960
+ }
961
+ }
962
+ return link.index;
963
+ });
719
964
  }
720
965
  /**
721
966
  * Reconcile pivot tables by linking them to worksheets and their cache data.
@@ -971,6 +1216,29 @@ class XLSX {
971
1216
  model.pivotCacheRecords[name] = cacheRecords;
972
1217
  }
973
1218
  }
1219
+ /**
1220
+ * Parse `xl/externalLinks/externalLink{N}.xml` into the intermediate
1221
+ * ParsedExternalLink shape. Reconciliation (joining with the rels file
1222
+ * and the workbook's `<externalReferences>` list) happens later in
1223
+ * {@link reconcile}.
1224
+ */
1225
+ async _processExternalLinkEntry(stream, model, index) {
1226
+ const xform = new ExternalLinkXform();
1227
+ const parsed = await xform.parseStream(stream);
1228
+ if (parsed) {
1229
+ model.externalLinksByIndex[index] = parsed;
1230
+ }
1231
+ }
1232
+ /**
1233
+ * Parse `xl/externalLinks/_rels/externalLink{N}.xml.rels`. The Target /
1234
+ * TargetMode carried here is what Excel uses to locate the actual external
1235
+ * file at open time, so we must preserve it verbatim (including relative
1236
+ * paths like `"测试.xlsx"`).
1237
+ */
1238
+ async _processExternalLinkRelsEntry(stream, model, index) {
1239
+ const relationships = await this.parseRels(stream);
1240
+ model.externalLinkRelsByIndex[index] = relationships ?? [];
1241
+ }
974
1242
  // ===========================================================================
975
1243
  // loadFromFiles - shared logic for loading from pre-extracted ZIP data
976
1244
  // ===========================================================================
@@ -1082,6 +1350,20 @@ class XLSX {
1082
1350
  await this._processPivotCacheRecordsEntry(stream, model, pivotCacheRecordsName);
1083
1351
  return true;
1084
1352
  }
1353
+ // External workbook links: xl/externalLinks/externalLinkN.xml and its
1354
+ // sibling _rels file. Both parts are required to reconstruct the
1355
+ // ExternalLinkModel (the .xml carries sheet names + cached values; the
1356
+ // .rels carries the target path and TargetMode).
1357
+ const externalLinkIndex = getExternalLinkIndexFromPath(entryName);
1358
+ if (externalLinkIndex !== undefined) {
1359
+ await this._processExternalLinkEntry(stream, model, externalLinkIndex);
1360
+ return true;
1361
+ }
1362
+ const externalLinkRelsIndex = getExternalLinkIndexFromRelsPath(entryName);
1363
+ if (externalLinkRelsIndex !== undefined) {
1364
+ await this._processExternalLinkRelsEntry(stream, model, externalLinkRelsIndex);
1365
+ return true;
1366
+ }
1085
1367
  // Store passthrough files (charts, etc.) for preservation
1086
1368
  if (PassthroughManager.isPassthroughPath(entryName)) {
1087
1369
  // If raw data is available (loadFromFiles path), use it directly
@@ -1163,6 +1445,14 @@ class XLSX {
1163
1445
  Target: OOXML_REL_TARGETS.workbookFeaturePropertyBag
1164
1446
  });
1165
1447
  }
1448
+ // Add metadata relationship for dynamic array formulas
1449
+ if (model.hasDynamicArrayFormulas) {
1450
+ relationships.push({
1451
+ Id: `rId${count++}`,
1452
+ Type: XLSX.RelType.SheetMetadata,
1453
+ Target: OOXML_REL_TARGETS.workbookMetadata
1454
+ });
1455
+ }
1166
1456
  // R9-B6: Deduplicate pivot cache relationships by cacheId. When multiple pivot
1167
1457
  // tables share the same cache, only one workbook relationship should be created.
1168
1458
  // Also assigns rId to each pivot table (R9-B7: typed on PivotTable interface).
@@ -1192,6 +1482,26 @@ class XLSX {
1192
1482
  Target: worksheetRelTarget(worksheet.fileIndex)
1193
1483
  });
1194
1484
  });
1485
+ // External workbook link rels are written AFTER worksheets on purpose:
1486
+ // Excel tolerates either order, but stable ordering (worksheets then
1487
+ // externalLinks) keeps the emitted workbook.xml.rels diff-friendly for
1488
+ // round-trip tests. Each external link becomes a regular Relationship
1489
+ // entry targeting `externalLinks/externalLinkN.xml` inside `xl/`; the
1490
+ // actual external file path is pointed at by the nested
1491
+ // externalLinkN.xml.rels part written by addExternalLinks().
1492
+ //
1493
+ // The list items here are the deep-copies produced by
1494
+ // `_normaliseExternalLinks` — assigning `link.rId` is safe and does not
1495
+ // leak into the user's Workbook.externalLinks.
1496
+ const externalLinks = (model.externalLinks ?? []);
1497
+ for (const link of externalLinks) {
1498
+ link.rId = `rId${count++}`;
1499
+ relationships.push({
1500
+ Id: link.rId,
1501
+ Type: XLSX.RelType.ExternalLink,
1502
+ Target: externalLinkRelTargetFromWorkbook(link.index)
1503
+ });
1504
+ }
1195
1505
  const xform = new RelationshipsXform();
1196
1506
  await this._renderToZip(zip, OOXML_PATHS.xlWorkbookRels, xform, relationships);
1197
1507
  }
@@ -1201,6 +1511,14 @@ class XLSX {
1201
1511
  }
1202
1512
  await this._renderToZip(zip, OOXML_PATHS.xlFeaturePropertyBag, new FeaturePropertyBagXform(), {});
1203
1513
  }
1514
+ async addMetadata(zip, model) {
1515
+ if (!model.hasDynamicArrayFormulas) {
1516
+ return;
1517
+ }
1518
+ await this._renderToZip(zip, OOXML_PATHS.xlMetadata, new MetadataXform(), {
1519
+ dynamicArrayCount: model.dynamicArrayCount
1520
+ });
1521
+ }
1204
1522
  async addSharedStrings(zip, model) {
1205
1523
  if (model.sharedStrings && model.sharedStrings.count) {
1206
1524
  await this._renderToZip(zip, OOXML_PATHS.xlSharedStrings, model.sharedStrings, model.sharedStrings.model);
@@ -1316,6 +1634,41 @@ class XLSX {
1316
1634
  }
1317
1635
  }
1318
1636
  }
1637
+ /**
1638
+ * Write every external workbook reference into the archive. For each
1639
+ * {@link ExternalLinkModel} in `model.externalLinks` we emit two files:
1640
+ *
1641
+ * xl/externalLinks/externalLink{index}.xml — sheet names + cache
1642
+ * xl/externalLinks/_rels/externalLink{index}.xml.rels — target path
1643
+ *
1644
+ * The target-path rel carries `TargetMode="External"` with a **bare
1645
+ * relative** `Target` whenever the user supplied one. This is the single
1646
+ * line that makes Office / WPS resolve the referenced workbook relative
1647
+ * to the current file's directory (not the `%USERPROFILE%\Documents`
1648
+ * fallback) — the root of the behaviour reported in exceljs#3039.
1649
+ */
1650
+ async addExternalLinks(zip, model) {
1651
+ const externalLinks = (model.externalLinks ?? []);
1652
+ if (externalLinks.length === 0) {
1653
+ return;
1654
+ }
1655
+ const externalLinkXform = new ExternalLinkXform();
1656
+ const relsXform = new RelationshipsXform();
1657
+ for (const link of externalLinks) {
1658
+ await this._renderToZip(zip, externalLinkPath(link.index), externalLinkXform, link);
1659
+ // Always rId1 — the externalLink part only ever has a single rel.
1660
+ // `TargetMode="External"` is what flags Office to look the file up
1661
+ // at workbook-open time rather than embed it.
1662
+ await this._renderToZip(zip, externalLinkRelsPath(link.index), relsXform, [
1663
+ {
1664
+ Id: "rId1",
1665
+ Type: XLSX.RelType.ExternalLinkPath,
1666
+ Target: link.target,
1667
+ TargetMode: link.targetMode ?? "External"
1668
+ }
1669
+ ]);
1670
+ }
1671
+ }
1319
1672
  /**
1320
1673
  * Write passthrough files (charts, etc.) that were preserved during read.
1321
1674
  * These files are written back unchanged to preserve unsupported features.
@@ -1409,6 +1762,18 @@ class XLSX {
1409
1762
  const workbookXform = new WorkbookXform();
1410
1763
  const worksheetXform = new WorkSheetXform();
1411
1764
  workbookXform.prepare(model);
1765
+ // Normalise external-workbook references before any formula rendering.
1766
+ // Two jobs:
1767
+ // 1. Scan every formula cell and make sure each referenced workbook
1768
+ // has a matching ExternalLinkModel in `model.externalLinks`, with
1769
+ // a stable 1-based index.
1770
+ // 2. Rewrite formula strings from `[filename.xlsx]Sheet!A1` to
1771
+ // `[N]Sheet!A1`, which is the canonical on-disk form Excel
1772
+ // expects inside `<f>` elements.
1773
+ //
1774
+ // Done once up-front (not per cell) so the index assignment is
1775
+ // deterministic and every cell sees the final externalLinks list.
1776
+ this._normaliseExternalLinks(model);
1412
1777
  const worksheetOptions = {
1413
1778
  sharedStrings: model.sharedStrings,
1414
1779
  styles: model.styles,
@@ -1422,6 +1787,7 @@ class XLSX {
1422
1787
  model.hasHeaderWatermark = false;
1423
1788
  let tableCount = 0;
1424
1789
  model.tables = [];
1790
+ const tableNameMap = new Map(); // name (lowercase) → worksheet name
1425
1791
  model.worksheets.forEach((worksheet, index) => {
1426
1792
  // Assign fileIndex early so that worksheet-xform.prepare() can use it
1427
1793
  // for comment/VML relationship targets and content type names.
@@ -1429,6 +1795,16 @@ class XLSX {
1429
1795
  // using the same fileIndex.
1430
1796
  worksheet.fileIndex = index + 1;
1431
1797
  worksheet.tables.forEach((table) => {
1798
+ // OOXML requires table names to be unique across the entire workbook
1799
+ // (case-insensitive). Detect duplicates early to produce a clear error
1800
+ // instead of generating a corrupt file that Excel must repair.
1801
+ const nameKey = table.name.toLowerCase();
1802
+ const existingSheet = tableNameMap.get(nameKey);
1803
+ if (existingSheet !== undefined) {
1804
+ throw new TableError(`Duplicate table name "${table.name}": already used in worksheet "${existingSheet}". ` +
1805
+ `Table names must be unique across the entire workbook (case-insensitive).`);
1806
+ }
1807
+ tableNameMap.set(nameKey, worksheet.name);
1432
1808
  tableCount++;
1433
1809
  table.target = `table${tableCount}.xml`;
1434
1810
  table.id = tableCount;
@@ -1438,6 +1814,20 @@ class XLSX {
1438
1814
  });
1439
1815
  // ContentTypesXform expects this flag
1440
1816
  model.hasCheckboxes = model.styles.hasCheckboxes;
1817
+ // Scan all worksheets for dynamic array formulas.
1818
+ // cm=1 is assigned later by cell-xform.prepare() during worksheet rendering.
1819
+ let dynamicArrayCount = 0;
1820
+ model.worksheets.forEach((worksheet) => {
1821
+ (worksheet.rows ?? []).forEach((row) => {
1822
+ (row.cells ?? []).forEach((cell) => {
1823
+ if (cell.isDynamicArray) {
1824
+ dynamicArrayCount++;
1825
+ }
1826
+ });
1827
+ });
1828
+ });
1829
+ model.hasDynamicArrayFormulas = dynamicArrayCount > 0;
1830
+ model.dynamicArrayCount = dynamicArrayCount;
1441
1831
  // Propagate header watermark flag from worksheet prepare options
1442
1832
  if (worksheetOptions.hasHeaderWatermark) {
1443
1833
  model.hasHeaderWatermark = true;
@@ -100,7 +100,7 @@ class XLSX extends XLSXBase {
100
100
  }
101
101
  }
102
102
  catch (e) {
103
- callback(e);
103
+ callback(toError(e));
104
104
  }
105
105
  },
106
106
  final(callback) {
@@ -109,13 +109,13 @@ class XLSX extends XLSXBase {
109
109
  callback();
110
110
  }
111
111
  catch (e) {
112
- callback(e);
112
+ callback(toError(e));
113
113
  }
114
114
  }
115
115
  });
116
116
  const onParserError = (err) => {
117
117
  try {
118
- sink.destroy(err);
118
+ sink.destroy(toError(err));
119
119
  }
120
120
  catch {
121
121
  // ignore
@@ -127,8 +127,7 @@ class XLSX extends XLSXBase {
127
127
  for await (const entry of XLSX.iterateZipEntries(parser)) {
128
128
  entry.on("error", swallowError);
129
129
  const drain = async () => {
130
- const anyEntry = entry;
131
- if (anyEntry?.readableEnded || anyEntry?.destroyed) {
130
+ if (entry.readableEnded || entry.destroyed) {
132
131
  return;
133
132
  }
134
133
  const draining = entry.autodrain();