@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
@@ -0,0 +1,616 @@
1
+ /**
2
+ * Formula Calculation Implementation
3
+ *
4
+ * The pipeline that implements the snapshot → compile → evaluate →
5
+ * materialize → apply architecture.
6
+ *
7
+ * ## Pipeline Steps
8
+ *
9
+ * 1. **Snapshot** — `buildWorkbookSnapshot()` creates an immutable snapshot
10
+ * of the entire workbook.
11
+ * 2. **Normalize** — `collectFormulaInstances()` extracts all formula cells
12
+ * into uniform `FormulaInstance` objects.
13
+ * 3. **Parse** — Each formula's source text is tokenized and parsed into an AST.
14
+ * 4. **Compile** — The binder transforms each AST into a `BoundExpr` tree,
15
+ * resolving names, structured references, and sheet references.
16
+ * 5. **Dependency Analysis** — Static dependencies are extracted from bound
17
+ * expressions and a topological evaluation order is computed.
18
+ * 6. **Evaluate** — Formulas are evaluated in dependency order using the
19
+ * evaluator which operates on `BoundExpr` and produces `RuntimeValue`.
20
+ * 7. **Materialize** — Evaluation results are converted into a `WritebackPlan`.
21
+ * 8. **Apply** — The plan is applied to the live workbook.
22
+ */
23
+ import { parseRefRange } from "../compile/address-utils.js";
24
+ import { bind } from "../compile/binder.js";
25
+ import { extractStaticDeps, analyzeExpr, detectDynamicArrayFunction, detectSubtotalOutput } from "../compile/compiled-formula.js";
26
+ import { buildDependencyGraphFromDeps, topologicalSort, mergeDynamicDeps } from "../compile/dependency-analysis.js";
27
+ import { setDate1904 } from "../functions/_date-context.js";
28
+ import { buildWritebackPlan } from "../materialize/build-writeback-plan.js";
29
+ import { getPersistentSpillMap, getGhostSnapshots } from "../materialize/spill-engine.js";
30
+ import { EvalSession, evaluateFormula, evaluateFormulaRaw } from "../runtime/evaluator.js";
31
+ import { RVKind, rvNumber, BLANK, ERRORS } from "../runtime/values.js";
32
+ import { parse } from "../syntax/parser.js";
33
+ import { tokenize } from "../syntax/tokenizer.js";
34
+ import { applyWritebackPlan } from "./apply-writeback-plan.js";
35
+ import { collectFormulaInstances } from "./formula-instance.js";
36
+ import { buildWorkbookSnapshot } from "./workbook-adapter.js";
37
+ import { formulaCellKey, resolveDefinedName } from "./workbook-snapshot.js";
38
+ // ============================================================================
39
+ // Persistent Caches (keyed by workbook — survive across invocations)
40
+ // ============================================================================
41
+ /**
42
+ * Persistent AST cache: formula text → parsed AstNode.
43
+ * Since AST is a pure function of formula text, this is safe to cache
44
+ * across calculation cycles. Keyed by workbook to allow GC.
45
+ *
46
+ * The inner `Map` is bounded by `AST_CACHE_MAX_ENTRIES` using simple LRU
47
+ * eviction (least-recently-used key removed when full). This prevents a
48
+ * long-lived workbook that churns through unique formula texts — e.g.
49
+ * templated generation that embeds timestamps — from growing the cache
50
+ * without bound. See `parseFormulaText` for the hit-path bookkeeping.
51
+ */
52
+ const AST_CACHE_MAX_ENTRIES = 10000;
53
+ const persistentAstCache = new WeakMap();
54
+ function getPersistentAstCache(workbook) {
55
+ let cache = persistentAstCache.get(workbook);
56
+ if (!cache) {
57
+ cache = new Map();
58
+ persistentAstCache.set(workbook, cache);
59
+ }
60
+ return cache;
61
+ }
62
+ // ============================================================================
63
+ // Main: Formula Calculation Implementation
64
+ // ============================================================================
65
+ /**
66
+ * Recalculate all formula cells using the new pipeline.
67
+ *
68
+ * This implements the full snapshot → compile → evaluate → materialize → apply
69
+ * architecture. The workbook is mutated only at the final apply step.
70
+ */
71
+ export function calculateFormulasImpl(workbook) {
72
+ // ── Step 1: Snapshot ──
73
+ const snapshot = buildWorkbookSnapshot(workbook);
74
+ // Propagate the workbook-wide `date1904` mode to the module-local date
75
+ // context used by date/time/financial/text formula functions. Those
76
+ // functions have a context-free `NativeFn` signature and cannot receive
77
+ // the flag through an argument, so we thread it via a setter instead.
78
+ // See functions/_date-context.ts for the threading rationale and the
79
+ // concurrency caveat.
80
+ setDate1904(snapshot.properties.date1904 ?? false);
81
+ // ── Step 2: Normalize ──
82
+ const instances = collectFormulaInstances(snapshot);
83
+ if (instances.length === 0) {
84
+ // Clean up stale spills even when there are no formulas
85
+ cleanupStaleSpillsIfNeeded(workbook, snapshot);
86
+ return;
87
+ }
88
+ // ── Step 3: Parse ──
89
+ // Use persistent AST cache — formula text → AST is a pure function,
90
+ // so parsed ASTs can be safely reused across calculation cycles.
91
+ const astCache = getPersistentAstCache(workbook);
92
+ for (const inst of instances) {
93
+ parseFormulaText(inst.sourceText, astCache);
94
+ }
95
+ // ── Step 4: Compile (Bind) ──
96
+ const compiledMap = new Map();
97
+ const results = new Map();
98
+ for (const inst of instances) {
99
+ const compiled = compileFormula(inst, astCache, snapshot);
100
+ const key = formulaCellKey(inst.sheetName, inst.row, inst.col);
101
+ if ("reason" in compiled) {
102
+ // Parse or bind failure — produce an explicit error result.
103
+ // #CALC! for engine-level failures, #NAME? for parse errors that
104
+ // may indicate an unsupported construct.
105
+ results.set(key, compiled.reason === "parse" ? ERRORS.NAME : ERRORS.CALC);
106
+ }
107
+ else {
108
+ compiledMap.set(key, compiled);
109
+ }
110
+ }
111
+ // ── Step 5: Dependency Analysis + Topological Sort ──
112
+ // Build a producer map so that formulas depending on cells that are:
113
+ // (a) CSE target range slaves, or
114
+ // (b) previous-cycle spill ghost cells
115
+ // correctly get ordered after their producing formula.
116
+ const producerMap = new Map();
117
+ // CSE: distribute targetRef across all slave cells → master key
118
+ for (const [masterKey, cf] of compiledMap) {
119
+ const inst = cf.instance;
120
+ if (inst.kind === "cse" && inst.targetRef) {
121
+ const rng = parseRefRange(inst.targetRef);
122
+ if (rng) {
123
+ for (let r = rng.top; r <= rng.bottom; r++) {
124
+ for (let c = rng.left; c <= rng.right; c++) {
125
+ const slaveKey = formulaCellKey(inst.sheetName, r, c);
126
+ if (slaveKey !== masterKey) {
127
+ producerMap.set(slaveKey, masterKey);
128
+ }
129
+ }
130
+ }
131
+ }
132
+ }
133
+ }
134
+ // Dynamic array spill: use previous-cycle spill regions as static hint
135
+ // for dependency ordering. The actual spill may differ this cycle — if the
136
+ // master formula is no longer a dynamic-array producer, skip it so stale
137
+ // ghost cells don't introduce false edges.
138
+ const persistentSpills = getPersistentSpillMap(workbook);
139
+ for (const [, region] of persistentSpills) {
140
+ const ws = snapshot.worksheetsById.get(region.worksheetId);
141
+ if (!ws) {
142
+ continue;
143
+ }
144
+ const masterKey = formulaCellKey(ws.name, region.sourceRow, region.sourceCol);
145
+ const masterCf = compiledMap.get(masterKey);
146
+ if (!masterCf) {
147
+ continue; // source formula no longer exists
148
+ }
149
+ // Only remap if the master is still a dynamic-array formula this cycle
150
+ const isStillDynamic = masterCf.instance.isDynamicArray || masterCf.isDynamicArrayFunction;
151
+ if (!isStillDynamic) {
152
+ continue;
153
+ }
154
+ for (let r = 0; r < region.rows; r++) {
155
+ for (let c = 0; c < region.cols; c++) {
156
+ if (r === 0 && c === 0) {
157
+ continue; // skip source
158
+ }
159
+ const ghostKey = formulaCellKey(ws.name, region.sourceRow + r, region.sourceCol + c);
160
+ if (!producerMap.has(ghostKey)) {
161
+ producerMap.set(ghostKey, masterKey);
162
+ }
163
+ }
164
+ }
165
+ }
166
+ // Build the dependency graph with producer remapping.
167
+ let graph = buildDependencyGraphFromDeps(compiledMap, producerMap);
168
+ let evalOrder = topologicalSort(graph);
169
+ // ── Step 6: Evaluate ──
170
+ const session = new EvalSession();
171
+ const ctx = {
172
+ snapshot,
173
+ compiledFormulas: compiledMap,
174
+ currentSheet: snapshot.worksheets[0]?.name ?? ""
175
+ };
176
+ // Evaluate in topological order
177
+ evaluateInOrder(evalOrder, compiledMap, results, ctx, session);
178
+ // ── Step 6b: Merge dynamic dependencies and re-evaluate if needed ──
179
+ // After the first pass, formulas with INDIRECT/OFFSET have recorded their
180
+ // actual runtime cell accesses in session.dynamicDeps. Merge these edges
181
+ // into the graph and re-evaluate any formulas whose dependencies changed.
182
+ if (session.dynamicDeps.size > 0) {
183
+ const mergeResult = mergeDynamicDeps(graph, session.dynamicDeps);
184
+ if (mergeResult.changed) {
185
+ const prevCircularKeys = graph.circularKeys;
186
+ graph = mergeResult.graph;
187
+ evalOrder = topologicalSort(graph);
188
+ // Collect formulas that gained new deps AND their transitive dependents.
189
+ // Without clearing dependents, downstream cells could see stale values.
190
+ const toClear = new Set();
191
+ const queue = [];
192
+ for (const [formulaKey] of session.dynamicDeps) {
193
+ if (!toClear.has(formulaKey)) {
194
+ toClear.add(formulaKey);
195
+ queue.push(formulaKey);
196
+ }
197
+ }
198
+ // If the merge introduced new circular refs, include all new members too
199
+ for (const key of graph.circularKeys) {
200
+ if (!prevCircularKeys.has(key) && !toClear.has(key)) {
201
+ toClear.add(key);
202
+ queue.push(key);
203
+ }
204
+ }
205
+ // BFS through reverse edges to find all transitive dependents
206
+ let head = 0;
207
+ while (head < queue.length) {
208
+ const key = queue[head++];
209
+ const deps = graph.dependedBy.get(key);
210
+ if (deps) {
211
+ for (const depKey of deps) {
212
+ if (!toClear.has(depKey)) {
213
+ toClear.add(depKey);
214
+ queue.push(depKey);
215
+ }
216
+ }
217
+ }
218
+ }
219
+ // Clear all affected formulas
220
+ for (const key of toClear) {
221
+ session.resultCache.delete(key);
222
+ results.delete(key);
223
+ }
224
+ // Formula-based name results may depend on cell values that just changed
225
+ session.nameCache.clear();
226
+ // Re-evaluate the full order (evaluateInOrder skips already-computed cells)
227
+ evaluateInOrder(evalOrder, compiledMap, results, ctx, session);
228
+ }
229
+ }
230
+ // ── Iterative Calculation for Circular References ──
231
+ const iterateEnabled = snapshot.calcProperties.iterate === true;
232
+ if (iterateEnabled && graph.circularKeys.size > 0) {
233
+ runIterativeCalc(evalOrder, graph, compiledMap, results, ctx, session, snapshot);
234
+ reevaluateDownstreamOfCircular(evalOrder, graph, compiledMap, results, ctx, session);
235
+ }
236
+ // ── Step 7: Materialize (Build Writeback Plan) ──
237
+ const previousSpills = getPersistentSpillMap(workbook);
238
+ const previousGhosts = getGhostSnapshots(workbook);
239
+ const plan = buildWritebackPlan(snapshot, [...compiledMap.values()], results, previousSpills, previousGhosts);
240
+ // ── Step 8: Apply ──
241
+ applyWritebackPlan(workbook, plan);
242
+ }
243
+ // ============================================================================
244
+ // Helper: Evaluate Formulas in Order
245
+ // ============================================================================
246
+ /**
247
+ * Evaluate compiled formulas in the given topological order.
248
+ *
249
+ * For each formula in `evalOrder`:
250
+ * - If already in `results` (from a previous pass or compile failure), skip.
251
+ * - Volatile formulas always re-evaluate (bypass scalar cache).
252
+ * - CSE / dynamic array formulas use `evaluateFormulaRaw`.
253
+ * - Normal scalar formulas use `evaluateFormula` with cache.
254
+ */
255
+ function evaluateInOrder(evalOrder, compiledMap, results, ctx, session) {
256
+ for (const key of evalOrder) {
257
+ // Skip if already computed (e.g. compile failure → #CALC!, or previous pass)
258
+ if (results.has(key)) {
259
+ continue;
260
+ }
261
+ const compiled = compiledMap.get(key);
262
+ if (!compiled) {
263
+ continue;
264
+ }
265
+ const inst = compiled.instance;
266
+ const isCSE = inst.kind === "cse" && inst.targetRef;
267
+ const isDynamic = inst.isDynamicArray || compiled.isDynamicArrayFunction;
268
+ // Volatile formulas always re-evaluate — bypass cache
269
+ if (compiled.isVolatile) {
270
+ session.resultCache.delete(key);
271
+ }
272
+ if (isCSE || isDynamic) {
273
+ try {
274
+ const raw = evaluateFormulaRaw(compiled, ctx, session);
275
+ results.set(key, raw);
276
+ if (isCSE && inst.targetRef) {
277
+ populateCSECache(inst, raw, session);
278
+ }
279
+ }
280
+ catch {
281
+ results.set(key, ERRORS.CALC);
282
+ }
283
+ }
284
+ else {
285
+ if (!compiled.isVolatile) {
286
+ const cachedResult = session.resultCache.get(key);
287
+ if (cachedResult !== undefined) {
288
+ results.set(key, cachedResult.scalar);
289
+ continue;
290
+ }
291
+ }
292
+ try {
293
+ const scalar = evaluateFormula(compiled, ctx, session);
294
+ results.set(key, scalar);
295
+ }
296
+ catch {
297
+ results.set(key, ERRORS.CALC);
298
+ }
299
+ }
300
+ }
301
+ }
302
+ // ============================================================================
303
+ // Helper: Parse Formula Text
304
+ // ============================================================================
305
+ // Sentinel cached in the AST map to record a failed parse. Using a distinct
306
+ // object means callers that do an explicit identity check against this value
307
+ // can short-circuit before any truthy branch; it is never exposed outside the
308
+ // cache so cannot be mistaken for a real AST by downstream consumers.
309
+ const PARSE_FAILED_SENTINEL = {};
310
+ /**
311
+ * Touch a cache entry to move it to the MRU (most-recently-used) position.
312
+ * Relies on `Map`'s guaranteed insertion-order iteration: delete + re-set
313
+ * pushes the entry to the end of the iteration order without changing its
314
+ * value identity. Only called on cache hits.
315
+ */
316
+ function touchAstCacheEntry(astCache, formula, ast) {
317
+ astCache.delete(formula);
318
+ astCache.set(formula, ast);
319
+ }
320
+ /**
321
+ * Insert a new entry into the AST cache, evicting the least-recently-used
322
+ * entry if the cache is at capacity. The LRU entry is the first key returned
323
+ * by `Map.keys()` iteration, which corresponds to the oldest insertion/touch.
324
+ */
325
+ function insertAstCacheEntry(astCache, formula, ast) {
326
+ if (astCache.size >= AST_CACHE_MAX_ENTRIES) {
327
+ // Evict one entry before adding the new one. `Map.keys().next().value`
328
+ // is the least-recently-inserted (or -touched) key — O(1) access.
329
+ const oldestKey = astCache.keys().next().value;
330
+ if (oldestKey !== undefined) {
331
+ astCache.delete(oldestKey);
332
+ }
333
+ }
334
+ astCache.set(formula, ast);
335
+ }
336
+ function parseFormulaText(formula, astCache) {
337
+ const cached = astCache.get(formula);
338
+ if (cached === PARSE_FAILED_SENTINEL) {
339
+ // Touch so that a repeatedly-evaluated failing formula doesn't get
340
+ // evicted and re-parsed (and re-failed) every cycle.
341
+ touchAstCacheEntry(astCache, formula, PARSE_FAILED_SENTINEL);
342
+ return null;
343
+ }
344
+ if (cached) {
345
+ touchAstCacheEntry(astCache, formula, cached);
346
+ return cached;
347
+ }
348
+ try {
349
+ const tokens = tokenize(formula);
350
+ const ast = parse(tokens);
351
+ insertAstCacheEntry(astCache, formula, ast);
352
+ return ast;
353
+ }
354
+ catch {
355
+ insertAstCacheEntry(astCache, formula, PARSE_FAILED_SENTINEL);
356
+ return null;
357
+ }
358
+ }
359
+ function compileFormula(inst, astCache, snapshot) {
360
+ const ast = astCache.get(inst.sourceText);
361
+ if (!ast) {
362
+ return { reason: "parse", formula: inst.sourceText };
363
+ }
364
+ const bindCtx = {
365
+ snapshot,
366
+ currentSheet: inst.sheetName
367
+ };
368
+ try {
369
+ const bound = bind(ast, bindCtx);
370
+ // Build a name resolver that parses formula-based defined names
371
+ // so their deps and dynamic-ref flags propagate to the outer formula.
372
+ const nameDepCache = new Map();
373
+ const nameResolver = upperName => {
374
+ // Include sheet in cache key for scope-aware resolution
375
+ const cacheKey = `${upperName}\0${inst.sheetName}`;
376
+ if (nameDepCache.has(cacheKey)) {
377
+ return nameDepCache.get(cacheKey);
378
+ }
379
+ // Prevent infinite recursion
380
+ nameDepCache.set(cacheKey, undefined);
381
+ // Use scope-aware resolution (sheet-local → workbook-global)
382
+ const dn = resolveDefinedName(snapshot.definedNames, upperName, inst.sheetName);
383
+ if (!dn || dn.ranges.length !== 1) {
384
+ return undefined;
385
+ }
386
+ const rangeStr = dn.ranges[0];
387
+ // Only process formula-based names (not cell/range refs)
388
+ if (parseRefRange(rangeStr)) {
389
+ return undefined;
390
+ }
391
+ try {
392
+ const nameTokens = tokenize(rangeStr);
393
+ const nameAst = parse(nameTokens);
394
+ const nameBound = bind(nameAst, { snapshot, currentSheet: inst.sheetName });
395
+ const nameDeps = extractStaticDeps(nameBound, snapshot, nameResolver);
396
+ const nameAnalysis = analyzeExpr(nameBound, nameResolver);
397
+ const result = {
398
+ deps: nameDeps,
399
+ hasDynamicRefs: nameAnalysis.hasDynamicRefs,
400
+ // Propagate volatility so a defined-name body containing NOW()/
401
+ // RAND() correctly invalidates the outer formula's session
402
+ // cache on every calculation pass.
403
+ isVolatile: nameAnalysis.isVolatile
404
+ };
405
+ nameDepCache.set(cacheKey, result);
406
+ return result;
407
+ }
408
+ catch {
409
+ return undefined;
410
+ }
411
+ };
412
+ const staticDeps = extractStaticDeps(bound, snapshot, nameResolver);
413
+ const analysis = analyzeExpr(bound, nameResolver);
414
+ return {
415
+ instance: inst,
416
+ bound,
417
+ staticDeps,
418
+ isVolatile: analysis.isVolatile,
419
+ hasDynamicRefs: analysis.hasDynamicRefs,
420
+ containsLambda: analysis.containsLambda,
421
+ isDynamicArrayFunction: detectDynamicArrayFunction(ast, bound),
422
+ isSubtotalOutput: detectSubtotalOutput(ast, bound)
423
+ };
424
+ }
425
+ catch {
426
+ return { reason: "bind", formula: inst.sourceText, sheet: inst.sheetName };
427
+ }
428
+ }
429
+ // ============================================================================
430
+ // Helper: Cleanup Stale Spills
431
+ // ============================================================================
432
+ function cleanupStaleSpillsIfNeeded(workbook, snapshot) {
433
+ const previousSpills = getPersistentSpillMap(workbook);
434
+ if (previousSpills.size === 0) {
435
+ return;
436
+ }
437
+ // No formula cells → all spills are stale
438
+ const plan = buildWritebackPlan(snapshot, [], new Map(), previousSpills, getGhostSnapshots(workbook));
439
+ applyWritebackPlan(workbook, plan);
440
+ }
441
+ // ============================================================================
442
+ // Helper: Populate CSE Session Cache
443
+ // ============================================================================
444
+ /**
445
+ * For CSE array formulas, immediately populate the session cache for all
446
+ * cells in the target range. This ensures that when other formulas reference
447
+ * cells within a CSE range, they see the correct distributed values instead
448
+ * of re-evaluating independently.
449
+ */
450
+ function populateCSECache(inst, result, session) {
451
+ const ref = inst.targetRef;
452
+ if (!ref) {
453
+ return;
454
+ }
455
+ const range = parseRefRange(ref);
456
+ if (!range) {
457
+ return;
458
+ }
459
+ const { top, left, bottom, right } = range;
460
+ const numRows = bottom - top + 1;
461
+ const numCols = right - left + 1;
462
+ if (result.kind === RVKind.Array) {
463
+ for (let r = 0; r < numRows; r++) {
464
+ for (let c = 0; c < numCols; c++) {
465
+ const cellKey = formulaCellKey(inst.sheetName, top + r, left + c);
466
+ const val = result.rows[r]?.[c];
467
+ const sv = val ?? BLANK;
468
+ session.resultCache.set(cellKey, { scalar: sv, raw: sv });
469
+ }
470
+ }
471
+ }
472
+ else {
473
+ // Scalar — fill entire range
474
+ const scalar = result.kind === RVKind.Error ||
475
+ result.kind === RVKind.Number ||
476
+ result.kind === RVKind.String ||
477
+ result.kind === RVKind.Boolean
478
+ ? result
479
+ : BLANK;
480
+ for (let r = 0; r < numRows; r++) {
481
+ for (let c = 0; c < numCols; c++) {
482
+ const cellKey = formulaCellKey(inst.sheetName, top + r, left + c);
483
+ session.resultCache.set(cellKey, { scalar, raw: scalar });
484
+ }
485
+ }
486
+ }
487
+ }
488
+ // ============================================================================
489
+ // Iterative calculation (for circular references)
490
+ // ============================================================================
491
+ /**
492
+ * Drive the iterative-calculation loop for the cells that participate in
493
+ * cycles. Pre-computes the transitive downstream set once, then on each
494
+ * iteration:
495
+ * 1. invalidates the cached result of circular cells and their descendants;
496
+ * 2. seeds `circularFallback` from the previous iteration's numbers;
497
+ * 3. re-evaluates each circular cell and tracks the maximum absolute change.
498
+ *
499
+ * Exits early when all cells converge within `iterateDelta`, or after
500
+ * `iterateCount` iterations. The `circularFallback` map is cleared on exit
501
+ * so subsequent (non-iterative) evaluation paths revert to the zero-seed
502
+ * fallback behaviour.
503
+ */
504
+ function runIterativeCalc(evalOrder, graph, compiledMap, results, ctx, session, snapshot) {
505
+ const maxIter = snapshot.calcProperties.iterateCount ?? 100;
506
+ const delta = snapshot.calcProperties.iterateDelta ?? 0.001;
507
+ const circularKeys = [];
508
+ for (const key of evalOrder) {
509
+ if (graph.circularKeys.has(key)) {
510
+ circularKeys.push(key);
511
+ }
512
+ }
513
+ // Transitive downstream of circularKeys — cells upstream of every cycle
514
+ // are stable across iterations, so their cached values remain valid.
515
+ const circularAndDownstream = new Set(graph.circularKeys);
516
+ const downstreamQueue = [...graph.circularKeys];
517
+ let downstreamHead = 0;
518
+ while (downstreamHead < downstreamQueue.length) {
519
+ const key = downstreamQueue[downstreamHead++];
520
+ const deps = graph.dependedBy.get(key);
521
+ if (!deps) {
522
+ continue;
523
+ }
524
+ for (const depKey of deps) {
525
+ if (!circularAndDownstream.has(depKey)) {
526
+ circularAndDownstream.add(depKey);
527
+ downstreamQueue.push(depKey);
528
+ }
529
+ }
530
+ }
531
+ for (let iter = 0; iter < maxIter; iter++) {
532
+ let maxChange = 0;
533
+ // Clear only circular cells and their transitive downstream.
534
+ for (const key of circularAndDownstream) {
535
+ session.resultCache.delete(key);
536
+ }
537
+ // Defined-name cache: cleared wholesale because formula-based names may
538
+ // indirectly reference circular cells, and tracking which names touch
539
+ // which cells would complicate the engine substantially.
540
+ session.nameCache.clear();
541
+ // Seed fallback from previous results
542
+ for (const key of circularKeys) {
543
+ const compiled = compiledMap.get(key);
544
+ if (!compiled) {
545
+ continue;
546
+ }
547
+ const prev = results.get(key);
548
+ if (prev !== undefined && prev.kind !== RVKind.Error) {
549
+ session.circularFallback.set(key, prev);
550
+ }
551
+ else {
552
+ session.circularFallback.set(key, rvNumber(0));
553
+ }
554
+ }
555
+ for (const key of circularKeys) {
556
+ const compiled = compiledMap.get(key);
557
+ if (!compiled) {
558
+ continue;
559
+ }
560
+ try {
561
+ const oldResult = results.get(key);
562
+ const newResult = evaluateFormula(compiled, ctx, session);
563
+ results.set(key, newResult);
564
+ session.circularFallback.set(key, newResult);
565
+ if (oldResult && oldResult.kind === RVKind.Number && newResult.kind === RVKind.Number) {
566
+ maxChange = Math.max(maxChange, Math.abs(newResult.value - oldResult.value));
567
+ }
568
+ }
569
+ catch {
570
+ // Iterative evaluation threw — set error and continue convergence.
571
+ results.set(key, ERRORS.CALC);
572
+ }
573
+ }
574
+ if (maxChange <= delta) {
575
+ break;
576
+ }
577
+ }
578
+ session.circularFallback.clear();
579
+ }
580
+ /**
581
+ * After iterative calculation has converged, cells that sit on the non-
582
+ * circular side but depend transitively on a circular cell still hold stale
583
+ * values from the first pass (which used the fallback zero-seed). Find them
584
+ * and re-evaluate, preserving topological order.
585
+ */
586
+ function reevaluateDownstreamOfCircular(evalOrder, graph, compiledMap, results, ctx, session) {
587
+ const affected = new Set();
588
+ const queue = [...graph.circularKeys];
589
+ let head = 0;
590
+ while (head < queue.length) {
591
+ const key = queue[head++];
592
+ const deps = graph.dependedBy.get(key);
593
+ if (!deps) {
594
+ continue;
595
+ }
596
+ for (const depKey of deps) {
597
+ if (!graph.circularKeys.has(depKey) && !affected.has(depKey)) {
598
+ affected.add(depKey);
599
+ queue.push(depKey);
600
+ }
601
+ }
602
+ }
603
+ if (affected.size === 0) {
604
+ return;
605
+ }
606
+ for (const key of affected) {
607
+ session.resultCache.delete(key);
608
+ results.delete(key);
609
+ }
610
+ session.nameCache.clear();
611
+ // Only re-evaluate the affected subset; filtering preserves the
612
+ // topological ordering within that subset while skipping the (already
613
+ // computed) non-affected cells entirely.
614
+ const filteredOrder = evalOrder.filter(k => affected.has(k));
615
+ evaluateInOrder(filteredOrder, compiledMap, results, ctx, session);
616
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Formula Calculation Engine
3
+ *
4
+ * Provides `calculateFormulas()` as the primary function-style entry
5
+ * point. The same work runs when a host calls
6
+ * `Workbook.calculateFormulas()` — both paths dispatch through the
7
+ * engine registered via `installFormulaEngine()`.
8
+ *
9
+ * ## Architecture
10
+ *
11
+ * 1. **Snapshot** — immutable capture of all workbook state
12
+ * 2. **Normalize** — uniform FormulaInstance objects
13
+ * 3. **Parse** — tokenize → AST
14
+ * 4. **Compile** — bind AST → BoundExpr (resolve names, structured refs, sheets)
15
+ * 5. **Dependency Analysis** — topological sort
16
+ * 6. **Evaluate** — execute BoundExpr with RuntimeValue system
17
+ * 7. **Materialize** — build declarative WritebackPlan
18
+ * 8. **Apply** — write plan to live workbook
19
+ */
20
+ import type { WorkbookLike } from "../materialize/types.js";
21
+ export type { DefinedNamesLike, WorkbookLike } from "../materialize/types.js";
22
+ /**
23
+ * Recalculate all formula cells in a workbook.
24
+ *
25
+ * Evaluates every formula cell using the built-in calculation engine
26
+ * and updates each cell's `result` value. Formulas are evaluated lazily
27
+ * with recursive dependency resolution, memoization, and circular
28
+ * reference detection.
29
+ *
30
+ * All evaluation state is scoped to this invocation — concurrent calls
31
+ * for different workbooks are safe.
32
+ *
33
+ * **Supported formula features:**
34
+ * - Cell references: `A1`, `$B$2`, `Sheet1!A1`, `'Sheet Name'!A1:B10`
35
+ * - Operators: `+ - * / ^`, `& (concat)`, `= <> < > <= >=`, `%`
36
+ * - 433 built-in functions across math, text, logical, date, lookup,
37
+ * statistical, financial, dynamic-array, database and engineering
38
+ * categories.
39
+ * - Shared formulas, array constants, nested expressions
40
+ * - Dynamic array spill: FILTER, SORT, UNIQUE, SORTBY results are
41
+ * written to adjacent cells. #SPILL! error if target cells are occupied.
42
+ * - CSE array formulas: `{=formula}` with a ref range distribute results
43
+ * across the designated range.
44
+ * - Array arithmetic broadcasting: `{1,2,3} + {4;5;6}` produces a 3x3 matrix.
45
+ * - Implicit intersection: range references in scalar context pick the
46
+ * value from the formula cell's row or column.
47
+ *
48
+ * **Unsupported formula behavior:**
49
+ * - If a formula uses a function the engine does not implement, the engine
50
+ * returns `#NAME?`. However, if the cell already has a cached result
51
+ * (e.g., pre-computed by Excel when the XLSX was saved), that cached
52
+ * result is **preserved** — the engine will not overwrite usable data.
53
+ * - If no cached result exists, the cell's result becomes `#NAME?`.
54
+ *
55
+ * **Volatile functions:**
56
+ * - `RAND`, `RANDBETWEEN`, `NOW`, `TODAY` are re-evaluated on every call.
57
+ * This is intentional — these functions are expected to produce fresh values.
58
+ *
59
+ * **Side effects:**
60
+ * - This function **mutates** the workbook by updating formula cells' `result`
61
+ * property in-place. For dynamic array formulas, adjacent cells are also
62
+ * written with spill results. If you need the original cached results
63
+ * preserved, clone the workbook before calling this function.
64
+ *
65
+ * @param workbook - The workbook whose formulas should be recalculated
66
+ */
67
+ export declare function calculateFormulas(workbook: WorkbookLike): void;