@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
package/batteries/media.cjs
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_chunk = require("../chunk-Cek0wNdY.js");
|
|
3
|
-
const require_tool_registry = require("../tool_registry-
|
|
3
|
+
const require_tool_registry = require("../tool_registry-DwZ6pzN4.js");
|
|
4
4
|
require("../guards.cjs");
|
|
5
5
|
const require_batteries_media_contracts = require("./media/contracts.cjs");
|
|
6
6
|
const require_exceptions = require("../exceptions-D79HIKeX.js");
|
|
7
|
-
const require_validate = require("../validate-
|
|
7
|
+
const require_validate = require("../validate-gaSa9bsk.js");
|
|
8
|
+
const require_formats = require("../formats-DZVuqZUN.js");
|
|
8
9
|
let _nhtio_validation = require("@nhtio/validation");
|
|
9
10
|
let _nhtio_middleware = require("@nhtio/middleware");
|
|
10
11
|
let moo = require("moo");
|
|
@@ -48,7 +49,7 @@ var lexer = moo.default.compile({
|
|
|
48
49
|
pipe: "|",
|
|
49
50
|
eq: "=",
|
|
50
51
|
comma: ",",
|
|
51
|
-
ref: /@[A-Za-z0-
|
|
52
|
+
ref: /@[A-Za-z0-9:_-]+/,
|
|
52
53
|
regex: /\/(?:\\.|\[(?:\\.|[^\]\\\n])*\]|[^/\\[\n])+\/[a-z]*/,
|
|
53
54
|
dstring: /"(?:\\.|[^"\\\n])*"/,
|
|
54
55
|
sstring: /'(?:\\.|[^'\\\n])*'/,
|
|
@@ -87,7 +88,7 @@ var spanOf = (tok, length) => ({
|
|
|
87
88
|
length: length ?? tok.text.length
|
|
88
89
|
});
|
|
89
90
|
var unquote = (raw) => raw.slice(1, -1).replace(/\\n/g, "\n").replace(/\\t/g, " ").replace(/\\(["'\\/])/g, "$1");
|
|
90
|
-
var fail$
|
|
91
|
+
var fail$6 = (tok, message, exemplar) => {
|
|
91
92
|
throw new require_exceptions.E_MEDIA_PIPE_SYNTAX([`${message}${tok ? ` at line ${tok.line}, col ${tok.col}` : " at end of input"}. Write it like: ${exemplar}`]);
|
|
92
93
|
};
|
|
93
94
|
var Parser = class {
|
|
@@ -104,19 +105,19 @@ var Parser = class {
|
|
|
104
105
|
}
|
|
105
106
|
parsePipeline() {
|
|
106
107
|
const segments = [];
|
|
107
|
-
if (this.#toks.length === 0) fail$
|
|
108
|
+
if (this.#toks.length === 0) fail$6(void 0, "empty pipe expression", "extract text | chunk by=sentence");
|
|
108
109
|
segments.push(this.#parseSegment());
|
|
109
110
|
while (this.#peek()?.type === "pipe") {
|
|
110
111
|
this.#next();
|
|
111
112
|
segments.push(this.#parseSegment());
|
|
112
113
|
}
|
|
113
114
|
const trailing = this.#peek();
|
|
114
|
-
if (trailing) fail$
|
|
115
|
+
if (trailing) fail$6(trailing, `unexpected "${trailing.text}" after a complete segment`, "segment | segment (segments are separated by |)");
|
|
115
116
|
return segments;
|
|
116
117
|
}
|
|
117
118
|
#parseSegment() {
|
|
118
119
|
const first = this.#peek();
|
|
119
|
-
if (!first || first.type !== "ident") fail$
|
|
120
|
+
if (!first || first.type !== "ident") fail$6(first, "expected a verb to start the segment", "convert to=pdf");
|
|
120
121
|
const head = this.#next();
|
|
121
122
|
const collected = [head.value];
|
|
122
123
|
const posAfterWord = [this.#pos];
|
|
@@ -139,12 +140,12 @@ var Parser = class {
|
|
|
139
140
|
for (;;) {
|
|
140
141
|
const tok = this.#peek();
|
|
141
142
|
if (!tok || tok.type === "pipe") break;
|
|
142
|
-
if (tok.type === "dash") fail$
|
|
143
|
-
if (tok.type !== "ident" || this.#peek(1)?.type !== "eq") fail$
|
|
143
|
+
if (tok.type === "dash") fail$6(tok, "unexpected \"-\" — values containing dashes must be quoted", `${verbText} name="value-with-dashes"`);
|
|
144
|
+
if (tok.type !== "ident" || this.#peek(1)?.type !== "eq") fail$6(tok, `unexpected "${tok.text}" — args must be name=value`, `${verbText} name=value`);
|
|
144
145
|
const name = this.#next().value;
|
|
145
146
|
this.#next();
|
|
146
147
|
const value = this.#parseValue(verbText, name);
|
|
147
|
-
if (args.has(name)) fail$
|
|
148
|
+
if (args.has(name)) fail$6(tok, `duplicate arg "${name}"`, `${verbText} ${name}=value (give each arg once)`);
|
|
148
149
|
args.set(name, value);
|
|
149
150
|
}
|
|
150
151
|
return {
|
|
@@ -173,7 +174,7 @@ var Parser = class {
|
|
|
173
174
|
}
|
|
174
175
|
#parseScalar(verbText, argName) {
|
|
175
176
|
const tok = this.#peek();
|
|
176
|
-
if (!tok) fail$
|
|
177
|
+
if (!tok) fail$6(tok, `missing value for "${argName}"`, `${verbText} ${argName}=value`);
|
|
177
178
|
const t = tok;
|
|
178
179
|
switch (t.type) {
|
|
179
180
|
case "int":
|
|
@@ -193,7 +194,7 @@ var Parser = class {
|
|
|
193
194
|
case "range": {
|
|
194
195
|
this.#next();
|
|
195
196
|
const [lo, hi] = t.value.split("-").map(Number);
|
|
196
|
-
if (lo > hi) fail$
|
|
197
|
+
if (lo > hi) fail$6(t, `range ${t.value} is descending (start must be ≤ end)`, `${argName}=${hi}-${lo}`);
|
|
197
198
|
const nums = [];
|
|
198
199
|
for (let n = lo; n <= hi; n++) nums.push(n);
|
|
199
200
|
return {
|
|
@@ -236,7 +237,7 @@ var Parser = class {
|
|
|
236
237
|
new RegExp(source, flags);
|
|
237
238
|
} catch (err) {
|
|
238
239
|
const detail = require_tool_registry.isError(err) ? err.message : String(err);
|
|
239
|
-
fail$
|
|
240
|
+
fail$6(t, `invalid regex ${t.text}: ${detail}`, `${argName}=/pattern/flags`);
|
|
240
241
|
}
|
|
241
242
|
return {
|
|
242
243
|
value: {
|
|
@@ -258,9 +259,9 @@ var Parser = class {
|
|
|
258
259
|
span: spanOf(t)
|
|
259
260
|
};
|
|
260
261
|
case "dash":
|
|
261
|
-
fail$
|
|
262
|
+
fail$6(t, "unexpected \"-\" in a bare value", `${argName}="value-with-dashes" (quote values containing dashes)`);
|
|
262
263
|
break;
|
|
263
|
-
default: fail$
|
|
264
|
+
default: fail$6(t, `unexpected "${t.text}"`, `${verbText} ${argName}=value`);
|
|
264
265
|
}
|
|
265
266
|
throw new require_exceptions.E_MEDIA_PIPE_SYNTAX(["internal parser error"]);
|
|
266
267
|
}
|
|
@@ -798,137 +799,255 @@ var materializedName = (filename) => {
|
|
|
798
799
|
/** Convenience: read a step arg with a typed cast (validation already ran). */
|
|
799
800
|
var argOf = (step, name) => step.args[name];
|
|
800
801
|
//#endregion
|
|
801
|
-
//#region src/batteries/media/
|
|
802
|
-
|
|
803
|
-
var
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
JPEG: "image/jpeg",
|
|
824
|
-
WEBP: "image/webp",
|
|
825
|
-
TIFF: "image/tiff",
|
|
826
|
-
AVIF: "image/avif",
|
|
827
|
-
WAV: "audio/wav",
|
|
828
|
-
MP3: "audio/mpeg"
|
|
829
|
-
};
|
|
830
|
-
/** Legacy Office MIME types (extraction only — mutation is rejected). */
|
|
831
|
-
var LEGACY_OFFICE_MIMES = new Set([
|
|
832
|
-
MIME.DOC,
|
|
833
|
-
MIME.XLS,
|
|
834
|
-
MIME.PPT
|
|
835
|
-
]);
|
|
836
|
-
/** Apple iWork MIME types (extraction only — mutation is rejected). */
|
|
837
|
-
var IWORK_MIMES = new Set([
|
|
838
|
-
MIME.PAGES,
|
|
839
|
-
MIME.NUMBERS,
|
|
840
|
-
MIME.KEYNOTE
|
|
841
|
-
]);
|
|
842
|
-
var SPREADSHEET_MIMES = new Set([
|
|
843
|
-
MIME.XLSX,
|
|
844
|
-
MIME.ODS,
|
|
845
|
-
MIME.XLS,
|
|
846
|
-
MIME.CSV
|
|
847
|
-
]);
|
|
848
|
-
var PRESENTATION_MIMES = new Set([
|
|
849
|
-
MIME.PPTX,
|
|
850
|
-
MIME.ODP,
|
|
851
|
-
MIME.PPT,
|
|
852
|
-
MIME.KEYNOTE
|
|
853
|
-
]);
|
|
854
|
-
var DOCUMENT_MIMES = new Set([
|
|
855
|
-
MIME.PDF,
|
|
856
|
-
MIME.DOCX,
|
|
857
|
-
MIME.ODT,
|
|
858
|
-
MIME.DOC,
|
|
859
|
-
MIME.PAGES,
|
|
860
|
-
MIME.TXT,
|
|
861
|
-
MIME.MD,
|
|
862
|
-
MIME.HTML,
|
|
863
|
-
MIME.JSON,
|
|
864
|
-
"application/rtf",
|
|
865
|
-
"text/rtf"
|
|
866
|
-
]);
|
|
802
|
+
//#region src/batteries/media/steps/patch.ts
|
|
803
|
+
var PATCH_PREFIX = "*** Begin Patch";
|
|
804
|
+
var PATCH_SUFFIX = "*** End Patch";
|
|
805
|
+
var ADD_FILE_PREFIX = "*** Add File:";
|
|
806
|
+
var DELETE_FILE_PREFIX = "*** Delete File:";
|
|
807
|
+
var UPDATE_FILE_PREFIX = "*** Update File:";
|
|
808
|
+
var MOVE_TO_PREFIX = "*** Move to:";
|
|
809
|
+
/** `true` when `patch` is the structured envelope rather than a unified diff. */
|
|
810
|
+
var isStructuredPatch = (patch) => patch.trimStart().startsWith(PATCH_PREFIX);
|
|
811
|
+
/** Normalize a workspace path: relative, no `.`/`..`/empty segments, forward slashes. */
|
|
812
|
+
var normalizeWorkspacePath = (path) => {
|
|
813
|
+
const normalized = path.replace(/\\/g, "/").trim();
|
|
814
|
+
if (!normalized) throw new Error("apply_patch path cannot be empty");
|
|
815
|
+
if (normalized.startsWith("/") || /^[A-Za-z]:/.test(normalized)) throw new Error(`apply_patch path "${path}" must be relative to the workspace root`);
|
|
816
|
+
const segments = normalized.split("/");
|
|
817
|
+
const sanitizedSegments = [];
|
|
818
|
+
for (const segment of segments) {
|
|
819
|
+
if (!segment || segment === "." || segment === "..") throw new Error(`apply_patch path "${path}" contains invalid segment "${segment || "(empty)"}"`);
|
|
820
|
+
sanitizedSegments.push(segment);
|
|
821
|
+
}
|
|
822
|
+
return sanitizedSegments.join("/");
|
|
823
|
+
};
|
|
867
824
|
/**
|
|
868
|
-
*
|
|
825
|
+
* Parse a structured `*** Begin Patch` envelope.
|
|
869
826
|
*
|
|
870
|
-
* @param
|
|
871
|
-
* @returns The
|
|
827
|
+
* @param patch - The raw patch text.
|
|
828
|
+
* @returns The parsed operations.
|
|
872
829
|
*/
|
|
873
|
-
var
|
|
874
|
-
const
|
|
875
|
-
if (
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
if (
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
830
|
+
var parseStructuredPatch = (patch) => {
|
|
831
|
+
const lines = patch.replace(/\r\n/g, "\n").split("\n");
|
|
832
|
+
if (lines[0]?.trim() !== PATCH_PREFIX) throw new Error("a structured patch must start with \"*** Begin Patch\"");
|
|
833
|
+
const lastNonEmptyIndex = [...lines].reverse().findIndex((line) => line.trim() !== "");
|
|
834
|
+
const endIndex = lastNonEmptyIndex === -1 ? -1 : lines.length - 1 - lastNonEmptyIndex;
|
|
835
|
+
if (endIndex < 0 || lines[endIndex]?.trim() !== PATCH_SUFFIX) throw new Error("a structured patch must end with \"*** End Patch\"");
|
|
836
|
+
const body = lines.slice(1, endIndex);
|
|
837
|
+
const operations = [];
|
|
838
|
+
let totalAdded = 0;
|
|
839
|
+
let totalRemoved = 0;
|
|
840
|
+
let i = 0;
|
|
841
|
+
while (i < body.length) {
|
|
842
|
+
const line = body[i] ?? "";
|
|
843
|
+
if (line.trim() === "" || line.startsWith("*** End of File")) {
|
|
844
|
+
i += 1;
|
|
845
|
+
continue;
|
|
846
|
+
}
|
|
847
|
+
if (line.startsWith(ADD_FILE_PREFIX)) {
|
|
848
|
+
const path = normalizeWorkspacePath(line.slice(13).trim());
|
|
849
|
+
i += 1;
|
|
850
|
+
const contentLines = [];
|
|
851
|
+
while (i < body.length) {
|
|
852
|
+
const next = body[i] ?? "";
|
|
853
|
+
if (next.startsWith("*** ")) break;
|
|
854
|
+
if (!next.startsWith("+")) throw new Error(`apply_patch add-file line must start with "+": "${next}"`);
|
|
855
|
+
contentLines.push(next.slice(1));
|
|
856
|
+
i += 1;
|
|
857
|
+
}
|
|
858
|
+
operations.push({
|
|
859
|
+
type: "add",
|
|
860
|
+
path,
|
|
861
|
+
content: contentLines.join("\n"),
|
|
862
|
+
added: contentLines.length
|
|
863
|
+
});
|
|
864
|
+
totalAdded += contentLines.length;
|
|
865
|
+
continue;
|
|
866
|
+
}
|
|
867
|
+
if (line.startsWith(DELETE_FILE_PREFIX)) {
|
|
868
|
+
const path = normalizeWorkspacePath(line.slice(16).trim());
|
|
869
|
+
operations.push({
|
|
870
|
+
type: "delete",
|
|
871
|
+
path
|
|
872
|
+
});
|
|
873
|
+
i += 1;
|
|
874
|
+
continue;
|
|
875
|
+
}
|
|
876
|
+
if (!line.startsWith(UPDATE_FILE_PREFIX)) throw new Error(`apply_patch invalid structured patch header: "${line}"`);
|
|
877
|
+
const path = normalizeWorkspacePath(line.slice(16).trim());
|
|
878
|
+
i += 1;
|
|
879
|
+
let movePath;
|
|
880
|
+
if ((body[i] ?? "").startsWith(MOVE_TO_PREFIX)) {
|
|
881
|
+
movePath = normalizeWorkspacePath((body[i] ?? "").slice(12).trim());
|
|
882
|
+
i += 1;
|
|
883
|
+
}
|
|
884
|
+
const hunks = [];
|
|
885
|
+
let opAdded = 0;
|
|
886
|
+
let opRemoved = 0;
|
|
887
|
+
while (i < body.length && (body[i] ?? "").startsWith("@@")) {
|
|
888
|
+
i += 1;
|
|
889
|
+
const hunkLines = [];
|
|
890
|
+
while (i < body.length) {
|
|
891
|
+
const next = body[i] ?? "";
|
|
892
|
+
if (next.startsWith("@@") || next.startsWith("*** ")) break;
|
|
893
|
+
hunkLines.push(next);
|
|
894
|
+
i += 1;
|
|
895
|
+
}
|
|
896
|
+
if (hunkLines.length === 0) throw new Error("apply_patch contains an empty hunk");
|
|
897
|
+
const oldLines = [];
|
|
898
|
+
const newLines = [];
|
|
899
|
+
let added = 0;
|
|
900
|
+
let removed = 0;
|
|
901
|
+
for (const hunkLine of hunkLines) if (hunkLine.startsWith("+")) {
|
|
902
|
+
newLines.push(hunkLine.slice(1));
|
|
903
|
+
added += 1;
|
|
904
|
+
} else if (hunkLine.startsWith("-")) {
|
|
905
|
+
oldLines.push(hunkLine.slice(1));
|
|
906
|
+
removed += 1;
|
|
907
|
+
} else if (hunkLine.startsWith(" ")) {
|
|
908
|
+
const contextLine = hunkLine.slice(1);
|
|
909
|
+
oldLines.push(contextLine);
|
|
910
|
+
newLines.push(contextLine);
|
|
911
|
+
} else throw new Error(`apply_patch invalid hunk line (must start with +, -, or space): "${hunkLine}"`);
|
|
912
|
+
if (added === 0 && removed === 0) throw new Error("apply_patch hunk must contain at least one added or removed line");
|
|
913
|
+
opAdded += added;
|
|
914
|
+
opRemoved += removed;
|
|
915
|
+
hunks.push({
|
|
916
|
+
oldLines,
|
|
917
|
+
newLines,
|
|
918
|
+
added,
|
|
919
|
+
removed
|
|
920
|
+
});
|
|
921
|
+
}
|
|
922
|
+
if (hunks.length === 0) throw new Error(`apply_patch update operation for "${path}" must contain at least one hunk`);
|
|
923
|
+
operations.push({
|
|
924
|
+
type: "update",
|
|
925
|
+
path,
|
|
926
|
+
movePath,
|
|
927
|
+
hunks,
|
|
928
|
+
added: opAdded,
|
|
929
|
+
removed: opRemoved
|
|
930
|
+
});
|
|
931
|
+
totalAdded += opAdded;
|
|
932
|
+
totalRemoved += opRemoved;
|
|
933
|
+
}
|
|
934
|
+
if (operations.length === 0) throw new Error("apply_patch contains no operations");
|
|
935
|
+
return {
|
|
936
|
+
operations,
|
|
937
|
+
added: totalAdded,
|
|
938
|
+
removed: totalRemoved
|
|
939
|
+
};
|
|
940
|
+
};
|
|
941
|
+
var countSequenceMatches = (haystack, needle, start) => {
|
|
942
|
+
if (needle.length === 0) return 0;
|
|
943
|
+
let matches = 0;
|
|
944
|
+
let i = start;
|
|
945
|
+
while (i <= haystack.length - needle.length) {
|
|
946
|
+
let matched = true;
|
|
947
|
+
let j = 0;
|
|
948
|
+
while (j < needle.length) {
|
|
949
|
+
if (haystack[i + j] !== needle[j]) {
|
|
950
|
+
matched = false;
|
|
951
|
+
break;
|
|
952
|
+
}
|
|
953
|
+
j += 1;
|
|
954
|
+
}
|
|
955
|
+
if (matched) matches += 1;
|
|
956
|
+
i += 1;
|
|
957
|
+
}
|
|
958
|
+
return matches;
|
|
959
|
+
};
|
|
960
|
+
var findExactSequence = (haystack, needle, start) => {
|
|
961
|
+
if (needle.length === 0) return start;
|
|
962
|
+
let i = start;
|
|
963
|
+
while (i <= haystack.length - needle.length) {
|
|
964
|
+
let matched = true;
|
|
965
|
+
let j = 0;
|
|
966
|
+
while (j < needle.length) {
|
|
967
|
+
if (haystack[i + j] !== needle[j]) {
|
|
968
|
+
matched = false;
|
|
969
|
+
break;
|
|
970
|
+
}
|
|
971
|
+
j += 1;
|
|
972
|
+
}
|
|
973
|
+
if (matched) return i;
|
|
974
|
+
i += 1;
|
|
975
|
+
}
|
|
976
|
+
return -1;
|
|
882
977
|
};
|
|
883
978
|
/**
|
|
884
|
-
*
|
|
885
|
-
*
|
|
979
|
+
* Apply an update operation's hunks to `inputText`. Context matching is exact and rejects
|
|
980
|
+
* ambiguity: a hunk whose old-lines match more than one location past the cursor fails
|
|
981
|
+
* rather than guessing.
|
|
886
982
|
*
|
|
887
|
-
* @param
|
|
888
|
-
* @
|
|
983
|
+
* @param inputText - The file's current text.
|
|
984
|
+
* @param hunks - The parsed hunks, in order.
|
|
985
|
+
* @returns The patched text.
|
|
889
986
|
*/
|
|
890
|
-
var
|
|
891
|
-
const
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
odt: MIME.ODT,
|
|
902
|
-
ods: MIME.ODS,
|
|
903
|
-
odp: MIME.ODP,
|
|
904
|
-
doc: MIME.DOC,
|
|
905
|
-
xls: MIME.XLS,
|
|
906
|
-
ppt: MIME.PPT,
|
|
907
|
-
txt: MIME.TXT,
|
|
908
|
-
md: MIME.MD,
|
|
909
|
-
csv: MIME.CSV,
|
|
910
|
-
html: MIME.HTML,
|
|
911
|
-
json: MIME.JSON,
|
|
912
|
-
rtf: "application/rtf",
|
|
913
|
-
png: MIME.PNG,
|
|
914
|
-
jpg: MIME.JPEG,
|
|
915
|
-
jpeg: MIME.JPEG,
|
|
916
|
-
webp: MIME.WEBP,
|
|
917
|
-
tiff: MIME.TIFF,
|
|
918
|
-
avif: MIME.AVIF,
|
|
919
|
-
wav: MIME.WAV,
|
|
920
|
-
mp3: MIME.MP3
|
|
987
|
+
var applyUpdateHunks = (inputText, hunks) => {
|
|
988
|
+
const lines = inputText.replace(/\r\n/g, "\n").split("\n");
|
|
989
|
+
let cursor = 0;
|
|
990
|
+
for (const hunk of hunks) {
|
|
991
|
+
if (countSequenceMatches(lines, hunk.oldLines, cursor) > 1) throw new Error("patch context is ambiguous and matches multiple locations");
|
|
992
|
+
const start = findExactSequence(lines, hunk.oldLines, cursor);
|
|
993
|
+
if (start === -1) throw new Error("the patch could not be applied cleanly to the source text");
|
|
994
|
+
lines.splice(start, hunk.oldLines.length, ...hunk.newLines);
|
|
995
|
+
cursor = start + hunk.newLines.length;
|
|
996
|
+
}
|
|
997
|
+
return lines.join("\n");
|
|
921
998
|
};
|
|
922
999
|
/**
|
|
923
|
-
*
|
|
1000
|
+
* Apply a parsed structured patch to a virtual workspace of files keyed by normalized path.
|
|
924
1001
|
*
|
|
925
|
-
* @param
|
|
926
|
-
* @param
|
|
927
|
-
* @returns The
|
|
1002
|
+
* @param files - The workspace (mutated in place).
|
|
1003
|
+
* @param patch - The parsed envelope.
|
|
1004
|
+
* @returns The workspace and the number of files touched.
|
|
928
1005
|
*/
|
|
929
|
-
var
|
|
930
|
-
|
|
931
|
-
|
|
1006
|
+
var applyOperations = (files, patch) => {
|
|
1007
|
+
let modifiedFiles = 0;
|
|
1008
|
+
for (const operation of patch.operations) {
|
|
1009
|
+
if (operation.type === "add") {
|
|
1010
|
+
if (files.has(operation.path)) throw new Error(`cannot add file "${operation.path}": the path already exists`);
|
|
1011
|
+
files.set(operation.path, {
|
|
1012
|
+
text: operation.content,
|
|
1013
|
+
mimeType: inferTextMimeFromPath(operation.path)
|
|
1014
|
+
});
|
|
1015
|
+
modifiedFiles += 1;
|
|
1016
|
+
continue;
|
|
1017
|
+
}
|
|
1018
|
+
if (operation.type === "delete") {
|
|
1019
|
+
if (!files.has(operation.path)) throw new Error(`cannot delete file "${operation.path}": the path does not exist`);
|
|
1020
|
+
files.delete(operation.path);
|
|
1021
|
+
modifiedFiles += 1;
|
|
1022
|
+
continue;
|
|
1023
|
+
}
|
|
1024
|
+
const current = files.get(operation.path);
|
|
1025
|
+
if (!current) throw new Error(`cannot update file "${operation.path}": the path does not exist`);
|
|
1026
|
+
const updatedText = applyUpdateHunks(current.text, operation.hunks);
|
|
1027
|
+
const targetPath = operation.movePath ?? operation.path;
|
|
1028
|
+
if (operation.movePath && files.has(targetPath)) throw new Error(`cannot move file "${operation.path}" to "${targetPath}": the target already exists`);
|
|
1029
|
+
files.delete(operation.path);
|
|
1030
|
+
files.set(targetPath, {
|
|
1031
|
+
text: updatedText,
|
|
1032
|
+
mimeType: current.mimeType
|
|
1033
|
+
});
|
|
1034
|
+
modifiedFiles += 1;
|
|
1035
|
+
}
|
|
1036
|
+
return {
|
|
1037
|
+
files,
|
|
1038
|
+
modifiedFiles
|
|
1039
|
+
};
|
|
1040
|
+
};
|
|
1041
|
+
/** Infer a text MIME from a workspace path's extension (Add File outputs). */
|
|
1042
|
+
var inferTextMimeFromPath = (path) => {
|
|
1043
|
+
const dot = path.lastIndexOf(".");
|
|
1044
|
+
const ext = dot > 0 ? path.slice(dot + 1).toLowerCase() : "";
|
|
1045
|
+
if (ext === "json") return "application/json";
|
|
1046
|
+
if (ext === "md" || ext === "markdown") return "text/markdown";
|
|
1047
|
+
if (ext === "csv") return "text/csv";
|
|
1048
|
+
if (ext === "yaml" || ext === "yml") return "application/yaml";
|
|
1049
|
+
if (ext === "html" || ext === "htm") return "text/html";
|
|
1050
|
+
return "text/plain";
|
|
932
1051
|
};
|
|
933
1052
|
//#endregion
|
|
934
1053
|
//#region src/batteries/media/steps/text.ts
|
|
@@ -999,20 +1118,20 @@ var splitFixed = (text, chunkSize, overlap) => {
|
|
|
999
1118
|
}
|
|
1000
1119
|
return chunks;
|
|
1001
1120
|
};
|
|
1002
|
-
var decoder = new TextDecoder("utf-8", { fatal: false });
|
|
1003
|
-
var encoder = new TextEncoder();
|
|
1121
|
+
var decoder$1 = new TextDecoder("utf-8", { fatal: false });
|
|
1122
|
+
var encoder$1 = new TextEncoder();
|
|
1004
1123
|
/** Decode payload bytes as UTF-8 text (UTF-16 BOM aware). */
|
|
1005
1124
|
var decodeText = (bytes) => {
|
|
1006
1125
|
if (bytes.length >= 2) {
|
|
1007
1126
|
if (bytes[0] === 255 && bytes[1] === 254) return new TextDecoder("utf-16le").decode(bytes.subarray(2));
|
|
1008
1127
|
if (bytes[0] === 254 && bytes[1] === 255) return new TextDecoder("utf-16be").decode(bytes.subarray(2));
|
|
1009
1128
|
}
|
|
1010
|
-
return decoder.decode(bytes).replace(/\r\n?/g, "\n");
|
|
1129
|
+
return decoder$1.decode(bytes).replace(/\r\n?/g, "\n");
|
|
1011
1130
|
};
|
|
1012
1131
|
var textPayload = (source, text, suffix) => ({
|
|
1013
|
-
bytes: encoder.encode(text),
|
|
1014
|
-
mimeType: MIME.TXT,
|
|
1015
|
-
filename: replaceExtension(source.filename, suffix)
|
|
1132
|
+
bytes: encoder$1.encode(text),
|
|
1133
|
+
mimeType: require_formats.MIME.TXT,
|
|
1134
|
+
filename: require_formats.replaceExtension(source.filename, suffix)
|
|
1016
1135
|
});
|
|
1017
1136
|
/** `chunk` — split text content into retrieval chunks (data result). */
|
|
1018
1137
|
var chunkStep = async (ctx) => {
|
|
@@ -1033,9 +1152,9 @@ var chunkStep = async (ctx) => {
|
|
|
1033
1152
|
* clear reason.
|
|
1034
1153
|
*/
|
|
1035
1154
|
var extractTextStep = async (ctx) => {
|
|
1036
|
-
const family = familyOf(ctx.payload.mimeType);
|
|
1155
|
+
const family = require_formats.familyOf(ctx.payload.mimeType);
|
|
1037
1156
|
const mime = ctx.payload.mimeType.toLowerCase().split(";")[0].trim();
|
|
1038
|
-
if (!(mime.startsWith("text/") || mime === MIME.JSON || mime === MIME.MD || mime === MIME.CSV)) throw new require_exceptions.E_MEDIA_STEP_FAILED(["extract.text", `extraction for ${family} media (${mime}) is not yet implemented in this build`]);
|
|
1157
|
+
if (!(mime.startsWith("text/") || mime === require_formats.MIME.JSON || mime === require_formats.MIME.MD || mime === require_formats.MIME.CSV)) throw new require_exceptions.E_MEDIA_STEP_FAILED(["extract.text", `extraction for ${family} media (${mime}) is not yet implemented in this build`]);
|
|
1039
1158
|
const text = decodeText(ctx.payload.bytes);
|
|
1040
1159
|
return {
|
|
1041
1160
|
kind: "data",
|
|
@@ -1049,7 +1168,7 @@ var extractMetadataStep = async (ctx) => {
|
|
|
1049
1168
|
filename: ctx.payload.filename,
|
|
1050
1169
|
mime_type: ctx.payload.mimeType,
|
|
1051
1170
|
size_bytes: ctx.payload.bytes.byteLength,
|
|
1052
|
-
family: familyOf(ctx.payload.mimeType)
|
|
1171
|
+
family: require_formats.familyOf(ctx.payload.mimeType)
|
|
1053
1172
|
};
|
|
1054
1173
|
return {
|
|
1055
1174
|
kind: "data",
|
|
@@ -1094,7 +1213,7 @@ var updateTextStep = async (ctx) => {
|
|
|
1094
1213
|
kind: "media",
|
|
1095
1214
|
payload: {
|
|
1096
1215
|
...ctx.payload,
|
|
1097
|
-
bytes: encoder.encode(updated)
|
|
1216
|
+
bytes: encoder$1.encode(updated)
|
|
1098
1217
|
}
|
|
1099
1218
|
};
|
|
1100
1219
|
};
|
|
@@ -1124,9 +1243,13 @@ var diffStep = async (ctx) => {
|
|
|
1124
1243
|
asText: patch
|
|
1125
1244
|
};
|
|
1126
1245
|
};
|
|
1127
|
-
/**
|
|
1246
|
+
/**
|
|
1247
|
+
* `apply_patch` — apply a unified diff, or a structured `*** Begin Patch` envelope
|
|
1248
|
+
* (multi-file Add/Delete/Update/Move — the GitHub Copilot apply_patch dialect).
|
|
1249
|
+
*/
|
|
1128
1250
|
var applyPatchStep = async (ctx) => {
|
|
1129
1251
|
const patch = argOf(ctx.step, "patch");
|
|
1252
|
+
if (isStructuredPatch(patch)) return applyStructuredPatch(ctx, patch);
|
|
1130
1253
|
const text = decodeText(ctx.payload.bytes);
|
|
1131
1254
|
const result = (await import("diff")).applyPatch(text, patch);
|
|
1132
1255
|
if (result === false) throw new require_exceptions.E_MEDIA_STEP_FAILED(["apply_patch", "the patch does not apply to this media content (context mismatch)"]);
|
|
@@ -1134,30 +1257,86 @@ var applyPatchStep = async (ctx) => {
|
|
|
1134
1257
|
kind: "media",
|
|
1135
1258
|
payload: {
|
|
1136
1259
|
...ctx.payload,
|
|
1137
|
-
bytes: encoder.encode(result)
|
|
1260
|
+
bytes: encoder$1.encode(result)
|
|
1138
1261
|
}
|
|
1139
1262
|
};
|
|
1140
1263
|
};
|
|
1264
|
+
/** The structured-envelope path: primary media + `with=@refs` form a virtual workspace. */
|
|
1265
|
+
var applyStructuredPatch = async (ctx, patch) => {
|
|
1266
|
+
const verb = "apply_patch";
|
|
1267
|
+
const fail = (message) => {
|
|
1268
|
+
throw new require_exceptions.E_MEDIA_STEP_FAILED([verb, message]);
|
|
1269
|
+
};
|
|
1270
|
+
const files = /* @__PURE__ */ new Map();
|
|
1271
|
+
const addToWorkspace = (payload) => {
|
|
1272
|
+
let path;
|
|
1273
|
+
try {
|
|
1274
|
+
path = normalizeWorkspacePath(payload.filename);
|
|
1275
|
+
} catch (err) {
|
|
1276
|
+
fail(require_tool_registry.isError(err) ? err.message : String(err));
|
|
1277
|
+
throw err;
|
|
1278
|
+
}
|
|
1279
|
+
if (files.has(path)) fail(`duplicate workspace path in inputs: "${path}"`);
|
|
1280
|
+
files.set(path, {
|
|
1281
|
+
text: decodeText(payload.bytes),
|
|
1282
|
+
mimeType: payload.mimeType
|
|
1283
|
+
});
|
|
1284
|
+
};
|
|
1285
|
+
addToWorkspace(ctx.payload);
|
|
1286
|
+
const raw = ctx.step.args.with;
|
|
1287
|
+
if (raw !== void 0) {
|
|
1288
|
+
const refs = Array.isArray(raw) ? raw : [raw];
|
|
1289
|
+
for (const ref of refs) {
|
|
1290
|
+
if (ref.kind !== "id") fail("builder refs are not yet supported here");
|
|
1291
|
+
addToWorkspace(await ctx.resolveRef(ref.id));
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
let parsed;
|
|
1295
|
+
let applied;
|
|
1296
|
+
try {
|
|
1297
|
+
parsed = parseStructuredPatch(patch);
|
|
1298
|
+
applied = applyOperations(files, parsed);
|
|
1299
|
+
} catch (err) {
|
|
1300
|
+
fail(require_tool_registry.isError(err) ? err.message : String(err));
|
|
1301
|
+
throw err;
|
|
1302
|
+
}
|
|
1303
|
+
const payloads = [...applied.files.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([path, file]) => ({
|
|
1304
|
+
bytes: encoder$1.encode(file.text),
|
|
1305
|
+
mimeType: file.mimeType,
|
|
1306
|
+
filename: path
|
|
1307
|
+
}));
|
|
1308
|
+
if (payloads.length === 0) fail("the patch deleted every file in the workspace");
|
|
1309
|
+
if (payloads.length === 1) return {
|
|
1310
|
+
kind: "media",
|
|
1311
|
+
payload: payloads[0]
|
|
1312
|
+
};
|
|
1313
|
+
return {
|
|
1314
|
+
kind: "media-list",
|
|
1315
|
+
payloads
|
|
1316
|
+
};
|
|
1317
|
+
};
|
|
1141
1318
|
//#endregion
|
|
1142
1319
|
//#region src/batteries/media/steps/doc.ts
|
|
1143
1320
|
/**
|
|
1144
|
-
* Doc-domain step extensions: format-preserving text mutation for DOCX/PPTX,
|
|
1145
|
-
* extraction from OOXML/ODF archives and PDFs, and format conversion via the
|
|
1321
|
+
* Doc-domain step extensions: format-preserving text mutation for DOCX/PPTX/ODF, PDF visual
|
|
1322
|
+
* redaction, asset extraction from OOXML/ODF archives and PDFs, and format conversion via the
|
|
1323
|
+
* convert engine.
|
|
1146
1324
|
*
|
|
1147
1325
|
* @remarks
|
|
1148
1326
|
* Internal sibling of the `@nhtio/adk/batteries/media` entry. Ported from the source server's
|
|
1149
1327
|
* doc adapters; the format-dispatch contract follows the server's green e2e suite: DOCX and
|
|
1150
1328
|
* PPTX redact/sanitize/normalize/update_text mutate text in place inside the container
|
|
1151
|
-
* (preserving the source format);
|
|
1152
|
-
* text
|
|
1153
|
-
*
|
|
1329
|
+
* (preserving the source format); ODT/ODS/ODP redact/update_text mutate `content.xml` the
|
|
1330
|
+
* same way; text-like inputs mutate as text. PDF redact is VISUAL (pdf-lib draw-over +
|
|
1331
|
+
* metadata strip — content streams keep the text); the result carries that warning verbatim
|
|
1332
|
+
* because it is a trust boundary, not a footnote.
|
|
1154
1333
|
*/
|
|
1155
1334
|
var zipPromise$2;
|
|
1156
1335
|
var jszip$2 = () => {
|
|
1157
1336
|
zipPromise$2 ??= import("jszip").then((m) => "default" in m ? m.default : m);
|
|
1158
1337
|
return zipPromise$2;
|
|
1159
1338
|
};
|
|
1160
|
-
var fail$
|
|
1339
|
+
var fail$5 = (verb, message) => {
|
|
1161
1340
|
throw new require_exceptions.E_MEDIA_STEP_FAILED([verb, message]);
|
|
1162
1341
|
};
|
|
1163
1342
|
var escapeXml$1 = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
@@ -1203,17 +1382,17 @@ var replaceInOoxml = (xml, paragraphTag, textTag, pattern, replacement) => {
|
|
|
1203
1382
|
};
|
|
1204
1383
|
/** Map text-node mutation over every relevant part of a DOCX or PPTX archive. */
|
|
1205
1384
|
var mutateOoxmlText = async (ctx, verb, mutateXml) => {
|
|
1206
|
-
const kind = ctx.payload.mimeType.toLowerCase().split(";")[0].trim() === MIME.DOCX ? "docx" : "pptx";
|
|
1385
|
+
const kind = ctx.payload.mimeType.toLowerCase().split(";")[0].trim() === require_formats.MIME.DOCX ? "docx" : "pptx";
|
|
1207
1386
|
const JSZip = await jszip$2();
|
|
1208
1387
|
let zip;
|
|
1209
1388
|
try {
|
|
1210
1389
|
zip = await JSZip.loadAsync(ctx.payload.bytes);
|
|
1211
1390
|
} catch (err) {
|
|
1212
|
-
fail$
|
|
1391
|
+
fail$5(verb, `could not open the document container: ${require_tool_registry.isError(err) ? err.message : String(err)}`);
|
|
1213
1392
|
throw err;
|
|
1214
1393
|
}
|
|
1215
1394
|
const parts = kind === "docx" ? Object.keys(zip.files).filter((n) => /^word\/(document|header\d*|footer\d*)\.xml$/.test(n)) : Object.keys(zip.files).filter((n) => /^ppt\/slides\/slide\d+\.xml$/.test(n));
|
|
1216
|
-
if (parts.length === 0) fail$
|
|
1395
|
+
if (parts.length === 0) fail$5(verb, "the container has no text parts to mutate (corrupt file?)");
|
|
1217
1396
|
for (const part of parts) {
|
|
1218
1397
|
const xml = await zip.file(part).async("text");
|
|
1219
1398
|
const next = mutateXml(xml, kind);
|
|
@@ -1239,21 +1418,170 @@ var toGlobalRegex = (value) => {
|
|
|
1239
1418
|
const ref = value;
|
|
1240
1419
|
return new RegExp(ref.source, ref.flags.includes("g") ? ref.flags : `${ref.flags}g`);
|
|
1241
1420
|
};
|
|
1242
|
-
|
|
1421
|
+
var ODF_MIMES = new Set([
|
|
1422
|
+
require_formats.MIME.ODT,
|
|
1423
|
+
require_formats.MIME.ODS,
|
|
1424
|
+
require_formats.MIME.ODP
|
|
1425
|
+
]);
|
|
1426
|
+
/**
|
|
1427
|
+
* Replace matches inside the text nodes of ODF `content.xml`, aggregating per paragraph so
|
|
1428
|
+
* patterns spanning `<text:span>` formatting splits still match.
|
|
1429
|
+
*/
|
|
1430
|
+
var replaceTextInContentXml = (xml, pattern, replacement) => {
|
|
1431
|
+
let totalMatches = 0;
|
|
1432
|
+
return {
|
|
1433
|
+
xml: xml.replace(/<text:(?:p|h)\b[^>]*>[\s\S]*?<\/text:(?:p|h)>/g, (paragraphXml) => {
|
|
1434
|
+
const textNodes = [];
|
|
1435
|
+
const textRegex = />([^<]+)</g;
|
|
1436
|
+
let match;
|
|
1437
|
+
while (match = textRegex.exec(paragraphXml)) {
|
|
1438
|
+
const content = unescapeXml(match[1]);
|
|
1439
|
+
if (content.trim() === "") continue;
|
|
1440
|
+
textNodes.push({
|
|
1441
|
+
content,
|
|
1442
|
+
start: match.index,
|
|
1443
|
+
end: match.index + match[0].length
|
|
1444
|
+
});
|
|
1445
|
+
}
|
|
1446
|
+
if (textNodes.length === 0) return paragraphXml;
|
|
1447
|
+
const aggregated = textNodes.map((n) => n.content).join("");
|
|
1448
|
+
const p = new RegExp(pattern.source, pattern.flags);
|
|
1449
|
+
const replaced = aggregated.replace(p, replacement);
|
|
1450
|
+
if (replaced === aggregated) return paragraphXml;
|
|
1451
|
+
const counter = new RegExp(pattern.source, pattern.flags);
|
|
1452
|
+
while (counter.exec(aggregated)) {
|
|
1453
|
+
totalMatches += 1;
|
|
1454
|
+
if (!counter.global) break;
|
|
1455
|
+
}
|
|
1456
|
+
let out = paragraphXml;
|
|
1457
|
+
for (let i = textNodes.length - 1; i >= 0; i--) {
|
|
1458
|
+
const node = textNodes[i];
|
|
1459
|
+
const newContent = i === 0 ? escapeXml$1(replaced) : "";
|
|
1460
|
+
out = out.slice(0, node.start) + `>${newContent}<` + out.slice(node.end);
|
|
1461
|
+
}
|
|
1462
|
+
return out;
|
|
1463
|
+
}),
|
|
1464
|
+
matchCount: totalMatches
|
|
1465
|
+
};
|
|
1466
|
+
};
|
|
1467
|
+
/** Map a `content.xml` mutation over an ODF container (ODT/ODS/ODP). */
|
|
1468
|
+
var mutateOdfText = async (ctx, verb, mutateXml) => {
|
|
1469
|
+
const JSZip = await jszip$2();
|
|
1470
|
+
let zip;
|
|
1471
|
+
try {
|
|
1472
|
+
zip = await JSZip.loadAsync(ctx.payload.bytes);
|
|
1473
|
+
} catch (err) {
|
|
1474
|
+
fail$5(verb, `could not open the document container: ${require_tool_registry.isError(err) ? err.message : String(err)}`);
|
|
1475
|
+
throw err;
|
|
1476
|
+
}
|
|
1477
|
+
const part = zip.file("content.xml");
|
|
1478
|
+
if (!part) fail$5(verb, "the ODF container has no content.xml (corrupt file?)");
|
|
1479
|
+
const xml = await part.async("text");
|
|
1480
|
+
const next = mutateXml(xml);
|
|
1481
|
+
if (next !== xml) zip.file("content.xml", next);
|
|
1482
|
+
const bytes = await zip.generateAsync({
|
|
1483
|
+
type: "uint8array",
|
|
1484
|
+
compression: "DEFLATE"
|
|
1485
|
+
});
|
|
1486
|
+
return {
|
|
1487
|
+
kind: "media",
|
|
1488
|
+
payload: {
|
|
1489
|
+
...ctx.payload,
|
|
1490
|
+
bytes
|
|
1491
|
+
}
|
|
1492
|
+
};
|
|
1493
|
+
};
|
|
1494
|
+
/** Visual page-level redaction: draw-over banner on matching pages + metadata strip. */
|
|
1495
|
+
var redactPdf = async (ctx, verb, patterns) => {
|
|
1496
|
+
const { PDFDocument, PDFName, PDFString, rgb, StandardFonts } = await import("pdf-lib");
|
|
1497
|
+
let doc;
|
|
1498
|
+
try {
|
|
1499
|
+
doc = await PDFDocument.load(ctx.payload.bytes, { ignoreEncryption: true });
|
|
1500
|
+
} catch (err) {
|
|
1501
|
+
fail$5(verb, `could not parse the PDF: ${require_tool_registry.isError(err) ? err.message : String(err)}`);
|
|
1502
|
+
throw err;
|
|
1503
|
+
}
|
|
1504
|
+
let pageTexts = [];
|
|
1505
|
+
try {
|
|
1506
|
+
const { PDFParse } = await import("pdf-parse");
|
|
1507
|
+
pageTexts = (await new PDFParse({ data: ctx.payload.bytes }).getText()).pages.map((p) => p.text ?? "");
|
|
1508
|
+
} catch {
|
|
1509
|
+
pageTexts = [];
|
|
1510
|
+
}
|
|
1511
|
+
const pages = doc.getPages();
|
|
1512
|
+
const font = await doc.embedFont(StandardFonts.Helvetica);
|
|
1513
|
+
let pagesRedacted = 0;
|
|
1514
|
+
for (const [i, page] of pages.entries()) {
|
|
1515
|
+
const pageText = pageTexts[i] ?? "";
|
|
1516
|
+
if (!patterns.some((pattern) => new RegExp(pattern.source, pattern.flags).test(pageText))) continue;
|
|
1517
|
+
const { width } = page.getSize();
|
|
1518
|
+
page.drawRectangle({
|
|
1519
|
+
x: 0,
|
|
1520
|
+
y: 0,
|
|
1521
|
+
width,
|
|
1522
|
+
height: 14,
|
|
1523
|
+
color: rgb(1, 1, .9)
|
|
1524
|
+
});
|
|
1525
|
+
page.drawText(`[Page ${i + 1}: content redacted]`, {
|
|
1526
|
+
x: 4,
|
|
1527
|
+
y: 2,
|
|
1528
|
+
size: 8,
|
|
1529
|
+
font,
|
|
1530
|
+
color: rgb(.5, 0, 0)
|
|
1531
|
+
});
|
|
1532
|
+
pagesRedacted += 1;
|
|
1533
|
+
}
|
|
1534
|
+
for (const field of [
|
|
1535
|
+
"Title",
|
|
1536
|
+
"Author",
|
|
1537
|
+
"Subject",
|
|
1538
|
+
"Keywords",
|
|
1539
|
+
"Creator",
|
|
1540
|
+
"Producer"
|
|
1541
|
+
]) try {
|
|
1542
|
+
const infoDict = doc.context.lookup(doc.context.trailerInfo.Info);
|
|
1543
|
+
if (infoDict && typeof infoDict.get === "function" && typeof infoDict.set === "function") {
|
|
1544
|
+
if (infoDict.get(PDFName.of(field))) infoDict.set(PDFName.of(field), PDFString.of(""));
|
|
1545
|
+
}
|
|
1546
|
+
} catch {}
|
|
1547
|
+
try {
|
|
1548
|
+
const catalog = doc.context.lookup(doc.context.trailerInfo.Root);
|
|
1549
|
+
if (catalog && typeof catalog.get === "function" && typeof catalog.delete === "function") {
|
|
1550
|
+
if (catalog.get(PDFName.of("Metadata"))) catalog.delete(PDFName.of("Metadata"));
|
|
1551
|
+
}
|
|
1552
|
+
} catch {}
|
|
1553
|
+
if (pagesRedacted === 0) fail$5(verb, "no page text matched the redaction pattern(s) — nothing was redacted");
|
|
1554
|
+
const bytes = new Uint8Array(await doc.save());
|
|
1555
|
+
return {
|
|
1556
|
+
kind: "media",
|
|
1557
|
+
payload: {
|
|
1558
|
+
...ctx.payload,
|
|
1559
|
+
bytes
|
|
1560
|
+
}
|
|
1561
|
+
};
|
|
1562
|
+
};
|
|
1563
|
+
/**
|
|
1564
|
+
* `redact` — format dispatch: text media → text path; DOCX/PPTX/ODF → in-place container
|
|
1565
|
+
* edit; PDF → visual redaction (draw-over + metadata strip — see {@link PDF_REDACTION_WARNING}).
|
|
1566
|
+
*/
|
|
1243
1567
|
var docRedactStep = async (ctx) => {
|
|
1244
1568
|
const mime = ctx.payload.mimeType.toLowerCase().split(";")[0].trim();
|
|
1245
1569
|
if (mime.startsWith("text/")) return redactStep(ctx);
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1570
|
+
const raw = ctx.step.args.match;
|
|
1571
|
+
const patterns = Array.isArray(raw) ? raw : [raw];
|
|
1572
|
+
const replacement = argOf(ctx.step, "replace") ?? "█";
|
|
1573
|
+
if (mime === require_formats.MIME.DOCX || mime === require_formats.MIME.PPTX) return mutateOoxmlText(ctx, "redact", (xml, kind) => {
|
|
1574
|
+
let out = xml;
|
|
1575
|
+
for (const p of patterns) out = replaceInOoxml(out, kind === "docx" ? "w:p" : "a:p", kind === "docx" ? "w:t" : "a:t", toGlobalRegex(p), replacement).xml;
|
|
1576
|
+
return out;
|
|
1577
|
+
});
|
|
1578
|
+
if (ODF_MIMES.has(mime)) return mutateOdfText(ctx, "redact", (xml) => {
|
|
1579
|
+
let out = xml;
|
|
1580
|
+
for (const p of patterns) out = replaceTextInContentXml(out, toGlobalRegex(p), replacement).xml;
|
|
1581
|
+
return out;
|
|
1582
|
+
});
|
|
1583
|
+
if (mime === require_formats.MIME.PDF) return redactPdf(ctx, "redact", patterns.map(toGlobalRegex));
|
|
1584
|
+
fail$5("redact", `redaction for ${mime} is not supported in this build (text, DOCX, PPTX, ODT/ODS/ODP, and PDF are).`);
|
|
1257
1585
|
throw new Error("unreachable");
|
|
1258
1586
|
};
|
|
1259
1587
|
var BLOCKED_CONTROL = (code) => code >= 0 && code <= 8 || code === 11 || code === 12 || code >= 14 && code <= 31 || code === 127;
|
|
@@ -1283,8 +1611,8 @@ var docSanitizeStep = async (ctx) => {
|
|
|
1283
1611
|
}
|
|
1284
1612
|
};
|
|
1285
1613
|
}
|
|
1286
|
-
if (mime === MIME.DOCX || mime === MIME.PPTX) return mutateOoxmlText(ctx, "sanitize", (xml, kind) => sanitizeTextNodes(xml, kind === "docx" ? "w:t" : "a:t"));
|
|
1287
|
-
fail$
|
|
1614
|
+
if (mime === require_formats.MIME.DOCX || mime === require_formats.MIME.PPTX) return mutateOoxmlText(ctx, "sanitize", (xml, kind) => sanitizeTextNodes(xml, kind === "docx" ? "w:t" : "a:t"));
|
|
1615
|
+
fail$5("sanitize", `sanitize for ${mime} is not supported in this build (text, DOCX, PPTX are)`);
|
|
1288
1616
|
throw new Error("unreachable");
|
|
1289
1617
|
};
|
|
1290
1618
|
var normalizeText = (text) => text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/[\t ]+$/gm, "");
|
|
@@ -1301,7 +1629,7 @@ var docNormalizeStep = async (ctx) => {
|
|
|
1301
1629
|
}
|
|
1302
1630
|
};
|
|
1303
1631
|
}
|
|
1304
|
-
if (mime === MIME.DOCX || mime === MIME.PPTX) return mutateOoxmlText(ctx, "normalize", (xml, kind) => {
|
|
1632
|
+
if (mime === require_formats.MIME.DOCX || mime === require_formats.MIME.PPTX) return mutateOoxmlText(ctx, "normalize", (xml, kind) => {
|
|
1305
1633
|
const tag = kind === "docx" ? "w:t" : "a:t";
|
|
1306
1634
|
return xml.replace(new RegExp(`<${tag}(?:\\s[^>]*)?>([^<]*)</${tag}>`, "g"), (full, content) => {
|
|
1307
1635
|
const normalized = normalizeText(unescapeXml(content));
|
|
@@ -1309,28 +1637,38 @@ var docNormalizeStep = async (ctx) => {
|
|
|
1309
1637
|
return full.replace(content, escapeXml$1(normalized));
|
|
1310
1638
|
});
|
|
1311
1639
|
});
|
|
1312
|
-
fail$
|
|
1640
|
+
fail$5("normalize", `normalize for ${mime} is not supported in this build (text, DOCX, PPTX are)`);
|
|
1313
1641
|
throw new Error("unreachable");
|
|
1314
1642
|
};
|
|
1315
|
-
/** `update_text` — anchor replacement; DOCX/PPTX in-place, TARGET_NOT_FOUND when absent. */
|
|
1643
|
+
/** `update_text` — anchor replacement; DOCX/PPTX/ODF in-place, TARGET_NOT_FOUND when absent. */
|
|
1316
1644
|
var docUpdateTextStep = async (ctx) => {
|
|
1317
1645
|
const mime = ctx.payload.mimeType.toLowerCase().split(";")[0].trim();
|
|
1318
1646
|
if (mime.startsWith("text/")) return updateTextStep(ctx);
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1647
|
+
const anchor = argOf(ctx.step, "anchor");
|
|
1648
|
+
const replace = argOf(ctx.step, "replace") ?? "";
|
|
1649
|
+
const escapedAnchor = anchor.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1650
|
+
if (mime === require_formats.MIME.DOCX || mime === require_formats.MIME.PPTX) {
|
|
1322
1651
|
let found = 0;
|
|
1323
1652
|
const result = await mutateOoxmlText(ctx, "update_text", (xml, kind) => {
|
|
1324
1653
|
if (found > 0) return xml;
|
|
1325
|
-
const
|
|
1326
|
-
const { xml: next, matchCount } = replaceInOoxml(xml, kind === "docx" ? "w:p" : "a:p", kind === "docx" ? "w:t" : "a:t", new RegExp(escaped), replace);
|
|
1654
|
+
const { xml: next, matchCount } = replaceInOoxml(xml, kind === "docx" ? "w:p" : "a:p", kind === "docx" ? "w:t" : "a:t", new RegExp(escapedAnchor), replace);
|
|
1327
1655
|
found += matchCount;
|
|
1328
1656
|
return next;
|
|
1329
1657
|
});
|
|
1330
|
-
if (found === 0) fail$
|
|
1658
|
+
if (found === 0) fail$5("update_text", `anchor text not found: "${anchor.slice(0, 80)}"`);
|
|
1331
1659
|
return result;
|
|
1332
1660
|
}
|
|
1333
|
-
|
|
1661
|
+
if (ODF_MIMES.has(mime)) {
|
|
1662
|
+
let found = 0;
|
|
1663
|
+
const result = await mutateOdfText(ctx, "update_text", (xml) => {
|
|
1664
|
+
const { xml: next, matchCount } = replaceTextInContentXml(xml, new RegExp(escapedAnchor), replace);
|
|
1665
|
+
found += matchCount;
|
|
1666
|
+
return next;
|
|
1667
|
+
});
|
|
1668
|
+
if (found === 0) fail$5("update_text", `anchor text not found: "${anchor.slice(0, 80)}"`);
|
|
1669
|
+
return result;
|
|
1670
|
+
}
|
|
1671
|
+
fail$5("update_text", `text update for ${mime} is not supported (text, DOCX, PPTX, ODT/ODS/ODP are; PDF text is not reliably editable)`);
|
|
1334
1672
|
throw new Error("unreachable");
|
|
1335
1673
|
};
|
|
1336
1674
|
var ASSET_PATTERNS = {
|
|
@@ -1365,9 +1703,9 @@ var extractAssetsStep = async (ctx) => {
|
|
|
1365
1703
|
const requested = ctx.step.args.types ?? ["all"];
|
|
1366
1704
|
const wantAll = requested.includes("all");
|
|
1367
1705
|
const want = (kind) => wantAll || requested.includes(kind);
|
|
1368
|
-
if (mime === MIME.PDF) {
|
|
1369
|
-
if (!want("image")) fail$
|
|
1370
|
-
if (!ctx.engines.hasConvert(mime, "images")) fail$
|
|
1706
|
+
if (mime === require_formats.MIME.PDF) {
|
|
1707
|
+
if (!want("image")) fail$5(verb, "PDF asset extraction currently supports images (types=image or all)");
|
|
1708
|
+
if (!ctx.engines.hasConvert(mime, "images")) fail$5(verb, "PDF asset extraction requires an engine that converts application/pdf to \"images\", and none is configured. Do not retry this verb on PDFs in this deployment.");
|
|
1371
1709
|
const format = argOf(ctx.step, "format");
|
|
1372
1710
|
const result = await ctx.engines.convert({
|
|
1373
1711
|
bytes: ctx.payload.bytes,
|
|
@@ -1377,7 +1715,7 @@ var extractAssetsStep = async (ctx) => {
|
|
|
1377
1715
|
options: format ? { format } : void 0,
|
|
1378
1716
|
signal: ctx.signal
|
|
1379
1717
|
});
|
|
1380
|
-
if (result.outputs.length === 0) fail$
|
|
1718
|
+
if (result.outputs.length === 0) fail$5(verb, "no embedded images were found in the PDF");
|
|
1381
1719
|
const wantedMime = format ? EXT_MIME[format] ?? `image/${format}` : void 0;
|
|
1382
1720
|
const payloads = [];
|
|
1383
1721
|
for (const [i, img] of result.outputs.entries()) {
|
|
@@ -1405,13 +1743,13 @@ var extractAssetsStep = async (ctx) => {
|
|
|
1405
1743
|
};
|
|
1406
1744
|
}
|
|
1407
1745
|
if (![
|
|
1408
|
-
MIME.DOCX,
|
|
1409
|
-
MIME.XLSX,
|
|
1410
|
-
MIME.PPTX,
|
|
1411
|
-
MIME.ODT,
|
|
1412
|
-
MIME.ODS,
|
|
1413
|
-
MIME.ODP
|
|
1414
|
-
].includes(mime)) fail$
|
|
1746
|
+
require_formats.MIME.DOCX,
|
|
1747
|
+
require_formats.MIME.XLSX,
|
|
1748
|
+
require_formats.MIME.PPTX,
|
|
1749
|
+
require_formats.MIME.ODT,
|
|
1750
|
+
require_formats.MIME.ODS,
|
|
1751
|
+
require_formats.MIME.ODP
|
|
1752
|
+
].includes(mime)) fail$5(verb, `asset extraction for ${mime} is not supported (OOXML/ODF archives and PDF are)`);
|
|
1415
1753
|
const zip = await (await jszip$2()).loadAsync(ctx.payload.bytes);
|
|
1416
1754
|
const payloads = [];
|
|
1417
1755
|
for (const [name, entry] of Object.entries(zip.files)) {
|
|
@@ -1427,7 +1765,7 @@ var extractAssetsStep = async (ctx) => {
|
|
|
1427
1765
|
filename: basename
|
|
1428
1766
|
});
|
|
1429
1767
|
}
|
|
1430
|
-
if (payloads.length === 0) fail$
|
|
1768
|
+
if (payloads.length === 0) fail$5(verb, "no embedded assets matched the requested types");
|
|
1431
1769
|
return {
|
|
1432
1770
|
kind: "media-list",
|
|
1433
1771
|
payloads
|
|
@@ -1438,7 +1776,7 @@ var convertStep = async (ctx) => {
|
|
|
1438
1776
|
const verb = "convert";
|
|
1439
1777
|
const to = argOf(ctx.step, "to");
|
|
1440
1778
|
const supported = ctx.engines.convertTargets(ctx.payload.mimeType);
|
|
1441
|
-
if (!supported.includes(to)) fail$
|
|
1779
|
+
if (!supported.includes(to)) fail$5(verb, `no configured engine (or computed path) can produce "${to}" from ${ctx.payload.mimeType}; reachable targets: ${supported.join(", ") || "(none)"}`);
|
|
1442
1780
|
const output = (await ctx.engines.convert({
|
|
1443
1781
|
bytes: ctx.payload.bytes,
|
|
1444
1782
|
mimeType: ctx.payload.mimeType,
|
|
@@ -1446,7 +1784,7 @@ var convertStep = async (ctx) => {
|
|
|
1446
1784
|
to,
|
|
1447
1785
|
signal: ctx.signal
|
|
1448
1786
|
})).outputs[0];
|
|
1449
|
-
if (!output) fail$
|
|
1787
|
+
if (!output) fail$5(verb, "the conversion produced no output");
|
|
1450
1788
|
const dot = ctx.payload.filename.lastIndexOf(".");
|
|
1451
1789
|
const base = dot > 0 ? ctx.payload.filename.slice(0, dot) : ctx.payload.filename;
|
|
1452
1790
|
return {
|
|
@@ -1468,32 +1806,222 @@ var DOC_STEPS = [
|
|
|
1468
1806
|
["convert", convertStep]
|
|
1469
1807
|
];
|
|
1470
1808
|
//#endregion
|
|
1471
|
-
//#region src/batteries/media/steps/
|
|
1809
|
+
//#region src/batteries/media/steps/data.ts
|
|
1472
1810
|
/**
|
|
1473
|
-
* `
|
|
1811
|
+
* `append` and `data.*` step implementations: deterministic text/data mutations on in-memory
|
|
1812
|
+
* bytes — append a line, set/merge/delete at a JSON or YAML path.
|
|
1474
1813
|
*
|
|
1475
1814
|
* @remarks
|
|
1476
|
-
* Internal sibling of the `@nhtio/adk/batteries/media` entry.
|
|
1477
|
-
*
|
|
1478
|
-
*
|
|
1479
|
-
*
|
|
1480
|
-
*
|
|
1815
|
+
* Internal sibling of the `@nhtio/adk/batteries/media` entry. Pure and cross-env: text ops
|
|
1816
|
+
* are string concatenation; structured ops parse (JSON natively, YAML via the `js-yaml`
|
|
1817
|
+
* dependency), mutate the value tree, and re-serialize **preserving the source format** —
|
|
1818
|
+
* `data.set` on a YAML file yields YAML, on a JSON file yields JSON. These are the verbs
|
|
1819
|
+
* that make the lossy text family first-class media: `empty:json | data set path=… value=…`
|
|
1820
|
+
* is a create-then-populate chain with no engine requirement at all.
|
|
1481
1821
|
*/
|
|
1482
|
-
var
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1822
|
+
var fail$4 = (verb, message) => {
|
|
1823
|
+
throw new require_exceptions.E_MEDIA_STEP_FAILED([verb, message]);
|
|
1824
|
+
};
|
|
1825
|
+
var encoder = new TextEncoder();
|
|
1826
|
+
var decoder = new TextDecoder();
|
|
1827
|
+
/** MIME types the text-shaped verbs operate on. */
|
|
1828
|
+
var TEXTUAL = (mime) => mime.startsWith("text/") || mime === require_formats.MIME.JSON || mime === require_formats.MIME.YAML;
|
|
1829
|
+
/** `append` — append text to text-family media. */
|
|
1830
|
+
var appendStep = async (ctx) => {
|
|
1831
|
+
const verb = "append";
|
|
1832
|
+
const mime = ctx.payload.mimeType.toLowerCase().split(";")[0].trim();
|
|
1833
|
+
if (!TEXTUAL(mime)) fail$4(verb, `append expects text-family media (txt/md/csv/yaml/json-lines); the media is ${mime}`);
|
|
1834
|
+
const text = argOf(ctx.step, "text");
|
|
1835
|
+
const newline = argOf(ctx.step, "newline") ?? true;
|
|
1836
|
+
const current = decoder.decode(ctx.payload.bytes);
|
|
1837
|
+
const next = current + (newline && current.length > 0 && !current.endsWith("\n") ? "\n" : "") + text + (newline ? "\n" : "");
|
|
1838
|
+
return {
|
|
1839
|
+
kind: "media",
|
|
1840
|
+
payload: {
|
|
1841
|
+
...ctx.payload,
|
|
1842
|
+
bytes: encoder.encode(next)
|
|
1843
|
+
}
|
|
1844
|
+
};
|
|
1845
|
+
};
|
|
1846
|
+
var parseDoc = async (verb, payload) => {
|
|
1847
|
+
const mime = payload.mimeType.toLowerCase().split(";")[0].trim();
|
|
1848
|
+
const text = decoder.decode(payload.bytes);
|
|
1849
|
+
if (mime === require_formats.MIME.JSON) try {
|
|
1850
|
+
return {
|
|
1851
|
+
value: text.trim() === "" ? null : JSON.parse(text),
|
|
1852
|
+
format: "json"
|
|
1853
|
+
};
|
|
1854
|
+
} catch (err) {
|
|
1855
|
+
fail$4(verb, `the media is not valid JSON: ${require_tool_registry.isError(err) ? err.message : String(err)}`);
|
|
1856
|
+
}
|
|
1857
|
+
if (mime === require_formats.MIME.YAML) try {
|
|
1858
|
+
const { load } = await import("js-yaml");
|
|
1859
|
+
return {
|
|
1860
|
+
value: load(text) ?? null,
|
|
1861
|
+
format: "yaml"
|
|
1862
|
+
};
|
|
1863
|
+
} catch (err) {
|
|
1864
|
+
fail$4(verb, `the media is not valid YAML: ${require_tool_registry.isError(err) ? err.message : String(err)}`);
|
|
1865
|
+
}
|
|
1866
|
+
fail$4(verb, `data operations expect JSON or YAML media; the media is ${mime}`);
|
|
1867
|
+
throw new Error("unreachable");
|
|
1868
|
+
};
|
|
1869
|
+
var serializeDoc = async (doc, payload) => {
|
|
1870
|
+
if (doc.format === "yaml") {
|
|
1871
|
+
const { dump } = await import("js-yaml");
|
|
1872
|
+
return {
|
|
1873
|
+
...payload,
|
|
1874
|
+
bytes: encoder.encode(dump(doc.value))
|
|
1875
|
+
};
|
|
1876
|
+
}
|
|
1877
|
+
return {
|
|
1878
|
+
...payload,
|
|
1879
|
+
bytes: encoder.encode(JSON.stringify(doc.value, null, 2))
|
|
1880
|
+
};
|
|
1881
|
+
};
|
|
1882
|
+
/** Parse a dot/bracket path (`a.b[2].c`) into segments. */
|
|
1883
|
+
var parsePath = (verb, raw) => {
|
|
1884
|
+
if (typeof raw !== "string" || raw.trim() === "") fail$4(verb, "path must be a non-empty string");
|
|
1885
|
+
const segments = [];
|
|
1886
|
+
const re = /([^.[\]]+)|\[(\d+)\]/g;
|
|
1887
|
+
let match;
|
|
1888
|
+
while (match = re.exec(raw)) if (match[2] !== void 0) segments.push(Number(match[2]));
|
|
1889
|
+
else segments.push(match[1]);
|
|
1890
|
+
if (segments.length === 0) fail$4(verb, `could not parse path "${raw}"`);
|
|
1891
|
+
return segments;
|
|
1892
|
+
};
|
|
1893
|
+
/** Walk to the parent of the path target, creating containers when `create` is set. */
|
|
1894
|
+
var walkToParent = (verb, root, segments, create) => {
|
|
1895
|
+
let node = root;
|
|
1896
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
1897
|
+
const seg = segments[i];
|
|
1898
|
+
const isIndex = typeof seg === "number";
|
|
1899
|
+
if (node === null || typeof node !== "object") fail$4(verb, `path segment "${String(seg)}" walks through a non-container value`);
|
|
1900
|
+
const container = node;
|
|
1901
|
+
let next = isIndex ? container[seg] : container[seg];
|
|
1902
|
+
if (next === void 0 || next === null) {
|
|
1903
|
+
if (!create) fail$4(verb, `path segment "${String(seg)}" does not exist`);
|
|
1904
|
+
next = typeof segments[i + 1] === "number" ? [] : {};
|
|
1905
|
+
if (isIndex) container[seg] = next;
|
|
1906
|
+
else container[seg] = next;
|
|
1907
|
+
}
|
|
1908
|
+
node = next;
|
|
1909
|
+
}
|
|
1910
|
+
if (node === null || typeof node !== "object") fail$4(verb, "the path target parent is not an object or array");
|
|
1911
|
+
return {
|
|
1912
|
+
parent: node,
|
|
1913
|
+
key: segments[segments.length - 1]
|
|
1914
|
+
};
|
|
1915
|
+
};
|
|
1916
|
+
/** `data.set` — set a value at a JSON/YAML path, creating intermediate containers. */
|
|
1917
|
+
var dataSetStep = async (ctx) => {
|
|
1918
|
+
const verb = "data set";
|
|
1919
|
+
const path = argOf(ctx.step, "path");
|
|
1920
|
+
const rawValue = argOf(ctx.step, "value");
|
|
1921
|
+
if (rawValue === void 0) fail$4(verb, "value is required (JSON-encoded scalar or structure)");
|
|
1922
|
+
let value;
|
|
1923
|
+
try {
|
|
1924
|
+
value = JSON.parse(String(rawValue));
|
|
1925
|
+
} catch {
|
|
1926
|
+
value = String(rawValue);
|
|
1927
|
+
}
|
|
1928
|
+
const doc = await parseDoc(verb, ctx.payload);
|
|
1929
|
+
const segments = parsePath(verb, path);
|
|
1930
|
+
if (doc.value === null || typeof doc.value !== "object") doc.value = typeof segments[0] === "number" ? [] : {};
|
|
1931
|
+
const { parent, key } = walkToParent(verb, doc.value, segments, true);
|
|
1932
|
+
if (Array.isArray(parent)) parent[Number(key)] = value;
|
|
1933
|
+
else parent[String(key)] = value;
|
|
1934
|
+
return {
|
|
1935
|
+
kind: "media",
|
|
1936
|
+
payload: await serializeDoc(doc, ctx.payload)
|
|
1937
|
+
};
|
|
1486
1938
|
};
|
|
1939
|
+
/** Deep merge `fragment` into `target` (objects merge recursively; everything else replaces). */
|
|
1940
|
+
var deepMerge = (target, fragment) => {
|
|
1941
|
+
if (target === null || fragment === null || typeof target !== "object" || typeof fragment !== "object" || Array.isArray(target) !== Array.isArray(fragment) || Array.isArray(fragment)) return fragment;
|
|
1942
|
+
const out = { ...target };
|
|
1943
|
+
for (const [k, v] of Object.entries(fragment)) out[k] = k in out ? deepMerge(out[k], v) : v;
|
|
1944
|
+
return out;
|
|
1945
|
+
};
|
|
1946
|
+
/** `data.merge` — merge a JSON fragment into the document (deep by default). */
|
|
1947
|
+
var dataMergeStep = async (ctx) => {
|
|
1948
|
+
const verb = "data merge";
|
|
1949
|
+
const rawFragment = argOf(ctx.step, "fragment");
|
|
1950
|
+
const strategy = argOf(ctx.step, "strategy") ?? "deep";
|
|
1951
|
+
let fragment;
|
|
1952
|
+
try {
|
|
1953
|
+
fragment = JSON.parse(String(rawFragment));
|
|
1954
|
+
} catch (err) {
|
|
1955
|
+
fail$4(verb, `fragment must be valid JSON: ${require_tool_registry.isError(err) ? err.message : String(err)}`);
|
|
1956
|
+
}
|
|
1957
|
+
if (fragment === null || typeof fragment !== "object" || Array.isArray(fragment)) fail$4(verb, "fragment must be a JSON object");
|
|
1958
|
+
const doc = await parseDoc(verb, ctx.payload);
|
|
1959
|
+
if (doc.value === null || typeof doc.value !== "object" || Array.isArray(doc.value)) fail$4(verb, "merge requires the document root to be an object");
|
|
1960
|
+
doc.value = strategy === "shallow" ? {
|
|
1961
|
+
...doc.value,
|
|
1962
|
+
...fragment
|
|
1963
|
+
} : deepMerge(doc.value, fragment);
|
|
1964
|
+
return {
|
|
1965
|
+
kind: "media",
|
|
1966
|
+
payload: await serializeDoc(doc, ctx.payload)
|
|
1967
|
+
};
|
|
1968
|
+
};
|
|
1969
|
+
/** `data.delete` — remove a key/index at a JSON/YAML path. */
|
|
1970
|
+
var dataDeleteStep = async (ctx) => {
|
|
1971
|
+
const verb = "data delete";
|
|
1972
|
+
const path = argOf(ctx.step, "path");
|
|
1973
|
+
const doc = await parseDoc(verb, ctx.payload);
|
|
1974
|
+
const segments = parsePath(verb, path);
|
|
1975
|
+
if (doc.value === null || typeof doc.value !== "object") fail$4(verb, "the document has no structure to delete from");
|
|
1976
|
+
const { parent, key } = walkToParent(verb, doc.value, segments, false);
|
|
1977
|
+
if (Array.isArray(parent)) {
|
|
1978
|
+
const idx = Number(key);
|
|
1979
|
+
if (idx < 0 || idx >= parent.length) fail$4(verb, `index ${idx} is out of range`);
|
|
1980
|
+
parent.splice(idx, 1);
|
|
1981
|
+
} else {
|
|
1982
|
+
if (!(String(key) in parent)) fail$4(verb, `key "${String(key)}" does not exist at that path`);
|
|
1983
|
+
delete parent[String(key)];
|
|
1984
|
+
}
|
|
1985
|
+
return {
|
|
1986
|
+
kind: "media",
|
|
1987
|
+
payload: await serializeDoc(doc, ctx.payload)
|
|
1988
|
+
};
|
|
1989
|
+
};
|
|
1990
|
+
/** The data step registry fragment, keyed by canonical verb id. */
|
|
1991
|
+
var DATA_STEPS = [
|
|
1992
|
+
["append", appendStep],
|
|
1993
|
+
["data.set", dataSetStep],
|
|
1994
|
+
["data.merge", dataMergeStep],
|
|
1995
|
+
["data.delete", dataDeleteStep]
|
|
1996
|
+
];
|
|
1997
|
+
//#endregion
|
|
1998
|
+
//#region src/batteries/media/steps/sheet.ts
|
|
1999
|
+
/**
|
|
2000
|
+
* `sheet.*` step implementations: thin dispatchers into the engine registry's edit
|
|
2001
|
+
* capability.
|
|
2002
|
+
*
|
|
2003
|
+
* @remarks
|
|
2004
|
+
* Internal sibling of the `@nhtio/adk/batteries/media` entry. The actual workbook surgery
|
|
2005
|
+
* lives in edit-capable engines (`engines/exceljs` preserves styling; `engines/sheetjs`
|
|
2006
|
+
* covers the wider read matrix but strips styling — SheetJS CE limitation). Each step here:
|
|
2007
|
+
* normalizes non-xlsx spreadsheet inputs to xlsx when the selected edit coverage requires it
|
|
2008
|
+
* (any spreadsheet-family MIME with a declared `hasConvert(mime, 'xlsx')` edge qualifies —
|
|
2009
|
+
* ODS/XLS via soffice or sheetjs, XLSB/FODS/SYLK/DIF/DBF/NUMBERS via sheetjs), dispatches the
|
|
2010
|
+
* op through `ctx.engines.edit`, and degrades to a model-actionable failure when no edit
|
|
2011
|
+
* engine is configured.
|
|
2012
|
+
*/
|
|
1487
2013
|
var fail$3 = (verb, message) => {
|
|
1488
2014
|
throw new require_exceptions.E_MEDIA_STEP_FAILED([verb, message]);
|
|
1489
2015
|
};
|
|
1490
|
-
/** Acquire
|
|
1491
|
-
var
|
|
2016
|
+
/** Acquire bytes the edit dispatch can work on, normalizing to xlsx when necessary. */
|
|
2017
|
+
var acquireEditable = async (ctx, verb) => {
|
|
1492
2018
|
const mime = ctx.payload.mimeType.toLowerCase().split(";")[0].trim();
|
|
1493
|
-
if (mime
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
2019
|
+
if (ctx.engines.hasEdit?.(mime)) return {
|
|
2020
|
+
bytes: ctx.payload.bytes,
|
|
2021
|
+
mimeType: mime
|
|
2022
|
+
};
|
|
2023
|
+
if (require_formats.SPREADSHEET_MIMES.has(mime) && mime !== require_formats.MIME.XLSX) {
|
|
2024
|
+
if (!ctx.engines.hasConvert(mime, "xlsx")) fail$3(verb, require_formats.unsupportedForMutationReason(mime) ?? `this input is ${mime} — an engine that converts it to xlsx is required first, and none is configured. Do not retry this verb on this media in this deployment.`);
|
|
1497
2025
|
const output = (await ctx.engines.convert({
|
|
1498
2026
|
bytes: ctx.payload.bytes,
|
|
1499
2027
|
mimeType: mime,
|
|
@@ -1502,247 +2030,93 @@ var acquireXlsx = async (ctx, verb) => {
|
|
|
1502
2030
|
signal: ctx.signal
|
|
1503
2031
|
})).outputs[0];
|
|
1504
2032
|
if (!output) fail$3(verb, "normalizing the workbook to xlsx produced no output");
|
|
1505
|
-
return
|
|
2033
|
+
return {
|
|
2034
|
+
bytes: output.bytes,
|
|
2035
|
+
mimeType: require_formats.MIME.XLSX
|
|
2036
|
+
};
|
|
1506
2037
|
}
|
|
1507
|
-
if (
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
};
|
|
1511
|
-
/** The shared open → mutate → write-back lifecycle every sheet verb uses. */
|
|
1512
|
-
var withWorkbook = async (ctx, verb, mutate) => {
|
|
1513
|
-
const xlsxBytes = await acquireXlsx(ctx, verb);
|
|
1514
|
-
const wb = new (await (excel())).Workbook();
|
|
1515
|
-
try {
|
|
1516
|
-
await wb.xlsx.load(xlsxBytes.buffer.slice(xlsxBytes.byteOffset, xlsxBytes.byteOffset + xlsxBytes.byteLength));
|
|
1517
|
-
} catch (err) {
|
|
1518
|
-
fail$3(verb, `could not open the spreadsheet: ${require_tool_registry.isError(err) ? err.message : String(err)}`);
|
|
2038
|
+
if (mime !== require_formats.MIME.XLSX) {
|
|
2039
|
+
const mutationBlock = require_formats.unsupportedForMutationReason(mime);
|
|
2040
|
+
if (mutationBlock) fail$3(verb, mutationBlock);
|
|
2041
|
+
fail$3(verb, `sheet operations expect a spreadsheet; the media is ${mime}`);
|
|
1519
2042
|
}
|
|
1520
|
-
await mutate(wb);
|
|
1521
2043
|
return {
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
bytes: new Uint8Array(await wb.xlsx.writeBuffer()),
|
|
1525
|
-
mimeType: MIME.XLSX,
|
|
1526
|
-
filename: replaceExtension(ctx.payload.filename, "xlsx")
|
|
1527
|
-
}
|
|
2044
|
+
bytes: ctx.payload.bytes,
|
|
2045
|
+
mimeType: require_formats.MIME.XLSX
|
|
1528
2046
|
};
|
|
1529
2047
|
};
|
|
1530
|
-
/**
|
|
1531
|
-
var
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
2048
|
+
/** Dispatch one `sheet.*` op through the registry's edit capability. */
|
|
2049
|
+
var dispatchEdit = async (ctx, verb, op) => {
|
|
2050
|
+
const { bytes, mimeType } = await acquireEditable(ctx, verb);
|
|
2051
|
+
if (!ctx.engines.hasEdit?.(mimeType, op)) fail$3(verb, `no engine that edits spreadsheets is configured — add engines/exceljs (or engines/sheetjs) to the pipeline's engines array. Do not retry this verb in this deployment.`);
|
|
2052
|
+
try {
|
|
2053
|
+
const result = await ctx.engines.edit({
|
|
2054
|
+
bytes,
|
|
2055
|
+
mimeType,
|
|
2056
|
+
op,
|
|
2057
|
+
args: ctx.step.args,
|
|
2058
|
+
signal: ctx.signal
|
|
2059
|
+
});
|
|
2060
|
+
return {
|
|
2061
|
+
kind: "media",
|
|
2062
|
+
payload: {
|
|
2063
|
+
bytes: result.bytes,
|
|
2064
|
+
mimeType: result.mimeType,
|
|
2065
|
+
filename: require_formats.replaceExtension(ctx.payload.filename, "xlsx")
|
|
2066
|
+
}
|
|
2067
|
+
};
|
|
2068
|
+
} catch (err) {
|
|
2069
|
+
if (require_tool_registry.isError(err) && err.name.startsWith("E_MEDIA_")) throw err;
|
|
2070
|
+
fail$3(verb, require_tool_registry.isError(err) ? err.message : String(err));
|
|
2071
|
+
throw err;
|
|
1536
2072
|
}
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
fail$3(verb, `sheet index ${sheetArg} is out of range (1-based; the workbook has ${wb.worksheets.length})`);
|
|
1542
|
-
}
|
|
1543
|
-
const ws = wb.getWorksheet(sheetArg);
|
|
1544
|
-
if (!ws) fail$3(verb, `no sheet named "${sheetArg}". Sheets: ${wb.worksheets.map((w) => `"${w.name}"`).join(", ")}`);
|
|
1545
|
-
return ws;
|
|
1546
|
-
};
|
|
1547
|
-
var columnLetterToNumber = (col) => {
|
|
1548
|
-
let n = 0;
|
|
1549
|
-
for (const ch of col.toUpperCase()) n = n * 26 + (ch.charCodeAt(0) - 64);
|
|
1550
|
-
return n;
|
|
1551
|
-
};
|
|
1552
|
-
var asCellValue = (verb, value) => {
|
|
1553
|
-
if (value === null) return null;
|
|
1554
|
-
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value;
|
|
1555
|
-
fail$3(verb, `cell values must be string, number, boolean, or null; got ${typeof value}`);
|
|
1556
|
-
throw new Error("unreachable");
|
|
2073
|
+
};
|
|
2074
|
+
/** Pre-dispatch arg validation: keep the historical readable failures for malformed args. */
|
|
2075
|
+
var requireJsonArray = (verb, value, message) => {
|
|
2076
|
+
if (!Array.isArray(value) || value.length === 0) fail$3(verb, message);
|
|
1557
2077
|
};
|
|
1558
2078
|
/** `sheet.add_rows` — insert rows before/after an index, or append. */
|
|
1559
2079
|
var sheetAddRowsStep = async (ctx) => {
|
|
1560
2080
|
const verb = "sheet add_rows";
|
|
1561
2081
|
const rows = ctx.step.args.rows;
|
|
1562
2082
|
if (!Array.isArray(rows) || rows.length === 0 || !rows.every(Array.isArray)) fail$3(verb, `rows must be a non-empty JSON array of arrays, e.g. rows='[["a",1]]'`);
|
|
1563
|
-
|
|
1564
|
-
const after = argOf(ctx.step, "after");
|
|
1565
|
-
return withWorkbook(ctx, verb, (wb) => {
|
|
1566
|
-
const ws = resolveSheet(verb, wb, argOf(ctx.step, "sheet"));
|
|
1567
|
-
const insertAt = before !== void 0 ? before : after !== void 0 ? after + 1 : ws.rowCount + 1;
|
|
1568
|
-
const cellRows = rows.map((r) => r.map((v) => asCellValue(verb, v)));
|
|
1569
|
-
ws.spliceRows(insertAt, 0, ...cellRows);
|
|
1570
|
-
return { added: cellRows.length };
|
|
1571
|
-
});
|
|
2083
|
+
return dispatchEdit(ctx, verb, "sheet.add_rows");
|
|
1572
2084
|
};
|
|
1573
2085
|
/** `sheet.add_columns` — insert columns with headers or full descriptors. */
|
|
1574
2086
|
var sheetAddColumnsStep = async (ctx) => {
|
|
1575
2087
|
const verb = "sheet add_columns";
|
|
1576
2088
|
const headers = ctx.step.args.headers;
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
const before = argOf(ctx.step, "before");
|
|
1580
|
-
const after = argOf(ctx.step, "after");
|
|
1581
|
-
return withWorkbook(ctx, verb, (wb) => {
|
|
1582
|
-
const ws = resolveSheet(verb, wb, argOf(ctx.step, "sheet"));
|
|
1583
|
-
const insertAt = before !== void 0 ? before : after !== void 0 ? after + 1 : ws.columnCount + 1;
|
|
1584
|
-
for (const [i, col] of columns.entries()) {
|
|
1585
|
-
if (typeof col?.header !== "string") fail$3(verb, "every column needs a string header");
|
|
1586
|
-
ws.spliceColumns(insertAt + i, 0, [col.header, ...(col.values ?? []).map((v) => asCellValue(verb, v))]);
|
|
1587
|
-
}
|
|
1588
|
-
return { added: columns.length };
|
|
1589
|
-
});
|
|
2089
|
+
requireJsonArray(verb, ctx.step.args.columns ?? headers, `provide headers=a,b or columns='[{"header":"X","values":[1]}]'`);
|
|
2090
|
+
return dispatchEdit(ctx, verb, "sheet.add_columns");
|
|
1590
2091
|
};
|
|
1591
2092
|
/** `sheet.update_cells` — set cells by A1 address or row/col. Leading `=` writes a formula. */
|
|
1592
2093
|
var sheetUpdateCellsStep = async (ctx) => {
|
|
1593
2094
|
const verb = "sheet update_cells";
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
return withWorkbook(ctx, verb, (wb) => {
|
|
1597
|
-
const ws = resolveSheet(verb, wb, argOf(ctx.step, "sheet"));
|
|
1598
|
-
let modified = 0;
|
|
1599
|
-
for (const raw of updates) {
|
|
1600
|
-
const address = raw.address;
|
|
1601
|
-
const row = raw.row;
|
|
1602
|
-
const col = raw.col;
|
|
1603
|
-
let cell;
|
|
1604
|
-
if (address) cell = ws.getCell(address);
|
|
1605
|
-
else if (row !== void 0 && col !== void 0) cell = typeof col === "string" ? ws.getCell(row, columnLetterToNumber(col)) : ws.getCell(row, col);
|
|
1606
|
-
else {
|
|
1607
|
-
fail$3(verb, "each update needs address, or both row and col");
|
|
1608
|
-
continue;
|
|
1609
|
-
}
|
|
1610
|
-
const value = raw.value;
|
|
1611
|
-
if (typeof value === "string" && value.startsWith("=")) cell.value = { formula: value.slice(1) };
|
|
1612
|
-
else cell.value = asCellValue(verb, value);
|
|
1613
|
-
modified += 1;
|
|
1614
|
-
}
|
|
1615
|
-
return { modified };
|
|
1616
|
-
});
|
|
2095
|
+
requireJsonArray(verb, ctx.step.args.updates, `updates must be a non-empty JSON array, e.g. updates='[{"address":"B2","value":3}]'`);
|
|
2096
|
+
return dispatchEdit(ctx, verb, "sheet.update_cells");
|
|
1617
2097
|
};
|
|
1618
2098
|
/** `sheet.delete_rows` — delete 1-based rows (bottom-up so indices stay valid). */
|
|
1619
|
-
var sheetDeleteRowsStep = async (ctx) =>
|
|
1620
|
-
const verb = "sheet delete_rows";
|
|
1621
|
-
const rows = ctx.step.args.rows;
|
|
1622
|
-
return withWorkbook(ctx, verb, (wb) => {
|
|
1623
|
-
const ws = resolveSheet(verb, wb, argOf(ctx.step, "sheet"));
|
|
1624
|
-
for (const r of [...rows].sort((a, b) => b - a)) ws.spliceRows(r, 1);
|
|
1625
|
-
return { removed: rows.length };
|
|
1626
|
-
});
|
|
1627
|
-
};
|
|
2099
|
+
var sheetDeleteRowsStep = async (ctx) => dispatchEdit(ctx, "sheet delete_rows", "sheet.delete_rows");
|
|
1628
2100
|
/** `sheet.delete_columns` — delete 1-based columns. */
|
|
1629
|
-
var sheetDeleteColumnsStep = async (ctx) =>
|
|
1630
|
-
const verb = "sheet delete_columns";
|
|
1631
|
-
const columns = ctx.step.args.columns;
|
|
1632
|
-
return withWorkbook(ctx, verb, (wb) => {
|
|
1633
|
-
const ws = resolveSheet(verb, wb, argOf(ctx.step, "sheet"));
|
|
1634
|
-
for (const c of [...columns].sort((a, b) => b - a)) ws.spliceColumns(c, 1);
|
|
1635
|
-
return { removed: columns.length };
|
|
1636
|
-
});
|
|
1637
|
-
};
|
|
2101
|
+
var sheetDeleteColumnsStep = async (ctx) => dispatchEdit(ctx, "sheet delete_columns", "sheet.delete_columns");
|
|
1638
2102
|
/** `sheet.rename_sheet` — rename by NAME (the server contract; index targeting is rejected). */
|
|
1639
|
-
var sheetRenameSheetStep = async (ctx) =>
|
|
1640
|
-
const verb = "sheet rename_sheet";
|
|
1641
|
-
const sheet = argOf(ctx.step, "sheet");
|
|
1642
|
-
const to = argOf(ctx.step, "to");
|
|
1643
|
-
return withWorkbook(ctx, verb, (wb) => {
|
|
1644
|
-
const ws = wb.getWorksheet(sheet);
|
|
1645
|
-
if (!ws) fail$3(verb, `rename targets a sheet NAME; no sheet named "${sheet}". Sheets: ${wb.worksheets.map((w) => `"${w.name}"`).join(", ")}`);
|
|
1646
|
-
ws.name = to;
|
|
1647
|
-
return { modified: 1 };
|
|
1648
|
-
});
|
|
1649
|
-
};
|
|
2103
|
+
var sheetRenameSheetStep = async (ctx) => dispatchEdit(ctx, "sheet rename_sheet", "sheet.rename_sheet");
|
|
1650
2104
|
/** `sheet.add_sheet` — add a worksheet, optionally at a 1-based position. */
|
|
1651
|
-
var sheetAddSheetStep = async (ctx) =>
|
|
1652
|
-
const verb = "sheet add_sheet";
|
|
1653
|
-
const name = argOf(ctx.step, "name");
|
|
1654
|
-
const at = argOf(ctx.step, "at");
|
|
1655
|
-
return withWorkbook(ctx, verb, (wb) => {
|
|
1656
|
-
if (wb.getWorksheet(name)) fail$3(verb, `a sheet named "${name}" already exists`);
|
|
1657
|
-
const ws = wb.addWorksheet(name);
|
|
1658
|
-
if (at !== void 0) {
|
|
1659
|
-
const all = wb.worksheets;
|
|
1660
|
-
const wsIdx = all.indexOf(ws);
|
|
1661
|
-
if (wsIdx !== at - 1) all.forEach((w, i) => {
|
|
1662
|
-
w.orderNo = w === ws ? at : i + 1 >= at && i < wsIdx ? i + 2 : i + 1;
|
|
1663
|
-
});
|
|
1664
|
-
}
|
|
1665
|
-
return { added: 1 };
|
|
1666
|
-
});
|
|
1667
|
-
};
|
|
2105
|
+
var sheetAddSheetStep = async (ctx) => dispatchEdit(ctx, "sheet add_sheet", "sheet.add_sheet");
|
|
1668
2106
|
/** `sheet.remove_sheet` — remove by NAME (the server contract). */
|
|
1669
|
-
var sheetRemoveSheetStep = async (ctx) =>
|
|
1670
|
-
const verb = "sheet remove_sheet";
|
|
1671
|
-
const sheet = argOf(ctx.step, "sheet");
|
|
1672
|
-
return withWorkbook(ctx, verb, (wb) => {
|
|
1673
|
-
const ws = wb.getWorksheet(sheet);
|
|
1674
|
-
if (!ws) fail$3(verb, `remove targets a sheet NAME; no sheet named "${sheet}". Sheets: ${wb.worksheets.map((w) => `"${w.name}"`).join(", ")}`);
|
|
1675
|
-
wb.removeWorksheet(ws.id);
|
|
1676
|
-
return { removed: 1 };
|
|
1677
|
-
});
|
|
1678
|
-
};
|
|
2107
|
+
var sheetRemoveSheetStep = async (ctx) => dispatchEdit(ctx, "sheet remove_sheet", "sheet.remove_sheet");
|
|
1679
2108
|
/** `sheet.reorder_sheets` — every sheet exactly once, by name or 1-based index. */
|
|
1680
2109
|
var sheetReorderSheetsStep = async (ctx) => {
|
|
1681
2110
|
const verb = "sheet reorder_sheets";
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
return withWorkbook(ctx, verb, (wb) => {
|
|
1685
|
-
const refs = order;
|
|
1686
|
-
if (refs.length !== wb.worksheets.length) fail$3(verb, `order must include every worksheet exactly once (the workbook has ${wb.worksheets.length})`);
|
|
1687
|
-
const reordered = [];
|
|
1688
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1689
|
-
for (const ref of refs) {
|
|
1690
|
-
const ws = typeof ref === "number" ? wb.worksheets[ref - 1] : wb.getWorksheet(ref);
|
|
1691
|
-
if (!ws) fail$3(verb, `sheet not found: ${String(ref)}`);
|
|
1692
|
-
if (seen.has(ws.id)) fail$3(verb, `duplicate sheet reference: ${String(ref)}`);
|
|
1693
|
-
seen.add(ws.id);
|
|
1694
|
-
reordered.push(ws);
|
|
1695
|
-
}
|
|
1696
|
-
reordered.forEach((ws, i) => {
|
|
1697
|
-
ws.orderNo = i + 1;
|
|
1698
|
-
});
|
|
1699
|
-
return { modified: reordered.length };
|
|
1700
|
-
});
|
|
2111
|
+
requireJsonArray(verb, ctx.step.args.order, `order must be a JSON array of names/indices, e.g. order='["Summary",2]'`);
|
|
2112
|
+
return dispatchEdit(ctx, verb, "sheet.reorder_sheets");
|
|
1701
2113
|
};
|
|
1702
2114
|
/** `sheet.transform_table` — rename/select/drop columns by header name. */
|
|
1703
2115
|
var sheetTransformTableStep = async (ctx) => {
|
|
1704
2116
|
const verb = "sheet transform_table";
|
|
1705
|
-
const headerRow = argOf(ctx.step, "header_row")
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
const drop = ctx.step.args.drop;
|
|
1709
|
-
return withWorkbook(ctx, verb, (wb) => {
|
|
1710
|
-
const ws = resolveSheet(verb, wb, argOf(ctx.step, "sheet"));
|
|
1711
|
-
let modified = 0;
|
|
1712
|
-
const headerMap = /* @__PURE__ */ new Map();
|
|
1713
|
-
ws.getRow(headerRow).eachCell({ includeEmpty: false }, (cell, colNumber) => {
|
|
1714
|
-
if (typeof cell.value === "string") headerMap.set(cell.value, colNumber);
|
|
1715
|
-
});
|
|
1716
|
-
if (rename) for (const { from, to } of rename) {
|
|
1717
|
-
const colNum = headerMap.get(from);
|
|
1718
|
-
if (colNum !== void 0) {
|
|
1719
|
-
ws.getRow(headerRow).getCell(colNum).value = to;
|
|
1720
|
-
headerMap.delete(from);
|
|
1721
|
-
headerMap.set(to, colNum);
|
|
1722
|
-
modified += 1;
|
|
1723
|
-
}
|
|
1724
|
-
}
|
|
1725
|
-
if (drop) {
|
|
1726
|
-
const cols = drop.map((name) => headerMap.get(name)).filter((n) => n !== void 0).sort((a, b) => b - a);
|
|
1727
|
-
for (const col of cols) {
|
|
1728
|
-
ws.spliceColumns(col, 1);
|
|
1729
|
-
modified += 1;
|
|
1730
|
-
}
|
|
1731
|
-
}
|
|
1732
|
-
if (select) {
|
|
1733
|
-
const keep = new Set(select);
|
|
1734
|
-
const cols = [];
|
|
1735
|
-
ws.getRow(headerRow).eachCell({ includeEmpty: false }, (cell, colNumber) => {
|
|
1736
|
-
if (typeof cell.value === "string" && !keep.has(cell.value)) cols.push(colNumber);
|
|
1737
|
-
});
|
|
1738
|
-
cols.sort((a, b) => b - a);
|
|
1739
|
-
for (const col of cols) {
|
|
1740
|
-
ws.spliceColumns(col, 1);
|
|
1741
|
-
modified += 1;
|
|
1742
|
-
}
|
|
1743
|
-
}
|
|
1744
|
-
return { modified };
|
|
1745
|
-
});
|
|
2117
|
+
const headerRow = argOf(ctx.step, "header_row");
|
|
2118
|
+
if (headerRow !== void 0 && (!Number.isFinite(headerRow) || headerRow < 1)) fail$3(verb, "header_row must be a 1-based row number");
|
|
2119
|
+
return dispatchEdit(ctx, verb, "sheet.transform_table");
|
|
1746
2120
|
};
|
|
1747
2121
|
/** The sheet step registry fragment, keyed by canonical verb id. */
|
|
1748
2122
|
var SHEET_STEPS = [
|
|
@@ -1876,8 +2250,8 @@ var setSlideTitle = (slideXml, title) => {
|
|
|
1876
2250
|
/** Acquire pptx bytes, converting ODP to PPTX via the engine registry. */
|
|
1877
2251
|
var acquirePptx = async (ctx, verb) => {
|
|
1878
2252
|
const mime = ctx.payload.mimeType.toLowerCase().split(";")[0].trim();
|
|
1879
|
-
if (mime === MIME.PPTX) return ctx.payload.bytes;
|
|
1880
|
-
if (mime === MIME.ODP) {
|
|
2253
|
+
if (mime === require_formats.MIME.PPTX) return ctx.payload.bytes;
|
|
2254
|
+
if (mime === require_formats.MIME.ODP) {
|
|
1881
2255
|
if (!ctx.engines.hasConvert(mime, "pptx")) fail$2(verb, "this input is ODP — an engine that converts it to PPTX is required first, and none is configured. Do not retry this verb on this media in this deployment.");
|
|
1882
2256
|
const output = (await ctx.engines.convert({
|
|
1883
2257
|
bytes: ctx.payload.bytes,
|
|
@@ -1889,7 +2263,7 @@ var acquirePptx = async (ctx, verb) => {
|
|
|
1889
2263
|
if (!output) fail$2(verb, "normalizing the presentation to PPTX produced no output");
|
|
1890
2264
|
return output.bytes;
|
|
1891
2265
|
}
|
|
1892
|
-
const reason = unsupportedForMutationReason(mime);
|
|
2266
|
+
const reason = require_formats.unsupportedForMutationReason(mime);
|
|
1893
2267
|
if (reason) fail$2(verb, reason);
|
|
1894
2268
|
fail$2(verb, `slides operations expect a presentation; the media is ${mime}`);
|
|
1895
2269
|
throw new Error("unreachable");
|
|
@@ -1913,8 +2287,8 @@ var withPresentation = async (ctx, verb, mutate) => {
|
|
|
1913
2287
|
type: "uint8array",
|
|
1914
2288
|
compression: "DEFLATE"
|
|
1915
2289
|
}),
|
|
1916
|
-
mimeType: MIME.PPTX,
|
|
1917
|
-
filename: replaceExtension(ctx.payload.filename, "pptx")
|
|
2290
|
+
mimeType: require_formats.MIME.PPTX,
|
|
2291
|
+
filename: require_formats.replaceExtension(ctx.payload.filename, "pptx")
|
|
1918
2292
|
}
|
|
1919
2293
|
};
|
|
1920
2294
|
};
|
|
@@ -2258,7 +2632,7 @@ var extractTextDeepStep = async (ctx) => {
|
|
|
2258
2632
|
});
|
|
2259
2633
|
if (mime.startsWith("image/")) return asData(await runOcr(ctx));
|
|
2260
2634
|
if (ocrMode === "force") return asData(await runOcr(ctx));
|
|
2261
|
-
if (mime === MIME.PDF) {
|
|
2635
|
+
if (mime === require_formats.MIME.PDF) {
|
|
2262
2636
|
const text = await extractPdfText(ctx);
|
|
2263
2637
|
if (text.trim().length === 0 && ocrMode === "auto") {
|
|
2264
2638
|
if (!ctx.engines.hasConvert(mime, "txt")) fail$1(VERB, "this PDF has no text layer (scanned?). An engine that converts application/pdf to text (OCR) is required to read it, and none is configured. Do not retry this verb on this media in this deployment.");
|
|
@@ -2266,11 +2640,11 @@ var extractTextDeepStep = async (ctx) => {
|
|
|
2266
2640
|
}
|
|
2267
2641
|
return asData(text);
|
|
2268
2642
|
}
|
|
2269
|
-
if (mime === MIME.DOCX || mime === MIME.DOC) return asData(await extractDocxText(ctx));
|
|
2270
|
-
if (mime === MIME.XLSX) return asData(await extractXlsxText(ctx));
|
|
2271
|
-
if (mime === MIME.ODT || mime === MIME.ODS || mime === MIME.ODP) return asData(await extractOdfText(ctx));
|
|
2272
|
-
if (mime === MIME.PPTX) return asData(await extractPptxText(ctx));
|
|
2273
|
-
if (mime.startsWith("text/") || mime === MIME.JSON) return asData(decodeText(ctx.payload.bytes));
|
|
2643
|
+
if (mime === require_formats.MIME.DOCX || mime === require_formats.MIME.DOC) return asData(await extractDocxText(ctx));
|
|
2644
|
+
if (mime === require_formats.MIME.XLSX) return asData(await extractXlsxText(ctx));
|
|
2645
|
+
if (mime === require_formats.MIME.ODT || mime === require_formats.MIME.ODS || mime === require_formats.MIME.ODP) return asData(await extractOdfText(ctx));
|
|
2646
|
+
if (mime === require_formats.MIME.PPTX) return asData(await extractPptxText(ctx));
|
|
2647
|
+
if (mime.startsWith("text/") || mime === require_formats.MIME.JSON) return asData(decodeText(ctx.payload.bytes));
|
|
2274
2648
|
fail$1(VERB, `text extraction for ${mime} is not supported in this build. For PPT/legacy formats: convert to=pptx | extract text (requires a convert engine).`);
|
|
2275
2649
|
throw new Error("unreachable");
|
|
2276
2650
|
};
|
|
@@ -2281,16 +2655,16 @@ var extractMetadataDeepStep = async (ctx) => {
|
|
|
2281
2655
|
filename: ctx.payload.filename,
|
|
2282
2656
|
mime_type: ctx.payload.mimeType,
|
|
2283
2657
|
size_bytes: ctx.payload.bytes.byteLength,
|
|
2284
|
-
family: familyOf(ctx.payload.mimeType)
|
|
2658
|
+
family: require_formats.familyOf(ctx.payload.mimeType)
|
|
2285
2659
|
};
|
|
2286
2660
|
try {
|
|
2287
|
-
if (mime === MIME.PDF) {
|
|
2661
|
+
if (mime === require_formats.MIME.PDF) {
|
|
2288
2662
|
const { PDFDocument } = await import("pdf-lib");
|
|
2289
2663
|
const doc = await PDFDocument.load(ctx.payload.bytes, { ignoreEncryption: true });
|
|
2290
2664
|
meta.page_count = doc.getPageCount();
|
|
2291
2665
|
meta.title = doc.getTitle() ?? void 0;
|
|
2292
2666
|
meta.author = doc.getAuthor() ?? void 0;
|
|
2293
|
-
} else if (mime === MIME.XLSX) {
|
|
2667
|
+
} else if (mime === require_formats.MIME.XLSX) {
|
|
2294
2668
|
const mod = await import("exceljs");
|
|
2295
2669
|
const wb = new ("default" in mod ? mod.default : mod).Workbook();
|
|
2296
2670
|
await wb.xlsx.load(ctx.payload.bytes.buffer.slice(ctx.payload.bytes.byteOffset, ctx.payload.bytes.byteOffset + ctx.payload.bytes.byteLength));
|
|
@@ -2298,7 +2672,7 @@ var extractMetadataDeepStep = async (ctx) => {
|
|
|
2298
2672
|
name: ws.name,
|
|
2299
2673
|
rows: ws.rowCount
|
|
2300
2674
|
}));
|
|
2301
|
-
} else if (mime === MIME.PPTX) {
|
|
2675
|
+
} else if (mime === require_formats.MIME.PPTX) {
|
|
2302
2676
|
const zip = await (await jszip()).loadAsync(ctx.payload.bytes);
|
|
2303
2677
|
meta.slide_count = Object.keys(zip.files).filter((n) => /^ppt\/slides\/slide\d+\.xml$/.test(n)).length;
|
|
2304
2678
|
}
|
|
@@ -2332,10 +2706,16 @@ var INGEST_STEPS = [["extract.text", extractTextDeepStep], ["extract.metadata",
|
|
|
2332
2706
|
/** The maximum number of conversion hops a computed path may take. */
|
|
2333
2707
|
var MAX_HOPS = 3;
|
|
2334
2708
|
/**
|
|
2335
|
-
*
|
|
2336
|
-
*
|
|
2337
|
-
*
|
|
2338
|
-
* step performs between those
|
|
2709
|
+
* The no-lossy-intermediates ROUTING rule — and nothing else. These format tokens are valid
|
|
2710
|
+
* conversion endpoints but never intermediate path nodes: without this rule the pathfinder
|
|
2711
|
+
* would happily route `docx → txt → …` (lossy garbage in, garbage out) or auto-chain
|
|
2712
|
+
* `audio → pcm → txt` (skipping the resample the transcription step performs between those
|
|
2713
|
+
* legs).
|
|
2714
|
+
*
|
|
2715
|
+
* This set does NOT gate mutation, creation, or advertising. A lossy format as the *desired
|
|
2716
|
+
* output* is still valid media: `txt` can be created (`empty:txt`), appended to, patched,
|
|
2717
|
+
* diffed, and targeted by `convert to=txt` — terminality only forbids silently routing
|
|
2718
|
+
* *through* it on the way to somewhere else.
|
|
2339
2719
|
*/
|
|
2340
2720
|
var TERMINAL_TOKENS = new Set([
|
|
2341
2721
|
"txt",
|
|
@@ -2403,6 +2783,20 @@ var buildEngineRegistry = (engines, selection = []) => {
|
|
|
2403
2783
|
}
|
|
2404
2784
|
return out;
|
|
2405
2785
|
};
|
|
2786
|
+
const editCandidates = (mimeType, op) => {
|
|
2787
|
+
const mime = normalizeMime(mimeType);
|
|
2788
|
+
const out = [];
|
|
2789
|
+
for (const engine of engines) for (const capability of engine.edits ?? []) {
|
|
2790
|
+
if (!matchesAny(capability.over, mime)) continue;
|
|
2791
|
+
if (!capability.ops.includes(op)) continue;
|
|
2792
|
+
out.push({
|
|
2793
|
+
engine,
|
|
2794
|
+
capability
|
|
2795
|
+
});
|
|
2796
|
+
break;
|
|
2797
|
+
}
|
|
2798
|
+
return out;
|
|
2799
|
+
};
|
|
2406
2800
|
const arbitrate = async (kind, request, candidates) => {
|
|
2407
2801
|
if (candidates.length === 0) return void 0;
|
|
2408
2802
|
if (selection.length === 0 || candidates.length === 1) return candidates[0];
|
|
@@ -2441,7 +2835,7 @@ var buildEngineRegistry = (engines, selection = []) => {
|
|
|
2441
2835
|
if (!matchesAny(capability.from, mime)) continue;
|
|
2442
2836
|
for (const to of capability.to) {
|
|
2443
2837
|
if (TERMINAL_TOKENS.has(to)) continue;
|
|
2444
|
-
const nextMime = EXT_TO_MIME[to];
|
|
2838
|
+
const nextMime = require_formats.EXT_TO_MIME[to];
|
|
2445
2839
|
if (!nextMime || visited.has(nextMime)) continue;
|
|
2446
2840
|
visited.add(nextMime);
|
|
2447
2841
|
queue.push({
|
|
@@ -2478,7 +2872,7 @@ var buildEngineRegistry = (engines, selection = []) => {
|
|
|
2478
2872
|
reachable.push(to);
|
|
2479
2873
|
}
|
|
2480
2874
|
if (TERMINAL_TOKENS.has(to)) continue;
|
|
2481
|
-
const nextMime = EXT_TO_MIME[to];
|
|
2875
|
+
const nextMime = require_formats.EXT_TO_MIME[to];
|
|
2482
2876
|
if (nextMime && !visitedMimes.has(nextMime)) {
|
|
2483
2877
|
visitedMimes.add(nextMime);
|
|
2484
2878
|
next.push(nextMime);
|
|
@@ -2546,13 +2940,38 @@ var buildEngineRegistry = (engines, selection = []) => {
|
|
|
2546
2940
|
if (!chosen) throw new Error(`all engines capable of this mutate (${candidates.map((c) => c.engine.id).join(", ")}) were excluded by selection middleware`);
|
|
2547
2941
|
return chosen.capability.mutate(request);
|
|
2548
2942
|
};
|
|
2943
|
+
const edit = async (request) => {
|
|
2944
|
+
const candidates = editCandidates(request.mimeType, request.op);
|
|
2945
|
+
if (candidates.length === 0) {
|
|
2946
|
+
const allOps = /* @__PURE__ */ new Set();
|
|
2947
|
+
for (const engine of engines) for (const capability of engine.edits ?? []) for (const op of capability.ops) allOps.add(op);
|
|
2948
|
+
throw new Error(`no configured engine can apply "${request.op}" to ${request.mimeType}; declared edit ops: ${[...allOps].join(", ") || "(none)"} (engines: ${ids()})`);
|
|
2949
|
+
}
|
|
2950
|
+
const chosen = await arbitrate("edit", {
|
|
2951
|
+
mimeType: request.mimeType,
|
|
2952
|
+
filename: "",
|
|
2953
|
+
bytes: request.bytes,
|
|
2954
|
+
ops: [request.op]
|
|
2955
|
+
}, candidates);
|
|
2956
|
+
if (!chosen) throw new Error(`all engines capable of "${request.op}" (${candidates.map((c) => c.engine.id).join(", ")}) were excluded by selection middleware`);
|
|
2957
|
+
return chosen.capability.edit(request);
|
|
2958
|
+
};
|
|
2549
2959
|
return {
|
|
2550
2960
|
engines,
|
|
2551
2961
|
hasConvert,
|
|
2552
2962
|
hasMutate: () => engines.some((e) => (e.mutates ?? []).length > 0),
|
|
2963
|
+
hasEdit: (mime, op) => {
|
|
2964
|
+
for (const engine of engines) for (const capability of engine.edits ?? []) {
|
|
2965
|
+
if (mime !== void 0 && !matchesAny(capability.over, normalizeMime(mime))) continue;
|
|
2966
|
+
if (op !== void 0 && !capability.ops.includes(op)) continue;
|
|
2967
|
+
return true;
|
|
2968
|
+
}
|
|
2969
|
+
return false;
|
|
2970
|
+
},
|
|
2553
2971
|
convertTargets,
|
|
2554
2972
|
convert,
|
|
2555
|
-
mutate
|
|
2973
|
+
mutate,
|
|
2974
|
+
edit
|
|
2556
2975
|
};
|
|
2557
2976
|
};
|
|
2558
2977
|
//#endregion
|
|
@@ -2752,7 +3171,7 @@ var pdfLib = () => {
|
|
|
2752
3171
|
return pdfLibPromise;
|
|
2753
3172
|
};
|
|
2754
3173
|
var requirePdf = (verb, payload) => {
|
|
2755
|
-
if (payload.mimeType.toLowerCase().split(";")[0].trim() !== MIME.PDF) throw new require_exceptions.E_MEDIA_STEP_FAILED([verb, `page operations for ${payload.mimeType} are not yet implemented in this build (PDF is supported)`]);
|
|
3174
|
+
if (payload.mimeType.toLowerCase().split(";")[0].trim() !== require_formats.MIME.PDF) throw new require_exceptions.E_MEDIA_STEP_FAILED([verb, `page operations for ${payload.mimeType} are not yet implemented in this build (PDF is supported)`]);
|
|
2756
3175
|
};
|
|
2757
3176
|
var loadPdf = async (verb, bytes) => {
|
|
2758
3177
|
const { PDFDocument } = await pdfLib();
|
|
@@ -2810,8 +3229,8 @@ var splitStep = async (ctx) => {
|
|
|
2810
3229
|
const bytes = new Uint8Array(await out.save());
|
|
2811
3230
|
payloads.push({
|
|
2812
3231
|
bytes,
|
|
2813
|
-
mimeType: MIME.PDF,
|
|
2814
|
-
filename: replaceExtension(ctx.payload.filename, "").replace(/\.$/, "") + `.part${part}.pdf`
|
|
3232
|
+
mimeType: require_formats.MIME.PDF,
|
|
3233
|
+
filename: require_formats.replaceExtension(ctx.payload.filename, "").replace(/\.$/, "") + `.part${part}.pdf`
|
|
2815
3234
|
});
|
|
2816
3235
|
part += 1;
|
|
2817
3236
|
}
|
|
@@ -2930,6 +3349,7 @@ var PURE_STEPS = [
|
|
|
2930
3349
|
["extract.text", extractTextStep],
|
|
2931
3350
|
["extract.metadata", extractMetadataStep],
|
|
2932
3351
|
["chunk", chunkStep],
|
|
3352
|
+
...DATA_STEPS,
|
|
2933
3353
|
...SHEET_STEPS,
|
|
2934
3354
|
...SLIDES_STEPS,
|
|
2935
3355
|
...DOC_STEPS,
|
|
@@ -3007,7 +3427,8 @@ var createMediaPipeline = async (config = {}) => {
|
|
|
3007
3427
|
return callable;
|
|
3008
3428
|
};
|
|
3009
3429
|
//#endregion
|
|
3010
|
-
exports.
|
|
3430
|
+
exports.EMPTY_MIME = require_batteries_media_contracts.EMPTY_MIME;
|
|
3431
|
+
exports.EXT_TO_MIME = require_formats.EXT_TO_MIME;
|
|
3011
3432
|
exports.E_INVALID_MEDIA_PIPELINE_CONFIG = require_exceptions.E_INVALID_MEDIA_PIPELINE_CONFIG;
|
|
3012
3433
|
exports.E_MEDIA_BAD_ARG = require_exceptions.E_MEDIA_BAD_ARG;
|
|
3013
3434
|
exports.E_MEDIA_ENGINE_REQUIRED = require_exceptions.E_MEDIA_ENGINE_REQUIRED;
|
|
@@ -3020,7 +3441,7 @@ exports.E_MEDIA_UNKNOWN_ARG = require_exceptions.E_MEDIA_UNKNOWN_ARG;
|
|
|
3020
3441
|
exports.E_MEDIA_UNKNOWN_VERB = require_exceptions.E_MEDIA_UNKNOWN_VERB;
|
|
3021
3442
|
exports.E_MEDIA_UNSUPPORTED_OP = require_exceptions.E_MEDIA_UNSUPPORTED_OP;
|
|
3022
3443
|
exports.FOLDED_VERBS = require_validate.FOLDED_VERBS;
|
|
3023
|
-
exports.MIME = MIME;
|
|
3444
|
+
exports.MIME = require_formats.MIME;
|
|
3024
3445
|
exports.MediaChain = MediaChain;
|
|
3025
3446
|
exports.PCM_MIME = require_batteries_media_contracts.PCM_MIME;
|
|
3026
3447
|
exports.VERBS = require_validate.VERBS;
|
|
@@ -3029,7 +3450,7 @@ exports.availableVerbs = require_validate.availableVerbs;
|
|
|
3029
3450
|
exports.buildEngineRegistry = buildEngineRegistry;
|
|
3030
3451
|
exports.bytesToPcm = require_batteries_media_contracts.bytesToPcm;
|
|
3031
3452
|
exports.createMediaPipeline = createMediaPipeline;
|
|
3032
|
-
exports.familyOf = familyOf;
|
|
3453
|
+
exports.familyOf = require_formats.familyOf;
|
|
3033
3454
|
exports.foldVerb = require_validate.foldVerb;
|
|
3034
3455
|
exports.fromOps = require_validate.fromOps;
|
|
3035
3456
|
exports.implementsMediaEngine = require_batteries_media_contracts.implementsMediaEngine;
|
|
@@ -3038,11 +3459,11 @@ exports.isRegExpRef = require_validate.isRegExpRef;
|
|
|
3038
3459
|
exports.lowerSegments = lowerSegments;
|
|
3039
3460
|
exports.parsePipeRaw = parsePipeRaw;
|
|
3040
3461
|
exports.pcmToBytes = require_batteries_media_contracts.pcmToBytes;
|
|
3041
|
-
exports.replaceExtension = replaceExtension;
|
|
3462
|
+
exports.replaceExtension = require_formats.replaceExtension;
|
|
3042
3463
|
exports.suggestVerbs = require_validate.suggestVerbs;
|
|
3043
3464
|
exports.toOps = require_validate.toOps;
|
|
3044
3465
|
exports.toPipe = require_validate.toPipe;
|
|
3045
|
-
exports.unsupportedForMutationReason = unsupportedForMutationReason;
|
|
3466
|
+
exports.unsupportedForMutationReason = require_formats.unsupportedForMutationReason;
|
|
3046
3467
|
exports.validateOps = require_validate.validateOps;
|
|
3047
3468
|
exports.validateSegments = require_validate.validateSegments;
|
|
3048
3469
|
|