@nhtio/adk 0.1.0-master-e6c58205 → 0.1.0-master-89b47bec
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/CHANGELOG.md +94 -0
- package/__vite-browser-external-CU7uua8b.mjs +9 -0
- package/__vite-browser-external-CU7uua8b.mjs.map +1 -0
- package/__vite-browser-external-DAHxjh-D.js +13 -0
- package/__vite-browser-external-DAHxjh-D.js.map +1 -0
- package/batteries/embeddings/openai/adapter.cjs +1 -1
- package/batteries/embeddings/openai/adapter.mjs +1 -1
- package/batteries/embeddings/webllm/adapter.cjs +1 -1
- package/batteries/embeddings/webllm/adapter.mjs +1 -1
- package/batteries/llm/ollama/adapter.cjs +5 -5
- package/batteries/llm/ollama/adapter.mjs +5 -5
- package/batteries/llm/ollama/helpers.cjs +2 -2
- package/batteries/llm/ollama/helpers.mjs +2 -2
- package/batteries/llm/ollama/validation.cjs +1 -1
- package/batteries/llm/ollama/validation.mjs +1 -1
- package/batteries/llm/openai_chat_completions/adapter.cjs +5 -5
- package/batteries/llm/openai_chat_completions/adapter.mjs +5 -5
- package/batteries/llm/openai_chat_completions/helpers.cjs +2 -2
- package/batteries/llm/openai_chat_completions/helpers.mjs +2 -2
- package/batteries/llm/openai_chat_completions/validation.cjs +1 -1
- package/batteries/llm/openai_chat_completions/validation.mjs +1 -1
- package/batteries/llm/webllm_chat_completions/adapter.cjs +5 -5
- package/batteries/llm/webllm_chat_completions/adapter.mjs +5 -5
- package/batteries/llm/webllm_chat_completions/validation.cjs +1 -1
- package/batteries/llm/webllm_chat_completions/validation.mjs +1 -1
- package/batteries/media/contracts.cjs +38 -8
- package/batteries/media/contracts.cjs.map +1 -1
- package/batteries/media/contracts.d.ts +86 -6
- package/batteries/media/contracts.mjs +37 -9
- package/batteries/media/contracts.mjs.map +1 -1
- package/batteries/media/engines/audio_decode.cjs +46 -6
- package/batteries/media/engines/audio_decode.cjs.map +1 -1
- package/batteries/media/engines/audio_decode.d.ts +9 -5
- package/batteries/media/engines/audio_decode.mjs +47 -7
- package/batteries/media/engines/audio_decode.mjs.map +1 -1
- package/batteries/media/engines/data.cjs +159 -0
- package/batteries/media/engines/data.cjs.map +1 -0
- package/batteries/media/engines/data.d.ts +36 -0
- package/batteries/media/engines/data.mjs +157 -0
- package/batteries/media/engines/data.mjs.map +1 -0
- package/batteries/media/engines/exceljs.cjs +290 -0
- package/batteries/media/engines/exceljs.cjs.map +1 -0
- package/batteries/media/engines/exceljs.d.ts +41 -0
- package/batteries/media/engines/exceljs.mjs +288 -0
- package/batteries/media/engines/exceljs.mjs.map +1 -0
- package/batteries/media/engines/execa_executor.cjs +1 -1
- package/batteries/media/engines/execa_executor.mjs +1 -1
- package/batteries/media/engines/fs_workspace.cjs +1 -1
- package/batteries/media/engines/fs_workspace.mjs +1 -1
- package/batteries/media/engines/jimp.cjs +24 -2
- package/batteries/media/engines/jimp.cjs.map +1 -1
- package/batteries/media/engines/jimp.d.ts +3 -1
- package/batteries/media/engines/jimp.mjs +24 -2
- package/batteries/media/engines/jimp.mjs.map +1 -1
- package/batteries/media/engines/sharp.cjs +37 -4
- package/batteries/media/engines/sharp.cjs.map +1 -1
- package/batteries/media/engines/sharp.d.ts +5 -3
- package/batteries/media/engines/sharp.mjs +37 -4
- package/batteries/media/engines/sharp.mjs.map +1 -1
- package/batteries/media/engines/sheetjs.cjs +438 -0
- package/batteries/media/engines/sheetjs.cjs.map +1 -0
- package/batteries/media/engines/sheetjs.d.ts +51 -0
- package/batteries/media/engines/sheetjs.mjs +436 -0
- package/batteries/media/engines/sheetjs.mjs.map +1 -0
- package/batteries/media/engines/soffice.cjs +37 -3
- package/batteries/media/engines/soffice.cjs.map +1 -1
- package/batteries/media/engines/soffice.mjs +38 -4
- package/batteries/media/engines/soffice.mjs.map +1 -1
- package/batteries/media/engines/tesseract_js.cjs +1 -1
- package/batteries/media/engines/tesseract_js.mjs +1 -1
- package/batteries/media/engines/transformers_asr.cjs +1 -1
- package/batteries/media/engines/transformers_asr.mjs +1 -1
- package/batteries/media/forge.cjs +89 -20
- package/batteries/media/forge.cjs.map +1 -1
- package/batteries/media/forge.mjs +89 -20
- package/batteries/media/forge.mjs.map +1 -1
- package/batteries/media/formats.d.ts +9 -0
- package/batteries/media/index.d.ts +8 -6
- package/batteries/media/lint.cjs +270 -58
- package/batteries/media/lint.cjs.map +1 -1
- package/batteries/media/lint.d.ts +45 -0
- package/batteries/media/lint.mjs +267 -58
- package/batteries/media/lint.mjs.map +1 -1
- package/batteries/media/registry.d.ts +11 -2
- package/batteries/media/steps/data.d.ts +26 -0
- package/batteries/media/steps/doc.d.ts +17 -7
- package/batteries/media/steps/patch.d.ts +90 -0
- package/batteries/media/steps/sheet.d.ts +10 -6
- package/batteries/media/steps/text.d.ts +4 -1
- package/batteries/media/validate.d.ts +5 -0
- package/batteries/media/verbs.d.ts +12 -1
- package/batteries/media.cjs +869 -448
- package/batteries/media.cjs.map +1 -1
- package/batteries/media.mjs +829 -409
- package/batteries/media.mjs.map +1 -1
- package/batteries/storage/flydrive.cjs +3 -6
- package/batteries/storage/flydrive.cjs.map +1 -1
- package/batteries/storage/flydrive.mjs +3 -6
- package/batteries/storage/flydrive.mjs.map +1 -1
- package/batteries/storage/in_memory.cjs +1 -1
- package/batteries/storage/in_memory.mjs +1 -1
- package/batteries/storage/opfs.cjs +1 -1
- package/batteries/storage/opfs.mjs +1 -1
- package/batteries/tools/_shared.cjs +2 -2
- package/batteries/tools/_shared.mjs +2 -2
- package/batteries/tools/color.cjs +2 -2
- package/batteries/tools/color.mjs +2 -2
- package/batteries/tools/comparison.cjs +3 -3
- package/batteries/tools/comparison.mjs +3 -3
- package/batteries/tools/data_structure.cjs +3 -3
- package/batteries/tools/data_structure.mjs +3 -3
- package/batteries/tools/datetime_extended.cjs +3 -3
- package/batteries/tools/datetime_extended.mjs +3 -3
- package/batteries/tools/datetime_math.cjs +3 -3
- package/batteries/tools/datetime_math.mjs +3 -3
- package/batteries/tools/encoding.cjs +3 -3
- package/batteries/tools/encoding.mjs +3 -3
- package/batteries/tools/formatting.cjs +3 -3
- package/batteries/tools/formatting.mjs +3 -3
- package/batteries/tools/geo_basics.cjs +2 -2
- package/batteries/tools/geo_basics.mjs +2 -2
- package/batteries/tools/math.cjs +3 -3
- package/batteries/tools/math.mjs +3 -3
- package/batteries/tools/memory.cjs +6 -6
- package/batteries/tools/memory.mjs +6 -6
- package/batteries/tools/parsing.cjs +4 -4
- package/batteries/tools/parsing.mjs +4 -4
- package/batteries/tools/retrievables.cjs +5 -5
- package/batteries/tools/retrievables.mjs +5 -5
- package/batteries/tools/scrapper.cjs +1 -1
- package/batteries/tools/scrapper.mjs +1 -1
- package/batteries/tools/searxng.cjs +1 -1
- package/batteries/tools/searxng.mjs +1 -1
- package/batteries/tools/standing_instructions.cjs +4 -4
- package/batteries/tools/standing_instructions.mjs +4 -4
- package/batteries/tools/statistics.cjs +4 -4
- package/batteries/tools/statistics.mjs +4 -4
- package/batteries/tools/string_processing.cjs +3 -3
- package/batteries/tools/string_processing.mjs +3 -3
- package/batteries/tools/structured_data.cjs +3 -3
- package/batteries/tools/structured_data.mjs +3 -3
- package/batteries/tools/text_analysis.cjs +3 -3
- package/batteries/tools/text_analysis.mjs +3 -3
- package/batteries/tools/text_comparison.cjs +2 -2
- package/batteries/tools/text_comparison.mjs +2 -2
- package/batteries/tools/time.cjs +3 -3
- package/batteries/tools/time.mjs +3 -3
- package/batteries/tools/unit_conversion.cjs +2 -2
- package/batteries/tools/unit_conversion.mjs +2 -2
- package/batteries/tools/web_retrieval.cjs +1 -1
- package/batteries/tools/web_retrieval.mjs +1 -1
- package/batteries/tools.cjs +2 -2
- package/batteries/tools.mjs +2 -2
- package/batteries/vector/arangodb.cjs +1 -1
- package/batteries/vector/arangodb.mjs +1 -1
- package/batteries/vector/clickhouse.cjs +1 -1
- package/batteries/vector/clickhouse.mjs +1 -1
- package/batteries/vector/cloudflare.cjs +1 -1
- package/batteries/vector/cloudflare.mjs +1 -1
- package/batteries/vector/couchbase.cjs +1 -1
- package/batteries/vector/couchbase.mjs +1 -1
- package/batteries/vector/duckdb.cjs +1 -1
- package/batteries/vector/duckdb.mjs +1 -1
- package/batteries/vector/elasticsearch.cjs +1 -1
- package/batteries/vector/elasticsearch.mjs +1 -1
- package/batteries/vector/filters.cjs +1 -1
- package/batteries/vector/filters.mjs +1 -1
- package/batteries/vector/hnswlib.cjs +1 -1
- package/batteries/vector/hnswlib.mjs +1 -1
- package/batteries/vector/lancedb.cjs +1 -1
- package/batteries/vector/lancedb.mjs +1 -1
- package/batteries/vector/mariadb.cjs +1 -1
- package/batteries/vector/mariadb.mjs +1 -1
- package/batteries/vector/meilisearch.cjs +1 -1
- package/batteries/vector/meilisearch.mjs +1 -1
- package/batteries/vector/migrate.cjs +1 -1
- package/batteries/vector/migrate.mjs +1 -1
- package/batteries/vector/mongodb.cjs +1 -1
- package/batteries/vector/mongodb.mjs +1 -1
- package/batteries/vector/neo4j.cjs +1 -1
- package/batteries/vector/neo4j.mjs +1 -1
- package/batteries/vector/opensearch.cjs +1 -1
- package/batteries/vector/opensearch.mjs +1 -1
- package/batteries/vector/oracle23ai.cjs +1 -1
- package/batteries/vector/oracle23ai.mjs +1 -1
- package/batteries/vector/pinecone.cjs +1 -1
- package/batteries/vector/pinecone.mjs +1 -1
- package/batteries/vector/redis.cjs +1 -1
- package/batteries/vector/redis.mjs +1 -1
- package/batteries/vector/retrievable.cjs +1 -1
- package/batteries/vector/retrievable.mjs +1 -1
- package/batteries/vector/s3vectors.cjs +1 -1
- package/batteries/vector/s3vectors.mjs +1 -1
- package/batteries/vector/solr.cjs +1 -1
- package/batteries/vector/solr.mjs +1 -1
- package/batteries/vector/surrealdb.cjs +1 -1
- package/batteries/vector/surrealdb.mjs +1 -1
- package/batteries/vector/typesense.cjs +1 -1
- package/batteries/vector/typesense.mjs +1 -1
- package/batteries/vector/vespa.cjs +1 -1
- package/batteries/vector/vespa.mjs +1 -1
- package/batteries/vector/weaviate.cjs +1 -1
- package/batteries/vector/weaviate.mjs +1 -1
- package/batteries.cjs +2 -2
- package/batteries.mjs +2 -2
- package/bundled_xlsx.d.ts +1075 -0
- package/{common-BxoznqbX.mjs → common-BQoFv108.mjs} +7 -7
- package/{common-BxoznqbX.mjs.map → common-BQoFv108.mjs.map} +1 -1
- package/{common-BBK2e5ro.js → common-DmDn4Gfl.js} +7 -7
- package/{common-BBK2e5ro.js.map → common-DmDn4Gfl.js.map} +1 -1
- package/common.cjs +7 -7
- package/common.mjs +7 -7
- package/{dispatch_runner-Hn5idJYx.js → dispatch_runner-BwplbCPr.js} +3 -3
- package/{dispatch_runner-Hn5idJYx.js.map → dispatch_runner-BwplbCPr.js.map} +1 -1
- package/{dispatch_runner-DUrpj0xR.mjs → dispatch_runner-C1ar_D7W.mjs} +3 -3
- package/{dispatch_runner-DUrpj0xR.mjs.map → dispatch_runner-C1ar_D7W.mjs.map} +1 -1
- package/dispatch_runner.cjs +1 -1
- package/dispatch_runner.mjs +1 -1
- package/eslint.cjs +1 -1
- package/eslint.mjs +1 -1
- package/forge.cjs +4 -4
- package/forge.mjs +4 -4
- package/formats-CCylUHZw.mjs +161 -0
- package/formats-CCylUHZw.mjs.map +1 -0
- package/formats-DZVuqZUN.js +196 -0
- package/formats-DZVuqZUN.js.map +1 -0
- package/guards.cjs +8 -8
- package/guards.mjs +8 -8
- package/index.cjs +11 -11
- package/index.mjs +11 -11
- package/mcp/adk-docs-corpus.json +1 -1
- package/package.json +198 -182
- package/{scrapper-DLj_KkxK.mjs → scrapper-Dn2wXu0Y.mjs} +5 -5
- package/{scrapper-DLj_KkxK.mjs.map → scrapper-Dn2wXu0Y.mjs.map} +1 -1
- package/{scrapper-Cmf1CSA-.js → scrapper-mBZ0Odq7.js} +5 -5
- package/{scrapper-Cmf1CSA-.js.map → scrapper-mBZ0Odq7.js.map} +1 -1
- package/{searxng-BKs5s8Su.js → searxng-Dj-iP_Vz.js} +5 -5
- package/{searxng-BKs5s8Su.js.map → searxng-Dj-iP_Vz.js.map} +1 -1
- package/{searxng-BJ-aZcMz.mjs → searxng-DjFxtFf6.mjs} +5 -5
- package/{searxng-BJ-aZcMz.mjs.map → searxng-DjFxtFf6.mjs.map} +1 -1
- package/server.json +21 -0
- package/skills/adk-assembly/SKILL.md +2 -2
- package/{spooled_artifact-BR0IhU9y.js → spooled_artifact-BCmPrxZS.js} +3 -3
- package/{spooled_artifact-BR0IhU9y.js.map → spooled_artifact-BCmPrxZS.js.map} +1 -1
- package/{spooled_artifact-nTE6ujM8.mjs → spooled_artifact-DL36jnqA.mjs} +3 -3
- package/{spooled_artifact-nTE6ujM8.mjs.map → spooled_artifact-DL36jnqA.mjs.map} +1 -1
- package/spooled_artifact.cjs +2 -2
- package/spooled_artifact.mjs +2 -2
- package/{spooled_markdown_artifact-Dj3fNowz.js → spooled_markdown_artifact-CD4PrrpV.js} +3 -3
- package/{spooled_markdown_artifact-Dj3fNowz.js.map → spooled_markdown_artifact-CD4PrrpV.js.map} +1 -1
- package/{spooled_markdown_artifact-DIYzbQv8.mjs → spooled_markdown_artifact-Da-Xnebh.mjs} +3 -3
- package/{spooled_markdown_artifact-DIYzbQv8.mjs.map → spooled_markdown_artifact-Da-Xnebh.mjs.map} +1 -1
- package/{thought-Cde2Y_GK.mjs → thought-CtOoZUSE.mjs} +3 -3
- package/{thought-Cde2Y_GK.mjs.map → thought-CtOoZUSE.mjs.map} +1 -1
- package/{thought-BFtsnUTU.js → thought-eka4iUHa.js} +3 -3
- package/{thought-BFtsnUTU.js.map → thought-eka4iUHa.js.map} +1 -1
- package/{tool-CcxYxA5X.mjs → tool-CITF20yJ.mjs} +3 -3
- package/{tool-CcxYxA5X.mjs.map → tool-CITF20yJ.mjs.map} +1 -1
- package/{tool-B2nnkwNU.js → tool-Dhjca7rC.js} +3 -3
- package/{tool-B2nnkwNU.js.map → tool-Dhjca7rC.js.map} +1 -1
- package/{tool_call-BxL3eU1k.js → tool_call-Cyi0hUUc.js} +4 -4
- package/{tool_call-BxL3eU1k.js.map → tool_call-Cyi0hUUc.js.map} +1 -1
- package/{tool_call-B3MOog2R.mjs → tool_call-D_k36aI4.mjs} +4 -4
- package/{tool_call-B3MOog2R.mjs.map → tool_call-D_k36aI4.mjs.map} +1 -1
- package/{tool_registry-BaLUIzWz.mjs → tool_registry-CZ55mIti.mjs} +152 -143
- package/tool_registry-CZ55mIti.mjs.map +1 -0
- package/{tool_registry-BdKlPuHS.js → tool_registry-DwZ6pzN4.js} +151 -142
- package/tool_registry-DwZ6pzN4.js.map +1 -0
- package/{turn_runner-M2I8G2ok.js → turn_runner-CCZcsoDU.js} +5 -5
- package/{turn_runner-M2I8G2ok.js.map → turn_runner-CCZcsoDU.js.map} +1 -1
- package/{turn_runner-BV4BaZK9.mjs → turn_runner-DRPpVvsd.mjs} +5 -5
- package/{turn_runner-BV4BaZK9.mjs.map → turn_runner-DRPpVvsd.mjs.map} +1 -1
- package/turn_runner.cjs +1 -1
- package/turn_runner.mjs +1 -1
- package/{validate-JOHfh-BB.mjs → validate-C_hTnDBE.mjs} +95 -8
- package/validate-C_hTnDBE.mjs.map +1 -0
- package/{validate-DgDZkUKs.js → validate-gaSa9bsk.js} +95 -8
- package/validate-gaSa9bsk.js.map +1 -0
- package/xlsx-B0sTpEC2.js +30615 -0
- package/xlsx-B0sTpEC2.js.map +1 -0
- package/xlsx-DiMpn_Xv.mjs +30596 -0
- package/xlsx-DiMpn_Xv.mjs.map +1 -0
- package/xlsx.zahl-D2_Q-Xs4.mjs +6 -0
- package/xlsx.zahl-D2_Q-Xs4.mjs.map +1 -0
- package/xlsx.zahl-ZIcUQVv8.js +7 -0
- package/xlsx.zahl-ZIcUQVv8.js.map +1 -0
- package/tool_registry-BaLUIzWz.mjs.map +0 -1
- package/tool_registry-BdKlPuHS.js.map +0 -1
- package/validate-DgDZkUKs.js.map +0 -1
- package/validate-JOHfh-BB.mjs.map +0 -1
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
import { o as isError } from "../../../tool_registry-CZ55mIti.mjs";
|
|
2
|
+
import "../../../guards.mjs";
|
|
3
|
+
import { EMPTY_MIME } from "../contracts.mjs";
|
|
4
|
+
import { t as E_INVALID_MEDIA_PIPELINE_CONFIG } from "../../../exceptions-BLTn4Ofs.mjs";
|
|
5
|
+
import { n as MIME } from "../../../formats-CCylUHZw.mjs";
|
|
6
|
+
//#region src/batteries/media/engines/sheetjs.ts
|
|
7
|
+
/**
|
|
8
|
+
* The SheetJS-backed spreadsheet {@link @nhtio/adk/batteries/media/contracts!MediaEngine}:
|
|
9
|
+
* the in-process, cross-environment spreadsheet converter, generator, and structural editor.
|
|
10
|
+
*
|
|
11
|
+
* @module @nhtio/adk/batteries/media/engines/sheetjs
|
|
12
|
+
*
|
|
13
|
+
* @remarks
|
|
14
|
+
* SheetJS (the `xlsx` package) reads and writes the widest spreadsheet matrix of any pure-JS
|
|
15
|
+
* library — XLSX/XLSM/XLSB, every XLS BIFF era, ODS/FODS, CSV, SYLK, DIF, DBF, HTML, RTF,
|
|
16
|
+
* Apple NUMBERS — with no binary, no native bindings, in any environment. This engine exposes
|
|
17
|
+
* that matrix three ways:
|
|
18
|
+
*
|
|
19
|
+
* - **convert**: any readable spreadsheet MIME → any writable format token.
|
|
20
|
+
* - **generate**: `EMPTY_MIME` → any writable token (a blank `Sheet1` workbook, written out).
|
|
21
|
+
* - **edit**: the `sheet.*` structural ops via CSF cell-object surgery. ⚠️ SheetJS Community
|
|
22
|
+
* Edition does NOT model cell styling — fonts, fills, and comments are STRIPPED by any edit
|
|
23
|
+
* or convert that round-trips a styled workbook (styling is a SheetJS Pro feature). For
|
|
24
|
+
* fidelity-preserving edits compose the exceljs engine FIRST in the engines array; this
|
|
25
|
+
* engine's edits are for unstyled/data workbooks and for breadth (it edits ODS, XLS, and
|
|
26
|
+
* every other readable format by normalizing through CSF).
|
|
27
|
+
*
|
|
28
|
+
* ⚠️ **Install from the SheetJS CDN, not the npm registry.** The registry copy of `xlsx` is
|
|
29
|
+
* frozen at 0.18.5 (2023) and carries CVE-2023-30533 and CVE-2024-22363; this engine requires
|
|
30
|
+
* `>=0.20.2`. Install: `pnpm add xlsx@https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz`
|
|
31
|
+
* (SheetJS recommends vendoring the tarball — see docs.sheetjs.com).
|
|
32
|
+
*
|
|
33
|
+
* `xlsx` is an optional peer dependency, lazily imported on first actual use. The NUMBERS
|
|
34
|
+
* write target additionally loads the ZAHL payload (`xlsx/dist/xlsx.zahl.mjs`, ~110 KB) on
|
|
35
|
+
* first request of that specific target.
|
|
36
|
+
*/
|
|
37
|
+
/** Every format token this engine can write, mapped to the SheetJS bookType that produces it. */
|
|
38
|
+
var BOOKTYPE_BY_TARGET = {
|
|
39
|
+
xlsx: "xlsx",
|
|
40
|
+
xlsm: "xlsm",
|
|
41
|
+
xlsb: "xlsb",
|
|
42
|
+
xls: "biff8",
|
|
43
|
+
ods: "ods",
|
|
44
|
+
fods: "fods",
|
|
45
|
+
csv: "csv",
|
|
46
|
+
txt: "txt",
|
|
47
|
+
html: "html",
|
|
48
|
+
rtf: "rtf",
|
|
49
|
+
sylk: "sylk",
|
|
50
|
+
dif: "dif",
|
|
51
|
+
dbf: "dbf",
|
|
52
|
+
numbers: "numbers"
|
|
53
|
+
};
|
|
54
|
+
/** The convert/generation target tokens, json included. */
|
|
55
|
+
var SHEET_WRITE_TARGETS = [...Object.keys(BOOKTYPE_BY_TARGET), "json"];
|
|
56
|
+
/** Output MIME per target token. */
|
|
57
|
+
var MIME_BY_TARGET = {
|
|
58
|
+
xlsx: MIME.XLSX,
|
|
59
|
+
xlsm: MIME.XLSM,
|
|
60
|
+
xlsb: MIME.XLSB,
|
|
61
|
+
xls: MIME.XLS,
|
|
62
|
+
ods: MIME.ODS,
|
|
63
|
+
fods: MIME.FODS,
|
|
64
|
+
csv: MIME.CSV,
|
|
65
|
+
txt: MIME.TXT,
|
|
66
|
+
html: MIME.HTML,
|
|
67
|
+
rtf: "application/rtf",
|
|
68
|
+
sylk: MIME.SYLK,
|
|
69
|
+
dif: MIME.DIF,
|
|
70
|
+
dbf: MIME.DBF,
|
|
71
|
+
numbers: MIME.NUMBERS,
|
|
72
|
+
json: MIME.JSON
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Every spreadsheet MIME this engine reads. SheetJS sniffs the actual format from the bytes
|
|
76
|
+
* (zip vs CFB vs text heuristics), so one entry serves the whole read matrix. Formats SheetJS
|
|
77
|
+
* can parse but that have no standard MIME (Quattro Pro, Works, Lotus variants) are readable
|
|
78
|
+
* whenever a resolver hands us one of these MIMEs for them — the MIME is the only addressable
|
|
79
|
+
* contract the battery has.
|
|
80
|
+
*/
|
|
81
|
+
var SHEET_READ_MIMES = [
|
|
82
|
+
MIME.XLSX,
|
|
83
|
+
MIME.XLSM,
|
|
84
|
+
MIME.XLSB,
|
|
85
|
+
MIME.XLS,
|
|
86
|
+
MIME.ODS,
|
|
87
|
+
MIME.FODS,
|
|
88
|
+
MIME.CSV,
|
|
89
|
+
MIME.NUMBERS,
|
|
90
|
+
MIME.SYLK,
|
|
91
|
+
MIME.DIF,
|
|
92
|
+
MIME.DBF
|
|
93
|
+
];
|
|
94
|
+
/** The structural ops this engine edits (same names the verb table uses). */
|
|
95
|
+
var SHEET_OPS = [
|
|
96
|
+
"sheet.add_rows",
|
|
97
|
+
"sheet.add_columns",
|
|
98
|
+
"sheet.update_cells",
|
|
99
|
+
"sheet.delete_rows",
|
|
100
|
+
"sheet.delete_columns",
|
|
101
|
+
"sheet.rename_sheet",
|
|
102
|
+
"sheet.add_sheet",
|
|
103
|
+
"sheet.remove_sheet",
|
|
104
|
+
"sheet.reorder_sheets",
|
|
105
|
+
"sheet.transform_table"
|
|
106
|
+
];
|
|
107
|
+
/**
|
|
108
|
+
* Construct the SheetJS-backed spreadsheet engine.
|
|
109
|
+
*
|
|
110
|
+
* @param options - Optional module resolver overrides.
|
|
111
|
+
* @returns The engine.
|
|
112
|
+
*/
|
|
113
|
+
var sheetjsEngine = (options = {}) => {
|
|
114
|
+
let modPromise;
|
|
115
|
+
const getXlsx = () => {
|
|
116
|
+
modPromise ??= Promise.resolve(options.xlsx ? options.xlsx() : import("../../../xlsx-DiMpn_Xv.mjs")).catch((err) => {
|
|
117
|
+
throw new E_INVALID_MEDIA_PIPELINE_CONFIG([`the sheetjs engine could not load its peer dependency "xlsx": ${isError(err) ? err.message : String(err)} — install it FROM THE SHEETJS CDN (the npm registry copy is stale and vulnerable): pnpm add xlsx@https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz`]);
|
|
118
|
+
});
|
|
119
|
+
return modPromise;
|
|
120
|
+
};
|
|
121
|
+
let zahlPromise;
|
|
122
|
+
const getZahl = () => {
|
|
123
|
+
zahlPromise ??= Promise.resolve(options.zahl ? options.zahl() : import("../../../xlsx.zahl-D2_Q-Xs4.mjs")).then((m) => m.default ?? m, (err) => {
|
|
124
|
+
const detail = isError(err) ? err.message : String(err);
|
|
125
|
+
throw new Error(`NUMBERS output needs the SheetJS ZAHL payload (xlsx/dist/xlsx.zahl.mjs) and it could not be loaded: ${detail}`);
|
|
126
|
+
});
|
|
127
|
+
return zahlPromise;
|
|
128
|
+
};
|
|
129
|
+
/** Write `wb` to the requested target token. */
|
|
130
|
+
const writeOut = async (X, wb, to) => {
|
|
131
|
+
if (to === "json") {
|
|
132
|
+
const ws = wb.Sheets[wb.SheetNames[0]];
|
|
133
|
+
const rows = ws ? X.utils.sheet_to_json(ws, { header: 1 }) : [];
|
|
134
|
+
return {
|
|
135
|
+
bytes: new TextEncoder().encode(JSON.stringify(rows)),
|
|
136
|
+
mimeType: MIME.JSON
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
const bookType = BOOKTYPE_BY_TARGET[to];
|
|
140
|
+
if (!bookType) throw new Error(`sheetjs cannot write "${to}"; supported: ${SHEET_WRITE_TARGETS.join(", ")}`);
|
|
141
|
+
const writeOpts = {
|
|
142
|
+
bookType,
|
|
143
|
+
type: "array"
|
|
144
|
+
};
|
|
145
|
+
if (to === "numbers") writeOpts.numbers = await getZahl();
|
|
146
|
+
const out = X.write(wb, writeOpts);
|
|
147
|
+
return {
|
|
148
|
+
bytes: ArrayBuffer.isView(out) ? out : new Uint8Array(out),
|
|
149
|
+
mimeType: MIME_BY_TARGET[to] ?? "application/octet-stream"
|
|
150
|
+
};
|
|
151
|
+
};
|
|
152
|
+
const convert = async (request) => {
|
|
153
|
+
const X = await getXlsx();
|
|
154
|
+
const mime = request.mimeType.toLowerCase().split(";")[0].trim();
|
|
155
|
+
let wb;
|
|
156
|
+
if (mime === "application/x-adk-empty") {
|
|
157
|
+
wb = X.utils.book_new();
|
|
158
|
+
const seed = request.to === "numbers" ? [[""]] : [[]];
|
|
159
|
+
X.utils.book_append_sheet(wb, X.utils.aoa_to_sheet(seed), "Sheet1");
|
|
160
|
+
} else {
|
|
161
|
+
wb = X.read(request.bytes);
|
|
162
|
+
if (wb.SheetNames.length === 0) throw new Error(`sheetjs parsed ${request.mimeType} but found no worksheets — the file may be corrupt or not actually a spreadsheet`);
|
|
163
|
+
}
|
|
164
|
+
const { bytes, mimeType } = await writeOut(X, wb, request.to);
|
|
165
|
+
return { outputs: [{
|
|
166
|
+
bytes,
|
|
167
|
+
mimeType
|
|
168
|
+
}] };
|
|
169
|
+
};
|
|
170
|
+
const edit = async (request) => {
|
|
171
|
+
const X = await getXlsx();
|
|
172
|
+
const wb = X.read(request.bytes);
|
|
173
|
+
if (wb.SheetNames.length === 0) throw new Error("sheetjs parsed the workbook but found no worksheets");
|
|
174
|
+
const summary = applyEdit(X, wb, request.op, request.args);
|
|
175
|
+
const { bytes, mimeType } = await writeOut(X, wb, "xlsx");
|
|
176
|
+
return {
|
|
177
|
+
bytes,
|
|
178
|
+
mimeType,
|
|
179
|
+
summary
|
|
180
|
+
};
|
|
181
|
+
};
|
|
182
|
+
return {
|
|
183
|
+
id: "sheetjs",
|
|
184
|
+
converts: [{
|
|
185
|
+
from: [EMPTY_MIME],
|
|
186
|
+
to: SHEET_WRITE_TARGETS,
|
|
187
|
+
convert
|
|
188
|
+
}, {
|
|
189
|
+
from: SHEET_READ_MIMES,
|
|
190
|
+
to: SHEET_WRITE_TARGETS,
|
|
191
|
+
convert
|
|
192
|
+
}],
|
|
193
|
+
edits: [{
|
|
194
|
+
over: SHEET_READ_MIMES,
|
|
195
|
+
ops: SHEET_OPS,
|
|
196
|
+
edit
|
|
197
|
+
}]
|
|
198
|
+
};
|
|
199
|
+
};
|
|
200
|
+
var asAoa = (X, ws) => X.utils.sheet_to_json(ws, {
|
|
201
|
+
header: 1,
|
|
202
|
+
defval: null
|
|
203
|
+
});
|
|
204
|
+
var resolveSheetName = (wb, sheetArg) => {
|
|
205
|
+
if (sheetArg === void 0 || sheetArg === null) {
|
|
206
|
+
const first = wb.SheetNames[0];
|
|
207
|
+
if (!first) throw new Error("the workbook has no worksheets");
|
|
208
|
+
return first;
|
|
209
|
+
}
|
|
210
|
+
if (typeof sheetArg === "number") {
|
|
211
|
+
const byIndex = wb.SheetNames[sheetArg - 1];
|
|
212
|
+
if (byIndex) return byIndex;
|
|
213
|
+
if (wb.SheetNames.includes(String(sheetArg))) throw new Error(`no sheet at index ${sheetArg}, but a sheet NAMED "${sheetArg}" exists — quote it: sheet="${sheetArg}"`);
|
|
214
|
+
throw new Error(`sheet index ${sheetArg} is out of range (1-based; the workbook has ${wb.SheetNames.length})`);
|
|
215
|
+
}
|
|
216
|
+
const name = String(sheetArg);
|
|
217
|
+
if (!wb.SheetNames.includes(name)) {
|
|
218
|
+
const names = wb.SheetNames.map((n) => `"${n}"`).join(", ");
|
|
219
|
+
throw new Error(`no sheet named "${name}" (sheets: ${names})`);
|
|
220
|
+
}
|
|
221
|
+
return name;
|
|
222
|
+
};
|
|
223
|
+
/** Column letters → 1-based number (`A` → 1, `AA` → 27). */
|
|
224
|
+
var columnLetterToNumber = (letters) => {
|
|
225
|
+
let n = 0;
|
|
226
|
+
for (const ch of letters.toUpperCase()) n = n * 26 + (ch.charCodeAt(0) - 64);
|
|
227
|
+
return n;
|
|
228
|
+
};
|
|
229
|
+
/** Parse an A1 address into 1-based row/col. */
|
|
230
|
+
var parseA1 = (address) => {
|
|
231
|
+
const m = /^([A-Za-z]+)(\d+)$/.exec(address.trim());
|
|
232
|
+
if (!m) throw new Error(`invalid cell address "${address}" (expected A1 form)`);
|
|
233
|
+
return {
|
|
234
|
+
row: Number(m[2]),
|
|
235
|
+
col: columnLetterToNumber(m[1])
|
|
236
|
+
};
|
|
237
|
+
};
|
|
238
|
+
var rebuild = (X, wb, name, aoa) => {
|
|
239
|
+
wb.Sheets[name] = X.utils.aoa_to_sheet(aoa.length > 0 ? aoa : [[]]);
|
|
240
|
+
};
|
|
241
|
+
/** Apply one structural op to the workbook in place; returns the change summary. */
|
|
242
|
+
var applyEdit = (X, wb, op, args) => {
|
|
243
|
+
switch (op) {
|
|
244
|
+
case "sheet.add_rows": {
|
|
245
|
+
const name = resolveSheetName(wb, args.sheet);
|
|
246
|
+
const rows = args.rows;
|
|
247
|
+
if (!Array.isArray(rows) || rows.length === 0 || !rows.every(Array.isArray)) throw new Error("rows must be a non-empty array of arrays");
|
|
248
|
+
const aoa = asAoa(X, wb.Sheets[name]);
|
|
249
|
+
const before = args.before;
|
|
250
|
+
const after = args.after;
|
|
251
|
+
const insertAt = before !== void 0 ? before - 1 : after !== void 0 ? after : aoa.length;
|
|
252
|
+
aoa.splice(insertAt, 0, ...rows);
|
|
253
|
+
rebuild(X, wb, name, aoa);
|
|
254
|
+
return { added: rows.length };
|
|
255
|
+
}
|
|
256
|
+
case "sheet.add_columns": {
|
|
257
|
+
const name = resolveSheetName(wb, args.sheet);
|
|
258
|
+
const headers = args.headers;
|
|
259
|
+
const descriptors = args.columns ?? headers?.map((header) => ({ header }));
|
|
260
|
+
if (!Array.isArray(descriptors) || descriptors.length === 0) throw new Error(`provide headers=a,b or columns='[{"header":"X","values":[1]}]'`);
|
|
261
|
+
const aoa = asAoa(X, wb.Sheets[name]);
|
|
262
|
+
const before = args.before;
|
|
263
|
+
const after = args.after;
|
|
264
|
+
const width = aoa.reduce((w, r) => Math.max(w, r.length), 0);
|
|
265
|
+
const insertAt = before !== void 0 ? before - 1 : after !== void 0 ? after : width;
|
|
266
|
+
for (const [i, col] of descriptors.entries()) {
|
|
267
|
+
if (typeof col?.header !== "string") throw new Error("every column needs a string header");
|
|
268
|
+
const columnCells = [col.header, ...col.values ?? []];
|
|
269
|
+
const rowsNeeded = Math.max(aoa.length, columnCells.length);
|
|
270
|
+
for (let r = 0; r < rowsNeeded; r++) {
|
|
271
|
+
aoa[r] ??= [];
|
|
272
|
+
while (aoa[r].length < insertAt + i) aoa[r].push(null);
|
|
273
|
+
aoa[r].splice(insertAt + i, 0, columnCells[r] ?? null);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
rebuild(X, wb, name, aoa);
|
|
277
|
+
return { added: descriptors.length };
|
|
278
|
+
}
|
|
279
|
+
case "sheet.update_cells": {
|
|
280
|
+
const name = resolveSheetName(wb, args.sheet);
|
|
281
|
+
const updates = args.updates;
|
|
282
|
+
if (!Array.isArray(updates) || updates.length === 0) throw new Error("updates must be a non-empty array");
|
|
283
|
+
const ws = wb.Sheets[name];
|
|
284
|
+
for (const update of updates) {
|
|
285
|
+
let row;
|
|
286
|
+
let col;
|
|
287
|
+
if (typeof update.address === "string") ({row, col} = parseA1(update.address));
|
|
288
|
+
else {
|
|
289
|
+
row = Number(update.row);
|
|
290
|
+
col = typeof update.col === "string" ? columnLetterToNumber(update.col) : Number(update.col);
|
|
291
|
+
if (!Number.isFinite(row) || !Number.isFinite(col)) throw new Error("each update needs an address (A1) or row+col");
|
|
292
|
+
}
|
|
293
|
+
const addr = X.utils.encode_cell({
|
|
294
|
+
r: row - 1,
|
|
295
|
+
c: col - 1
|
|
296
|
+
});
|
|
297
|
+
const value = update.value;
|
|
298
|
+
if (typeof value === "string" && value.startsWith("=")) ws[addr] = {
|
|
299
|
+
t: "n",
|
|
300
|
+
f: value.slice(1)
|
|
301
|
+
};
|
|
302
|
+
else if (value === null || value === void 0) delete ws[addr];
|
|
303
|
+
else if (typeof value === "number") ws[addr] = {
|
|
304
|
+
t: "n",
|
|
305
|
+
v: value
|
|
306
|
+
};
|
|
307
|
+
else if (typeof value === "boolean") ws[addr] = {
|
|
308
|
+
t: "b",
|
|
309
|
+
v: value
|
|
310
|
+
};
|
|
311
|
+
else ws[addr] = {
|
|
312
|
+
t: "s",
|
|
313
|
+
v: String(value)
|
|
314
|
+
};
|
|
315
|
+
const range = ws["!ref"] ? X.utils.decode_range(ws["!ref"]) : {
|
|
316
|
+
s: {
|
|
317
|
+
r: row - 1,
|
|
318
|
+
c: col - 1
|
|
319
|
+
},
|
|
320
|
+
e: {
|
|
321
|
+
r: row - 1,
|
|
322
|
+
c: col - 1
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
range.s.r = Math.min(range.s.r, row - 1);
|
|
326
|
+
range.s.c = Math.min(range.s.c, col - 1);
|
|
327
|
+
range.e.r = Math.max(range.e.r, row - 1);
|
|
328
|
+
range.e.c = Math.max(range.e.c, col - 1);
|
|
329
|
+
ws["!ref"] = X.utils.encode_range(range);
|
|
330
|
+
}
|
|
331
|
+
return { modified: updates.length };
|
|
332
|
+
}
|
|
333
|
+
case "sheet.delete_rows": {
|
|
334
|
+
const name = resolveSheetName(wb, args.sheet);
|
|
335
|
+
const rows = args.rows;
|
|
336
|
+
if (!Array.isArray(rows) || rows.length === 0) throw new Error("rows must be a non-empty array");
|
|
337
|
+
const aoa = asAoa(X, wb.Sheets[name]);
|
|
338
|
+
for (const r of [...rows].sort((a, b) => b - a)) aoa.splice(r - 1, 1);
|
|
339
|
+
rebuild(X, wb, name, aoa);
|
|
340
|
+
return { removed: rows.length };
|
|
341
|
+
}
|
|
342
|
+
case "sheet.delete_columns": {
|
|
343
|
+
const name = resolveSheetName(wb, args.sheet);
|
|
344
|
+
const columns = args.columns;
|
|
345
|
+
if (!Array.isArray(columns) || columns.length === 0) throw new Error("columns must be a non-empty array");
|
|
346
|
+
const aoa = asAoa(X, wb.Sheets[name]);
|
|
347
|
+
for (const c of [...columns].sort((a, b) => b - a)) for (const row of aoa) if (row.length >= c) row.splice(c - 1, 1);
|
|
348
|
+
rebuild(X, wb, name, aoa);
|
|
349
|
+
return { removed: columns.length };
|
|
350
|
+
}
|
|
351
|
+
case "sheet.rename_sheet": {
|
|
352
|
+
const from = String(args.sheet ?? "");
|
|
353
|
+
const to = String(args.to ?? "");
|
|
354
|
+
if (!wb.SheetNames.includes(from)) {
|
|
355
|
+
const names = wb.SheetNames.map((n) => `"${n}"`).join(", ");
|
|
356
|
+
throw new Error(`rename targets a sheet NAME; no sheet named "${from}". Sheets: ${names}`);
|
|
357
|
+
}
|
|
358
|
+
if (wb.SheetNames.includes(to)) throw new Error(`a sheet named "${to}" already exists`);
|
|
359
|
+
wb.SheetNames[wb.SheetNames.indexOf(from)] = to;
|
|
360
|
+
wb.Sheets[to] = wb.Sheets[from];
|
|
361
|
+
delete wb.Sheets[from];
|
|
362
|
+
return { modified: 1 };
|
|
363
|
+
}
|
|
364
|
+
case "sheet.add_sheet": {
|
|
365
|
+
const name = String(args.name ?? "");
|
|
366
|
+
if (!name) throw new Error("add_sheet requires a name");
|
|
367
|
+
if (wb.SheetNames.includes(name)) throw new Error(`a sheet named "${name}" already exists`);
|
|
368
|
+
const at = args.at;
|
|
369
|
+
X.utils.book_append_sheet(wb, X.utils.aoa_to_sheet([[]]), name);
|
|
370
|
+
if (at !== void 0) {
|
|
371
|
+
const idx = wb.SheetNames.indexOf(name);
|
|
372
|
+
wb.SheetNames.splice(idx, 1);
|
|
373
|
+
wb.SheetNames.splice(Math.max(0, at - 1), 0, name);
|
|
374
|
+
}
|
|
375
|
+
return { added: 1 };
|
|
376
|
+
}
|
|
377
|
+
case "sheet.remove_sheet": {
|
|
378
|
+
const name = String(args.sheet ?? "");
|
|
379
|
+
if (!wb.SheetNames.includes(name)) {
|
|
380
|
+
const names = wb.SheetNames.map((n) => `"${n}"`).join(", ");
|
|
381
|
+
throw new Error(`no sheet named "${name}" (sheets: ${names})`);
|
|
382
|
+
}
|
|
383
|
+
if (wb.SheetNames.length === 1) throw new Error("cannot remove the only worksheet");
|
|
384
|
+
wb.SheetNames.splice(wb.SheetNames.indexOf(name), 1);
|
|
385
|
+
delete wb.Sheets[name];
|
|
386
|
+
return { removed: 1 };
|
|
387
|
+
}
|
|
388
|
+
case "sheet.reorder_sheets": {
|
|
389
|
+
const order = args.order;
|
|
390
|
+
if (!Array.isArray(order) || order.length !== wb.SheetNames.length) throw new Error(`order must include every worksheet exactly once (the workbook has ${wb.SheetNames.length})`);
|
|
391
|
+
const resolved = order.map((ref) => typeof ref === "number" ? wb.SheetNames[ref - 1] : String(ref));
|
|
392
|
+
if (new Set(resolved).size !== resolved.length || resolved.some((n) => !n || !wb.SheetNames.includes(n))) throw new Error("order must reference every existing worksheet exactly once");
|
|
393
|
+
wb.SheetNames.length = 0;
|
|
394
|
+
wb.SheetNames.push(...resolved);
|
|
395
|
+
return { modified: resolved.length };
|
|
396
|
+
}
|
|
397
|
+
case "sheet.transform_table": {
|
|
398
|
+
const name = resolveSheetName(wb, args.sheet);
|
|
399
|
+
const headerRow = Number(args.header_row ?? 1);
|
|
400
|
+
const aoa = asAoa(X, wb.Sheets[name]);
|
|
401
|
+
const header = (aoa[headerRow - 1] ?? []).map((h) => h === null ? "" : String(h));
|
|
402
|
+
let modified = 0;
|
|
403
|
+
const rename = args.rename;
|
|
404
|
+
if (rename) for (const { from, to } of rename) {
|
|
405
|
+
const idx = header.indexOf(from);
|
|
406
|
+
if (idx !== -1) {
|
|
407
|
+
aoa[headerRow - 1][idx] = to;
|
|
408
|
+
header[idx] = to;
|
|
409
|
+
modified++;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
const drop = args.drop;
|
|
413
|
+
const select = args.select;
|
|
414
|
+
const toRemove = /* @__PURE__ */ new Set();
|
|
415
|
+
if (drop) for (const h of drop) {
|
|
416
|
+
const idx = header.indexOf(h);
|
|
417
|
+
if (idx !== -1) toRemove.add(idx);
|
|
418
|
+
}
|
|
419
|
+
if (select) {
|
|
420
|
+
for (const [i, element] of header.entries()) if (!select.includes(element)) toRemove.add(i);
|
|
421
|
+
}
|
|
422
|
+
if (toRemove.size > 0) {
|
|
423
|
+
const sorted = [...toRemove].sort((a, b) => b - a);
|
|
424
|
+
for (const row of aoa) for (const idx of sorted) if (row.length > idx) row.splice(idx, 1);
|
|
425
|
+
modified += toRemove.size;
|
|
426
|
+
}
|
|
427
|
+
rebuild(X, wb, name, aoa);
|
|
428
|
+
return { modified };
|
|
429
|
+
}
|
|
430
|
+
default: throw new Error(`sheetjs does not implement edit op "${op}"`);
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
//#endregion
|
|
434
|
+
export { sheetjsEngine };
|
|
435
|
+
|
|
436
|
+
//# sourceMappingURL=sheetjs.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sheetjs.mjs","names":[],"sources":["../../../../src/batteries/media/engines/sheetjs.ts"],"sourcesContent":["/**\n * The SheetJS-backed spreadsheet {@link @nhtio/adk/batteries/media/contracts!MediaEngine}:\n * the in-process, cross-environment spreadsheet converter, generator, and structural editor.\n *\n * @module @nhtio/adk/batteries/media/engines/sheetjs\n *\n * @remarks\n * SheetJS (the `xlsx` package) reads and writes the widest spreadsheet matrix of any pure-JS\n * library — XLSX/XLSM/XLSB, every XLS BIFF era, ODS/FODS, CSV, SYLK, DIF, DBF, HTML, RTF,\n * Apple NUMBERS — with no binary, no native bindings, in any environment. This engine exposes\n * that matrix three ways:\n *\n * - **convert**: any readable spreadsheet MIME → any writable format token.\n * - **generate**: `EMPTY_MIME` → any writable token (a blank `Sheet1` workbook, written out).\n * - **edit**: the `sheet.*` structural ops via CSF cell-object surgery. ⚠️ SheetJS Community\n * Edition does NOT model cell styling — fonts, fills, and comments are STRIPPED by any edit\n * or convert that round-trips a styled workbook (styling is a SheetJS Pro feature). For\n * fidelity-preserving edits compose the exceljs engine FIRST in the engines array; this\n * engine's edits are for unstyled/data workbooks and for breadth (it edits ODS, XLS, and\n * every other readable format by normalizing through CSF).\n *\n * ⚠️ **Install from the SheetJS CDN, not the npm registry.** The registry copy of `xlsx` is\n * frozen at 0.18.5 (2023) and carries CVE-2023-30533 and CVE-2024-22363; this engine requires\n * `>=0.20.2`. Install: `pnpm add xlsx@https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz`\n * (SheetJS recommends vendoring the tarball — see docs.sheetjs.com).\n *\n * `xlsx` is an optional peer dependency, lazily imported on first actual use. The NUMBERS\n * write target additionally loads the ZAHL payload (`xlsx/dist/xlsx.zahl.mjs`, ~110 KB) on\n * first request of that specific target.\n */\n\nimport { MIME } from '../formats'\nimport { EMPTY_MIME } from '../contracts'\nimport { isError } from '@nhtio/adk/guards'\nimport { E_INVALID_MEDIA_PIPELINE_CONFIG } from '../exceptions'\nimport type * as XlsxNS from 'xlsx'\nimport type {\n MediaEngine,\n ConvertRequest,\n ConvertResult,\n EditRequest,\n EditResult,\n} from '../contracts'\n\ntype XlsxModule = typeof XlsxNS\n\n/** Options for {@link sheetjsEngine}. */\nexport interface SheetjsEngineOptions {\n /** Override the module resolution (tests / custom builds). Default: `import('xlsx')`. */\n xlsx?: () => XlsxModule | Promise<XlsxModule>\n /**\n * Override the ZAHL payload resolution for NUMBERS output (tests / custom builds).\n * Default: `import('xlsx/dist/xlsx.zahl.mjs')`.\n */\n zahl?: () => unknown | Promise<unknown>\n}\n\n/** Every format token this engine can write, mapped to the SheetJS bookType that produces it. */\nconst BOOKTYPE_BY_TARGET: Record<string, string> = {\n xlsx: 'xlsx',\n xlsm: 'xlsm',\n xlsb: 'xlsb',\n xls: 'biff8',\n ods: 'ods',\n fods: 'fods',\n csv: 'csv',\n txt: 'txt',\n html: 'html',\n rtf: 'rtf',\n sylk: 'sylk',\n dif: 'dif',\n dbf: 'dbf',\n numbers: 'numbers',\n // json is synthesized via sheet_to_json, not a bookType — handled separately.\n}\n\n/** The convert/generation target tokens, json included. */\nconst SHEET_WRITE_TARGETS: readonly string[] = [...Object.keys(BOOKTYPE_BY_TARGET), 'json']\n\n/** Output MIME per target token. */\nconst MIME_BY_TARGET: Record<string, string> = {\n xlsx: MIME.XLSX,\n xlsm: MIME.XLSM,\n xlsb: MIME.XLSB,\n xls: MIME.XLS,\n ods: MIME.ODS,\n fods: MIME.FODS,\n csv: MIME.CSV,\n txt: MIME.TXT,\n html: MIME.HTML,\n rtf: 'application/rtf',\n sylk: MIME.SYLK,\n dif: MIME.DIF,\n dbf: MIME.DBF,\n numbers: MIME.NUMBERS,\n json: MIME.JSON,\n}\n\n/**\n * Every spreadsheet MIME this engine reads. SheetJS sniffs the actual format from the bytes\n * (zip vs CFB vs text heuristics), so one entry serves the whole read matrix. Formats SheetJS\n * can parse but that have no standard MIME (Quattro Pro, Works, Lotus variants) are readable\n * whenever a resolver hands us one of these MIMEs for them — the MIME is the only addressable\n * contract the battery has.\n */\nconst SHEET_READ_MIMES: readonly string[] = [\n MIME.XLSX,\n MIME.XLSM,\n MIME.XLSB,\n MIME.XLS,\n MIME.ODS,\n MIME.FODS,\n MIME.CSV,\n MIME.NUMBERS,\n MIME.SYLK,\n MIME.DIF,\n MIME.DBF,\n]\n\n/** The structural ops this engine edits (same names the verb table uses). */\nconst SHEET_OPS: readonly string[] = [\n 'sheet.add_rows',\n 'sheet.add_columns',\n 'sheet.update_cells',\n 'sheet.delete_rows',\n 'sheet.delete_columns',\n 'sheet.rename_sheet',\n 'sheet.add_sheet',\n 'sheet.remove_sheet',\n 'sheet.reorder_sheets',\n 'sheet.transform_table',\n]\n\n/**\n * Construct the SheetJS-backed spreadsheet engine.\n *\n * @param options - Optional module resolver overrides.\n * @returns The engine.\n */\nexport const sheetjsEngine = (options: SheetjsEngineOptions = {}): MediaEngine => {\n let modPromise: Promise<XlsxModule> | undefined\n const getXlsx = (): Promise<XlsxModule> => {\n modPromise ??= Promise.resolve(options.xlsx ? options.xlsx() : import('xlsx')).catch((err) => {\n const detail = isError(err) ? err.message : String(err)\n throw new E_INVALID_MEDIA_PIPELINE_CONFIG([\n `the sheetjs engine could not load its peer dependency \"xlsx\": ${detail} — install it FROM THE SHEETJS CDN (the npm registry copy is stale and vulnerable): pnpm add xlsx@https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz`,\n ])\n })\n return modPromise\n }\n\n let zahlPromise: Promise<unknown> | undefined\n const getZahl = (): Promise<unknown> => {\n zahlPromise ??= Promise.resolve(\n options.zahl ? options.zahl() : import('xlsx/dist/xlsx.zahl.mjs' as string)\n ).then(\n (m) => (m as { default?: unknown }).default ?? m,\n (err) => {\n const detail = isError(err) ? err.message : String(err)\n throw new Error(\n `NUMBERS output needs the SheetJS ZAHL payload (xlsx/dist/xlsx.zahl.mjs) and it could not be loaded: ${detail}`\n )\n }\n )\n return zahlPromise\n }\n\n /** Write `wb` to the requested target token. */\n const writeOut = async (\n X: XlsxModule,\n wb: XlsxNS.WorkBook,\n to: string\n ): Promise<{ bytes: Uint8Array; mimeType: string }> => {\n if (to === 'json') {\n const ws = wb.Sheets[wb.SheetNames[0]]\n const rows = ws ? X.utils.sheet_to_json(ws, { header: 1 }) : []\n return {\n bytes: new TextEncoder().encode(JSON.stringify(rows)),\n mimeType: MIME.JSON,\n }\n }\n const bookType = BOOKTYPE_BY_TARGET[to]\n if (!bookType) {\n throw new Error(`sheetjs cannot write \"${to}\"; supported: ${SHEET_WRITE_TARGETS.join(', ')}`)\n }\n const writeOpts: XlsxNS.WritingOptions = {\n bookType: bookType as XlsxNS.BookType,\n type: 'array',\n }\n if (to === 'numbers') {\n ;(writeOpts as { numbers?: unknown }).numbers = await getZahl()\n }\n const out = X.write(wb, writeOpts) as ArrayBuffer | Uint8Array\n const bytes = ArrayBuffer.isView(out) ? out : new Uint8Array(out)\n return { bytes, mimeType: MIME_BY_TARGET[to] ?? 'application/octet-stream' }\n }\n\n const convert = async (request: ConvertRequest): Promise<ConvertResult> => {\n const X = await getXlsx()\n const mime = request.mimeType.toLowerCase().split(';')[0].trim()\n let wb: XlsxNS.WorkBook\n if (mime === EMPTY_MIME) {\n // Generation: a blank workbook with one empty Sheet1 (steps expect ≥1 worksheet).\n // NUMBERS refuses a zero-cell sheet, so that target seeds one blank cell.\n wb = X.utils.book_new()\n const seed = request.to === 'numbers' ? [['']] : [[]]\n X.utils.book_append_sheet(wb, X.utils.aoa_to_sheet(seed), 'Sheet1')\n } else {\n wb = X.read(request.bytes)\n if (wb.SheetNames.length === 0) {\n throw new Error(\n `sheetjs parsed ${request.mimeType} but found no worksheets — the file may be corrupt or not actually a spreadsheet`\n )\n }\n }\n const { bytes, mimeType } = await writeOut(X, wb, request.to)\n return { outputs: [{ bytes, mimeType }] }\n }\n\n const edit = async (request: EditRequest): Promise<EditResult> => {\n const X = await getXlsx()\n const wb = X.read(request.bytes)\n if (wb.SheetNames.length === 0) {\n throw new Error('sheetjs parsed the workbook but found no worksheets')\n }\n const summary = applyEdit(X, wb, request.op, request.args)\n const { bytes, mimeType } = await writeOut(X, wb, 'xlsx')\n return { bytes, mimeType, summary }\n }\n\n return {\n id: 'sheetjs',\n converts: [\n // Generation: blank Sheet1 workbook, written to any target.\n { from: [EMPTY_MIME], to: SHEET_WRITE_TARGETS, convert },\n // The read matrix: format is sniffed from bytes, so one entry serves every MIME.\n { from: SHEET_READ_MIMES, to: SHEET_WRITE_TARGETS, convert },\n ],\n edits: [\n // ⚠️ CE strips styling (fonts/fills/comments) — compose exceljs first for fidelity.\n { over: SHEET_READ_MIMES, ops: SHEET_OPS, edit },\n ],\n }\n}\n\n// ── CSF structural edits ──────────────────────────────────────────────────────\n// SheetJS CE models a worksheet as a cell map; these ops normalize to array-of-arrays,\n// operate, and rebuild. Styling does not survive this (CE has none to carry).\n\nconst asAoa = (X: XlsxModule, ws: XlsxNS.WorkSheet): unknown[][] =>\n X.utils.sheet_to_json(ws, { header: 1, defval: null }) as unknown[][]\n\nconst resolveSheetName = (wb: XlsxNS.WorkBook, sheetArg: unknown): string => {\n if (sheetArg === undefined || sheetArg === null) {\n const first = wb.SheetNames[0]\n if (!first) throw new Error('the workbook has no worksheets')\n return first\n }\n if (typeof sheetArg === 'number') {\n const byIndex = wb.SheetNames[sheetArg - 1]\n if (byIndex) return byIndex\n if (wb.SheetNames.includes(String(sheetArg))) {\n throw new Error(\n `no sheet at index ${sheetArg}, but a sheet NAMED \"${sheetArg}\" exists — quote it: sheet=\"${sheetArg}\"`\n )\n }\n throw new Error(\n `sheet index ${sheetArg} is out of range (1-based; the workbook has ${wb.SheetNames.length})`\n )\n }\n const name = String(sheetArg)\n if (!wb.SheetNames.includes(name)) {\n const names = wb.SheetNames.map((n) => `\"${n}\"`).join(', ')\n throw new Error(`no sheet named \"${name}\" (sheets: ${names})`)\n }\n return name\n}\n\n/** Column letters → 1-based number (`A` → 1, `AA` → 27). */\nconst columnLetterToNumber = (letters: string): number => {\n let n = 0\n for (const ch of letters.toUpperCase()) n = n * 26 + (ch.charCodeAt(0) - 64)\n return n\n}\n\n/** Parse an A1 address into 1-based row/col. */\nconst parseA1 = (address: string): { row: number; col: number } => {\n const m = /^([A-Za-z]+)(\\d+)$/.exec(address.trim())\n if (!m) throw new Error(`invalid cell address \"${address}\" (expected A1 form)`)\n return { row: Number(m[2]), col: columnLetterToNumber(m[1]) }\n}\n\nconst rebuild = (X: XlsxModule, wb: XlsxNS.WorkBook, name: string, aoa: unknown[][]): void => {\n wb.Sheets[name] = X.utils.aoa_to_sheet(aoa.length > 0 ? aoa : [[]])\n}\n\n/** Apply one structural op to the workbook in place; returns the change summary. */\nconst applyEdit = (\n X: XlsxModule,\n wb: XlsxNS.WorkBook,\n op: string,\n args: Record<string, unknown>\n): { added?: number; removed?: number; modified?: number; warnings?: string[] } => {\n switch (op) {\n case 'sheet.add_rows': {\n const name = resolveSheetName(wb, args.sheet)\n const rows = args.rows as unknown[][]\n if (!Array.isArray(rows) || rows.length === 0 || !rows.every(Array.isArray)) {\n throw new Error('rows must be a non-empty array of arrays')\n }\n const aoa = asAoa(X, wb.Sheets[name])\n const before = args.before as number | undefined\n const after = args.after as number | undefined\n const insertAt = before !== undefined ? before - 1 : after !== undefined ? after : aoa.length\n aoa.splice(insertAt, 0, ...rows)\n rebuild(X, wb, name, aoa)\n return { added: rows.length }\n }\n case 'sheet.add_columns': {\n const name = resolveSheetName(wb, args.sheet)\n const headers = args.headers as string[] | undefined\n const descriptors: Array<{ header: string; values?: unknown[] }> | undefined =\n (args.columns as Array<{ header: string; values?: unknown[] }> | undefined) ??\n headers?.map((header) => ({ header }))\n if (!Array.isArray(descriptors) || descriptors.length === 0) {\n throw new Error(`provide headers=a,b or columns='[{\"header\":\"X\",\"values\":[1]}]'`)\n }\n const aoa = asAoa(X, wb.Sheets[name])\n const before = args.before as number | undefined\n const after = args.after as number | undefined\n const width = aoa.reduce((w, r) => Math.max(w, r.length), 0)\n const insertAt = before !== undefined ? before - 1 : after !== undefined ? after : width\n for (const [i, col] of descriptors.entries()) {\n if (typeof col?.header !== 'string') throw new Error('every column needs a string header')\n const columnCells = [col.header, ...(col.values ?? [])]\n const rowsNeeded = Math.max(aoa.length, columnCells.length)\n for (let r = 0; r < rowsNeeded; r++) {\n aoa[r] ??= []\n while (aoa[r].length < insertAt + i) aoa[r].push(null)\n aoa[r].splice(insertAt + i, 0, columnCells[r] ?? null)\n }\n }\n rebuild(X, wb, name, aoa)\n return { added: descriptors.length }\n }\n case 'sheet.update_cells': {\n const name = resolveSheetName(wb, args.sheet)\n const updates = args.updates as Array<Record<string, unknown>>\n if (!Array.isArray(updates) || updates.length === 0) {\n throw new Error('updates must be a non-empty array')\n }\n const ws = wb.Sheets[name]\n for (const update of updates) {\n let row: number\n let col: number\n if (typeof update.address === 'string') {\n ;({ row, col } = parseA1(update.address))\n } else {\n row = Number(update.row)\n col =\n typeof update.col === 'string' ? columnLetterToNumber(update.col) : Number(update.col)\n if (!Number.isFinite(row) || !Number.isFinite(col)) {\n throw new Error('each update needs an address (A1) or row+col')\n }\n }\n const addr = X.utils.encode_cell({ r: row - 1, c: col - 1 })\n const value = update.value\n if (typeof value === 'string' && value.startsWith('=')) {\n ws[addr] = { t: 'n', f: value.slice(1) }\n } else if (value === null || value === undefined) {\n delete ws[addr]\n } else if (typeof value === 'number') {\n ws[addr] = { t: 'n', v: value }\n } else if (typeof value === 'boolean') {\n ws[addr] = { t: 'b', v: value }\n } else {\n ws[addr] = { t: 's', v: String(value) }\n }\n const range = ws['!ref']\n ? X.utils.decode_range(ws['!ref'] as string)\n : { s: { r: row - 1, c: col - 1 }, e: { r: row - 1, c: col - 1 } }\n range.s.r = Math.min(range.s.r, row - 1)\n range.s.c = Math.min(range.s.c, col - 1)\n range.e.r = Math.max(range.e.r, row - 1)\n range.e.c = Math.max(range.e.c, col - 1)\n ws['!ref'] = X.utils.encode_range(range)\n }\n return { modified: updates.length }\n }\n case 'sheet.delete_rows': {\n const name = resolveSheetName(wb, args.sheet)\n const rows = args.rows as number[]\n if (!Array.isArray(rows) || rows.length === 0)\n throw new Error('rows must be a non-empty array')\n const aoa = asAoa(X, wb.Sheets[name])\n for (const r of [...rows].sort((a, b) => b - a)) aoa.splice(r - 1, 1)\n rebuild(X, wb, name, aoa)\n return { removed: rows.length }\n }\n case 'sheet.delete_columns': {\n const name = resolveSheetName(wb, args.sheet)\n const columns = args.columns as number[]\n if (!Array.isArray(columns) || columns.length === 0) {\n throw new Error('columns must be a non-empty array')\n }\n const aoa = asAoa(X, wb.Sheets[name])\n for (const c of [...columns].sort((a, b) => b - a)) {\n for (const row of aoa) if (row.length >= c) row.splice(c - 1, 1)\n }\n rebuild(X, wb, name, aoa)\n return { removed: columns.length }\n }\n case 'sheet.rename_sheet': {\n const from = String(args.sheet ?? '')\n const to = String(args.to ?? '')\n if (!wb.SheetNames.includes(from)) {\n const names = wb.SheetNames.map((n) => `\"${n}\"`).join(', ')\n throw new Error(`rename targets a sheet NAME; no sheet named \"${from}\". Sheets: ${names}`)\n }\n if (wb.SheetNames.includes(to)) throw new Error(`a sheet named \"${to}\" already exists`)\n wb.SheetNames[wb.SheetNames.indexOf(from)] = to\n wb.Sheets[to] = wb.Sheets[from]\n delete wb.Sheets[from]\n return { modified: 1 }\n }\n case 'sheet.add_sheet': {\n const name = String(args.name ?? '')\n if (!name) throw new Error('add_sheet requires a name')\n if (wb.SheetNames.includes(name)) throw new Error(`a sheet named \"${name}\" already exists`)\n const at = args.at as number | undefined\n X.utils.book_append_sheet(wb, X.utils.aoa_to_sheet([[]]), name)\n if (at !== undefined) {\n const idx = wb.SheetNames.indexOf(name)\n wb.SheetNames.splice(idx, 1)\n wb.SheetNames.splice(Math.max(0, at - 1), 0, name)\n }\n return { added: 1 }\n }\n case 'sheet.remove_sheet': {\n const name = String(args.sheet ?? '')\n if (!wb.SheetNames.includes(name)) {\n const names = wb.SheetNames.map((n) => `\"${n}\"`).join(', ')\n throw new Error(`no sheet named \"${name}\" (sheets: ${names})`)\n }\n if (wb.SheetNames.length === 1) throw new Error('cannot remove the only worksheet')\n wb.SheetNames.splice(wb.SheetNames.indexOf(name), 1)\n delete wb.Sheets[name]\n return { removed: 1 }\n }\n case 'sheet.reorder_sheets': {\n const order = args.order as Array<string | number>\n if (!Array.isArray(order) || order.length !== wb.SheetNames.length) {\n throw new Error(\n `order must include every worksheet exactly once (the workbook has ${wb.SheetNames.length})`\n )\n }\n const resolved = order.map((ref) =>\n typeof ref === 'number' ? wb.SheetNames[ref - 1] : String(ref)\n )\n if (\n new Set(resolved).size !== resolved.length ||\n resolved.some((n) => !n || !wb.SheetNames.includes(n))\n ) {\n throw new Error('order must reference every existing worksheet exactly once')\n }\n wb.SheetNames.length = 0\n wb.SheetNames.push(...resolved)\n return { modified: resolved.length }\n }\n case 'sheet.transform_table': {\n const name = resolveSheetName(wb, args.sheet)\n const headerRow = Number(args.header_row ?? 1)\n const aoa = asAoa(X, wb.Sheets[name])\n const header = (aoa[headerRow - 1] ?? []).map((h) => (h === null ? '' : String(h)))\n let modified = 0\n const rename = args.rename as Array<{ from: string; to: string }> | undefined\n if (rename) {\n for (const { from, to } of rename) {\n const idx = header.indexOf(from)\n if (idx !== -1) {\n aoa[headerRow - 1][idx] = to\n header[idx] = to\n modified++\n }\n }\n }\n const drop = args.drop as string[] | undefined\n const select = args.select as string[] | undefined\n const toRemove = new Set<number>()\n if (drop)\n for (const h of drop) {\n const idx = header.indexOf(h)\n if (idx !== -1) toRemove.add(idx)\n }\n if (select) {\n for (const [i, element] of header.entries()) {\n if (!select.includes(element)) toRemove.add(i)\n }\n }\n if (toRemove.size > 0) {\n const sorted = [...toRemove].sort((a, b) => b - a)\n for (const row of aoa) for (const idx of sorted) if (row.length > idx) row.splice(idx, 1)\n modified += toRemove.size\n }\n rebuild(X, wb, name, aoa)\n return { modified }\n }\n default:\n throw new Error(`sheetjs does not implement edit op \"${op}\"`)\n }\n}\n"],"mappings":"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"}
|
|
@@ -150,6 +150,34 @@ var CONVERT_TOKEN = {
|
|
|
150
150
|
md: "txt:Text",
|
|
151
151
|
json: "csv"
|
|
152
152
|
};
|
|
153
|
+
/**
|
|
154
|
+
* Seed extension per generation target: which silo's empty file to materialize when the
|
|
155
|
+
* source is {@link EMPTY_MIME}. LibreOffice treats a zero-byte input as an empty document of
|
|
156
|
+
* the extension's type (verified across all three silos, both directions, on 25.x — pinned by
|
|
157
|
+
* a binary-gated spec), so generation needs no templates at all. The seed is always a
|
|
158
|
+
* DIFFERENT extension than the target within the same silo, because soffice won't "convert"
|
|
159
|
+
* docx to docx.
|
|
160
|
+
*/
|
|
161
|
+
var SEED_EXT_FOR_TARGET = {
|
|
162
|
+
docx: "odt",
|
|
163
|
+
odt: "docx",
|
|
164
|
+
doc: "docx",
|
|
165
|
+
rtf: "docx",
|
|
166
|
+
pdf: "docx",
|
|
167
|
+
html: "docx",
|
|
168
|
+
txt: "docx",
|
|
169
|
+
md: "docx",
|
|
170
|
+
xlsx: "ods",
|
|
171
|
+
ods: "xlsx",
|
|
172
|
+
xls: "xlsx",
|
|
173
|
+
csv: "xlsx",
|
|
174
|
+
json: "xlsx",
|
|
175
|
+
pptx: "odp",
|
|
176
|
+
odp: "pptx",
|
|
177
|
+
ppt: "pptx"
|
|
178
|
+
};
|
|
179
|
+
/** Every format this engine can generate from {@link EMPTY_MIME}. */
|
|
180
|
+
var GENERATION_TARGETS = Object.keys(SEED_EXT_FOR_TARGET);
|
|
153
181
|
var validateOptions = (options, name) => {
|
|
154
182
|
if (typeof options?.path !== "string" || options.path.length === 0) throw new require_exceptions.E_INVALID_MEDIA_PIPELINE_CONFIG([`${name} requires the soffice binary path`]);
|
|
155
183
|
if (!require_batteries_media_contracts.implementsBinaryExecutor(options.executor)) throw new require_exceptions.E_INVALID_MEDIA_PIPELINE_CONFIG([`${name} requires an executor implementing the BinaryExecutor contract`]);
|
|
@@ -200,9 +228,10 @@ var MIMES_OF = (exts) => Object.entries(EXT_BY_MIME).filter(([, ext]) => exts.in
|
|
|
200
228
|
var sofficeEngine = (options) => {
|
|
201
229
|
validateOptions(options, "sofficeEngine");
|
|
202
230
|
const convert = async (request) => {
|
|
203
|
-
const
|
|
231
|
+
const mime = request.mimeType.toLowerCase().split(";")[0].trim();
|
|
232
|
+
const inputExt = mime === "application/x-adk-empty" ? SEED_EXT_FOR_TARGET[request.to] : EXT_BY_MIME[mime];
|
|
204
233
|
if (!inputExt) throw new Error(`unsupported input MIME for conversion: ${request.mimeType}`);
|
|
205
|
-
const targets = TARGETS_BY_EXT[inputExt] ?? [];
|
|
234
|
+
const targets = mime === "application/x-adk-empty" ? GENERATION_TARGETS : TARGETS_BY_EXT[inputExt] ?? [];
|
|
206
235
|
if (!targets.includes(request.to)) throw new Error(`cannot convert ${inputExt} to ${request.to}; supported: ${targets.join(", ")}`);
|
|
207
236
|
const convertTo = CONVERT_TOKEN[request.to] ?? request.to;
|
|
208
237
|
const outputExt = request.to === "md" ? "txt" : request.to === "json" ? "csv" : request.to;
|
|
@@ -236,7 +265,12 @@ var sofficeEngine = (options) => {
|
|
|
236
265
|
group(["pptx"], TARGETS_BY_EXT.pptx),
|
|
237
266
|
group(["odp"], TARGETS_BY_EXT.odp),
|
|
238
267
|
group(["ppt"], TARGETS_BY_EXT.ppt),
|
|
239
|
-
group(["pdf"], TARGETS_BY_EXT.pdf)
|
|
268
|
+
group(["pdf"], TARGETS_BY_EXT.pdf),
|
|
269
|
+
{
|
|
270
|
+
from: [require_batteries_media_contracts.EMPTY_MIME],
|
|
271
|
+
to: GENERATION_TARGETS,
|
|
272
|
+
convert
|
|
273
|
+
}
|
|
240
274
|
]
|
|
241
275
|
};
|
|
242
276
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"soffice.cjs","names":[],"sources":["../../../../src/batteries/media/engines/soffice.ts"],"sourcesContent":["/**\n * The LibreOffice-backed {@link @nhtio/adk/batteries/media/contracts!MediaEngine}: document,\n * spreadsheet, and presentation conversion via the `soffice` binary.\n *\n * @module @nhtio/adk/batteries/media/engines/soffice\n *\n * @remarks\n * Document conversion is the one capability in the media battery with no mature\n * cross-environment equivalent, so this engine is binary-backed by design. It composes the\n * two BYO runtime contracts: a {@link @nhtio/adk/batteries/media/contracts!BinaryExecutor}\n * runs the invocation (bundled: `execa_executor`) and a\n * {@link @nhtio/adk/batteries/media/contracts!ScratchWorkspace} exchanges bytes with it\n * (bundled: `fs_workspace`). The executor and workspace must agree on path visibility —\n * that pairing is the consumer's composition decision.\n *\n * One engine, one capability kind: the conversion matrix is declared as convert capability\n * groups (each format silo converts within itself plus to PDF/HTML; PDF converts to\n * html/txt/docx/odt). The spreadsheet group covers ODS/legacy-xls to xlsx — what used to be\n * a separate \"normalize\" engine is just an edge in the matrix.\n */\n\nimport { E_INVALID_MEDIA_PIPELINE_CONFIG } from '../exceptions'\nimport { implementsBinaryExecutor, implementsScratchWorkspace } from '../contracts'\nimport type {\n MediaEngine,\n ConvertCapability,\n ConvertRequest,\n ConvertResult,\n BinaryExecutor,\n ScratchWorkspaceFactory,\n} from '../contracts'\n\n/** Options for {@link sofficeEngine}. */\nexport interface SofficeEngineOptions {\n /** Path (or resolvable name) of the soffice binary. */\n path: string\n /** Runs the soffice invocation. Required — no platform default. */\n executor: BinaryExecutor\n /** Mints a scratch dir per invocation whose paths the executor can open. Required. */\n workspace: ScratchWorkspaceFactory\n /** Per-invocation timeout. Default 120000. */\n timeoutMs?: number\n}\n\nconst MIME_BY_TARGET: Record<string, string> = {\n pdf: 'application/pdf',\n html: 'text/html',\n txt: 'text/plain',\n md: 'text/markdown',\n csv: 'text/csv',\n json: 'application/json',\n docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n doc: 'application/msword',\n rtf: 'application/rtf',\n odt: 'application/vnd.oasis.opendocument.text',\n xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n xls: 'application/vnd.ms-excel',\n ods: 'application/vnd.oasis.opendocument.spreadsheet',\n pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n ppt: 'application/vnd.ms-powerpoint',\n odp: 'application/vnd.oasis.opendocument.presentation',\n}\n\nconst EXT_BY_MIME: Record<string, string> = {\n 'application/pdf': 'pdf',\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'docx',\n 'application/msword': 'doc',\n 'application/rtf': 'rtf',\n 'text/rtf': 'rtf',\n 'application/vnd.oasis.opendocument.text': 'odt',\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'xlsx',\n 'application/vnd.ms-excel': 'xls',\n 'application/vnd.oasis.opendocument.spreadsheet': 'ods',\n 'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'pptx',\n 'application/vnd.ms-powerpoint': 'ppt',\n 'application/vnd.oasis.opendocument.presentation': 'odp',\n 'text/plain': 'txt',\n 'text/html': 'html',\n 'text/csv': 'csv',\n}\n\n/** Server-parity conversion matrix: same-silo targets plus pdf/html. */\nconst TARGETS_BY_EXT: Record<string, readonly string[]> = {\n docx: ['pdf', 'html', 'txt', 'md', 'odt', 'doc', 'rtf'],\n odt: ['pdf', 'html', 'txt', 'md', 'docx', 'doc', 'rtf'],\n doc: ['pdf', 'html', 'txt', 'md', 'docx', 'odt', 'rtf'],\n rtf: ['pdf', 'html', 'txt', 'md', 'docx', 'odt', 'doc'],\n xlsx: ['pdf', 'html', 'csv', 'json', 'ods', 'xls'],\n ods: ['pdf', 'html', 'csv', 'json', 'xlsx', 'xls'],\n xls: ['pdf', 'html', 'csv', 'json', 'xlsx', 'ods'],\n pptx: ['pdf', 'html', 'odp', 'ppt'],\n odp: ['pdf', 'html', 'pptx', 'ppt'],\n ppt: ['pdf', 'html', 'pptx', 'odp'],\n pdf: ['html', 'txt', 'docx', 'odt'],\n}\n\n/** soffice convert-to filter token per target (md goes via txt; json via csv). */\nconst CONVERT_TOKEN: Record<string, string> = {\n md: 'txt:Text',\n json: 'csv',\n}\n\nconst validateOptions = (options: SofficeEngineOptions, name: string): void => {\n if (typeof options?.path !== 'string' || options.path.length === 0) {\n throw new E_INVALID_MEDIA_PIPELINE_CONFIG([`${name} requires the soffice binary path`])\n }\n if (!implementsBinaryExecutor(options.executor)) {\n throw new E_INVALID_MEDIA_PIPELINE_CONFIG([\n `${name} requires an executor implementing the BinaryExecutor contract`,\n ])\n }\n if (typeof options.workspace !== 'function') {\n throw new E_INVALID_MEDIA_PIPELINE_CONFIG([\n `${name} requires a workspace factory (e.g. fsScratchWorkspace({ root }))`,\n ])\n }\n}\n\nconst runSoffice = async (\n options: SofficeEngineOptions,\n bytes: Uint8Array,\n inputExt: string,\n convertTo: string,\n outputExt: string,\n signal?: AbortSignal\n): Promise<Uint8Array> => {\n const workspace = await options.workspace()\n if (!implementsScratchWorkspace(workspace)) {\n throw new E_INVALID_MEDIA_PIPELINE_CONFIG([\n 'the workspace factory minted a value that does not implement ScratchWorkspace',\n ])\n }\n try {\n const inputPath = await workspace.materialize(bytes, `input.${inputExt}`)\n const result = await options.executor.exec({\n cmd: options.path,\n args: [\n '--headless',\n '--nologo',\n '--nodefault',\n '--norestore',\n '--nolockcheck',\n '--convert-to',\n convertTo,\n '--outdir',\n workspace.dir(),\n inputPath,\n ],\n timeoutMs: options.timeoutMs ?? 120_000,\n signal,\n })\n if (result.failed) {\n const detail = result.stderr || result.stdout || `exit code ${result.exitCode}`\n throw new Error(`LibreOffice conversion failed: ${detail}`)\n }\n const files = await workspace.list()\n const produced = files.find(\n (f) => f.toLowerCase().endsWith(`.${outputExt}`) && f !== `input.${inputExt}`\n )\n if (!produced) {\n throw new Error(\n `LibreOffice reported success but produced no .${outputExt} output (files: ${files.join(', ')})`\n )\n }\n return workspace.read(`${workspace.dir()}/${produced}`)\n } finally {\n await workspace.dispose()\n }\n}\n\n/** The MIME types of one extension group, for capability `from` declarations. */\nconst MIMES_OF = (exts: readonly string[]): string[] =>\n Object.entries(EXT_BY_MIME)\n .filter(([, ext]) => exts.includes(ext))\n .map(([mime]) => mime)\n\n/**\n * Construct the LibreOffice engine.\n *\n * @param options - Binary path, executor, workspace factory, timeout.\n * @returns The engine, declaring one convert capability group per format silo.\n */\nexport const sofficeEngine = (options: SofficeEngineOptions): MediaEngine => {\n validateOptions(options, 'sofficeEngine')\n\n const convert = async (request: ConvertRequest): Promise<ConvertResult> => {\n const mime = request.mimeType.toLowerCase().split(';')[0].trim()\n const inputExt = EXT_BY_MIME[mime]\n if (!inputExt) throw new Error(`unsupported input MIME for conversion: ${request.mimeType}`)\n const targets = TARGETS_BY_EXT[inputExt] ?? []\n if (!targets.includes(request.to)) {\n throw new Error(\n `cannot convert ${inputExt} to ${request.to}; supported: ${targets.join(', ')}`\n )\n }\n const convertTo = CONVERT_TOKEN[request.to] ?? request.to\n const outputExt = request.to === 'md' ? 'txt' : request.to === 'json' ? 'csv' : request.to\n let bytes = await runSoffice(\n options,\n request.bytes,\n inputExt,\n convertTo,\n outputExt,\n request.signal\n )\n let mimeType = MIME_BY_TARGET[request.to] ?? 'application/octet-stream'\n if (request.to === 'json') {\n // soffice emits CSV; lower to a JSON array-of-arrays for predictable structure.\n const text = new TextDecoder().decode(bytes)\n const rows = text\n .split(/\\r?\\n/)\n .filter((line) => line.length > 0)\n .map((line) => line.split(','))\n bytes = new TextEncoder().encode(JSON.stringify(rows))\n mimeType = MIME_BY_TARGET.json\n }\n return { outputs: [{ bytes, mimeType }] }\n }\n\n // One capability group per uniform from×to block of the matrix.\n const group = (exts: readonly string[], to: readonly string[]): ConvertCapability => ({\n from: MIMES_OF(exts),\n to,\n convert,\n })\n\n return {\n id: 'soffice',\n converts: [\n group(['docx'], TARGETS_BY_EXT.docx),\n group(['odt'], TARGETS_BY_EXT.odt),\n group(['doc'], TARGETS_BY_EXT.doc),\n group(['rtf'], TARGETS_BY_EXT.rtf),\n group(['xlsx'], TARGETS_BY_EXT.xlsx),\n group(['ods'], TARGETS_BY_EXT.ods),\n group(['xls'], TARGETS_BY_EXT.xls),\n group(['pptx'], TARGETS_BY_EXT.pptx),\n group(['odp'], TARGETS_BY_EXT.odp),\n group(['ppt'], TARGETS_BY_EXT.ppt),\n group(['pdf'], TARGETS_BY_EXT.pdf),\n ],\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA4CA,IAAM,iBAAyC;CAC7C,KAAK;CACL,MAAM;CACN,KAAK;CACL,IAAI;CACJ,KAAK;CACL,MAAM;CACN,MAAM;CACN,KAAK;CACL,KAAK;CACL,KAAK;CACL,MAAM;CACN,KAAK;CACL,KAAK;CACL,MAAM;CACN,KAAK;CACL,KAAK;AACP;AAEA,IAAM,cAAsC;CAC1C,mBAAmB;CACnB,2EAA2E;CAC3E,sBAAsB;CACtB,mBAAmB;CACnB,YAAY;CACZ,2CAA2C;CAC3C,qEAAqE;CACrE,4BAA4B;CAC5B,kDAAkD;CAClD,6EAA6E;CAC7E,iCAAiC;CACjC,mDAAmD;CACnD,cAAc;CACd,aAAa;CACb,YAAY;AACd;;AAGA,IAAM,iBAAoD;CACxD,MAAM;EAAC;EAAO;EAAQ;EAAO;EAAM;EAAO;EAAO;CAAK;CACtD,KAAK;EAAC;EAAO;EAAQ;EAAO;EAAM;EAAQ;EAAO;CAAK;CACtD,KAAK;EAAC;EAAO;EAAQ;EAAO;EAAM;EAAQ;EAAO;CAAK;CACtD,KAAK;EAAC;EAAO;EAAQ;EAAO;EAAM;EAAQ;EAAO;CAAK;CACtD,MAAM;EAAC;EAAO;EAAQ;EAAO;EAAQ;EAAO;CAAK;CACjD,KAAK;EAAC;EAAO;EAAQ;EAAO;EAAQ;EAAQ;CAAK;CACjD,KAAK;EAAC;EAAO;EAAQ;EAAO;EAAQ;EAAQ;CAAK;CACjD,MAAM;EAAC;EAAO;EAAQ;EAAO;CAAK;CAClC,KAAK;EAAC;EAAO;EAAQ;EAAQ;CAAK;CAClC,KAAK;EAAC;EAAO;EAAQ;EAAQ;CAAK;CAClC,KAAK;EAAC;EAAQ;EAAO;EAAQ;CAAK;AACpC;;AAGA,IAAM,gBAAwC;CAC5C,IAAI;CACJ,MAAM;AACR;AAEA,IAAM,mBAAmB,SAA+B,SAAuB;CAC7E,IAAI,OAAO,SAAS,SAAS,YAAY,QAAQ,KAAK,WAAW,GAC/D,MAAM,IAAI,mBAAA,gCAAgC,CAAC,GAAG,KAAK,kCAAkC,CAAC;CAExF,IAAI,CAAC,kCAAA,yBAAyB,QAAQ,QAAQ,GAC5C,MAAM,IAAI,mBAAA,gCAAgC,CACxC,GAAG,KAAK,+DACV,CAAC;CAEH,IAAI,OAAO,QAAQ,cAAc,YAC/B,MAAM,IAAI,mBAAA,gCAAgC,CACxC,GAAG,KAAK,kEACV,CAAC;AAEL;AAEA,IAAM,aAAa,OACjB,SACA,OACA,UACA,WACA,WACA,WACwB;CACxB,MAAM,YAAY,MAAM,QAAQ,UAAU;CAC1C,IAAI,CAAC,kCAAA,2BAA2B,SAAS,GACvC,MAAM,IAAI,mBAAA,gCAAgC,CACxC,+EACF,CAAC;CAEH,IAAI;EACF,MAAM,YAAY,MAAM,UAAU,YAAY,OAAO,SAAS,UAAU;EACxE,MAAM,SAAS,MAAM,QAAQ,SAAS,KAAK;GACzC,KAAK,QAAQ;GACb,MAAM;IACJ;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA,UAAU,IAAI;IACd;GACF;GACA,WAAW,QAAQ,aAAa;GAChC;EACF,CAAC;EACD,IAAI,OAAO,QAAQ;GACjB,MAAM,SAAS,OAAO,UAAU,OAAO,UAAU,aAAa,OAAO;GACrE,MAAM,IAAI,MAAM,kCAAkC,QAAQ;EAC5D;EACA,MAAM,QAAQ,MAAM,UAAU,KAAK;EACnC,MAAM,WAAW,MAAM,MACpB,MAAM,EAAE,YAAY,EAAE,SAAS,IAAI,WAAW,KAAK,MAAM,SAAS,UACrE;EACA,IAAI,CAAC,UACH,MAAM,IAAI,MACR,iDAAiD,UAAU,kBAAkB,MAAM,KAAK,IAAI,EAAE,EAChG;EAEF,OAAO,UAAU,KAAK,GAAG,UAAU,IAAI,EAAE,GAAG,UAAU;CACxD,UAAU;EACR,MAAM,UAAU,QAAQ;CAC1B;AACF;;AAGA,IAAM,YAAY,SAChB,OAAO,QAAQ,WAAW,EACvB,QAAQ,GAAG,SAAS,KAAK,SAAS,GAAG,CAAC,EACtC,KAAK,CAAC,UAAU,IAAI;;;;;;;AAQzB,IAAa,iBAAiB,YAA+C;CAC3E,gBAAgB,SAAS,eAAe;CAExC,MAAM,UAAU,OAAO,YAAoD;EAEzE,MAAM,WAAW,YADJ,QAAQ,SAAS,YAAY,EAAE,MAAM,GAAG,EAAE,GAAG,KAC7B;EAC7B,IAAI,CAAC,UAAU,MAAM,IAAI,MAAM,0CAA0C,QAAQ,UAAU;EAC3F,MAAM,UAAU,eAAe,aAAa,CAAC;EAC7C,IAAI,CAAC,QAAQ,SAAS,QAAQ,EAAE,GAC9B,MAAM,IAAI,MACR,kBAAkB,SAAS,MAAM,QAAQ,GAAG,eAAe,QAAQ,KAAK,IAAI,GAC9E;EAEF,MAAM,YAAY,cAAc,QAAQ,OAAO,QAAQ;EACvD,MAAM,YAAY,QAAQ,OAAO,OAAO,QAAQ,QAAQ,OAAO,SAAS,QAAQ,QAAQ;EACxF,IAAI,QAAQ,MAAM,WAChB,SACA,QAAQ,OACR,UACA,WACA,WACA,QAAQ,MACV;EACA,IAAI,WAAW,eAAe,QAAQ,OAAO;EAC7C,IAAI,QAAQ,OAAO,QAAQ;GAGzB,MAAM,OADO,IAAI,YAAY,EAAE,OAAO,KACzB,EACV,MAAM,OAAO,EACb,QAAQ,SAAS,KAAK,SAAS,CAAC,EAChC,KAAK,SAAS,KAAK,MAAM,GAAG,CAAC;GAChC,QAAQ,IAAI,YAAY,EAAE,OAAO,KAAK,UAAU,IAAI,CAAC;GACrD,WAAW,eAAe;EAC5B;EACA,OAAO,EAAE,SAAS,CAAC;GAAE;GAAO;EAAS,CAAC,EAAE;CAC1C;CAGA,MAAM,SAAS,MAAyB,QAA8C;EACpF,MAAM,SAAS,IAAI;EACnB;EACA;CACF;CAEA,OAAO;EACL,IAAI;EACJ,UAAU;GACR,MAAM,CAAC,MAAM,GAAG,eAAe,IAAI;GACnC,MAAM,CAAC,KAAK,GAAG,eAAe,GAAG;GACjC,MAAM,CAAC,KAAK,GAAG,eAAe,GAAG;GACjC,MAAM,CAAC,KAAK,GAAG,eAAe,GAAG;GACjC,MAAM,CAAC,MAAM,GAAG,eAAe,IAAI;GACnC,MAAM,CAAC,KAAK,GAAG,eAAe,GAAG;GACjC,MAAM,CAAC,KAAK,GAAG,eAAe,GAAG;GACjC,MAAM,CAAC,MAAM,GAAG,eAAe,IAAI;GACnC,MAAM,CAAC,KAAK,GAAG,eAAe,GAAG;GACjC,MAAM,CAAC,KAAK,GAAG,eAAe,GAAG;GACjC,MAAM,CAAC,KAAK,GAAG,eAAe,GAAG;EACnC;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"soffice.cjs","names":[],"sources":["../../../../src/batteries/media/engines/soffice.ts"],"sourcesContent":["/**\n * The LibreOffice-backed {@link @nhtio/adk/batteries/media/contracts!MediaEngine}: document,\n * spreadsheet, and presentation conversion via the `soffice` binary.\n *\n * @module @nhtio/adk/batteries/media/engines/soffice\n *\n * @remarks\n * Document conversion is the one capability in the media battery with no mature\n * cross-environment equivalent, so this engine is binary-backed by design. It composes the\n * two BYO runtime contracts: a {@link @nhtio/adk/batteries/media/contracts!BinaryExecutor}\n * runs the invocation (bundled: `execa_executor`) and a\n * {@link @nhtio/adk/batteries/media/contracts!ScratchWorkspace} exchanges bytes with it\n * (bundled: `fs_workspace`). The executor and workspace must agree on path visibility —\n * that pairing is the consumer's composition decision.\n *\n * One engine, one capability kind: the conversion matrix is declared as convert capability\n * groups (each format silo converts within itself plus to PDF/HTML; PDF converts to\n * html/txt/docx/odt). The spreadsheet group covers ODS/legacy-xls to xlsx — what used to be\n * a separate \"normalize\" engine is just an edge in the matrix.\n */\n\nimport { E_INVALID_MEDIA_PIPELINE_CONFIG } from '../exceptions'\nimport { EMPTY_MIME, implementsBinaryExecutor, implementsScratchWorkspace } from '../contracts'\nimport type {\n MediaEngine,\n ConvertCapability,\n ConvertRequest,\n ConvertResult,\n BinaryExecutor,\n ScratchWorkspaceFactory,\n} from '../contracts'\n\n/** Options for {@link sofficeEngine}. */\nexport interface SofficeEngineOptions {\n /** Path (or resolvable name) of the soffice binary. */\n path: string\n /** Runs the soffice invocation. Required — no platform default. */\n executor: BinaryExecutor\n /** Mints a scratch dir per invocation whose paths the executor can open. Required. */\n workspace: ScratchWorkspaceFactory\n /** Per-invocation timeout. Default 120000. */\n timeoutMs?: number\n}\n\nconst MIME_BY_TARGET: Record<string, string> = {\n pdf: 'application/pdf',\n html: 'text/html',\n txt: 'text/plain',\n md: 'text/markdown',\n csv: 'text/csv',\n json: 'application/json',\n docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n doc: 'application/msword',\n rtf: 'application/rtf',\n odt: 'application/vnd.oasis.opendocument.text',\n xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n xls: 'application/vnd.ms-excel',\n ods: 'application/vnd.oasis.opendocument.spreadsheet',\n pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n ppt: 'application/vnd.ms-powerpoint',\n odp: 'application/vnd.oasis.opendocument.presentation',\n}\n\nconst EXT_BY_MIME: Record<string, string> = {\n 'application/pdf': 'pdf',\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'docx',\n 'application/msword': 'doc',\n 'application/rtf': 'rtf',\n 'text/rtf': 'rtf',\n 'application/vnd.oasis.opendocument.text': 'odt',\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'xlsx',\n 'application/vnd.ms-excel': 'xls',\n 'application/vnd.oasis.opendocument.spreadsheet': 'ods',\n 'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'pptx',\n 'application/vnd.ms-powerpoint': 'ppt',\n 'application/vnd.oasis.opendocument.presentation': 'odp',\n 'text/plain': 'txt',\n 'text/html': 'html',\n 'text/csv': 'csv',\n}\n\n/** Server-parity conversion matrix: same-silo targets plus pdf/html. */\nconst TARGETS_BY_EXT: Record<string, readonly string[]> = {\n docx: ['pdf', 'html', 'txt', 'md', 'odt', 'doc', 'rtf'],\n odt: ['pdf', 'html', 'txt', 'md', 'docx', 'doc', 'rtf'],\n doc: ['pdf', 'html', 'txt', 'md', 'docx', 'odt', 'rtf'],\n rtf: ['pdf', 'html', 'txt', 'md', 'docx', 'odt', 'doc'],\n xlsx: ['pdf', 'html', 'csv', 'json', 'ods', 'xls'],\n ods: ['pdf', 'html', 'csv', 'json', 'xlsx', 'xls'],\n xls: ['pdf', 'html', 'csv', 'json', 'xlsx', 'ods'],\n pptx: ['pdf', 'html', 'odp', 'ppt'],\n odp: ['pdf', 'html', 'pptx', 'ppt'],\n ppt: ['pdf', 'html', 'pptx', 'odp'],\n pdf: ['html', 'txt', 'docx', 'odt'],\n}\n\n/** soffice convert-to filter token per target (md goes via txt; json via csv). */\nconst CONVERT_TOKEN: Record<string, string> = {\n md: 'txt:Text',\n json: 'csv',\n}\n\n/**\n * Seed extension per generation target: which silo's empty file to materialize when the\n * source is {@link EMPTY_MIME}. LibreOffice treats a zero-byte input as an empty document of\n * the extension's type (verified across all three silos, both directions, on 25.x — pinned by\n * a binary-gated spec), so generation needs no templates at all. The seed is always a\n * DIFFERENT extension than the target within the same silo, because soffice won't \"convert\"\n * docx to docx.\n */\nconst SEED_EXT_FOR_TARGET: Record<string, string> = {\n docx: 'odt',\n odt: 'docx',\n doc: 'docx',\n rtf: 'docx',\n pdf: 'docx',\n html: 'docx',\n txt: 'docx',\n md: 'docx',\n xlsx: 'ods',\n ods: 'xlsx',\n xls: 'xlsx',\n csv: 'xlsx',\n json: 'xlsx',\n pptx: 'odp',\n odp: 'pptx',\n ppt: 'pptx',\n}\n\n/** Every format this engine can generate from {@link EMPTY_MIME}. */\nconst GENERATION_TARGETS: readonly string[] = Object.keys(SEED_EXT_FOR_TARGET)\n\nconst validateOptions = (options: SofficeEngineOptions, name: string): void => {\n if (typeof options?.path !== 'string' || options.path.length === 0) {\n throw new E_INVALID_MEDIA_PIPELINE_CONFIG([`${name} requires the soffice binary path`])\n }\n if (!implementsBinaryExecutor(options.executor)) {\n throw new E_INVALID_MEDIA_PIPELINE_CONFIG([\n `${name} requires an executor implementing the BinaryExecutor contract`,\n ])\n }\n if (typeof options.workspace !== 'function') {\n throw new E_INVALID_MEDIA_PIPELINE_CONFIG([\n `${name} requires a workspace factory (e.g. fsScratchWorkspace({ root }))`,\n ])\n }\n}\n\nconst runSoffice = async (\n options: SofficeEngineOptions,\n bytes: Uint8Array,\n inputExt: string,\n convertTo: string,\n outputExt: string,\n signal?: AbortSignal\n): Promise<Uint8Array> => {\n const workspace = await options.workspace()\n if (!implementsScratchWorkspace(workspace)) {\n throw new E_INVALID_MEDIA_PIPELINE_CONFIG([\n 'the workspace factory minted a value that does not implement ScratchWorkspace',\n ])\n }\n try {\n const inputPath = await workspace.materialize(bytes, `input.${inputExt}`)\n const result = await options.executor.exec({\n cmd: options.path,\n args: [\n '--headless',\n '--nologo',\n '--nodefault',\n '--norestore',\n '--nolockcheck',\n '--convert-to',\n convertTo,\n '--outdir',\n workspace.dir(),\n inputPath,\n ],\n timeoutMs: options.timeoutMs ?? 120_000,\n signal,\n })\n if (result.failed) {\n const detail = result.stderr || result.stdout || `exit code ${result.exitCode}`\n throw new Error(`LibreOffice conversion failed: ${detail}`)\n }\n const files = await workspace.list()\n const produced = files.find(\n (f) => f.toLowerCase().endsWith(`.${outputExt}`) && f !== `input.${inputExt}`\n )\n if (!produced) {\n throw new Error(\n `LibreOffice reported success but produced no .${outputExt} output (files: ${files.join(', ')})`\n )\n }\n return workspace.read(`${workspace.dir()}/${produced}`)\n } finally {\n await workspace.dispose()\n }\n}\n\n/** The MIME types of one extension group, for capability `from` declarations. */\nconst MIMES_OF = (exts: readonly string[]): string[] =>\n Object.entries(EXT_BY_MIME)\n .filter(([, ext]) => exts.includes(ext))\n .map(([mime]) => mime)\n\n/**\n * Construct the LibreOffice engine.\n *\n * @param options - Binary path, executor, workspace factory, timeout.\n * @returns The engine, declaring one convert capability group per format silo.\n */\nexport const sofficeEngine = (options: SofficeEngineOptions): MediaEngine => {\n validateOptions(options, 'sofficeEngine')\n\n const convert = async (request: ConvertRequest): Promise<ConvertResult> => {\n const mime = request.mimeType.toLowerCase().split(';')[0].trim()\n // Generation: a zero-byte seed file in the target's silo converts as an empty document.\n const inputExt = mime === EMPTY_MIME ? SEED_EXT_FOR_TARGET[request.to] : EXT_BY_MIME[mime]\n if (!inputExt) throw new Error(`unsupported input MIME for conversion: ${request.mimeType}`)\n const targets = mime === EMPTY_MIME ? GENERATION_TARGETS : (TARGETS_BY_EXT[inputExt] ?? [])\n if (!targets.includes(request.to)) {\n throw new Error(\n `cannot convert ${inputExt} to ${request.to}; supported: ${targets.join(', ')}`\n )\n }\n const convertTo = CONVERT_TOKEN[request.to] ?? request.to\n const outputExt = request.to === 'md' ? 'txt' : request.to === 'json' ? 'csv' : request.to\n let bytes = await runSoffice(\n options,\n request.bytes,\n inputExt,\n convertTo,\n outputExt,\n request.signal\n )\n let mimeType = MIME_BY_TARGET[request.to] ?? 'application/octet-stream'\n if (request.to === 'json') {\n // soffice emits CSV; lower to a JSON array-of-arrays for predictable structure.\n const text = new TextDecoder().decode(bytes)\n const rows = text\n .split(/\\r?\\n/)\n .filter((line) => line.length > 0)\n .map((line) => line.split(','))\n bytes = new TextEncoder().encode(JSON.stringify(rows))\n mimeType = MIME_BY_TARGET.json\n }\n return { outputs: [{ bytes, mimeType }] }\n }\n\n // One capability group per uniform from×to block of the matrix.\n const group = (exts: readonly string[], to: readonly string[]): ConvertCapability => ({\n from: MIMES_OF(exts),\n to,\n convert,\n })\n\n return {\n id: 'soffice',\n converts: [\n group(['docx'], TARGETS_BY_EXT.docx),\n group(['odt'], TARGETS_BY_EXT.odt),\n group(['doc'], TARGETS_BY_EXT.doc),\n group(['rtf'], TARGETS_BY_EXT.rtf),\n group(['xlsx'], TARGETS_BY_EXT.xlsx),\n group(['ods'], TARGETS_BY_EXT.ods),\n group(['xls'], TARGETS_BY_EXT.xls),\n group(['pptx'], TARGETS_BY_EXT.pptx),\n group(['odp'], TARGETS_BY_EXT.odp),\n group(['ppt'], TARGETS_BY_EXT.ppt),\n group(['pdf'], TARGETS_BY_EXT.pdf),\n // Generation: zero-byte seed in the target's silo (see SEED_EXT_FOR_TARGET).\n { from: [EMPTY_MIME], to: GENERATION_TARGETS, convert },\n ],\n }\n}\n"],"mappings":"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"}
|