omni-viewer-core 0.1.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/LICENSE +21 -0
- package/README.md +80 -0
- package/THIRD_PARTY_NOTICES.md +49 -0
- package/dist/assets/audio-engine/audio_engine.mjs +2 -0
- package/dist/assets/audio-engine/audio_engine.wasm +0 -0
- package/dist/assets/pdfjs/pdf.worker.min.mjs +21 -0
- package/dist/host/index.d.ts +133 -0
- package/dist/host/index.js +10 -0
- package/dist/i18n/catalog.en.d.ts +1 -0
- package/dist/i18n/catalog.en.js +411 -0
- package/dist/i18n/catalog.ko.d.ts +1 -0
- package/dist/i18n/catalog.ko.js +68 -0
- package/dist/i18n/index.d.ts +18 -0
- package/dist/i18n/index.js +41 -0
- package/dist/licenses/emscripten.txt +101 -0
- package/dist/licenses/kissfft.txt +28 -0
- package/dist/licenses/pdfjs-dist.txt +177 -0
- package/dist/parsers/a2l/index.d.ts +1 -0
- package/dist/parsers/a2l/index.js +1 -0
- package/dist/parsers/archive/index.d.ts +22 -0
- package/dist/parsers/archive/index.js +46 -0
- package/dist/parsers/archive/model.d.ts +29 -0
- package/dist/parsers/archive/model.js +1 -0
- package/dist/parsers/archive/worker.d.ts +21 -0
- package/dist/parsers/archive/worker.js +1 -0
- package/dist/parsers/arxml/index.d.ts +1 -0
- package/dist/parsers/arxml/index.js +1 -0
- package/dist/parsers/asc/index.d.ts +1 -0
- package/dist/parsers/asc/index.js +1 -0
- package/dist/parsers/audio/index.d.ts +9 -0
- package/dist/parsers/audio/index.js +46 -0
- package/dist/parsers/automotive/index.d.ts +33 -0
- package/dist/parsers/automotive/index.js +973 -0
- package/dist/parsers/blf/index.d.ts +1 -0
- package/dist/parsers/blf/index.js +1 -0
- package/dist/parsers/csv/delimiter.d.ts +31 -0
- package/dist/parsers/csv/delimiter.js +151 -0
- package/dist/parsers/csv/index.d.ts +30 -0
- package/dist/parsers/csv/index.js +108 -0
- package/dist/parsers/csv/parseText.d.ts +20 -0
- package/dist/parsers/csv/parseText.js +108 -0
- package/dist/parsers/csv/sort.d.ts +26 -0
- package/dist/parsers/csv/sort.js +224 -0
- package/dist/parsers/csv/statistics.d.ts +49 -0
- package/dist/parsers/csv/statistics.js +150 -0
- package/dist/parsers/dbc/index.d.ts +56 -0
- package/dist/parsers/dbc/index.js +94 -0
- package/dist/parsers/doc-binary/index.d.ts +35 -0
- package/dist/parsers/doc-binary/index.js +267 -0
- package/dist/parsers/doc-binary/vscode-parser.d.ts +142 -0
- package/dist/parsers/doc-binary/vscode-parser.js +3747 -0
- package/dist/parsers/excel/index.d.ts +17 -0
- package/dist/parsers/excel/index.js +406 -0
- package/dist/parsers/excel/model.d.ts +80 -0
- package/dist/parsers/excel/model.js +48 -0
- package/dist/parsers/excel/serialize.d.ts +3 -0
- package/dist/parsers/excel/serialize.js +78 -0
- package/dist/parsers/hdf5/index.d.ts +75 -0
- package/dist/parsers/hdf5/index.js +658 -0
- package/dist/parsers/hdf5/node.d.ts +21 -0
- package/dist/parsers/hdf5/node.js +95 -0
- package/dist/parsers/json/index.d.ts +15 -0
- package/dist/parsers/json/index.js +102 -0
- package/dist/parsers/json/model.d.ts +42 -0
- package/dist/parsers/json/model.js +140 -0
- package/dist/parsers/json/parse.d.ts +22 -0
- package/dist/parsers/json/parse.js +279 -0
- package/dist/parsers/json/tokenizer.d.ts +17 -0
- package/dist/parsers/json/tokenizer.js +175 -0
- package/dist/parsers/jsonl/index.d.ts +14 -0
- package/dist/parsers/jsonl/index.js +18 -0
- package/dist/parsers/jsonl/model.d.ts +23 -0
- package/dist/parsers/jsonl/model.js +1 -0
- package/dist/parsers/jsonl/parse.d.ts +7 -0
- package/dist/parsers/jsonl/parse.js +34 -0
- package/dist/parsers/markdown/index.d.ts +37 -0
- package/dist/parsers/markdown/index.js +59 -0
- package/dist/parsers/mat/index.d.ts +31 -0
- package/dist/parsers/mat/index.js +426 -0
- package/dist/parsers/mermaid/index.d.ts +5 -0
- package/dist/parsers/mermaid/index.js +3 -0
- package/dist/parsers/mf4/index.d.ts +1 -0
- package/dist/parsers/mf4/index.js +1 -0
- package/dist/parsers/parquet/index.d.ts +21 -0
- package/dist/parsers/parquet/index.js +68 -0
- package/dist/parsers/pcap/index.d.ts +1 -0
- package/dist/parsers/pcap/index.js +1 -0
- package/dist/parsers/pcapng/index.d.ts +1 -0
- package/dist/parsers/pcapng/index.js +1 -0
- package/dist/parsers/plantuml/index.d.ts +5 -0
- package/dist/parsers/plantuml/index.js +4 -0
- package/dist/parsers/ppt-binary/index.d.ts +5 -0
- package/dist/parsers/ppt-binary/index.js +62 -0
- package/dist/parsers/ppt-binary/pptBinaryBuildContext.d.ts +12 -0
- package/dist/parsers/ppt-binary/pptBinaryBuildContext.js +40 -0
- package/dist/parsers/ppt-binary/pptBinaryContainer.d.ts +4 -0
- package/dist/parsers/ppt-binary/pptBinaryContainer.js +174 -0
- package/dist/parsers/ppt-binary/pptBinaryTypes.d.ts +125 -0
- package/dist/parsers/ppt-binary/pptBinaryTypes.js +1 -0
- package/dist/parsers/ppt-binary/pptSlideLayouts.d.ts +9 -0
- package/dist/parsers/ppt-binary/pptSlideLayouts.js +510 -0
- package/dist/parsers/ppt-binary/vscode-parser.d.ts +116 -0
- package/dist/parsers/ppt-binary/vscode-parser.js +2853 -0
- package/dist/parsers/ppt-binary/vscode.d.ts +4 -0
- package/dist/parsers/ppt-binary/vscode.js +12 -0
- package/dist/parsers/pptx/index.d.ts +15 -0
- package/dist/parsers/pptx/index.js +88 -0
- package/dist/parsers/pptx/vscode-parser.d.ts +167 -0
- package/dist/parsers/pptx/vscode-parser.js +1503 -0
- package/dist/parsers/pptx/vscode.d.ts +4 -0
- package/dist/parsers/pptx/vscode.js +18 -0
- package/dist/parsers/pptx/zip-reader.d.ts +3 -0
- package/dist/parsers/pptx/zip-reader.js +20 -0
- package/dist/parsers/proto/index.d.ts +90 -0
- package/dist/parsers/proto/index.js +197 -0
- package/dist/parsers/psd/index.d.ts +16 -0
- package/dist/parsers/psd/index.js +10 -0
- package/dist/parsers/shapefile/dbf.d.ts +23 -0
- package/dist/parsers/shapefile/dbf.js +112 -0
- package/dist/parsers/shapefile/index.d.ts +44 -0
- package/dist/parsers/shapefile/index.js +119 -0
- package/dist/parsers/shapefile/reproject.d.ts +10 -0
- package/dist/parsers/shapefile/reproject.js +41 -0
- package/dist/parsers/slide-model.d.ts +106 -0
- package/dist/parsers/slide-model.js +1 -0
- package/dist/parsers/toml/index.d.ts +15 -0
- package/dist/parsers/toml/index.js +18 -0
- package/dist/parsers/toml/model.d.ts +15 -0
- package/dist/parsers/toml/model.js +1 -0
- package/dist/parsers/toml/parse.d.ts +16 -0
- package/dist/parsers/toml/parse.js +266 -0
- package/dist/parsers/types.d.ts +103 -0
- package/dist/parsers/types.js +106 -0
- package/dist/parsers/video/index.d.ts +6 -0
- package/dist/parsers/video/index.js +2 -0
- package/dist/parsers/yaml/index.d.ts +16 -0
- package/dist/parsers/yaml/index.js +58 -0
- package/dist/parsers/yaml/model.d.ts +22 -0
- package/dist/parsers/yaml/model.js +1 -0
- package/dist/parsers/yaml/self-loading.d.ts +3 -0
- package/dist/parsers/yaml/self-loading.js +19 -0
- package/dist/parsers/zip-scan.d.ts +8 -0
- package/dist/parsers/zip-scan.js +80 -0
- package/dist/registry/container.d.ts +17 -0
- package/dist/registry/container.js +49 -0
- package/dist/registry/index.d.ts +133 -0
- package/dist/registry/index.js +348 -0
- package/dist/registry/probe.d.ts +3 -0
- package/dist/registry/probe.js +177 -0
- package/dist/registry/sniff.d.ts +17 -0
- package/dist/registry/sniff.js +57 -0
- package/dist/styles/archive.css +10 -0
- package/dist/styles/audio.css +66 -0
- package/dist/styles/automotive.css +1 -0
- package/dist/styles/csv.css +262 -0
- package/dist/styles/excel.css +212 -0
- package/dist/styles/fallback.css +48 -0
- package/dist/styles/hdf5.css +3 -0
- package/dist/styles/hwp.css +12 -0
- package/dist/styles/image.css +115 -0
- package/dist/styles/json.css +342 -0
- package/dist/styles/jsonl.css +1 -0
- package/dist/styles/markdown.css +10 -0
- package/dist/styles/mat.css +2 -0
- package/dist/styles/mermaid.css +1 -0
- package/dist/styles/parquet.css +2 -0
- package/dist/styles/pdf.css +551 -0
- package/dist/styles/plantuml.css +1 -0
- package/dist/styles/ppt.css +1 -0
- package/dist/styles/proto.css +2 -0
- package/dist/styles/psd.css +1 -0
- package/dist/styles/shapefile.css +25 -0
- package/dist/styles/toml.css +1 -0
- package/dist/styles/video.css +74 -0
- package/dist/styles/word.css +685 -0
- package/dist/styles/yaml.css +1 -0
- package/dist/viewers/a2l/index.d.ts +15 -0
- package/dist/viewers/a2l/index.js +5 -0
- package/dist/viewers/archive/archive-preview-decoder.d.ts +4 -0
- package/dist/viewers/archive/archive-preview-decoder.js +223 -0
- package/dist/viewers/archive/controller.d.ts +24 -0
- package/dist/viewers/archive/controller.js +18 -0
- package/dist/viewers/archive/index.d.ts +25 -0
- package/dist/viewers/archive/index.js +230 -0
- package/dist/viewers/archive/preview.d.ts +2 -0
- package/dist/viewers/archive/preview.js +2 -0
- package/dist/viewers/archive/styles.d.ts +1 -0
- package/dist/viewers/archive/styles.js +11 -0
- package/dist/viewers/arxml/index.d.ts +15 -0
- package/dist/viewers/arxml/index.js +5 -0
- package/dist/viewers/asc/index.d.ts +15 -0
- package/dist/viewers/asc/index.js +5 -0
- package/dist/viewers/audio/controller.d.ts +33 -0
- package/dist/viewers/audio/controller.js +49 -0
- package/dist/viewers/audio/engine.d.ts +44 -0
- package/dist/viewers/audio/engine.js +108 -0
- package/dist/viewers/audio/index.d.ts +92 -0
- package/dist/viewers/audio/index.js +448 -0
- package/dist/viewers/audio/self-loading.d.ts +7 -0
- package/dist/viewers/audio/self-loading.js +42 -0
- package/dist/viewers/audio/styles.d.ts +1 -0
- package/dist/viewers/audio/styles.js +73 -0
- package/dist/viewers/automotive/index.d.ts +8 -0
- package/dist/viewers/automotive/index.js +152 -0
- package/dist/viewers/automotive/styles.d.ts +1 -0
- package/dist/viewers/automotive/styles.js +1 -0
- package/dist/viewers/avro/index.d.ts +15 -0
- package/dist/viewers/avro/index.js +5 -0
- package/dist/viewers/bag/index.d.ts +15 -0
- package/dist/viewers/bag/index.js +5 -0
- package/dist/viewers/blf/index.d.ts +15 -0
- package/dist/viewers/blf/index.js +5 -0
- package/dist/viewers/csv/controller.d.ts +126 -0
- package/dist/viewers/csv/controller.js +598 -0
- package/dist/viewers/csv/index.d.ts +48 -0
- package/dist/viewers/csv/index.js +846 -0
- package/dist/viewers/csv/styles.d.ts +1 -0
- package/dist/viewers/csv/styles.js +267 -0
- package/dist/viewers/db3/index.d.ts +15 -0
- package/dist/viewers/db3/index.js +5 -0
- package/dist/viewers/dbc/index.d.ts +15 -0
- package/dist/viewers/dbc/index.js +5 -0
- package/dist/viewers/diagram.d.ts +14 -0
- package/dist/viewers/diagram.js +95 -0
- package/dist/viewers/excel/controller.d.ts +108 -0
- package/dist/viewers/excel/controller.js +548 -0
- package/dist/viewers/excel/grid.d.ts +28 -0
- package/dist/viewers/excel/grid.js +99 -0
- package/dist/viewers/excel/index.d.ts +37 -0
- package/dist/viewers/excel/index.js +620 -0
- package/dist/viewers/excel/self-loading.d.ts +6 -0
- package/dist/viewers/excel/self-loading.js +13 -0
- package/dist/viewers/excel/styles.d.ts +1 -0
- package/dist/viewers/excel/styles.js +216 -0
- package/dist/viewers/fallback/index.d.ts +17 -0
- package/dist/viewers/fallback/index.js +130 -0
- package/dist/viewers/fallback/styles.d.ts +1 -0
- package/dist/viewers/fallback/styles.js +49 -0
- package/dist/viewers/hdf5/index.d.ts +19 -0
- package/dist/viewers/hdf5/index.js +170 -0
- package/dist/viewers/hdf5/styles.d.ts +1 -0
- package/dist/viewers/hdf5/styles.js +4 -0
- package/dist/viewers/hwp/controller.d.ts +19 -0
- package/dist/viewers/hwp/controller.js +22 -0
- package/dist/viewers/hwp/index.d.ts +39 -0
- package/dist/viewers/hwp/index.js +271 -0
- package/dist/viewers/hwp/self-loading.d.ts +2 -0
- package/dist/viewers/hwp/self-loading.js +6 -0
- package/dist/viewers/hwp/styles.d.ts +1 -0
- package/dist/viewers/hwp/styles.js +13 -0
- package/dist/viewers/image/controller.d.ts +210 -0
- package/dist/viewers/image/controller.js +336 -0
- package/dist/viewers/image/decode.d.ts +54 -0
- package/dist/viewers/image/decode.js +150 -0
- package/dist/viewers/image/index.d.ts +38 -0
- package/dist/viewers/image/index.js +749 -0
- package/dist/viewers/image/limits.d.ts +18 -0
- package/dist/viewers/image/limits.js +20 -0
- package/dist/viewers/image/styles.d.ts +1 -0
- package/dist/viewers/image/styles.js +119 -0
- package/dist/viewers/json/controller.d.ts +89 -0
- package/dist/viewers/json/controller.js +411 -0
- package/dist/viewers/json/index.d.ts +25 -0
- package/dist/viewers/json/index.js +503 -0
- package/dist/viewers/json/styles.d.ts +1 -0
- package/dist/viewers/json/styles.js +346 -0
- package/dist/viewers/json/transforms.d.ts +27 -0
- package/dist/viewers/json/transforms.js +253 -0
- package/dist/viewers/jsonl/controller.d.ts +48 -0
- package/dist/viewers/jsonl/controller.js +136 -0
- package/dist/viewers/jsonl/index.d.ts +22 -0
- package/dist/viewers/jsonl/index.js +184 -0
- package/dist/viewers/jsonl/styles.d.ts +1 -0
- package/dist/viewers/jsonl/styles.js +1 -0
- package/dist/viewers/markdown/controller.d.ts +50 -0
- package/dist/viewers/markdown/controller.js +62 -0
- package/dist/viewers/markdown/index.d.ts +64 -0
- package/dist/viewers/markdown/index.js +425 -0
- package/dist/viewers/markdown/math.d.ts +21 -0
- package/dist/viewers/markdown/math.js +100 -0
- package/dist/viewers/markdown/self-loading.d.ts +5 -0
- package/dist/viewers/markdown/self-loading.js +48 -0
- package/dist/viewers/markdown/styles.d.ts +1 -0
- package/dist/viewers/markdown/styles.js +11 -0
- package/dist/viewers/mat/index.d.ts +19 -0
- package/dist/viewers/mat/index.js +113 -0
- package/dist/viewers/mat/styles.d.ts +1 -0
- package/dist/viewers/mat/styles.js +3 -0
- package/dist/viewers/media.d.ts +9 -0
- package/dist/viewers/media.js +77 -0
- package/dist/viewers/mermaid/index.d.ts +17 -0
- package/dist/viewers/mermaid/index.js +6 -0
- package/dist/viewers/mermaid/self-loading.d.ts +2 -0
- package/dist/viewers/mermaid/self-loading.js +2 -0
- package/dist/viewers/mf4/index.d.ts +15 -0
- package/dist/viewers/mf4/index.js +5 -0
- package/dist/viewers/parquet/controller.d.ts +37 -0
- package/dist/viewers/parquet/controller.js +26 -0
- package/dist/viewers/parquet/index.d.ts +23 -0
- package/dist/viewers/parquet/index.js +134 -0
- package/dist/viewers/parquet/styles.d.ts +1 -0
- package/dist/viewers/parquet/styles.js +3 -0
- package/dist/viewers/pcap/index.d.ts +15 -0
- package/dist/viewers/pcap/index.js +5 -0
- package/dist/viewers/pcapng/index.d.ts +15 -0
- package/dist/viewers/pcapng/index.js +5 -0
- package/dist/viewers/pdf/controller.d.ts +120 -0
- package/dist/viewers/pdf/controller.js +191 -0
- package/dist/viewers/pdf/editing.d.ts +26 -0
- package/dist/viewers/pdf/editing.js +265 -0
- package/dist/viewers/pdf/index.d.ts +88 -0
- package/dist/viewers/pdf/index.js +1400 -0
- package/dist/viewers/pdf/self-loading.d.ts +6 -0
- package/dist/viewers/pdf/self-loading.js +14 -0
- package/dist/viewers/pdf/styles.d.ts +1 -0
- package/dist/viewers/pdf/styles.js +555 -0
- package/dist/viewers/plantuml/index.d.ts +17 -0
- package/dist/viewers/plantuml/index.js +6 -0
- package/dist/viewers/plantuml/self-loading.d.ts +2 -0
- package/dist/viewers/plantuml/self-loading.js +2 -0
- package/dist/viewers/ppt/chart.d.ts +8 -0
- package/dist/viewers/ppt/chart.js +83 -0
- package/dist/viewers/ppt/controller.d.ts +34 -0
- package/dist/viewers/ppt/controller.js +27 -0
- package/dist/viewers/ppt/index.d.ts +33 -0
- package/dist/viewers/ppt/index.js +101 -0
- package/dist/viewers/ppt/render.d.ts +5 -0
- package/dist/viewers/ppt/render.js +144 -0
- package/dist/viewers/ppt/self-loading.d.ts +9 -0
- package/dist/viewers/ppt/self-loading.js +4 -0
- package/dist/viewers/ppt/styles.d.ts +1 -0
- package/dist/viewers/ppt/styles.js +1 -0
- package/dist/viewers/proto/index.d.ts +17 -0
- package/dist/viewers/proto/index.js +170 -0
- package/dist/viewers/proto/styles.d.ts +1 -0
- package/dist/viewers/proto/styles.js +2 -0
- package/dist/viewers/psd/index.d.ts +27 -0
- package/dist/viewers/psd/index.js +39 -0
- package/dist/viewers/psd/self-loading.d.ts +10 -0
- package/dist/viewers/psd/self-loading.js +20 -0
- package/dist/viewers/psd/styles.d.ts +1 -0
- package/dist/viewers/psd/styles.js +1 -0
- package/dist/viewers/reqif/index.d.ts +15 -0
- package/dist/viewers/reqif/index.js +5 -0
- package/dist/viewers/shapefile/index.d.ts +35 -0
- package/dist/viewers/shapefile/index.js +336 -0
- package/dist/viewers/shapefile/self-loading.d.ts +7 -0
- package/dist/viewers/shapefile/self-loading.js +32 -0
- package/dist/viewers/shapefile/styles.d.ts +1 -0
- package/dist/viewers/shapefile/styles.js +28 -0
- package/dist/viewers/stp/index.d.ts +15 -0
- package/dist/viewers/stp/index.js +5 -0
- package/dist/viewers/structured-styles.d.ts +1 -0
- package/dist/viewers/structured-styles.js +1 -0
- package/dist/viewers/structured.d.ts +36 -0
- package/dist/viewers/structured.js +138 -0
- package/dist/viewers/toml/controller.d.ts +38 -0
- package/dist/viewers/toml/controller.js +61 -0
- package/dist/viewers/toml/index.d.ts +18 -0
- package/dist/viewers/toml/index.js +34 -0
- package/dist/viewers/types.d.ts +36 -0
- package/dist/viewers/types.js +9 -0
- package/dist/viewers/video/controller.d.ts +29 -0
- package/dist/viewers/video/controller.js +54 -0
- package/dist/viewers/video/index.d.ts +18 -0
- package/dist/viewers/video/index.js +305 -0
- package/dist/viewers/video/styles.d.ts +1 -0
- package/dist/viewers/video/styles.js +79 -0
- package/dist/viewers/word/controller.d.ts +19 -0
- package/dist/viewers/word/controller.js +22 -0
- package/dist/viewers/word/docx-preprocess.d.ts +63 -0
- package/dist/viewers/word/docx-preprocess.js +136 -0
- package/dist/viewers/word/index.d.ts +32 -0
- package/dist/viewers/word/index.js +187 -0
- package/dist/viewers/word/normalize-docx.d.ts +2 -0
- package/dist/viewers/word/normalize-docx.js +50 -0
- package/dist/viewers/word/paginate.d.ts +1 -0
- package/dist/viewers/word/paginate.js +1035 -0
- package/dist/viewers/word/self-loading.d.ts +2 -0
- package/dist/viewers/word/self-loading.js +7 -0
- package/dist/viewers/word/styles.d.ts +1 -0
- package/dist/viewers/word/styles.js +686 -0
- package/dist/viewers/yaml/controller.d.ts +42 -0
- package/dist/viewers/yaml/controller.js +41 -0
- package/dist/viewers/yaml/index.d.ts +21 -0
- package/dist/viewers/yaml/index.js +20 -0
- package/package.json +375 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
export type ParseResult<T> = {
|
|
2
|
+
status: 'ok';
|
|
3
|
+
document: T;
|
|
4
|
+
diagnostics: Diagnostic[];
|
|
5
|
+
} | {
|
|
6
|
+
status: 'partial';
|
|
7
|
+
document: T;
|
|
8
|
+
diagnostics: Diagnostic[];
|
|
9
|
+
} | {
|
|
10
|
+
status: 'failed';
|
|
11
|
+
failure: ParseFailure;
|
|
12
|
+
diagnostics: Diagnostic[];
|
|
13
|
+
};
|
|
14
|
+
export interface ParseFailure {
|
|
15
|
+
code: 'invalid-format' | 'corrupted' | 'password-required' | 'limit-exceeded' | 'aborted' | 'missing-dependency';
|
|
16
|
+
/** true when retrying with amended input can succeed (e.g. password). */
|
|
17
|
+
retryable: boolean;
|
|
18
|
+
messageKey: string;
|
|
19
|
+
args?: Record<string, string | number>;
|
|
20
|
+
}
|
|
21
|
+
export interface Diagnostic {
|
|
22
|
+
severity: 'info' | 'warning' | 'error';
|
|
23
|
+
code: string;
|
|
24
|
+
/** Core catalog key (never inline English — ADR 17/28). */
|
|
25
|
+
messageKey: string;
|
|
26
|
+
args?: Record<string, string | number>;
|
|
27
|
+
/** Sheet name, page number, entry path, row range … */
|
|
28
|
+
location?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface ParseOutcome<T> {
|
|
31
|
+
/** Deterministic — the conformance kit compares this. */
|
|
32
|
+
result: ParseResult<T>;
|
|
33
|
+
/** Environment-dependent — logging / UI badges only, never compared. */
|
|
34
|
+
execution: ExecutionReport;
|
|
35
|
+
}
|
|
36
|
+
export interface ExecutionReport {
|
|
37
|
+
workerUsed: boolean;
|
|
38
|
+
/** false = only cooperative (soft) limits applied in this environment. */
|
|
39
|
+
hardLimitEnforced: boolean;
|
|
40
|
+
elapsedMillis: number;
|
|
41
|
+
}
|
|
42
|
+
export interface ParseOptions {
|
|
43
|
+
/** Cooperative cancellation — parsers check this at loop checkpoints. */
|
|
44
|
+
signal?: AbortSignal;
|
|
45
|
+
limits?: ResourceLimits;
|
|
46
|
+
}
|
|
47
|
+
export interface ResourceLimits {
|
|
48
|
+
/** Enforced by the platform before loading bytes; parsers re-check as 2nd defense. */
|
|
49
|
+
maxInputBytes?: number;
|
|
50
|
+
/** Cumulative decompressed output cap (zip bomb defense). */
|
|
51
|
+
maxDecompressedBytes?: number;
|
|
52
|
+
/** Archive entries / table rows cap. */
|
|
53
|
+
maxEntries?: number;
|
|
54
|
+
/** Soft unless running in a terminatable Worker (DESIGN.md §3-①). */
|
|
55
|
+
maxParseMillis?: number;
|
|
56
|
+
}
|
|
57
|
+
/** Core-defined default limits; platforms may override per call. */
|
|
58
|
+
export declare const DEFAULT_LIMITS: Required<Pick<ResourceLimits, 'maxInputBytes' | 'maxEntries' | 'maxParseMillis'>>;
|
|
59
|
+
export type LimitViolation = {
|
|
60
|
+
kind: 'aborted';
|
|
61
|
+
} | {
|
|
62
|
+
kind: 'time';
|
|
63
|
+
elapsedMillis: number;
|
|
64
|
+
} | {
|
|
65
|
+
kind: 'entries';
|
|
66
|
+
count: number;
|
|
67
|
+
} | {
|
|
68
|
+
kind: 'decompressed';
|
|
69
|
+
bytes: number;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Cooperative limit tracking (DESIGN.md §3-①). Parsers call `checkpoint()`
|
|
73
|
+
* inside their record/row/chunk loops; a non-null return means stop now and
|
|
74
|
+
* report `partial`/`failed`. Wall-clock use is confined to the tracker so the
|
|
75
|
+
* *decision points* stay deterministic given the same limits and inputs
|
|
76
|
+
* (time-based stops are inherently soft and excluded from conformance
|
|
77
|
+
* fixtures).
|
|
78
|
+
*/
|
|
79
|
+
export declare class LimitTracker {
|
|
80
|
+
private readonly limits;
|
|
81
|
+
private readonly signal?;
|
|
82
|
+
private readonly startedAt;
|
|
83
|
+
private entries;
|
|
84
|
+
private decompressedBytes;
|
|
85
|
+
constructor(limits: ResourceLimits, signal?: AbortSignal | undefined);
|
|
86
|
+
addEntries(n: number): void;
|
|
87
|
+
addDecompressedBytes(n: number): void;
|
|
88
|
+
get entryCount(): number;
|
|
89
|
+
elapsedMillis(): number;
|
|
90
|
+
checkpoint(): LimitViolation | null;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Decode input bytes as UTF-8 text (BOM tolerated; parseCsv strips it).
|
|
94
|
+
* UTF-8 decoding is deterministic across engines. Legacy encodings must use
|
|
95
|
+
* core-bundled decoding tables instead (determinism rule, ADR 41).
|
|
96
|
+
*/
|
|
97
|
+
export declare function decodeUtf8(data: Uint8Array): string;
|
|
98
|
+
/**
|
|
99
|
+
* UTF-8 byte length of a string without allocating an encoded copy, so
|
|
100
|
+
* `maxInputBytes` can be enforced for string inputs too (allocating via
|
|
101
|
+
* TextEncoder would defeat the memory cap being checked).
|
|
102
|
+
*/
|
|
103
|
+
export declare function utf8ByteLength(text: string): number;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Parser layer contracts (DESIGN.md §3-①).
|
|
2
|
+
//
|
|
3
|
+
// Determinism rule: same bytes + same ParseOptions => identical ParseResult
|
|
4
|
+
// (including diagnostics) on every platform and engine. Environment-dependent
|
|
5
|
+
// information lives in ExecutionReport, which conformance comparison ignores
|
|
6
|
+
// (ADR 34). Input-caused failures never throw — they are `status: 'failed'`;
|
|
7
|
+
// throwing is reserved for programmer errors.
|
|
8
|
+
/** Core-defined default limits; platforms may override per call. */
|
|
9
|
+
export const DEFAULT_LIMITS = {
|
|
10
|
+
maxInputBytes: 256 * 1024 * 1024,
|
|
11
|
+
maxEntries: 1_000_000,
|
|
12
|
+
maxParseMillis: 30_000
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Cooperative limit tracking (DESIGN.md §3-①). Parsers call `checkpoint()`
|
|
16
|
+
* inside their record/row/chunk loops; a non-null return means stop now and
|
|
17
|
+
* report `partial`/`failed`. Wall-clock use is confined to the tracker so the
|
|
18
|
+
* *decision points* stay deterministic given the same limits and inputs
|
|
19
|
+
* (time-based stops are inherently soft and excluded from conformance
|
|
20
|
+
* fixtures).
|
|
21
|
+
*/
|
|
22
|
+
export class LimitTracker {
|
|
23
|
+
limits;
|
|
24
|
+
signal;
|
|
25
|
+
startedAt;
|
|
26
|
+
entries = 0;
|
|
27
|
+
decompressedBytes = 0;
|
|
28
|
+
constructor(limits, signal) {
|
|
29
|
+
this.limits = limits;
|
|
30
|
+
this.signal = signal;
|
|
31
|
+
this.startedAt = Date.now();
|
|
32
|
+
}
|
|
33
|
+
addEntries(n) {
|
|
34
|
+
this.entries += n;
|
|
35
|
+
}
|
|
36
|
+
addDecompressedBytes(n) {
|
|
37
|
+
this.decompressedBytes += n;
|
|
38
|
+
}
|
|
39
|
+
get entryCount() {
|
|
40
|
+
return this.entries;
|
|
41
|
+
}
|
|
42
|
+
elapsedMillis() {
|
|
43
|
+
return Date.now() - this.startedAt;
|
|
44
|
+
}
|
|
45
|
+
checkpoint() {
|
|
46
|
+
if (this.signal?.aborted)
|
|
47
|
+
return { kind: 'aborted' };
|
|
48
|
+
if (this.limits.maxEntries !== undefined &&
|
|
49
|
+
this.entries > this.limits.maxEntries) {
|
|
50
|
+
return { kind: 'entries', count: this.entries };
|
|
51
|
+
}
|
|
52
|
+
if (this.limits.maxDecompressedBytes !== undefined &&
|
|
53
|
+
this.decompressedBytes > this.limits.maxDecompressedBytes) {
|
|
54
|
+
return { kind: 'decompressed', bytes: this.decompressedBytes };
|
|
55
|
+
}
|
|
56
|
+
if (this.limits.maxParseMillis !== undefined) {
|
|
57
|
+
const elapsed = this.elapsedMillis();
|
|
58
|
+
if (elapsed > this.limits.maxParseMillis) {
|
|
59
|
+
return { kind: 'time', elapsedMillis: elapsed };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Decode input bytes as UTF-8 text (BOM tolerated; parseCsv strips it).
|
|
67
|
+
* UTF-8 decoding is deterministic across engines. Legacy encodings must use
|
|
68
|
+
* core-bundled decoding tables instead (determinism rule, ADR 41).
|
|
69
|
+
*/
|
|
70
|
+
export function decodeUtf8(data) {
|
|
71
|
+
return new TextDecoder('utf-8', { fatal: false }).decode(data);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* UTF-8 byte length of a string without allocating an encoded copy, so
|
|
75
|
+
* `maxInputBytes` can be enforced for string inputs too (allocating via
|
|
76
|
+
* TextEncoder would defeat the memory cap being checked).
|
|
77
|
+
*/
|
|
78
|
+
export function utf8ByteLength(text) {
|
|
79
|
+
let bytes = 0;
|
|
80
|
+
for (let i = 0; i < text.length; i++) {
|
|
81
|
+
const code = text.charCodeAt(i);
|
|
82
|
+
if (code < 0x80) {
|
|
83
|
+
bytes += 1;
|
|
84
|
+
}
|
|
85
|
+
else if (code < 0x800) {
|
|
86
|
+
bytes += 2;
|
|
87
|
+
}
|
|
88
|
+
else if (code >= 0xd800 && code <= 0xdbff) {
|
|
89
|
+
// Surrogate pair -> one 4-byte code point (lone surrogates encode
|
|
90
|
+
// as 3-byte replacement chars; counting 4 here is a safe upper
|
|
91
|
+
// bound only when the low surrogate follows, which we skip).
|
|
92
|
+
const next = text.charCodeAt(i + 1);
|
|
93
|
+
if (next >= 0xdc00 && next <= 0xdfff) {
|
|
94
|
+
bytes += 4;
|
|
95
|
+
i++;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
bytes += 3;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
bytes += 3;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return bytes;
|
|
106
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const MIME = { mp4: 'video/mp4', mts: 'video/mp2t', m2ts: 'video/mp2t', avi: 'video/x-msvideo', mov: 'video/quicktime', wmv: 'video/x-ms-wmv', flv: 'video/x-flv', webm: 'video/webm', mkv: 'video/x-matroska' };
|
|
2
|
+
export function parseVideoInfo(fileName, data) { const ext = fileName.toLowerCase().split('.').pop() ?? ''; return { format: ext.toUpperCase() || 'VIDEO', mimeType: MIME[ext] ?? 'application/octet-stream', warnings: data.byteLength ? [] : ['The video file is empty.'] }; }
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ParseOutcome } from '../types.js';
|
|
2
|
+
import type { YamlDocument, YamlParserDeps } from './model.js';
|
|
3
|
+
export * from './model.js';
|
|
4
|
+
export declare const YAML_INPUT_OWNERSHIP: "borrows";
|
|
5
|
+
export interface YamlParseOptions {
|
|
6
|
+
deps?: YamlParserDeps;
|
|
7
|
+
limits?: {
|
|
8
|
+
maxInputBytes?: number;
|
|
9
|
+
maxEntries?: number;
|
|
10
|
+
maxDocuments?: number;
|
|
11
|
+
maxDepth?: number;
|
|
12
|
+
maxAliases?: number;
|
|
13
|
+
};
|
|
14
|
+
signal?: AbortSignal;
|
|
15
|
+
}
|
|
16
|
+
export declare function parseYaml(input: Uint8Array | string, options?: YamlParseOptions): ParseOutcome<YamlDocument>;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { decodeUtf8, utf8ByteLength } from '../types.js';
|
|
2
|
+
export * from './model.js';
|
|
3
|
+
export const YAML_INPUT_OWNERSHIP = 'borrows';
|
|
4
|
+
export function parseYaml(input, options = {}) {
|
|
5
|
+
const started = Date.now();
|
|
6
|
+
const bytes = typeof input === 'string' ? utf8ByteLength(input) : input.byteLength;
|
|
7
|
+
const max = options.limits?.maxInputBytes ?? 10 * 1024 * 1024;
|
|
8
|
+
const finish = (result) => ({ result, execution: { workerUsed: false, hardLimitEnforced: false, elapsedMillis: Date.now() - started } });
|
|
9
|
+
if (bytes > max)
|
|
10
|
+
return finish({ status: 'failed', failure: { code: 'limit-exceeded', retryable: false, messageKey: 'diag.limit-exceeded.input', args: { maxBytes: max } }, diagnostics: [] });
|
|
11
|
+
if (options.signal?.aborted)
|
|
12
|
+
return finish({ status: 'failed', failure: { code: 'aborted', retryable: true, messageKey: 'diag.aborted' }, diagnostics: [] });
|
|
13
|
+
if (!options.deps)
|
|
14
|
+
return finish({ status: 'failed', failure: { code: 'missing-dependency', retryable: true, messageKey: 'diag.yaml.missing-dependency' }, diagnostics: [] });
|
|
15
|
+
const text = typeof input === 'string' ? input : decodeUtf8(input);
|
|
16
|
+
const diagnostics = [];
|
|
17
|
+
try {
|
|
18
|
+
const ast = options.deps.parse(text);
|
|
19
|
+
const maxDocs = options.limits?.maxDocuments ?? 100;
|
|
20
|
+
const docs = [];
|
|
21
|
+
for (let i = 0; i < Math.min(ast.length, maxDocs); i++) {
|
|
22
|
+
if (options.signal?.aborted)
|
|
23
|
+
return finish(docs.length ? { status: 'partial', document: { text, documents: docs }, diagnostics: [...diagnostics, { severity: 'warning', code: 'yaml.aborted', messageKey: 'diag.aborted' }] } : { status: 'failed', failure: { code: 'aborted', retryable: true, messageKey: 'diag.aborted' }, diagnostics });
|
|
24
|
+
const sourceDoc = ast[i];
|
|
25
|
+
const errors = sourceDoc?.errors ?? [];
|
|
26
|
+
for (const error of errors) {
|
|
27
|
+
const pos = error.linePos?.[0];
|
|
28
|
+
diagnostics.push({ severity: 'error', code: 'yaml.invalid', messageKey: 'diag.yaml.invalid', location: pos ? `line:${pos.line}:${pos.col}` : `document:${i + 1}` });
|
|
29
|
+
}
|
|
30
|
+
const node = options.deps.normalize ? options.deps.normalize(sourceDoc, i) : normalizeUnknown(sourceDoc, `$doc[${i}]`);
|
|
31
|
+
const violation = validateTree(node, options.limits?.maxEntries ?? 100_000, options.limits?.maxDepth ?? 100, options.limits?.maxAliases ?? 10_000);
|
|
32
|
+
if (violation)
|
|
33
|
+
diagnostics.push({ severity: 'warning', code: violation, messageKey: `diag.${violation}`, location: `document:${i + 1}` });
|
|
34
|
+
docs.push(node);
|
|
35
|
+
}
|
|
36
|
+
if (ast.length > maxDocs)
|
|
37
|
+
diagnostics.push({ severity: 'warning', code: 'yaml.document-limit', messageKey: 'diag.yaml.document-limit', args: { count: maxDocs } });
|
|
38
|
+
return finish(diagnostics.length ? { status: 'partial', document: { text, documents: docs }, diagnostics } : { status: 'ok', document: { text, documents: docs }, diagnostics });
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
return finish({ status: 'failed', failure: { code: 'invalid-format', retryable: false, messageKey: 'diag.yaml.invalid' }, diagnostics: [{ severity: 'error', code: 'yaml.invalid', messageKey: 'diag.yaml.invalid', location: String(error) }] });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function normalizeUnknown(value, path, key = '$') { if (Array.isArray(value))
|
|
45
|
+
return { kind: 'seq', key, path, children: value.map((v, i) => normalizeUnknown(v, `${path}[${i}]`, String(i))) }; if (value !== null && typeof value === 'object')
|
|
46
|
+
return { kind: 'map', key, path, children: Object.entries(value).map(([k, v]) => normalizeUnknown(v, `${path}.${k}`, k)) }; return { kind: 'scalar', key, path, value: value, raw: String(value) }; }
|
|
47
|
+
function validateTree(root, maxNodes, maxDepth, maxAliases) { let nodes = 0, aliases = 0; const stack = [{ node: root, depth: 0 }]; while (stack.length) {
|
|
48
|
+
const item = stack.pop();
|
|
49
|
+
nodes++;
|
|
50
|
+
if (nodes > maxNodes)
|
|
51
|
+
return 'yaml.node-limit';
|
|
52
|
+
if (item.depth > maxDepth)
|
|
53
|
+
return 'yaml.depth-limit';
|
|
54
|
+
if (item.node.kind === 'alias' && ++aliases > maxAliases)
|
|
55
|
+
return 'yaml.alias-limit';
|
|
56
|
+
for (const child of item.node.children ?? [])
|
|
57
|
+
stack.push({ node: child, depth: item.depth + 1 });
|
|
58
|
+
} return null; }
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type YamlNodeKind = 'map' | 'seq' | 'scalar' | 'alias';
|
|
2
|
+
export interface YamlNode {
|
|
3
|
+
kind: YamlNodeKind;
|
|
4
|
+
key: string;
|
|
5
|
+
path: string;
|
|
6
|
+
value?: string | boolean | number | null;
|
|
7
|
+
raw?: string;
|
|
8
|
+
range?: readonly number[] | undefined;
|
|
9
|
+
anchorId?: string | undefined;
|
|
10
|
+
aliasOf?: string;
|
|
11
|
+
children?: YamlNode[];
|
|
12
|
+
}
|
|
13
|
+
export interface YamlDocument {
|
|
14
|
+
text: string;
|
|
15
|
+
documents: YamlNode[];
|
|
16
|
+
}
|
|
17
|
+
export interface YamlParserDeps {
|
|
18
|
+
/** Must parse with YAML 1.2 core schema and return YAML AST documents. */
|
|
19
|
+
parse(text: string): readonly unknown[];
|
|
20
|
+
/** Optional platform adapter to normalize its YAML AST into safe core nodes. */
|
|
21
|
+
normalize?(document: unknown, index: number): YamlNode;
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Dynamic optional-peer loader; base parser/viewer modules never import yaml. */
|
|
2
|
+
export async function loadYamlParserDeps() {
|
|
3
|
+
const yaml = await import('yaml');
|
|
4
|
+
const normalize = (node, path, key = '$') => {
|
|
5
|
+
const n = node;
|
|
6
|
+
if (n.contents !== undefined)
|
|
7
|
+
return normalize(n.contents, path, key);
|
|
8
|
+
const common = { key, path, range: n.range, anchorId: n.anchor };
|
|
9
|
+
if (n.type === 'ALIAS' || n.constructor?.name === 'Alias')
|
|
10
|
+
return { ...common, kind: 'alias', aliasOf: String(n.source ?? '') };
|
|
11
|
+
if (Array.isArray(n.items)) {
|
|
12
|
+
const pairs = n.items.every(item => item && typeof item === 'object' && 'key' in item);
|
|
13
|
+
return { ...common, kind: pairs ? 'map' : 'seq', children: n.items.map((item, i) => { const pair = item; const childKey = pairs ? String(pair.key?.value ?? pair.key?.source ?? i) : String(i); return normalize(pairs ? pair.value : item, pairs ? `${path}.${childKey}` : `${path}[${i}]`, childKey); }) };
|
|
14
|
+
}
|
|
15
|
+
const value = n.value ?? node;
|
|
16
|
+
return { ...common, kind: 'scalar', value: value, raw: n.source === undefined ? String(value) : String(n.source) };
|
|
17
|
+
};
|
|
18
|
+
return { parse: (text) => yaml.parseAllDocuments(text, { schema: 'core', customTags: [] }), normalize: (document, index) => normalize(document, `$doc[${index}]`) };
|
|
19
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Total uncompressed bytes declared by a ZIP central directory.
|
|
3
|
+
*
|
|
4
|
+
* Returns `null` when the input is not recognizably a ZIP, and `Infinity` when
|
|
5
|
+
* ZIP64 markers or structural inconsistencies make the total unknowable from
|
|
6
|
+
* the 32-bit central-directory fields (always over any finite limit).
|
|
7
|
+
*/
|
|
8
|
+
export declare function declaredZipUncompressedBytes(input: Uint8Array): number | null;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Declared-size pre-scan for ZIP containers. Sums the uncompressed sizes
|
|
2
|
+
// recorded in the central directory *before* any inflate runs, so archives
|
|
3
|
+
// whose headers admit an oversized payload (zip bombs) are rejected up front
|
|
4
|
+
// without allocating anything. A crafted archive can lie in these fields, so
|
|
5
|
+
// callers that hand the bytes to a library with its own inflater (SheetJS,
|
|
6
|
+
// JSZip, docx-preview) must still treat this as a first line of defense, not
|
|
7
|
+
// a hard guarantee.
|
|
8
|
+
const EOCD = 0x06054b50;
|
|
9
|
+
const CDFH = 0x02014b50;
|
|
10
|
+
const LFH = 0x04034b50;
|
|
11
|
+
const CENTRAL_DIRECTORY_DIGITAL_SIGNATURE = 0x05054b50;
|
|
12
|
+
/** EOCD is 22 bytes plus a comment of at most 0xffff bytes. */
|
|
13
|
+
const EOCD_SEARCH_SPAN = 22 + 0xffff;
|
|
14
|
+
/**
|
|
15
|
+
* Total uncompressed bytes declared by a ZIP central directory.
|
|
16
|
+
*
|
|
17
|
+
* Returns `null` when the input is not recognizably a ZIP, and `Infinity` when
|
|
18
|
+
* ZIP64 markers or structural inconsistencies make the total unknowable from
|
|
19
|
+
* the 32-bit central-directory fields (always over any finite limit).
|
|
20
|
+
*/
|
|
21
|
+
export function declaredZipUncompressedBytes(input) {
|
|
22
|
+
if (input.length < 22)
|
|
23
|
+
return null;
|
|
24
|
+
const v = new DataView(input.buffer, input.byteOffset, input.byteLength);
|
|
25
|
+
let eocd = -1;
|
|
26
|
+
for (let p = input.length - 22; p >= Math.max(0, input.length - EOCD_SEARCH_SPAN); p--) {
|
|
27
|
+
if (v.getUint32(p, true) !== EOCD)
|
|
28
|
+
continue;
|
|
29
|
+
// A signature-like byte sequence inside the ZIP comment is not an EOCD.
|
|
30
|
+
// The real EOCD comment must end exactly at the end of the input.
|
|
31
|
+
if (p + 22 + v.getUint16(p + 20, true) !== input.length)
|
|
32
|
+
continue;
|
|
33
|
+
eocd = p;
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
if (eocd < 0) {
|
|
37
|
+
// Office ZIP containers start with a local-file header. If that marker
|
|
38
|
+
// is present but a complete EOCD is not, treat the truncated/malformed
|
|
39
|
+
// archive as unbounded instead of allowing the downstream inflater to
|
|
40
|
+
// decide whether it can recover partial entries.
|
|
41
|
+
return v.getUint32(0, true) === LFH ? Number.POSITIVE_INFINITY : null;
|
|
42
|
+
}
|
|
43
|
+
const disk = v.getUint16(eocd + 4, true);
|
|
44
|
+
const centralDisk = v.getUint16(eocd + 6, true);
|
|
45
|
+
const diskCount = v.getUint16(eocd + 8, true);
|
|
46
|
+
const count = v.getUint16(eocd + 10, true);
|
|
47
|
+
const centralSize = v.getUint32(eocd + 12, true);
|
|
48
|
+
let p = v.getUint32(eocd + 16, true);
|
|
49
|
+
if (disk === 0xffff || centralDisk === 0xffff || diskCount === 0xffff ||
|
|
50
|
+
count === 0xffff || centralSize === 0xffffffff || p === 0xffffffff)
|
|
51
|
+
return Number.POSITIVE_INFINITY; // ZIP64
|
|
52
|
+
// Multi-disk archives cannot be bounded from this single input.
|
|
53
|
+
if (disk !== 0 || centralDisk !== 0 || diskCount !== count)
|
|
54
|
+
return Number.POSITIVE_INFINITY;
|
|
55
|
+
const centralEnd = p + centralSize;
|
|
56
|
+
if (p > eocd || centralEnd !== eocd)
|
|
57
|
+
return Number.POSITIVE_INFINITY;
|
|
58
|
+
let total = 0;
|
|
59
|
+
for (let i = 0; i < count; i++) {
|
|
60
|
+
if (p + 46 > centralEnd || v.getUint32(p, true) !== CDFH) {
|
|
61
|
+
return Number.POSITIVE_INFINITY;
|
|
62
|
+
}
|
|
63
|
+
const size = v.getUint32(p + 24, true);
|
|
64
|
+
if (size === 0xffffffff)
|
|
65
|
+
return Number.POSITIVE_INFINITY; // ZIP64
|
|
66
|
+
total += size;
|
|
67
|
+
const next = p + 46 + v.getUint16(p + 28, true) + v.getUint16(p + 30, true) + v.getUint16(p + 32, true);
|
|
68
|
+
if (next > centralEnd)
|
|
69
|
+
return Number.POSITIVE_INFINITY;
|
|
70
|
+
p = next;
|
|
71
|
+
}
|
|
72
|
+
// A central-directory digital signature is permitted after the file
|
|
73
|
+
// headers and is included in the EOCD central-directory byte count.
|
|
74
|
+
if (p < centralEnd && p + 6 <= centralEnd && v.getUint32(p, true) === CENTRAL_DIRECTORY_DIGITAL_SIGNATURE) {
|
|
75
|
+
p += 6 + v.getUint16(p + 4, true);
|
|
76
|
+
}
|
|
77
|
+
if (p !== centralEnd)
|
|
78
|
+
return Number.POSITIVE_INFINITY;
|
|
79
|
+
return total;
|
|
80
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type ContainerKind = 'zip' | 'ole';
|
|
2
|
+
/** Local file header 'PK\x03\x04' — the start of every OOXML/ODF/zip file. */
|
|
3
|
+
export declare const ZIP_MAGIC: readonly [80, 75, 3, 4];
|
|
4
|
+
/** Empty ZIP end-of-central-directory record. */
|
|
5
|
+
export declare const EMPTY_ZIP_MAGIC: readonly [80, 75, 5, 6];
|
|
6
|
+
/** OLE2 / Compound File Binary signature. */
|
|
7
|
+
export declare const OLE_MAGIC: readonly [208, 207, 17, 224, 161, 177, 26, 225];
|
|
8
|
+
/** Longest container signature — how many leading bytes a probe needs to sniff. */
|
|
9
|
+
export declare const CONTAINER_SNIFF_BYTES = 262;
|
|
10
|
+
/**
|
|
11
|
+
* Which container an office extension lives in. Used in stage-1 detection to
|
|
12
|
+
* verify a trusted extension against the actual bytes and to route ambiguous
|
|
13
|
+
* containers. Extend this as word/ppt/hwpx land (they share the same map).
|
|
14
|
+
*/
|
|
15
|
+
export declare const OFFICE_CONTAINER_BY_EXT: Readonly<Record<string, ContainerKind>>;
|
|
16
|
+
/** Classify the container from leading bytes, or null when neither matches. */
|
|
17
|
+
export declare function sniffContainer(bytes: Uint8Array): ContainerKind | null;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Container-format signatures and the office extension → container map
|
|
2
|
+
// (DESIGN.md §7-3, docs/viewers/excel.md §5). ZIP (xlsx/xlsm/docx/pptx/hwpx…)
|
|
3
|
+
// and OLE/CFB (xls/doc/ppt/hwp…) each share one signature across many formats,
|
|
4
|
+
// so the leading bytes only tell us the *container* — which viewer it holds is
|
|
5
|
+
// resolved by probeContainer (stage 2). Excel is the first such viewer; the
|
|
6
|
+
// office family inherits this contract.
|
|
7
|
+
/** Local file header 'PK\x03\x04' — the start of every OOXML/ODF/zip file. */
|
|
8
|
+
export const ZIP_MAGIC = [0x50, 0x4b, 0x03, 0x04];
|
|
9
|
+
/** Empty ZIP end-of-central-directory record. */
|
|
10
|
+
export const EMPTY_ZIP_MAGIC = [0x50, 0x4b, 0x05, 0x06];
|
|
11
|
+
/** OLE2 / Compound File Binary signature. */
|
|
12
|
+
export const OLE_MAGIC = [0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1];
|
|
13
|
+
/** Longest container signature — how many leading bytes a probe needs to sniff. */
|
|
14
|
+
export const CONTAINER_SNIFF_BYTES = 262; // TAR's `ustar` reaches byte 261
|
|
15
|
+
/**
|
|
16
|
+
* Which container an office extension lives in. Used in stage-1 detection to
|
|
17
|
+
* verify a trusted extension against the actual bytes and to route ambiguous
|
|
18
|
+
* containers. Extend this as word/ppt/hwpx land (they share the same map).
|
|
19
|
+
*/
|
|
20
|
+
export const OFFICE_CONTAINER_BY_EXT = {
|
|
21
|
+
xlsx: 'zip',
|
|
22
|
+
xlsm: 'zip',
|
|
23
|
+
xlsb: 'zip', // binary workbook — still an OOXML-style zip with an xl/ tree
|
|
24
|
+
ods: 'zip', // OpenDocument spreadsheet — zip with a spreadsheet mimetype
|
|
25
|
+
xls: 'ole',
|
|
26
|
+
pptx: 'zip',
|
|
27
|
+
ppt: 'ole',
|
|
28
|
+
docx: 'zip',
|
|
29
|
+
doc: 'ole',
|
|
30
|
+
hwpx: 'zip',
|
|
31
|
+
hwp: 'ole'
|
|
32
|
+
};
|
|
33
|
+
function startsWith(bytes, magic) {
|
|
34
|
+
if (bytes.byteLength < magic.length)
|
|
35
|
+
return false;
|
|
36
|
+
for (let i = 0; i < magic.length; i++) {
|
|
37
|
+
if (bytes[i] !== magic[i])
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
/** Classify the container from leading bytes, or null when neither matches. */
|
|
43
|
+
export function sniffContainer(bytes) {
|
|
44
|
+
if (startsWith(bytes, ZIP_MAGIC) || startsWith(bytes, EMPTY_ZIP_MAGIC))
|
|
45
|
+
return 'zip';
|
|
46
|
+
if (startsWith(bytes, OLE_MAGIC))
|
|
47
|
+
return 'ole';
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import type { ServiceId } from '../host/index.js';
|
|
2
|
+
import { type ContainerKind } from './container.js';
|
|
3
|
+
/** One contiguous byte run at a fixed offset. */
|
|
4
|
+
export interface MagicByteRun {
|
|
5
|
+
offset: number;
|
|
6
|
+
bytes: readonly number[];
|
|
7
|
+
}
|
|
8
|
+
/** A signature matches when *every* run matches (AND). A descriptor's
|
|
9
|
+
* `magicSignatures` list matches when *any* signature matches (OR) — this is
|
|
10
|
+
* how one viewer covers several formats (e.g. the image viewer's PNG/JPEG/
|
|
11
|
+
* GIF/BMP/WebP), and how a multi-part signature like WebP's `RIFF`@0 + `WEBP`@8
|
|
12
|
+
* is expressed (docs/viewers/image.md §1). */
|
|
13
|
+
export type MagicSignature = readonly MagicByteRun[];
|
|
14
|
+
export interface ViewerDescriptor {
|
|
15
|
+
id: string;
|
|
16
|
+
displayNameKey: string;
|
|
17
|
+
/** Lower-case, no leading dot. */
|
|
18
|
+
extensions: string[];
|
|
19
|
+
priority: number;
|
|
20
|
+
/** Leading file bytes that must match when sniff bytes are provided
|
|
21
|
+
* (e.g. '%PDF-'). A mismatch excludes the viewer (docs/viewers/pdf.md). */
|
|
22
|
+
magicPrefix?: readonly number[];
|
|
23
|
+
/** Offset-aware alternative to magicPrefix for viewers that span multiple
|
|
24
|
+
* formats or need non-zero offsets. Excludes the viewer when sniff bytes
|
|
25
|
+
* are present and no signature matches. */
|
|
26
|
+
magicSignatures?: readonly MagicSignature[];
|
|
27
|
+
requiredServices: readonly ServiceId[];
|
|
28
|
+
optionalServices: readonly ServiceId[];
|
|
29
|
+
}
|
|
30
|
+
/** Raster image signatures (docs/viewers/image.md §1). SVG has no fixed magic
|
|
31
|
+
* and is handled by extension + text probe in the viewer, not here. */
|
|
32
|
+
export declare const IMAGE_MAGIC_SIGNATURES: readonly MagicSignature[];
|
|
33
|
+
export declare const AVRO_MAGIC_SIGNATURES: readonly MagicSignature[];
|
|
34
|
+
export declare const BAG_MAGIC_SIGNATURES: readonly MagicSignature[];
|
|
35
|
+
export declare const STP_MAGIC_SIGNATURES: readonly MagicSignature[];
|
|
36
|
+
export declare const DB3_MAGIC_SIGNATURES: readonly MagicSignature[];
|
|
37
|
+
export declare const BLF_MAGIC_SIGNATURES: readonly MagicSignature[];
|
|
38
|
+
export declare const MF4_MAGIC_SIGNATURES: readonly MagicSignature[];
|
|
39
|
+
export declare const PCAP_MAGIC_SIGNATURES: readonly MagicSignature[];
|
|
40
|
+
export declare const PCAPNG_MAGIC_SIGNATURES: readonly MagicSignature[];
|
|
41
|
+
export declare const PSD_MAGIC_SIGNATURES: readonly MagicSignature[];
|
|
42
|
+
export declare const SHAPEFILE_MAGIC_SIGNATURES: readonly MagicSignature[];
|
|
43
|
+
/**
|
|
44
|
+
* How many leading bytes an adapter should sniff for detection — the longest of
|
|
45
|
+
* the longest magicPrefix (PDF, 5B), the container signatures (OLE, 8B), and the
|
|
46
|
+
* image signatures (WebP, 12B). Stage-1 uses only these; container probing
|
|
47
|
+
* (stage 2) needs the whole file to read the central directory / CFB
|
|
48
|
+
* (docs/viewers/excel.md §5).
|
|
49
|
+
*/
|
|
50
|
+
export declare const REQUIRED_SNIFF_BYTES: number;
|
|
51
|
+
export declare const CSV_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
52
|
+
export declare const FALLBACK_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
53
|
+
export declare const PDF_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
54
|
+
export declare const PARQUET_MAGIC_SIGNATURES: readonly MagicSignature[];
|
|
55
|
+
export declare const PARQUET_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
56
|
+
export declare const AVRO_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
57
|
+
export declare const BAG_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
58
|
+
export declare const STP_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
59
|
+
export declare const DB3_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
60
|
+
export declare const REQIF_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
61
|
+
export declare const HDF5_MAGIC_PREFIX: readonly [137, 72, 68, 70, 13, 10, 26, 10];
|
|
62
|
+
export declare const HDF5_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
63
|
+
export declare const MAT_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
64
|
+
export declare const AUDIO_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
65
|
+
export declare const VIDEO_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
66
|
+
export declare const DBC_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
67
|
+
export declare const ARXML_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
68
|
+
export declare const A2L_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
69
|
+
export declare const ASC_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
70
|
+
export declare const BLF_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
71
|
+
export declare const MF4_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
72
|
+
export declare const PCAP_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
73
|
+
export declare const PCAPNG_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
74
|
+
export declare const MERMAID_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
75
|
+
export declare const PLANTUML_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
76
|
+
export declare const SHAPEFILE_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
77
|
+
export declare const PSD_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
78
|
+
export declare const JSON_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
79
|
+
export declare const TOML_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
80
|
+
export declare const JSONL_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
81
|
+
export declare const YAML_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
82
|
+
export declare const PROTO_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
83
|
+
/**
|
|
84
|
+
* Excel spans two containers (xlsx/xlsm are zip, xls is ole), so it has no
|
|
85
|
+
* single magicPrefix — the container is verified via OFFICE_CONTAINER_BY_EXT
|
|
86
|
+
* and, when ambiguous, probeContainer (docs/viewers/excel.md §5, X-감지).
|
|
87
|
+
*/
|
|
88
|
+
export declare const EXCEL_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
89
|
+
export declare const PPT_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
90
|
+
export declare const WORD_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
91
|
+
export declare const HWP_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
92
|
+
/**
|
|
93
|
+
* Image viewer detection metadata (docs/viewers/image.md §1). Background removal
|
|
94
|
+
* and sprite detection are optional host services (image.md I8), never
|
|
95
|
+
* requiredServices: gating them here would exclude the image viewer on platforms
|
|
96
|
+
* that don't provide them. SVG has no fixed magic — it is admitted by extension
|
|
97
|
+
* and the viewer's text probe (decode.ts).
|
|
98
|
+
*/
|
|
99
|
+
export declare const IMAGE_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
100
|
+
export declare const MARKDOWN_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
101
|
+
export declare const ARCHIVE_MAGIC_SIGNATURES: readonly MagicSignature[];
|
|
102
|
+
/** Archive is intentionally lower priority than Office container viewers. */
|
|
103
|
+
export declare const ARCHIVE_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
104
|
+
export declare const CORE_VIEWER_DESCRIPTORS: readonly ViewerDescriptor[];
|
|
105
|
+
export { looksLikeJsonDocument, looksLikeJsonl, looksLikeProto, sniffTextViewer } from './sniff.js';
|
|
106
|
+
export { CONTAINER_SNIFF_BYTES, OFFICE_CONTAINER_BY_EXT, OLE_MAGIC, ZIP_MAGIC, sniffContainer, type ContainerKind } from './container.js';
|
|
107
|
+
export { probeContainer } from './probe.js';
|
|
108
|
+
export interface DetectionResult {
|
|
109
|
+
/** The resolved viewer, or 'fallback' when unresolved. For
|
|
110
|
+
* 'ambiguous-container' this is 'fallback' (the safe default if the caller
|
|
111
|
+
* does not run probeContainer). */
|
|
112
|
+
viewerId: string;
|
|
113
|
+
/** 'extension' for an extension match; 'content' for a text sniff (J17);
|
|
114
|
+
* 'ambiguous-container' when the bytes are a zip/ole container the caller
|
|
115
|
+
* should refine with probeContainer; 'fallback' when nothing matched. */
|
|
116
|
+
matchedBy: 'extension' | 'content' | 'ambiguous-container' | 'fallback';
|
|
117
|
+
/** Present only when matchedBy === 'ambiguous-container'. */
|
|
118
|
+
container?: ContainerKind;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Detection (DESIGN.md §7): ASCII-lowercased extension matching with priority
|
|
122
|
+
* tie-breaking (stage 1). When no extension matches and a text sample is
|
|
123
|
+
* provided, content sniffing (J17, 부록 B-6) classifies signature-less text
|
|
124
|
+
* formats — the extension is always trusted first (§7 point 3).
|
|
125
|
+
*
|
|
126
|
+
* Container-format viewers (excel; later word/ppt/hwpx) share zip/ole
|
|
127
|
+
* signatures, so stage-1 verifies a trusted office extension against the actual
|
|
128
|
+
* bytes: a match confirms it, a contradicting container (or a container with no
|
|
129
|
+
* matching extension) returns 'ambiguous-container' for the caller to resolve
|
|
130
|
+
* with probeContainer (stage 2, docs/viewers/excel.md §5). Services the host
|
|
131
|
+
* cannot provide exclude a viewer whose requiredServices demand them.
|
|
132
|
+
*/
|
|
133
|
+
export declare function detectViewer(fileName: string, availableServices?: ReadonlySet<ServiceId>, descriptors?: readonly ViewerDescriptor[], sniffBytes?: Uint8Array, sniffText?: string): DetectionResult;
|