@cj-tech-master/excelts 4.2.1 → 4.2.2-canary.20260115044841.88820eb
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/THIRD_PARTY_NOTICES.md +0 -31
- package/dist/browser/index.browser.d.ts +1 -0
- package/dist/browser/index.browser.js +12 -0
- package/dist/{esm/modules/archive → browser/modules/archive/compression}/compress.base.js +1 -1
- package/dist/{types/modules/archive → browser/modules/archive/compression}/compress.browser.d.ts +2 -8
- package/dist/browser/modules/archive/{compress.browser.js → compression/compress.browser.js} +3 -11
- package/dist/browser/modules/archive/{compress.d.ts → compression/compress.d.ts} +2 -2
- package/dist/{esm/modules/archive → browser/modules/archive/compression}/compress.js +1 -1
- package/dist/browser/modules/archive/{crc32.browser.d.ts → compression/crc32.browser.d.ts} +1 -1
- package/dist/browser/modules/archive/{crc32.d.ts → compression/crc32.d.ts} +1 -1
- package/dist/browser/modules/archive/{crc32.js → compression/crc32.js} +1 -1
- package/dist/browser/modules/archive/{deflate-fallback.js → compression/deflate-fallback.js} +1 -1
- package/dist/browser/modules/archive/{streaming-compress.browser.d.ts → compression/streaming-compress.browser.d.ts} +2 -2
- package/dist/browser/modules/archive/{streaming-compress.browser.js → compression/streaming-compress.browser.js} +3 -3
- package/dist/browser/modules/archive/{streaming-compress.d.ts → compression/streaming-compress.d.ts} +2 -2
- package/dist/browser/modules/archive/{streaming-compress.js → compression/streaming-compress.js} +2 -2
- package/dist/browser/modules/archive/defaults.d.ts +1 -0
- package/dist/browser/modules/archive/defaults.js +6 -3
- package/dist/browser/modules/archive/index.base.d.ts +4 -4
- package/dist/browser/modules/archive/index.base.js +3 -6
- package/dist/browser/modules/archive/index.browser.d.ts +3 -4
- package/dist/browser/modules/archive/index.browser.js +3 -7
- package/dist/browser/modules/archive/index.d.ts +3 -4
- package/dist/browser/modules/archive/index.js +3 -5
- package/dist/browser/modules/archive/internal/byte-queue.d.ts +33 -0
- package/dist/browser/modules/archive/internal/byte-queue.js +407 -0
- package/dist/browser/modules/archive/io/archive-sink.d.ts +8 -0
- package/dist/browser/modules/archive/io/archive-sink.js +45 -0
- package/dist/browser/modules/archive/io/archive-source.d.ts +6 -0
- package/dist/browser/modules/archive/io/archive-source.js +100 -0
- package/dist/browser/modules/archive/{extract.d.ts → unzip/extract.d.ts} +2 -2
- package/dist/browser/modules/archive/unzip/index.d.ts +40 -0
- package/dist/browser/modules/archive/unzip/index.js +164 -0
- package/dist/browser/modules/archive/{parse.base.d.ts → unzip/stream.base.d.ts} +36 -2
- package/dist/browser/modules/archive/unzip/stream.base.js +1022 -0
- package/dist/browser/modules/archive/{parse.browser.d.ts → unzip/stream.browser.d.ts} +1 -1
- package/dist/browser/modules/archive/{parse.browser.js → unzip/stream.browser.js} +371 -110
- package/dist/browser/modules/archive/{parse.d.ts → unzip/stream.d.ts} +2 -2
- package/dist/{esm/modules/archive/parse.js → browser/modules/archive/unzip/stream.js} +6 -5
- package/dist/browser/modules/archive/{zip-parser.d.ts → unzip/zip-parser.d.ts} +1 -1
- package/dist/{esm/modules/archive → browser/modules/archive/unzip}/zip-parser.js +38 -24
- package/dist/browser/modules/archive/utils/async-queue.d.ts +7 -0
- package/dist/browser/modules/archive/utils/async-queue.js +103 -0
- package/dist/browser/modules/archive/utils/bytes.js +16 -16
- package/dist/browser/modules/archive/utils/compressibility.d.ts +10 -0
- package/dist/browser/modules/archive/utils/compressibility.js +57 -0
- package/dist/browser/modules/archive/utils/parse-buffer.js +21 -23
- package/dist/browser/modules/archive/utils/pattern-scanner.d.ts +21 -0
- package/dist/browser/modules/archive/utils/pattern-scanner.js +27 -0
- package/dist/browser/modules/archive/utils/timestamps.js +62 -1
- package/dist/browser/modules/archive/utils/zip-extra-fields.d.ts +1 -1
- package/dist/browser/modules/archive/utils/zip-extra-fields.js +26 -14
- package/dist/browser/modules/archive/zip/index.d.ts +42 -0
- package/dist/browser/modules/archive/zip/index.js +157 -0
- package/dist/browser/modules/archive/{streaming-zip.d.ts → zip/stream.d.ts} +28 -5
- package/dist/browser/modules/archive/{streaming-zip.js → zip/stream.js} +192 -48
- package/dist/browser/modules/archive/zip/zip-bytes.d.ts +73 -0
- package/dist/browser/modules/archive/zip/zip-bytes.js +239 -0
- package/dist/{esm/modules/archive → browser/modules/archive/zip}/zip-entry-metadata.js +3 -3
- package/dist/browser/modules/archive/{zip-records.d.ts → zip-spec/zip-records.d.ts} +20 -0
- package/dist/browser/modules/archive/zip-spec/zip-records.js +126 -0
- package/dist/browser/modules/excel/stream/workbook-reader.browser.js +1 -1
- package/dist/browser/modules/excel/stream/workbook-writer.browser.d.ts +1 -1
- package/dist/browser/modules/excel/stream/workbook-writer.browser.js +1 -1
- package/dist/browser/modules/excel/utils/ooxml-validator.d.ts +48 -0
- package/dist/browser/modules/excel/utils/ooxml-validator.js +469 -0
- package/dist/browser/modules/excel/utils/parse-sax.d.ts +3 -0
- package/dist/browser/modules/excel/utils/parse-sax.js +32 -13
- package/dist/browser/modules/excel/worksheet.js +5 -2
- package/dist/browser/modules/excel/xlsx/xform/core/app-xform.js +3 -3
- package/dist/browser/modules/excel/xlsx/xform/core/core-xform.js +56 -68
- package/dist/browser/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.d.ts +1 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.js +13 -1
- package/dist/browser/modules/excel/xlsx/xform/drawing/sp-xform.d.ts +18 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/sp-xform.js +112 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.d.ts +6 -1
- package/dist/browser/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +30 -2
- package/dist/browser/modules/excel/xlsx/xform/drawing/vml-drawing-xform.js +11 -0
- package/dist/browser/modules/excel/xlsx/xform/list-xform.js +8 -10
- package/dist/browser/modules/excel/xlsx/xform/sheet/page-setup-xform.d.ts +1 -0
- package/dist/browser/modules/excel/xlsx/xform/sheet/page-setup-xform.js +16 -2
- package/dist/browser/modules/excel/xlsx/xform/sheet/worksheet-xform.js +110 -12
- package/dist/browser/modules/excel/xlsx/xform/strings/shared-string-xform.js +2 -3
- package/dist/browser/modules/excel/xlsx/xform/strings/text-xform.js +5 -7
- package/dist/browser/modules/excel/xlsx/xlsx.browser.js +11 -10
- package/dist/browser/modules/excel/xlsx/xlsx.js +1 -1
- package/dist/browser/modules/stream/base-transform.d.ts +3 -0
- package/dist/browser/modules/stream/base-transform.js +34 -20
- package/dist/browser/modules/stream/buffered-stream.d.ts +2 -12
- package/dist/browser/modules/stream/chunked-builder.js +4 -4
- package/dist/browser/modules/stream/index.browser.d.ts +13 -19
- package/dist/browser/modules/stream/index.browser.js +10 -22
- package/dist/browser/modules/stream/index.d.ts +18 -41
- package/dist/browser/modules/stream/index.js +15 -44
- package/dist/browser/modules/stream/internal/event-utils.d.ts +17 -0
- package/dist/browser/modules/stream/internal/event-utils.js +40 -0
- package/dist/browser/modules/stream/internal/type-guards.d.ts +9 -0
- package/dist/browser/modules/stream/internal/type-guards.js +24 -0
- package/dist/browser/modules/stream/pull-stream.d.ts +5 -6
- package/dist/browser/modules/stream/pull-stream.js +107 -43
- package/dist/browser/modules/stream/shared.d.ts +1 -1
- package/dist/browser/modules/stream/shared.js +7 -4
- package/dist/browser/modules/stream/streams.browser.d.ts +32 -44
- package/dist/browser/modules/stream/streams.browser.js +921 -836
- package/dist/browser/modules/stream/streams.d.ts +4 -20
- package/dist/browser/modules/stream/streams.js +146 -95
- package/dist/browser/modules/stream/utils.js +5 -38
- package/dist/cjs/modules/archive/{compress.base.js → compression/compress.base.js} +1 -1
- package/dist/cjs/modules/archive/{compress.browser.js → compression/compress.browser.js} +3 -11
- package/dist/cjs/modules/archive/{compress.js → compression/compress.js} +1 -1
- package/dist/cjs/modules/archive/{crc32.js → compression/crc32.js} +1 -1
- package/dist/cjs/modules/archive/{deflate-fallback.js → compression/deflate-fallback.js} +1 -1
- package/dist/cjs/modules/archive/{streaming-compress.browser.js → compression/streaming-compress.browser.js} +3 -3
- package/dist/cjs/modules/archive/{streaming-compress.js → compression/streaming-compress.js} +2 -2
- package/dist/cjs/modules/archive/defaults.js +7 -4
- package/dist/cjs/modules/archive/index.base.js +9 -19
- package/dist/cjs/modules/archive/index.browser.js +4 -10
- package/dist/cjs/modules/archive/index.js +4 -8
- package/dist/cjs/modules/archive/internal/byte-queue.js +411 -0
- package/dist/cjs/modules/archive/io/archive-sink.js +49 -0
- package/dist/cjs/modules/archive/io/archive-source.js +105 -0
- package/dist/cjs/modules/archive/unzip/index.js +170 -0
- package/dist/cjs/modules/archive/unzip/stream.base.js +1044 -0
- package/dist/cjs/modules/archive/{parse.browser.js → unzip/stream.browser.js} +372 -111
- package/dist/cjs/modules/archive/{parse.js → unzip/stream.js} +9 -8
- package/dist/cjs/modules/archive/{zip-parser.js → unzip/zip-parser.js} +47 -33
- package/dist/cjs/modules/archive/utils/async-queue.js +106 -0
- package/dist/cjs/modules/archive/utils/bytes.js +16 -16
- package/dist/cjs/modules/archive/utils/compressibility.js +60 -0
- package/dist/cjs/modules/archive/utils/parse-buffer.js +21 -23
- package/dist/cjs/modules/archive/utils/pattern-scanner.js +31 -0
- package/dist/cjs/modules/archive/utils/timestamps.js +64 -3
- package/dist/cjs/modules/archive/utils/zip-extra-fields.js +26 -14
- package/dist/cjs/modules/archive/zip/index.js +162 -0
- package/dist/cjs/modules/archive/{streaming-zip.js → zip/stream.js} +194 -50
- package/dist/cjs/modules/archive/zip/zip-bytes.js +242 -0
- package/dist/cjs/modules/archive/{zip-entry-metadata.js → zip/zip-entry-metadata.js} +5 -5
- package/dist/cjs/modules/archive/zip-spec/zip-records.js +136 -0
- package/dist/cjs/modules/excel/stream/workbook-reader.browser.js +2 -2
- package/dist/cjs/modules/excel/stream/workbook-writer.browser.js +4 -4
- package/dist/cjs/modules/excel/utils/ooxml-validator.js +475 -0
- package/dist/cjs/modules/excel/utils/parse-sax.js +32 -13
- package/dist/cjs/modules/excel/worksheet.js +5 -2
- package/dist/cjs/modules/excel/xlsx/xform/core/app-xform.js +3 -3
- package/dist/cjs/modules/excel/xlsx/xform/core/core-xform.js +56 -68
- package/dist/cjs/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.js +13 -1
- package/dist/cjs/modules/excel/xlsx/xform/drawing/sp-xform.js +115 -0
- package/dist/cjs/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +30 -2
- package/dist/cjs/modules/excel/xlsx/xform/drawing/vml-drawing-xform.js +11 -0
- package/dist/cjs/modules/excel/xlsx/xform/list-xform.js +8 -10
- package/dist/cjs/modules/excel/xlsx/xform/sheet/page-setup-xform.js +16 -2
- package/dist/cjs/modules/excel/xlsx/xform/sheet/worksheet-xform.js +110 -12
- package/dist/cjs/modules/excel/xlsx/xform/strings/shared-string-xform.js +2 -3
- package/dist/cjs/modules/excel/xlsx/xform/strings/text-xform.js +5 -7
- package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +14 -13
- package/dist/cjs/modules/excel/xlsx/xlsx.js +2 -2
- package/dist/cjs/modules/stream/base-transform.js +34 -20
- package/dist/cjs/modules/stream/chunked-builder.js +4 -4
- package/dist/cjs/modules/stream/index.browser.js +10 -17
- package/dist/cjs/modules/stream/index.js +15 -39
- package/dist/cjs/modules/stream/internal/event-utils.js +43 -0
- package/dist/cjs/modules/stream/internal/type-guards.js +30 -0
- package/dist/cjs/modules/stream/pull-stream.js +107 -43
- package/dist/cjs/modules/stream/shared.js +7 -4
- package/dist/cjs/modules/stream/streams.browser.js +927 -847
- package/dist/cjs/modules/stream/streams.js +156 -107
- package/dist/cjs/modules/stream/utils.js +3 -36
- package/dist/esm/index.browser.js +12 -0
- package/dist/{browser/modules/archive → esm/modules/archive/compression}/compress.base.js +1 -1
- package/dist/esm/modules/archive/{compress.browser.js → compression/compress.browser.js} +3 -11
- package/dist/{browser/modules/archive → esm/modules/archive/compression}/compress.js +1 -1
- package/dist/esm/modules/archive/{crc32.js → compression/crc32.js} +1 -1
- package/dist/esm/modules/archive/{deflate-fallback.js → compression/deflate-fallback.js} +1 -1
- package/dist/esm/modules/archive/{streaming-compress.browser.js → compression/streaming-compress.browser.js} +3 -3
- package/dist/esm/modules/archive/{streaming-compress.js → compression/streaming-compress.js} +2 -2
- package/dist/esm/modules/archive/defaults.js +6 -3
- package/dist/esm/modules/archive/index.base.js +3 -6
- package/dist/esm/modules/archive/index.browser.js +3 -7
- package/dist/esm/modules/archive/index.js +3 -5
- package/dist/esm/modules/archive/internal/byte-queue.js +407 -0
- package/dist/esm/modules/archive/io/archive-sink.js +45 -0
- package/dist/esm/modules/archive/io/archive-source.js +100 -0
- package/dist/esm/modules/archive/unzip/index.js +164 -0
- package/dist/esm/modules/archive/unzip/stream.base.js +1022 -0
- package/dist/esm/modules/archive/{parse.browser.js → unzip/stream.browser.js} +371 -110
- package/dist/{browser/modules/archive/parse.js → esm/modules/archive/unzip/stream.js} +6 -5
- package/dist/{browser/modules/archive → esm/modules/archive/unzip}/zip-parser.js +38 -24
- package/dist/esm/modules/archive/utils/async-queue.js +103 -0
- package/dist/esm/modules/archive/utils/bytes.js +16 -16
- package/dist/esm/modules/archive/utils/compressibility.js +57 -0
- package/dist/esm/modules/archive/utils/parse-buffer.js +21 -23
- package/dist/esm/modules/archive/utils/pattern-scanner.js +27 -0
- package/dist/esm/modules/archive/utils/timestamps.js +62 -1
- package/dist/esm/modules/archive/utils/zip-extra-fields.js +26 -14
- package/dist/esm/modules/archive/zip/index.js +157 -0
- package/dist/esm/modules/archive/{streaming-zip.js → zip/stream.js} +192 -48
- package/dist/esm/modules/archive/zip/zip-bytes.js +239 -0
- package/dist/{browser/modules/archive → esm/modules/archive/zip}/zip-entry-metadata.js +3 -3
- package/dist/esm/modules/archive/zip-spec/zip-records.js +126 -0
- package/dist/esm/modules/excel/stream/workbook-reader.browser.js +1 -1
- package/dist/esm/modules/excel/stream/workbook-writer.browser.js +1 -1
- package/dist/esm/modules/excel/utils/ooxml-validator.js +469 -0
- package/dist/esm/modules/excel/utils/parse-sax.js +32 -13
- package/dist/esm/modules/excel/worksheet.js +5 -2
- package/dist/esm/modules/excel/xlsx/xform/core/app-xform.js +3 -3
- package/dist/esm/modules/excel/xlsx/xform/core/core-xform.js +56 -68
- package/dist/esm/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.js +13 -1
- package/dist/esm/modules/excel/xlsx/xform/drawing/sp-xform.js +112 -0
- package/dist/esm/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +30 -2
- package/dist/esm/modules/excel/xlsx/xform/drawing/vml-drawing-xform.js +11 -0
- package/dist/esm/modules/excel/xlsx/xform/list-xform.js +8 -10
- package/dist/esm/modules/excel/xlsx/xform/sheet/page-setup-xform.js +16 -2
- package/dist/esm/modules/excel/xlsx/xform/sheet/worksheet-xform.js +110 -12
- package/dist/esm/modules/excel/xlsx/xform/strings/shared-string-xform.js +2 -3
- package/dist/esm/modules/excel/xlsx/xform/strings/text-xform.js +5 -7
- package/dist/esm/modules/excel/xlsx/xlsx.browser.js +11 -10
- package/dist/esm/modules/excel/xlsx/xlsx.js +1 -1
- package/dist/esm/modules/stream/base-transform.js +34 -20
- package/dist/esm/modules/stream/chunked-builder.js +4 -4
- package/dist/esm/modules/stream/index.browser.js +10 -22
- package/dist/esm/modules/stream/index.js +15 -44
- package/dist/esm/modules/stream/internal/event-utils.js +40 -0
- package/dist/esm/modules/stream/internal/type-guards.js +24 -0
- package/dist/esm/modules/stream/pull-stream.js +107 -43
- package/dist/esm/modules/stream/shared.js +7 -4
- package/dist/esm/modules/stream/streams.browser.js +921 -836
- package/dist/esm/modules/stream/streams.js +146 -95
- package/dist/esm/modules/stream/utils.js +5 -38
- package/dist/iife/THIRD_PARTY_NOTICES.md +0 -31
- package/dist/iife/excelts.iife.js +6592 -4537
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +103 -31
- package/dist/types/index.browser.d.ts +1 -0
- package/dist/{browser/modules/archive → types/modules/archive/compression}/compress.browser.d.ts +2 -8
- package/dist/types/modules/archive/{streaming-compress.browser.d.ts → compression/streaming-compress.browser.d.ts} +1 -1
- package/dist/types/modules/archive/defaults.d.ts +1 -0
- package/dist/types/modules/archive/index.base.d.ts +4 -4
- package/dist/types/modules/archive/index.browser.d.ts +3 -4
- package/dist/types/modules/archive/index.d.ts +3 -4
- package/dist/types/modules/archive/internal/byte-queue.d.ts +33 -0
- package/dist/types/modules/archive/io/archive-sink.d.ts +8 -0
- package/dist/types/modules/archive/io/archive-source.d.ts +6 -0
- package/dist/types/modules/archive/unzip/index.d.ts +40 -0
- package/dist/types/modules/archive/{parse.base.d.ts → unzip/stream.base.d.ts} +38 -4
- package/dist/types/modules/archive/{parse.browser.d.ts → unzip/stream.browser.d.ts} +2 -2
- package/dist/types/modules/archive/{parse.d.ts → unzip/stream.d.ts} +3 -3
- package/dist/types/modules/archive/{zip-parser.d.ts → unzip/zip-parser.d.ts} +1 -1
- package/dist/types/modules/archive/utils/async-queue.d.ts +7 -0
- package/dist/types/modules/archive/utils/compressibility.d.ts +10 -0
- package/dist/types/modules/archive/utils/pattern-scanner.d.ts +21 -0
- package/dist/types/modules/archive/utils/zip-extra-fields.d.ts +1 -1
- package/dist/types/modules/archive/zip/index.d.ts +42 -0
- package/dist/types/modules/archive/{streaming-zip.d.ts → zip/stream.d.ts} +29 -6
- package/dist/types/modules/archive/zip/zip-bytes.d.ts +73 -0
- package/dist/types/modules/archive/{zip-entry-metadata.d.ts → zip/zip-entry-metadata.d.ts} +1 -1
- package/dist/types/modules/archive/{zip-records.d.ts → zip-spec/zip-records.d.ts} +20 -0
- package/dist/types/modules/excel/stream/workbook-writer.browser.d.ts +1 -1
- package/dist/types/modules/excel/utils/ooxml-validator.d.ts +48 -0
- package/dist/types/modules/excel/utils/parse-sax.d.ts +3 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.d.ts +1 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/sp-xform.d.ts +18 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.d.ts +6 -1
- package/dist/types/modules/excel/xlsx/xform/sheet/page-setup-xform.d.ts +1 -0
- package/dist/types/modules/stream/base-transform.d.ts +3 -0
- package/dist/types/modules/stream/buffered-stream.d.ts +2 -12
- package/dist/types/modules/stream/index.browser.d.ts +13 -19
- package/dist/types/modules/stream/index.d.ts +18 -41
- package/dist/types/modules/stream/internal/event-utils.d.ts +17 -0
- package/dist/types/modules/stream/internal/type-guards.d.ts +9 -0
- package/dist/types/modules/stream/pull-stream.d.ts +5 -6
- package/dist/types/modules/stream/shared.d.ts +1 -1
- package/dist/types/modules/stream/streams.browser.d.ts +32 -44
- package/dist/types/modules/stream/streams.d.ts +4 -20
- package/package.json +14 -10
- package/dist/browser/modules/archive/byte-queue.d.ts +0 -18
- package/dist/browser/modules/archive/byte-queue.js +0 -125
- package/dist/browser/modules/archive/parse.base.js +0 -644
- package/dist/browser/modules/archive/utils/zip-extra.d.ts +0 -18
- package/dist/browser/modules/archive/utils/zip-extra.js +0 -68
- package/dist/browser/modules/archive/zip-builder.d.ts +0 -117
- package/dist/browser/modules/archive/zip-builder.js +0 -292
- package/dist/browser/modules/archive/zip-constants.d.ts +0 -18
- package/dist/browser/modules/archive/zip-constants.js +0 -23
- package/dist/browser/modules/archive/zip-records.js +0 -84
- package/dist/cjs/modules/archive/byte-queue.js +0 -129
- package/dist/cjs/modules/archive/parse.base.js +0 -666
- package/dist/cjs/modules/archive/utils/zip-extra.js +0 -74
- package/dist/cjs/modules/archive/zip-builder.js +0 -297
- package/dist/cjs/modules/archive/zip-constants.js +0 -26
- package/dist/cjs/modules/archive/zip-records.js +0 -90
- package/dist/esm/modules/archive/byte-queue.js +0 -125
- package/dist/esm/modules/archive/parse.base.js +0 -644
- package/dist/esm/modules/archive/utils/zip-extra.js +0 -68
- package/dist/esm/modules/archive/zip-builder.js +0 -292
- package/dist/esm/modules/archive/zip-constants.js +0 -23
- package/dist/esm/modules/archive/zip-records.js +0 -84
- package/dist/types/modules/archive/byte-queue.d.ts +0 -18
- package/dist/types/modules/archive/utils/zip-extra.d.ts +0 -18
- package/dist/types/modules/archive/zip-builder.d.ts +0 -117
- package/dist/types/modules/archive/zip-constants.d.ts +0 -18
- /package/dist/browser/modules/archive/{compress.base.d.ts → compression/compress.base.d.ts} +0 -0
- /package/dist/browser/modules/archive/{crc32.base.d.ts → compression/crc32.base.d.ts} +0 -0
- /package/dist/browser/modules/archive/{crc32.base.js → compression/crc32.base.js} +0 -0
- /package/dist/browser/modules/archive/{crc32.browser.js → compression/crc32.browser.js} +0 -0
- /package/dist/browser/modules/archive/{deflate-fallback.d.ts → compression/deflate-fallback.d.ts} +0 -0
- /package/dist/browser/modules/archive/{streaming-compress.base.d.ts → compression/streaming-compress.base.d.ts} +0 -0
- /package/dist/browser/modules/archive/{streaming-compress.base.js → compression/streaming-compress.base.js} +0 -0
- /package/dist/browser/modules/archive/{extract.js → unzip/extract.js} +0 -0
- /package/dist/browser/modules/archive/{zip-entry-metadata.d.ts → zip/zip-entry-metadata.d.ts} +0 -0
- /package/dist/browser/modules/archive/{zip-entry-info.d.ts → zip-spec/zip-entry-info.d.ts} +0 -0
- /package/dist/browser/modules/archive/{zip-entry-info.js → zip-spec/zip-entry-info.js} +0 -0
- /package/dist/cjs/modules/archive/{crc32.base.js → compression/crc32.base.js} +0 -0
- /package/dist/cjs/modules/archive/{crc32.browser.js → compression/crc32.browser.js} +0 -0
- /package/dist/cjs/modules/archive/{streaming-compress.base.js → compression/streaming-compress.base.js} +0 -0
- /package/dist/cjs/modules/archive/{extract.js → unzip/extract.js} +0 -0
- /package/dist/cjs/modules/archive/{zip-entry-info.js → zip-spec/zip-entry-info.js} +0 -0
- /package/dist/esm/modules/archive/{crc32.base.js → compression/crc32.base.js} +0 -0
- /package/dist/esm/modules/archive/{crc32.browser.js → compression/crc32.browser.js} +0 -0
- /package/dist/esm/modules/archive/{streaming-compress.base.js → compression/streaming-compress.base.js} +0 -0
- /package/dist/esm/modules/archive/{extract.js → unzip/extract.js} +0 -0
- /package/dist/esm/modules/archive/{zip-entry-info.js → zip-spec/zip-entry-info.js} +0 -0
- /package/dist/types/modules/archive/{compress.base.d.ts → compression/compress.base.d.ts} +0 -0
- /package/dist/types/modules/archive/{compress.d.ts → compression/compress.d.ts} +0 -0
- /package/dist/types/modules/archive/{crc32.base.d.ts → compression/crc32.base.d.ts} +0 -0
- /package/dist/types/modules/archive/{crc32.browser.d.ts → compression/crc32.browser.d.ts} +0 -0
- /package/dist/types/modules/archive/{crc32.d.ts → compression/crc32.d.ts} +0 -0
- /package/dist/types/modules/archive/{deflate-fallback.d.ts → compression/deflate-fallback.d.ts} +0 -0
- /package/dist/types/modules/archive/{streaming-compress.base.d.ts → compression/streaming-compress.base.d.ts} +0 -0
- /package/dist/types/modules/archive/{streaming-compress.d.ts → compression/streaming-compress.d.ts} +0 -0
- /package/dist/types/modules/archive/{extract.d.ts → unzip/extract.d.ts} +0 -0
- /package/dist/types/modules/archive/{zip-entry-info.d.ts → zip-spec/zip-entry-info.d.ts} +0 -0
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.validateXlsxBuffer = validateXlsxBuffer;
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const extract_1 = require("../../archive/unzip/extract.js");
|
|
9
|
+
const parse_sax_1 = require("./parse-sax.js");
|
|
10
|
+
function pushProblem(problems, problem, maxProblems) {
|
|
11
|
+
if (maxProblems !== undefined && problems.length >= maxProblems) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
problems.push(problem);
|
|
15
|
+
}
|
|
16
|
+
function stripLeadingSlash(p) {
|
|
17
|
+
return p.startsWith("/") ? p.slice(1) : p;
|
|
18
|
+
}
|
|
19
|
+
function isXmlLike(pathName) {
|
|
20
|
+
return pathName.endsWith(".xml") || pathName.endsWith(".rels") || pathName.endsWith(".vml");
|
|
21
|
+
}
|
|
22
|
+
function getRelsSourceDir(relsPath) {
|
|
23
|
+
// In OPC, relationship targets are resolved relative to the SOURCE part directory,
|
|
24
|
+
// not the .rels part directory.
|
|
25
|
+
//
|
|
26
|
+
// Examples:
|
|
27
|
+
// - _rels/.rels -> base "" (package root)
|
|
28
|
+
// - xl/_rels/workbook.xml.rels -> base "xl"
|
|
29
|
+
// - xl/worksheets/_rels/sheet1.xml.rels -> base "xl/worksheets"
|
|
30
|
+
if (relsPath === "_rels/.rels") {
|
|
31
|
+
return "";
|
|
32
|
+
}
|
|
33
|
+
const relsMarker = "/_rels/";
|
|
34
|
+
const idx = relsPath.indexOf(relsMarker);
|
|
35
|
+
if (idx === -1) {
|
|
36
|
+
return relsPath.includes("/") ? relsPath.slice(0, relsPath.lastIndexOf("/")) : "";
|
|
37
|
+
}
|
|
38
|
+
return relsPath.slice(0, idx);
|
|
39
|
+
}
|
|
40
|
+
function resolveRelTarget(relsPath, target) {
|
|
41
|
+
const baseDir = getRelsSourceDir(relsPath);
|
|
42
|
+
const resolved = node_path_1.default.posix.normalize(node_path_1.default.posix.join(baseDir, target));
|
|
43
|
+
return resolved.replace(/^\//, "");
|
|
44
|
+
}
|
|
45
|
+
function isSafeResolvedPath(resolved) {
|
|
46
|
+
// Prevent path traversal out of package root.
|
|
47
|
+
// `normalize()` can produce paths starting with "../".
|
|
48
|
+
return !(resolved === ".." || resolved.startsWith("../") || resolved.includes("/../"));
|
|
49
|
+
}
|
|
50
|
+
async function assertXmlWellFormed(xmlText) {
|
|
51
|
+
// parseSax throws on malformed XML.
|
|
52
|
+
for await (const _events of (0, parse_sax_1.parseSax)([xmlText])) {
|
|
53
|
+
// no-op
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function parseContentTypes(xml) {
|
|
57
|
+
const defaults = [];
|
|
58
|
+
const overrides = [];
|
|
59
|
+
const defaultRe = /<Default\s+[^>]*Extension="([^"]+)"[^>]*ContentType="([^"]+)"[^>]*\/>/g;
|
|
60
|
+
const overrideRe = /<Override\s+[^>]*PartName="([^"]+)"[^>]*ContentType="([^"]+)"[^>]*\/>/g;
|
|
61
|
+
for (let match = defaultRe.exec(xml); match; match = defaultRe.exec(xml)) {
|
|
62
|
+
defaults.push({ extension: match[1], contentType: match[2] });
|
|
63
|
+
}
|
|
64
|
+
for (let match = overrideRe.exec(xml); match; match = overrideRe.exec(xml)) {
|
|
65
|
+
overrides.push({ partName: match[1], contentType: match[2] });
|
|
66
|
+
}
|
|
67
|
+
const parseOk = xml.includes("<Types") && (defaults.length > 0 || overrides.length > 0);
|
|
68
|
+
return { defaults, overrides, parseOk };
|
|
69
|
+
}
|
|
70
|
+
function getExtension(p) {
|
|
71
|
+
const base = node_path_1.default.posix.basename(p);
|
|
72
|
+
const idx = base.lastIndexOf(".");
|
|
73
|
+
return idx === -1 ? "" : base.slice(idx + 1);
|
|
74
|
+
}
|
|
75
|
+
function isPackagePart(pathName) {
|
|
76
|
+
// Exclude directories and the [Content_Types].xml pseudo-root.
|
|
77
|
+
return pathName !== "" && pathName !== "[Content_Types].xml";
|
|
78
|
+
}
|
|
79
|
+
function parseRelationships(xml) {
|
|
80
|
+
// Ensure the rels XML itself is parseable.
|
|
81
|
+
// We still use regex to extract rels for speed/portability.
|
|
82
|
+
const relRe = /<Relationship\s+[^>]*Id="([^"]+)"[^>]*Type="([^"]+)"[^>]*Target="([^"]*)"(?:[^>]*TargetMode="([^"]+)")?[^>]*\/>/g;
|
|
83
|
+
const rels = [];
|
|
84
|
+
for (let match = relRe.exec(xml); match; match = relRe.exec(xml)) {
|
|
85
|
+
rels.push({ id: match[1], type: match[2], target: match[3], targetMode: match[4] });
|
|
86
|
+
}
|
|
87
|
+
// Basic sanity: should have <Relationships ...> root.
|
|
88
|
+
const parseOk = xml.includes("<Relationships") && xml.includes("Relationship");
|
|
89
|
+
return { rels, parseOk };
|
|
90
|
+
}
|
|
91
|
+
async function validateXlsxBuffer(xlsxBuffer, options = {}) {
|
|
92
|
+
const { checkXmlWellFormed = true, checkRelationshipTargets = true, checkContentTypesOverrides = true, checkWorksheetControlWiring = true, maxProblems } = options;
|
|
93
|
+
const problems = [];
|
|
94
|
+
const entries = await (0, extract_1.extractAll)(xlsxBuffer);
|
|
95
|
+
const has = (p) => entries.has(p);
|
|
96
|
+
const mustExist = [
|
|
97
|
+
"[Content_Types].xml",
|
|
98
|
+
"_rels/.rels",
|
|
99
|
+
"xl/workbook.xml",
|
|
100
|
+
"xl/_rels/workbook.xml.rels"
|
|
101
|
+
];
|
|
102
|
+
for (const p of mustExist) {
|
|
103
|
+
if (!has(p)) {
|
|
104
|
+
pushProblem(problems, { kind: "missing-part", file: p, message: `Missing required part: ${p}` }, maxProblems);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// XML well-formedness for all XML-like parts.
|
|
108
|
+
if (checkXmlWellFormed) {
|
|
109
|
+
for (const [p, entry] of entries) {
|
|
110
|
+
if (maxProblems !== undefined && problems.length >= maxProblems) {
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
if (entry.isDirectory || !isXmlLike(p)) {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const xml = new TextDecoder().decode(entry.data);
|
|
117
|
+
try {
|
|
118
|
+
await assertXmlWellFormed(xml);
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
pushProblem(problems, {
|
|
122
|
+
kind: "xml-malformed",
|
|
123
|
+
file: p,
|
|
124
|
+
message: `Malformed XML: ${err?.message || String(err)}`
|
|
125
|
+
}, maxProblems);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Content types overrides must point to existing parts.
|
|
130
|
+
if (checkContentTypesOverrides && has("[Content_Types].xml")) {
|
|
131
|
+
const ctXml = new TextDecoder().decode(entries.get("[Content_Types].xml").data);
|
|
132
|
+
const { defaults, overrides, parseOk } = parseContentTypes(ctXml);
|
|
133
|
+
if (!parseOk) {
|
|
134
|
+
pushProblem(problems, {
|
|
135
|
+
kind: "content-types-malformed",
|
|
136
|
+
file: "[Content_Types].xml",
|
|
137
|
+
message: "Content types XML missing expected root/entries"
|
|
138
|
+
}, maxProblems);
|
|
139
|
+
}
|
|
140
|
+
const defaultByExt = new Map(defaults.map(d => [d.extension.toLowerCase(), d.contentType]));
|
|
141
|
+
const overrideByPart = new Map();
|
|
142
|
+
for (const ov of overrides) {
|
|
143
|
+
const key = stripLeadingSlash(ov.partName);
|
|
144
|
+
if (overrideByPart.has(key)) {
|
|
145
|
+
pushProblem(problems, {
|
|
146
|
+
kind: "content-types-duplicate-override",
|
|
147
|
+
file: "[Content_Types].xml",
|
|
148
|
+
message: `Duplicate Override PartName: ${ov.partName}`
|
|
149
|
+
}, maxProblems);
|
|
150
|
+
}
|
|
151
|
+
overrideByPart.set(key, ov.contentType);
|
|
152
|
+
}
|
|
153
|
+
// RFC: .rels and .xml defaults are expected in valid packages.
|
|
154
|
+
const relsDefault = defaultByExt.get("rels");
|
|
155
|
+
if (relsDefault !== "application/vnd.openxmlformats-package.relationships+xml") {
|
|
156
|
+
pushProblem(problems, {
|
|
157
|
+
kind: "content-types-missing-default",
|
|
158
|
+
file: "[Content_Types].xml",
|
|
159
|
+
message: "Missing/incorrect Default for .rels (expected application/vnd.openxmlformats-package.relationships+xml)"
|
|
160
|
+
}, maxProblems);
|
|
161
|
+
}
|
|
162
|
+
const xmlDefault = defaultByExt.get("xml");
|
|
163
|
+
if (xmlDefault !== "application/xml") {
|
|
164
|
+
pushProblem(problems, {
|
|
165
|
+
kind: "content-types-missing-default",
|
|
166
|
+
file: "[Content_Types].xml",
|
|
167
|
+
message: "Missing/incorrect Default for .xml (expected application/xml)"
|
|
168
|
+
}, maxProblems);
|
|
169
|
+
}
|
|
170
|
+
for (const ov of overrides) {
|
|
171
|
+
if (maxProblems !== undefined && problems.length >= maxProblems) {
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
const zipPath = stripLeadingSlash(ov.partName);
|
|
175
|
+
if (!has(zipPath)) {
|
|
176
|
+
pushProblem(problems, {
|
|
177
|
+
kind: "content-types-missing",
|
|
178
|
+
file: "[Content_Types].xml",
|
|
179
|
+
message: `Override PartName points to missing file: ${ov.partName}`
|
|
180
|
+
}, maxProblems);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
// Strong check: every part in the zip should have a content type via Default or Override.
|
|
184
|
+
for (const [p, entry] of entries) {
|
|
185
|
+
if (maxProblems !== undefined && problems.length >= maxProblems) {
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
if (entry.isDirectory || !isPackagePart(p)) {
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
const overrideType = overrideByPart.get(p);
|
|
192
|
+
if (overrideType) {
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
const ext = getExtension(p).toLowerCase();
|
|
196
|
+
if (!ext) {
|
|
197
|
+
pushProblem(problems, {
|
|
198
|
+
kind: "content-types-missing-for-part",
|
|
199
|
+
file: "[Content_Types].xml",
|
|
200
|
+
message: `No content type for part without extension: ${p}`
|
|
201
|
+
}, maxProblems);
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
if (!defaultByExt.has(ext)) {
|
|
205
|
+
pushProblem(problems, {
|
|
206
|
+
kind: "content-types-missing-for-part",
|
|
207
|
+
file: "[Content_Types].xml",
|
|
208
|
+
message: `No Default/Override content type for part: ${p} (extension .${ext})`
|
|
209
|
+
}, maxProblems);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
// Root relationships must point to the workbook (OPC officeDocument).
|
|
214
|
+
if (has("_rels/.rels")) {
|
|
215
|
+
const rootRelsXml = new TextDecoder().decode(entries.get("_rels/.rels").data);
|
|
216
|
+
const { rels } = parseRelationships(rootRelsXml);
|
|
217
|
+
const hasOfficeDocument = rels.some(r => r.type.includes("/relationships/officeDocument") && r.target === "xl/workbook.xml");
|
|
218
|
+
if (!hasOfficeDocument) {
|
|
219
|
+
pushProblem(problems, {
|
|
220
|
+
kind: "root-rels-missing-officeDocument",
|
|
221
|
+
file: "_rels/.rels",
|
|
222
|
+
message: "Missing officeDocument relationship to xl/workbook.xml"
|
|
223
|
+
}, maxProblems);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// Relationships: validate target existence and basic ID uniqueness.
|
|
227
|
+
if (checkRelationshipTargets) {
|
|
228
|
+
for (const [p, entry] of entries) {
|
|
229
|
+
if (maxProblems !== undefined && problems.length >= maxProblems) {
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
if (entry.isDirectory || !p.endsWith(".rels")) {
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
const relsXml = new TextDecoder().decode(entry.data);
|
|
236
|
+
const { rels, parseOk } = parseRelationships(relsXml);
|
|
237
|
+
if (!parseOk) {
|
|
238
|
+
pushProblem(problems, {
|
|
239
|
+
kind: "rels-malformed",
|
|
240
|
+
file: p,
|
|
241
|
+
message: "Relationships XML missing expected root/entries"
|
|
242
|
+
}, maxProblems);
|
|
243
|
+
}
|
|
244
|
+
const ids = new Set();
|
|
245
|
+
for (const rel of rels) {
|
|
246
|
+
if (maxProblems !== undefined && problems.length >= maxProblems) {
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
if (ids.has(rel.id)) {
|
|
250
|
+
pushProblem(problems, {
|
|
251
|
+
kind: "rels-duplicate-id",
|
|
252
|
+
file: p,
|
|
253
|
+
message: `Duplicate relationship Id: ${rel.id}`
|
|
254
|
+
}, maxProblems);
|
|
255
|
+
}
|
|
256
|
+
ids.add(rel.id);
|
|
257
|
+
if (rel.targetMode === "External") {
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
if (!rel.target) {
|
|
261
|
+
pushProblem(problems, {
|
|
262
|
+
kind: "rels-empty-target",
|
|
263
|
+
file: p,
|
|
264
|
+
message: `Relationship ${rel.id} (${rel.type}) has empty Target`
|
|
265
|
+
}, maxProblems);
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
const resolvedTarget = resolveRelTarget(p, rel.target);
|
|
269
|
+
if (!isSafeResolvedPath(resolvedTarget)) {
|
|
270
|
+
pushProblem(problems, {
|
|
271
|
+
kind: "rels-invalid-target-path",
|
|
272
|
+
file: p,
|
|
273
|
+
message: `Rel ${rel.id} (${rel.type}) target escapes package root: ${rel.target} -> ${resolvedTarget}`
|
|
274
|
+
}, maxProblems);
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
if (!has(resolvedTarget)) {
|
|
278
|
+
pushProblem(problems, {
|
|
279
|
+
kind: "rels-missing-target",
|
|
280
|
+
file: p,
|
|
281
|
+
message: `Rel ${rel.id} (${rel.type}) target missing: ${rel.target} -> ${resolvedTarget}`
|
|
282
|
+
}, maxProblems);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
// Optional: ensure the source part exists for non-root rels.
|
|
286
|
+
if (p !== "_rels/.rels") {
|
|
287
|
+
// Convert: xl/_rels/workbook.xml.rels -> xl/workbook.xml
|
|
288
|
+
// Convert: xl/worksheets/_rels/sheet1.xml.rels -> xl/worksheets/sheet1.xml
|
|
289
|
+
const srcDir = getRelsSourceDir(p);
|
|
290
|
+
const relsBaseName = node_path_1.default.posix.basename(p);
|
|
291
|
+
const sourceName = relsBaseName.replace(/\.rels$/, "");
|
|
292
|
+
const sourcePath = srcDir ? `${srcDir}/${sourceName}` : sourceName;
|
|
293
|
+
if (!has(sourcePath)) {
|
|
294
|
+
pushProblem(problems, {
|
|
295
|
+
kind: "rels-source-missing",
|
|
296
|
+
file: p,
|
|
297
|
+
message: `Relationships part has no corresponding source part: ${sourcePath}`
|
|
298
|
+
}, maxProblems);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
// Workbook -> worksheets wiring.
|
|
304
|
+
if (has("xl/workbook.xml") && has("xl/_rels/workbook.xml.rels")) {
|
|
305
|
+
const workbookXml = new TextDecoder().decode(entries.get("xl/workbook.xml").data);
|
|
306
|
+
const workbookRelsXml = new TextDecoder().decode(entries.get("xl/_rels/workbook.xml.rels").data);
|
|
307
|
+
const { rels: wbRels } = parseRelationships(workbookRelsXml);
|
|
308
|
+
const wbById = new Map(wbRels.map(r => [r.id, r]));
|
|
309
|
+
// Uniqueness checks: sheetId and r:id should not be duplicated.
|
|
310
|
+
const sheetIdRe = /<sheet\b[^>]*\bsheetId="(\d+)"[^>]*\/>/g;
|
|
311
|
+
const seenSheetIds = new Set();
|
|
312
|
+
for (let match = sheetIdRe.exec(workbookXml); match; match = sheetIdRe.exec(workbookXml)) {
|
|
313
|
+
const id = match[1];
|
|
314
|
+
if (seenSheetIds.has(id)) {
|
|
315
|
+
pushProblem(problems, {
|
|
316
|
+
kind: "workbook-duplicate-sheetId",
|
|
317
|
+
file: "xl/workbook.xml",
|
|
318
|
+
message: `Duplicate sheetId in workbook: ${id}`
|
|
319
|
+
}, maxProblems);
|
|
320
|
+
}
|
|
321
|
+
seenSheetIds.add(id);
|
|
322
|
+
}
|
|
323
|
+
const sheetRidSeen = new Set();
|
|
324
|
+
const sheetRidRe = /<sheet\b[^>]*\br:id="(rId\d+)"[^>]*\/>/g;
|
|
325
|
+
for (let match = sheetRidRe.exec(workbookXml); match; match = sheetRidRe.exec(workbookXml)) {
|
|
326
|
+
if (maxProblems !== undefined && problems.length >= maxProblems) {
|
|
327
|
+
break;
|
|
328
|
+
}
|
|
329
|
+
const rid = match[1];
|
|
330
|
+
if (sheetRidSeen.has(rid)) {
|
|
331
|
+
pushProblem(problems, {
|
|
332
|
+
kind: "workbook-duplicate-sheet-rid",
|
|
333
|
+
file: "xl/workbook.xml",
|
|
334
|
+
message: `Duplicate sheet r:id in workbook: ${rid}`
|
|
335
|
+
}, maxProblems);
|
|
336
|
+
}
|
|
337
|
+
sheetRidSeen.add(rid);
|
|
338
|
+
const rel = wbById.get(rid);
|
|
339
|
+
if (!rel) {
|
|
340
|
+
pushProblem(problems, {
|
|
341
|
+
kind: "workbook-sheet-missing-rel",
|
|
342
|
+
file: "xl/workbook.xml",
|
|
343
|
+
message: `Workbook <sheet> references missing relationship: ${rid} (in xl/_rels/workbook.xml.rels)`
|
|
344
|
+
}, maxProblems);
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
if (!rel.type.includes("/relationships/worksheet")) {
|
|
348
|
+
pushProblem(problems, {
|
|
349
|
+
kind: "workbook-sheet-wrong-rel-type",
|
|
350
|
+
file: "xl/workbook.xml",
|
|
351
|
+
message: `Workbook <sheet> ${rid} relationship is not worksheet: ${rel.type}`
|
|
352
|
+
}, maxProblems);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
// Worksheet <controls>/<legacyDrawing> wiring.
|
|
357
|
+
if (checkWorksheetControlWiring) {
|
|
358
|
+
for (const [p, entry] of entries) {
|
|
359
|
+
if (maxProblems !== undefined && problems.length >= maxProblems) {
|
|
360
|
+
break;
|
|
361
|
+
}
|
|
362
|
+
if (entry.isDirectory || !p.startsWith("xl/worksheets/sheet") || !p.endsWith(".xml")) {
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
const sheetXml = new TextDecoder().decode(entry.data);
|
|
366
|
+
const relsPath = `xl/worksheets/_rels/${node_path_1.default.posix.basename(p)}.rels`;
|
|
367
|
+
// Excel is sensitive to worksheet child element ordering. In particular,
|
|
368
|
+
// legacyDrawing must come before controls when both are present.
|
|
369
|
+
const legacyDrawingIdx = sheetXml.indexOf("<legacyDrawing");
|
|
370
|
+
const controlsIdx = sheetXml.indexOf("<controls");
|
|
371
|
+
if (legacyDrawingIdx !== -1 && controlsIdx !== -1 && legacyDrawingIdx > controlsIdx) {
|
|
372
|
+
pushProblem(problems, {
|
|
373
|
+
kind: "sheet-legacyDrawing-after-controls",
|
|
374
|
+
file: p,
|
|
375
|
+
message: "Worksheet has <legacyDrawing> after <controls>; Excel may repair or reject this sheet"
|
|
376
|
+
}, maxProblems);
|
|
377
|
+
}
|
|
378
|
+
// Match <control ...> elements (not necessarily self-closing).
|
|
379
|
+
const controlRidRe = /<control\b[^>]*\br:id="(rId\d+)"[^>]*>/g;
|
|
380
|
+
const controlRids = [];
|
|
381
|
+
for (let match = controlRidRe.exec(sheetXml); match; match = controlRidRe.exec(sheetXml)) {
|
|
382
|
+
controlRids.push(match[1]);
|
|
383
|
+
}
|
|
384
|
+
// Excel Online / strict Excel builds may reject or "repair" legacy form controls
|
|
385
|
+
// if the sheet doesn't also have a DrawingML <drawing> part.
|
|
386
|
+
if (controlRids.length > 0 && sheetXml.indexOf("<drawing") === -1) {
|
|
387
|
+
pushProblem(problems, {
|
|
388
|
+
kind: "sheet-controls-missing-drawing",
|
|
389
|
+
file: p,
|
|
390
|
+
message: "Worksheet has legacy <controls> but no <drawing>; Excel may repair/reject legacy form controls"
|
|
391
|
+
}, maxProblems);
|
|
392
|
+
}
|
|
393
|
+
const legacyDrawingRidRe = /<legacyDrawing\b[^>]*\br:id="(rId\d+)"[^>]*\/>/g;
|
|
394
|
+
const legacyDrawingRids = [];
|
|
395
|
+
for (let match = legacyDrawingRidRe.exec(sheetXml); match; match = legacyDrawingRidRe.exec(sheetXml)) {
|
|
396
|
+
legacyDrawingRids.push(match[1]);
|
|
397
|
+
}
|
|
398
|
+
if ((controlRids.length > 0 || legacyDrawingRids.length > 0) && !has(relsPath)) {
|
|
399
|
+
pushProblem(problems, {
|
|
400
|
+
kind: "sheet-missing-rels",
|
|
401
|
+
file: p,
|
|
402
|
+
message: `Worksheet has controls/legacyDrawing but missing rels part: ${relsPath}`
|
|
403
|
+
}, maxProblems);
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
if (!has(relsPath)) {
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
const sheetRelsXml = new TextDecoder().decode(entries.get(relsPath).data);
|
|
410
|
+
const { rels: sheetRels } = parseRelationships(sheetRelsXml);
|
|
411
|
+
const byId = new Map(sheetRels.map(r => [r.id, r]));
|
|
412
|
+
const assertRidType = (rid, expectedTypeIncludes, kindMissing, kindWrong, nodeLabel) => {
|
|
413
|
+
const rel = byId.get(rid);
|
|
414
|
+
if (!rel) {
|
|
415
|
+
pushProblem(problems, {
|
|
416
|
+
kind: kindMissing,
|
|
417
|
+
file: p,
|
|
418
|
+
message: `Sheet ${nodeLabel} references missing relationship: ${rid} (in ${relsPath})`
|
|
419
|
+
}, maxProblems);
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
if (!rel.type.includes(expectedTypeIncludes)) {
|
|
423
|
+
pushProblem(problems, {
|
|
424
|
+
kind: kindWrong,
|
|
425
|
+
file: p,
|
|
426
|
+
message: `Sheet ${nodeLabel} ${rid} relationship is not ${expectedTypeIncludes}: ${rel.type}`
|
|
427
|
+
}, maxProblems);
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
for (const rid of controlRids) {
|
|
431
|
+
if (maxProblems !== undefined && problems.length >= maxProblems) {
|
|
432
|
+
break;
|
|
433
|
+
}
|
|
434
|
+
assertRidType(rid, "/relationships/ctrlProp", "sheet-control-missing-rel", "sheet-control-wrong-rel-type", "<control>");
|
|
435
|
+
}
|
|
436
|
+
for (const rid of legacyDrawingRids) {
|
|
437
|
+
if (maxProblems !== undefined && problems.length >= maxProblems) {
|
|
438
|
+
break;
|
|
439
|
+
}
|
|
440
|
+
assertRidType(rid, "/relationships/vmlDrawing", "sheet-legacyDrawing-missing-rel", "sheet-legacyDrawing-wrong-rel-type", "<legacyDrawing>");
|
|
441
|
+
}
|
|
442
|
+
// Common worksheet nodes that are wired via r:id
|
|
443
|
+
// - <drawing r:id="..."/> -> drawing
|
|
444
|
+
// - <comments r:id="..."/> -> comments
|
|
445
|
+
// - <tableParts><tablePart r:id="..."/></tableParts> -> table
|
|
446
|
+
// - <hyperlink r:id="..."/> -> hyperlink
|
|
447
|
+
const drawingRidRe = /<drawing\b[^>]*\br:id="(rId\d+)"[^>]*\/>/g;
|
|
448
|
+
for (let match = drawingRidRe.exec(sheetXml); match; match = drawingRidRe.exec(sheetXml)) {
|
|
449
|
+
assertRidType(match[1], "/relationships/drawing", "sheet-drawing-missing-rel", "sheet-drawing-wrong-rel-type", "<drawing>");
|
|
450
|
+
}
|
|
451
|
+
const commentsRidRe = /<comments\b[^>]*\br:id="(rId\d+)"[^>]*\/>/g;
|
|
452
|
+
for (let match = commentsRidRe.exec(sheetXml); match; match = commentsRidRe.exec(sheetXml)) {
|
|
453
|
+
assertRidType(match[1], "/relationships/comments", "sheet-comments-missing-rel", "sheet-comments-wrong-rel-type", "<comments>");
|
|
454
|
+
}
|
|
455
|
+
const tablePartRidRe = /<tablePart\b[^>]*\br:id="(rId\d+)"[^>]*\/>/g;
|
|
456
|
+
for (let match = tablePartRidRe.exec(sheetXml); match; match = tablePartRidRe.exec(sheetXml)) {
|
|
457
|
+
assertRidType(match[1], "/relationships/table", "sheet-tablePart-missing-rel", "sheet-tablePart-wrong-rel-type", "<tablePart>");
|
|
458
|
+
}
|
|
459
|
+
const hyperlinkRidRe = /<hyperlink\b[^>]*\br:id="(rId\d+)"[^>]*\/>/g;
|
|
460
|
+
for (let match = hyperlinkRidRe.exec(sheetXml); match; match = hyperlinkRidRe.exec(sheetXml)) {
|
|
461
|
+
assertRidType(match[1], "/relationships/hyperlink", "sheet-hyperlink-missing-rel", "sheet-hyperlink-wrong-rel-type", "<hyperlink>");
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
const stats = {
|
|
466
|
+
entryCount: entries.size,
|
|
467
|
+
xmlLikeCount: [...entries.values()].filter((f) => !f.isDirectory && isXmlLike(f.path)).length,
|
|
468
|
+
relsCount: [...entries.values()].filter((f) => !f.isDirectory && f.path.endsWith(".rels")).length
|
|
469
|
+
};
|
|
470
|
+
return {
|
|
471
|
+
ok: problems.length === 0,
|
|
472
|
+
problems,
|
|
473
|
+
stats
|
|
474
|
+
};
|
|
475
|
+
}
|
|
@@ -146,6 +146,12 @@ const XML_ENTITIES = {
|
|
|
146
146
|
quot: '"',
|
|
147
147
|
apos: "'"
|
|
148
148
|
};
|
|
149
|
+
// HAN CELL namespace prefix normalization
|
|
150
|
+
// HAN CELL uses non-standard namespace prefixes (ep:, cp:, dc:, etc.)
|
|
151
|
+
// The x: prefix for spreadsheetml is detected dynamically from xmlns declarations
|
|
152
|
+
// See: https://github.com/exceljs/exceljs/issues/3014
|
|
153
|
+
const HAN_CELL_PREFIXES = /^(ep|cp|dc|dcterms|dcmitype|vt):/;
|
|
154
|
+
const SPREADSHEETML_NS = "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
|
|
149
155
|
// ============================================================================
|
|
150
156
|
// Parser States
|
|
151
157
|
// ============================================================================
|
|
@@ -206,6 +212,8 @@ class SaxesParser {
|
|
|
206
212
|
this.chunkPosition = 0;
|
|
207
213
|
// Entity storage
|
|
208
214
|
this.ENTITIES = { ...XML_ENTITIES };
|
|
215
|
+
// HAN CELL compatibility: spreadsheetml namespace prefix (e.g., "x")
|
|
216
|
+
this.nsPrefix = null;
|
|
209
217
|
this.trackPosition = opt?.position !== false;
|
|
210
218
|
this.fileName = opt?.fileName;
|
|
211
219
|
this.fragment = opt?.fragment ?? false;
|
|
@@ -240,6 +248,14 @@ class SaxesParser {
|
|
|
240
248
|
this.chunk = "";
|
|
241
249
|
this.i = 0;
|
|
242
250
|
this.prevI = 0;
|
|
251
|
+
this.nsPrefix = null;
|
|
252
|
+
}
|
|
253
|
+
// Strip HAN CELL namespace prefixes from element names
|
|
254
|
+
stripNsPrefix(name) {
|
|
255
|
+
const n = name.replace(HAN_CELL_PREFIXES, "");
|
|
256
|
+
return this.nsPrefix && n.startsWith(this.nsPrefix + ":")
|
|
257
|
+
? n.slice(this.nsPrefix.length + 1)
|
|
258
|
+
: n;
|
|
243
259
|
}
|
|
244
260
|
on(name, handler) {
|
|
245
261
|
switch (name) {
|
|
@@ -655,9 +671,8 @@ class SaxesParser {
|
|
|
655
671
|
this.name += charFromCode(c);
|
|
656
672
|
return;
|
|
657
673
|
}
|
|
658
|
-
// Tag name complete
|
|
659
674
|
this.tag = {
|
|
660
|
-
name: this.name,
|
|
675
|
+
name: this.stripNsPrefix(this.name),
|
|
661
676
|
attributes: Object.create(null),
|
|
662
677
|
isSelfClosing: false
|
|
663
678
|
};
|
|
@@ -1096,11 +1111,7 @@ class SaxesParser {
|
|
|
1096
1111
|
openTag() {
|
|
1097
1112
|
const tag = this.tag;
|
|
1098
1113
|
tag.isSelfClosing = false;
|
|
1099
|
-
|
|
1100
|
-
for (const { name, value } of this.attribList) {
|
|
1101
|
-
tag.attributes[name] = value;
|
|
1102
|
-
}
|
|
1103
|
-
this.attribList = [];
|
|
1114
|
+
this.processAttributes(tag);
|
|
1104
1115
|
this.openTagHandler?.(tag);
|
|
1105
1116
|
this.tags.push(tag);
|
|
1106
1117
|
this.name = "";
|
|
@@ -1109,11 +1120,7 @@ class SaxesParser {
|
|
|
1109
1120
|
openSelfClosingTag() {
|
|
1110
1121
|
const tag = this.tag;
|
|
1111
1122
|
tag.isSelfClosing = true;
|
|
1112
|
-
|
|
1113
|
-
for (const { name, value } of this.attribList) {
|
|
1114
|
-
tag.attributes[name] = value;
|
|
1115
|
-
}
|
|
1116
|
-
this.attribList = [];
|
|
1123
|
+
this.processAttributes(tag);
|
|
1117
1124
|
this.openTagHandler?.(tag);
|
|
1118
1125
|
this.closeTagHandler?.(tag);
|
|
1119
1126
|
if (this.tags.length === 0) {
|
|
@@ -1122,8 +1129,20 @@ class SaxesParser {
|
|
|
1122
1129
|
this.name = "";
|
|
1123
1130
|
this.state = S_TEXT;
|
|
1124
1131
|
}
|
|
1132
|
+
// Process attributes and detect spreadsheetml namespace prefix
|
|
1133
|
+
processAttributes(tag) {
|
|
1134
|
+
for (const { name, value } of this.attribList) {
|
|
1135
|
+
tag.attributes[name] = value;
|
|
1136
|
+
if (name.startsWith("xmlns:") && value === SPREADSHEETML_NS) {
|
|
1137
|
+
this.nsPrefix = name.slice(6);
|
|
1138
|
+
tag.name = this.stripNsPrefix(tag.name);
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
this.attribList = [];
|
|
1142
|
+
}
|
|
1125
1143
|
closeTag() {
|
|
1126
|
-
const { tags
|
|
1144
|
+
const { tags } = this;
|
|
1145
|
+
const name = this.stripNsPrefix(this.name);
|
|
1127
1146
|
this.state = S_TEXT;
|
|
1128
1147
|
this.name = "";
|
|
1129
1148
|
if (name === "") {
|
|
@@ -52,8 +52,11 @@ class Worksheet {
|
|
|
52
52
|
this.pageSetup = Object.assign({}, {
|
|
53
53
|
margins: { left: 0.7, right: 0.7, top: 0.75, bottom: 0.75, header: 0.3, footer: 0.3 },
|
|
54
54
|
orientation: "portrait",
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
// Excel does not normally write these unless explicitly set.
|
|
56
|
+
// Historically we used 4294967295 as a sentinel when parsing, but emitting it
|
|
57
|
+
// can cause strict Excel parsers to treat the workbook as corrupted.
|
|
58
|
+
horizontalDpi: undefined,
|
|
59
|
+
verticalDpi: undefined,
|
|
57
60
|
fitToPage: !!(options.pageSetup &&
|
|
58
61
|
(options.pageSetup.fitToWidth || options.pageSetup.fitToHeight) &&
|
|
59
62
|
!options.pageSetup.scale),
|
|
@@ -13,7 +13,7 @@ class AppXform extends base_xform_1.BaseXform {
|
|
|
13
13
|
Company: new string_xform_1.StringXform({ tag: "Company" }),
|
|
14
14
|
Manager: new string_xform_1.StringXform({ tag: "Manager" }),
|
|
15
15
|
HeadingPairs: new app_heading_pairs_xform_1.AppHeadingPairsXform(),
|
|
16
|
-
|
|
16
|
+
TitlesOfParts: new app_titles_of_parts_xform_1.AppTitlesOfPartsXform()
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
19
|
render(xmlStream, model) {
|
|
@@ -23,7 +23,7 @@ class AppXform extends base_xform_1.BaseXform {
|
|
|
23
23
|
xmlStream.leafNode("DocSecurity", undefined, "0");
|
|
24
24
|
xmlStream.leafNode("ScaleCrop", undefined, "false");
|
|
25
25
|
this.map.HeadingPairs.render(xmlStream, model.worksheets);
|
|
26
|
-
this.map.
|
|
26
|
+
this.map.TitlesOfParts.render(xmlStream, model.worksheets);
|
|
27
27
|
this.map.Company.render(xmlStream, model.company || "");
|
|
28
28
|
this.map.Manager.render(xmlStream, model.manager);
|
|
29
29
|
xmlStream.leafNode("LinksUpToDate", undefined, "false");
|
|
@@ -65,7 +65,7 @@ class AppXform extends base_xform_1.BaseXform {
|
|
|
65
65
|
switch (name) {
|
|
66
66
|
case "Properties":
|
|
67
67
|
this.model = {
|
|
68
|
-
worksheets: this.map.
|
|
68
|
+
worksheets: this.map.TitlesOfParts.model,
|
|
69
69
|
company: this.map.Company.model,
|
|
70
70
|
manager: this.map.Manager.model
|
|
71
71
|
};
|