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