@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.
Files changed (290) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/__vite-browser-external-CU7uua8b.mjs +9 -0
  3. package/__vite-browser-external-CU7uua8b.mjs.map +1 -0
  4. package/__vite-browser-external-DAHxjh-D.js +13 -0
  5. package/__vite-browser-external-DAHxjh-D.js.map +1 -0
  6. package/batteries/embeddings/openai/adapter.cjs +1 -1
  7. package/batteries/embeddings/openai/adapter.mjs +1 -1
  8. package/batteries/embeddings/webllm/adapter.cjs +1 -1
  9. package/batteries/embeddings/webllm/adapter.mjs +1 -1
  10. package/batteries/llm/ollama/adapter.cjs +5 -5
  11. package/batteries/llm/ollama/adapter.mjs +5 -5
  12. package/batteries/llm/ollama/helpers.cjs +2 -2
  13. package/batteries/llm/ollama/helpers.mjs +2 -2
  14. package/batteries/llm/ollama/validation.cjs +1 -1
  15. package/batteries/llm/ollama/validation.mjs +1 -1
  16. package/batteries/llm/openai_chat_completions/adapter.cjs +5 -5
  17. package/batteries/llm/openai_chat_completions/adapter.mjs +5 -5
  18. package/batteries/llm/openai_chat_completions/helpers.cjs +2 -2
  19. package/batteries/llm/openai_chat_completions/helpers.mjs +2 -2
  20. package/batteries/llm/openai_chat_completions/validation.cjs +1 -1
  21. package/batteries/llm/openai_chat_completions/validation.mjs +1 -1
  22. package/batteries/llm/webllm_chat_completions/adapter.cjs +5 -5
  23. package/batteries/llm/webllm_chat_completions/adapter.mjs +5 -5
  24. package/batteries/llm/webllm_chat_completions/validation.cjs +1 -1
  25. package/batteries/llm/webllm_chat_completions/validation.mjs +1 -1
  26. package/batteries/media/contracts.cjs +38 -8
  27. package/batteries/media/contracts.cjs.map +1 -1
  28. package/batteries/media/contracts.d.ts +86 -6
  29. package/batteries/media/contracts.mjs +37 -9
  30. package/batteries/media/contracts.mjs.map +1 -1
  31. package/batteries/media/engines/audio_decode.cjs +46 -6
  32. package/batteries/media/engines/audio_decode.cjs.map +1 -1
  33. package/batteries/media/engines/audio_decode.d.ts +9 -5
  34. package/batteries/media/engines/audio_decode.mjs +47 -7
  35. package/batteries/media/engines/audio_decode.mjs.map +1 -1
  36. package/batteries/media/engines/data.cjs +159 -0
  37. package/batteries/media/engines/data.cjs.map +1 -0
  38. package/batteries/media/engines/data.d.ts +36 -0
  39. package/batteries/media/engines/data.mjs +157 -0
  40. package/batteries/media/engines/data.mjs.map +1 -0
  41. package/batteries/media/engines/exceljs.cjs +290 -0
  42. package/batteries/media/engines/exceljs.cjs.map +1 -0
  43. package/batteries/media/engines/exceljs.d.ts +41 -0
  44. package/batteries/media/engines/exceljs.mjs +288 -0
  45. package/batteries/media/engines/exceljs.mjs.map +1 -0
  46. package/batteries/media/engines/execa_executor.cjs +1 -1
  47. package/batteries/media/engines/execa_executor.mjs +1 -1
  48. package/batteries/media/engines/fs_workspace.cjs +1 -1
  49. package/batteries/media/engines/fs_workspace.mjs +1 -1
  50. package/batteries/media/engines/jimp.cjs +24 -2
  51. package/batteries/media/engines/jimp.cjs.map +1 -1
  52. package/batteries/media/engines/jimp.d.ts +3 -1
  53. package/batteries/media/engines/jimp.mjs +24 -2
  54. package/batteries/media/engines/jimp.mjs.map +1 -1
  55. package/batteries/media/engines/sharp.cjs +37 -4
  56. package/batteries/media/engines/sharp.cjs.map +1 -1
  57. package/batteries/media/engines/sharp.d.ts +5 -3
  58. package/batteries/media/engines/sharp.mjs +37 -4
  59. package/batteries/media/engines/sharp.mjs.map +1 -1
  60. package/batteries/media/engines/sheetjs.cjs +438 -0
  61. package/batteries/media/engines/sheetjs.cjs.map +1 -0
  62. package/batteries/media/engines/sheetjs.d.ts +51 -0
  63. package/batteries/media/engines/sheetjs.mjs +436 -0
  64. package/batteries/media/engines/sheetjs.mjs.map +1 -0
  65. package/batteries/media/engines/soffice.cjs +37 -3
  66. package/batteries/media/engines/soffice.cjs.map +1 -1
  67. package/batteries/media/engines/soffice.mjs +38 -4
  68. package/batteries/media/engines/soffice.mjs.map +1 -1
  69. package/batteries/media/engines/tesseract_js.cjs +1 -1
  70. package/batteries/media/engines/tesseract_js.mjs +1 -1
  71. package/batteries/media/engines/transformers_asr.cjs +1 -1
  72. package/batteries/media/engines/transformers_asr.mjs +1 -1
  73. package/batteries/media/forge.cjs +89 -20
  74. package/batteries/media/forge.cjs.map +1 -1
  75. package/batteries/media/forge.mjs +89 -20
  76. package/batteries/media/forge.mjs.map +1 -1
  77. package/batteries/media/formats.d.ts +9 -0
  78. package/batteries/media/index.d.ts +8 -6
  79. package/batteries/media/lint.cjs +270 -58
  80. package/batteries/media/lint.cjs.map +1 -1
  81. package/batteries/media/lint.d.ts +45 -0
  82. package/batteries/media/lint.mjs +267 -58
  83. package/batteries/media/lint.mjs.map +1 -1
  84. package/batteries/media/registry.d.ts +11 -2
  85. package/batteries/media/steps/data.d.ts +26 -0
  86. package/batteries/media/steps/doc.d.ts +17 -7
  87. package/batteries/media/steps/patch.d.ts +90 -0
  88. package/batteries/media/steps/sheet.d.ts +10 -6
  89. package/batteries/media/steps/text.d.ts +4 -1
  90. package/batteries/media/validate.d.ts +5 -0
  91. package/batteries/media/verbs.d.ts +12 -1
  92. package/batteries/media.cjs +869 -448
  93. package/batteries/media.cjs.map +1 -1
  94. package/batteries/media.mjs +829 -409
  95. package/batteries/media.mjs.map +1 -1
  96. package/batteries/storage/flydrive.cjs +3 -6
  97. package/batteries/storage/flydrive.cjs.map +1 -1
  98. package/batteries/storage/flydrive.mjs +3 -6
  99. package/batteries/storage/flydrive.mjs.map +1 -1
  100. package/batteries/storage/in_memory.cjs +1 -1
  101. package/batteries/storage/in_memory.mjs +1 -1
  102. package/batteries/storage/opfs.cjs +1 -1
  103. package/batteries/storage/opfs.mjs +1 -1
  104. package/batteries/tools/_shared.cjs +2 -2
  105. package/batteries/tools/_shared.mjs +2 -2
  106. package/batteries/tools/color.cjs +2 -2
  107. package/batteries/tools/color.mjs +2 -2
  108. package/batteries/tools/comparison.cjs +3 -3
  109. package/batteries/tools/comparison.mjs +3 -3
  110. package/batteries/tools/data_structure.cjs +3 -3
  111. package/batteries/tools/data_structure.mjs +3 -3
  112. package/batteries/tools/datetime_extended.cjs +3 -3
  113. package/batteries/tools/datetime_extended.mjs +3 -3
  114. package/batteries/tools/datetime_math.cjs +3 -3
  115. package/batteries/tools/datetime_math.mjs +3 -3
  116. package/batteries/tools/encoding.cjs +3 -3
  117. package/batteries/tools/encoding.mjs +3 -3
  118. package/batteries/tools/formatting.cjs +3 -3
  119. package/batteries/tools/formatting.mjs +3 -3
  120. package/batteries/tools/geo_basics.cjs +2 -2
  121. package/batteries/tools/geo_basics.mjs +2 -2
  122. package/batteries/tools/math.cjs +3 -3
  123. package/batteries/tools/math.mjs +3 -3
  124. package/batteries/tools/memory.cjs +6 -6
  125. package/batteries/tools/memory.mjs +6 -6
  126. package/batteries/tools/parsing.cjs +4 -4
  127. package/batteries/tools/parsing.mjs +4 -4
  128. package/batteries/tools/retrievables.cjs +5 -5
  129. package/batteries/tools/retrievables.mjs +5 -5
  130. package/batteries/tools/scrapper.cjs +1 -1
  131. package/batteries/tools/scrapper.mjs +1 -1
  132. package/batteries/tools/searxng.cjs +1 -1
  133. package/batteries/tools/searxng.mjs +1 -1
  134. package/batteries/tools/standing_instructions.cjs +4 -4
  135. package/batteries/tools/standing_instructions.mjs +4 -4
  136. package/batteries/tools/statistics.cjs +4 -4
  137. package/batteries/tools/statistics.mjs +4 -4
  138. package/batteries/tools/string_processing.cjs +3 -3
  139. package/batteries/tools/string_processing.mjs +3 -3
  140. package/batteries/tools/structured_data.cjs +3 -3
  141. package/batteries/tools/structured_data.mjs +3 -3
  142. package/batteries/tools/text_analysis.cjs +3 -3
  143. package/batteries/tools/text_analysis.mjs +3 -3
  144. package/batteries/tools/text_comparison.cjs +2 -2
  145. package/batteries/tools/text_comparison.mjs +2 -2
  146. package/batteries/tools/time.cjs +3 -3
  147. package/batteries/tools/time.mjs +3 -3
  148. package/batteries/tools/unit_conversion.cjs +2 -2
  149. package/batteries/tools/unit_conversion.mjs +2 -2
  150. package/batteries/tools/web_retrieval.cjs +1 -1
  151. package/batteries/tools/web_retrieval.mjs +1 -1
  152. package/batteries/tools.cjs +2 -2
  153. package/batteries/tools.mjs +2 -2
  154. package/batteries/vector/arangodb.cjs +1 -1
  155. package/batteries/vector/arangodb.mjs +1 -1
  156. package/batteries/vector/clickhouse.cjs +1 -1
  157. package/batteries/vector/clickhouse.mjs +1 -1
  158. package/batteries/vector/cloudflare.cjs +1 -1
  159. package/batteries/vector/cloudflare.mjs +1 -1
  160. package/batteries/vector/couchbase.cjs +1 -1
  161. package/batteries/vector/couchbase.mjs +1 -1
  162. package/batteries/vector/duckdb.cjs +1 -1
  163. package/batteries/vector/duckdb.mjs +1 -1
  164. package/batteries/vector/elasticsearch.cjs +1 -1
  165. package/batteries/vector/elasticsearch.mjs +1 -1
  166. package/batteries/vector/filters.cjs +1 -1
  167. package/batteries/vector/filters.mjs +1 -1
  168. package/batteries/vector/hnswlib.cjs +1 -1
  169. package/batteries/vector/hnswlib.mjs +1 -1
  170. package/batteries/vector/lancedb.cjs +1 -1
  171. package/batteries/vector/lancedb.mjs +1 -1
  172. package/batteries/vector/mariadb.cjs +1 -1
  173. package/batteries/vector/mariadb.mjs +1 -1
  174. package/batteries/vector/meilisearch.cjs +1 -1
  175. package/batteries/vector/meilisearch.mjs +1 -1
  176. package/batteries/vector/migrate.cjs +1 -1
  177. package/batteries/vector/migrate.mjs +1 -1
  178. package/batteries/vector/mongodb.cjs +1 -1
  179. package/batteries/vector/mongodb.mjs +1 -1
  180. package/batteries/vector/neo4j.cjs +1 -1
  181. package/batteries/vector/neo4j.mjs +1 -1
  182. package/batteries/vector/opensearch.cjs +1 -1
  183. package/batteries/vector/opensearch.mjs +1 -1
  184. package/batteries/vector/oracle23ai.cjs +1 -1
  185. package/batteries/vector/oracle23ai.mjs +1 -1
  186. package/batteries/vector/pinecone.cjs +1 -1
  187. package/batteries/vector/pinecone.mjs +1 -1
  188. package/batteries/vector/redis.cjs +1 -1
  189. package/batteries/vector/redis.mjs +1 -1
  190. package/batteries/vector/retrievable.cjs +1 -1
  191. package/batteries/vector/retrievable.mjs +1 -1
  192. package/batteries/vector/s3vectors.cjs +1 -1
  193. package/batteries/vector/s3vectors.mjs +1 -1
  194. package/batteries/vector/solr.cjs +1 -1
  195. package/batteries/vector/solr.mjs +1 -1
  196. package/batteries/vector/surrealdb.cjs +1 -1
  197. package/batteries/vector/surrealdb.mjs +1 -1
  198. package/batteries/vector/typesense.cjs +1 -1
  199. package/batteries/vector/typesense.mjs +1 -1
  200. package/batteries/vector/vespa.cjs +1 -1
  201. package/batteries/vector/vespa.mjs +1 -1
  202. package/batteries/vector/weaviate.cjs +1 -1
  203. package/batteries/vector/weaviate.mjs +1 -1
  204. package/batteries.cjs +2 -2
  205. package/batteries.mjs +2 -2
  206. package/bundled_xlsx.d.ts +1075 -0
  207. package/{common-BxoznqbX.mjs → common-BQoFv108.mjs} +7 -7
  208. package/{common-BxoznqbX.mjs.map → common-BQoFv108.mjs.map} +1 -1
  209. package/{common-BBK2e5ro.js → common-DmDn4Gfl.js} +7 -7
  210. package/{common-BBK2e5ro.js.map → common-DmDn4Gfl.js.map} +1 -1
  211. package/common.cjs +7 -7
  212. package/common.mjs +7 -7
  213. package/{dispatch_runner-Hn5idJYx.js → dispatch_runner-BwplbCPr.js} +3 -3
  214. package/{dispatch_runner-Hn5idJYx.js.map → dispatch_runner-BwplbCPr.js.map} +1 -1
  215. package/{dispatch_runner-DUrpj0xR.mjs → dispatch_runner-C1ar_D7W.mjs} +3 -3
  216. package/{dispatch_runner-DUrpj0xR.mjs.map → dispatch_runner-C1ar_D7W.mjs.map} +1 -1
  217. package/dispatch_runner.cjs +1 -1
  218. package/dispatch_runner.mjs +1 -1
  219. package/eslint.cjs +1 -1
  220. package/eslint.mjs +1 -1
  221. package/forge.cjs +4 -4
  222. package/forge.mjs +4 -4
  223. package/formats-CCylUHZw.mjs +161 -0
  224. package/formats-CCylUHZw.mjs.map +1 -0
  225. package/formats-DZVuqZUN.js +196 -0
  226. package/formats-DZVuqZUN.js.map +1 -0
  227. package/guards.cjs +8 -8
  228. package/guards.mjs +8 -8
  229. package/index.cjs +11 -11
  230. package/index.mjs +11 -11
  231. package/mcp/adk-docs-corpus.json +1 -1
  232. package/package.json +198 -182
  233. package/{scrapper-DLj_KkxK.mjs → scrapper-Dn2wXu0Y.mjs} +5 -5
  234. package/{scrapper-DLj_KkxK.mjs.map → scrapper-Dn2wXu0Y.mjs.map} +1 -1
  235. package/{scrapper-Cmf1CSA-.js → scrapper-mBZ0Odq7.js} +5 -5
  236. package/{scrapper-Cmf1CSA-.js.map → scrapper-mBZ0Odq7.js.map} +1 -1
  237. package/{searxng-BKs5s8Su.js → searxng-Dj-iP_Vz.js} +5 -5
  238. package/{searxng-BKs5s8Su.js.map → searxng-Dj-iP_Vz.js.map} +1 -1
  239. package/{searxng-BJ-aZcMz.mjs → searxng-DjFxtFf6.mjs} +5 -5
  240. package/{searxng-BJ-aZcMz.mjs.map → searxng-DjFxtFf6.mjs.map} +1 -1
  241. package/server.json +21 -0
  242. package/skills/adk-assembly/SKILL.md +2 -2
  243. package/{spooled_artifact-BR0IhU9y.js → spooled_artifact-BCmPrxZS.js} +3 -3
  244. package/{spooled_artifact-BR0IhU9y.js.map → spooled_artifact-BCmPrxZS.js.map} +1 -1
  245. package/{spooled_artifact-nTE6ujM8.mjs → spooled_artifact-DL36jnqA.mjs} +3 -3
  246. package/{spooled_artifact-nTE6ujM8.mjs.map → spooled_artifact-DL36jnqA.mjs.map} +1 -1
  247. package/spooled_artifact.cjs +2 -2
  248. package/spooled_artifact.mjs +2 -2
  249. package/{spooled_markdown_artifact-Dj3fNowz.js → spooled_markdown_artifact-CD4PrrpV.js} +3 -3
  250. package/{spooled_markdown_artifact-Dj3fNowz.js.map → spooled_markdown_artifact-CD4PrrpV.js.map} +1 -1
  251. package/{spooled_markdown_artifact-DIYzbQv8.mjs → spooled_markdown_artifact-Da-Xnebh.mjs} +3 -3
  252. package/{spooled_markdown_artifact-DIYzbQv8.mjs.map → spooled_markdown_artifact-Da-Xnebh.mjs.map} +1 -1
  253. package/{thought-Cde2Y_GK.mjs → thought-CtOoZUSE.mjs} +3 -3
  254. package/{thought-Cde2Y_GK.mjs.map → thought-CtOoZUSE.mjs.map} +1 -1
  255. package/{thought-BFtsnUTU.js → thought-eka4iUHa.js} +3 -3
  256. package/{thought-BFtsnUTU.js.map → thought-eka4iUHa.js.map} +1 -1
  257. package/{tool-CcxYxA5X.mjs → tool-CITF20yJ.mjs} +3 -3
  258. package/{tool-CcxYxA5X.mjs.map → tool-CITF20yJ.mjs.map} +1 -1
  259. package/{tool-B2nnkwNU.js → tool-Dhjca7rC.js} +3 -3
  260. package/{tool-B2nnkwNU.js.map → tool-Dhjca7rC.js.map} +1 -1
  261. package/{tool_call-BxL3eU1k.js → tool_call-Cyi0hUUc.js} +4 -4
  262. package/{tool_call-BxL3eU1k.js.map → tool_call-Cyi0hUUc.js.map} +1 -1
  263. package/{tool_call-B3MOog2R.mjs → tool_call-D_k36aI4.mjs} +4 -4
  264. package/{tool_call-B3MOog2R.mjs.map → tool_call-D_k36aI4.mjs.map} +1 -1
  265. package/{tool_registry-BaLUIzWz.mjs → tool_registry-CZ55mIti.mjs} +152 -143
  266. package/tool_registry-CZ55mIti.mjs.map +1 -0
  267. package/{tool_registry-BdKlPuHS.js → tool_registry-DwZ6pzN4.js} +151 -142
  268. package/tool_registry-DwZ6pzN4.js.map +1 -0
  269. package/{turn_runner-M2I8G2ok.js → turn_runner-CCZcsoDU.js} +5 -5
  270. package/{turn_runner-M2I8G2ok.js.map → turn_runner-CCZcsoDU.js.map} +1 -1
  271. package/{turn_runner-BV4BaZK9.mjs → turn_runner-DRPpVvsd.mjs} +5 -5
  272. package/{turn_runner-BV4BaZK9.mjs.map → turn_runner-DRPpVvsd.mjs.map} +1 -1
  273. package/turn_runner.cjs +1 -1
  274. package/turn_runner.mjs +1 -1
  275. package/{validate-JOHfh-BB.mjs → validate-C_hTnDBE.mjs} +95 -8
  276. package/validate-C_hTnDBE.mjs.map +1 -0
  277. package/{validate-DgDZkUKs.js → validate-gaSa9bsk.js} +95 -8
  278. package/validate-gaSa9bsk.js.map +1 -0
  279. package/xlsx-B0sTpEC2.js +30615 -0
  280. package/xlsx-B0sTpEC2.js.map +1 -0
  281. package/xlsx-DiMpn_Xv.mjs +30596 -0
  282. package/xlsx-DiMpn_Xv.mjs.map +1 -0
  283. package/xlsx.zahl-D2_Q-Xs4.mjs +6 -0
  284. package/xlsx.zahl-D2_Q-Xs4.mjs.map +1 -0
  285. package/xlsx.zahl-ZIcUQVv8.js +7 -0
  286. package/xlsx.zahl-ZIcUQVv8.js.map +1 -0
  287. package/tool_registry-BaLUIzWz.mjs.map +0 -1
  288. package/tool_registry-BdKlPuHS.js.map +0 -1
  289. package/validate-DgDZkUKs.js.map +0 -1
  290. package/validate-JOHfh-BB.mjs.map +0 -1
@@ -7,12 +7,15 @@
7
7
  * @remarks
8
8
  * Engines are seams, not policies. An engine is a self-declaring capability provider: it
9
9
  * states exactly which transforms it supports — {@link ConvertCapability} edges (input MIME
10
- * patterns to output format tokens) and {@link MutateCapability} groups (same-format content
11
- * transforms) and the pipeline dispatches against those declarations. There are only two
12
- * capability shapes because there are only two things a media engine ever does: change the
13
- * format, or change the content. OCR is a convert (image to text). Transcription is a convert
14
- * (PCM to text). Decoding audio is a convert (container to PCM). Resizing is a mutate. A new
15
- * capability is a new edge in the data, never a new contract.
10
+ * patterns to output format tokens), {@link MutateCapability} groups (same-format content
11
+ * transforms), and {@link EditCapability} groups (structural document operations) and the
12
+ * pipeline dispatches against those declarations. There are only three capability shapes
13
+ * because there are only three things a media engine ever does: change the format, change the
14
+ * content, or restructure the document. OCR is a convert (image to text). Transcription is a
15
+ * convert (PCM to text). Decoding audio is a convert (container to PCM). Generating a blank
16
+ * file is a convert (from {@link EMPTY_MIME} — the source format happens to be nothing).
17
+ * Resizing is a mutate. Inserting a worksheet row is an edit. A new capability is a new edge
18
+ * in the data, never a new contract.
16
19
  *
17
20
  * Engines are supplied to `createMediaPipeline` as a flat ordered array and resolved eagerly
18
21
  * at construction (declarations drive verb narrowing, so they must be known up front).
@@ -133,6 +136,22 @@ export type MimePattern = string;
133
136
  * a {@link ConvertOutput} carrying PCM must set `meta.sampleRate` (Hz).
134
137
  */
135
138
  export declare const PCM_MIME = "audio/x-adk-pcm";
139
+ /**
140
+ * The virtual source MIME type for media generation — the single seam through which new media
141
+ * comes into existence. An engine that can mint a blank/seed file declares
142
+ * `converts: [{ from: [EMPTY_MIME], to: [...] }]` and receives a {@link ConvertRequest} with
143
+ * zero bytes; the format token in `to` names what gets created.
144
+ *
145
+ * @remarks
146
+ * Generating an .xlsx, a blank canvas, or a second of silence IS media generation — it is
147
+ * *deterministic* generation (same inputs, same bytes). *Model-based semantic* generation
148
+ * (diffusion, TTS) is the same edge with different machinery: a BYO engine declares
149
+ * `from: [EMPTY_MIME]` and consumes a prompt from `request.options`. Both kinds ride one
150
+ * declaration shape, which is exactly why this is a MIME and not a special API. `EMPTY_MIME`
151
+ * can never become a conversion intermediate: no engine declares `to: 'empty'`, so the
152
+ * pathfinder only ever sees it as a source.
153
+ */
154
+ export declare const EMPTY_MIME = "application/x-adk-empty";
136
155
  /**
137
156
  * Pack PCM samples into transport bytes for a {@link ConvertOutput}.
138
157
  *
@@ -294,6 +313,63 @@ export interface MutateCapability {
294
313
  */
295
314
  mutate(request: MutateRequest): Promise<EngineBytesResult>;
296
315
  }
316
+ /**
317
+ * A structural document operation handed to an {@link EditCapability}: one named op with its
318
+ * verb-table args, applied to in-memory bytes. Unlike the fused {@link MutateRequest} (which
319
+ * folds adjacent image transforms into one decode/encode), edits run one op per request — a
320
+ * worksheet splice is not a pixel pass and gains nothing from fusion.
321
+ */
322
+ export interface EditRequest {
323
+ /** The input bytes. */
324
+ bytes: Uint8Array;
325
+ /** The input MIME type. */
326
+ mimeType: string;
327
+ /** The operation name, namespaced as in the verb table (`sheet.update_cells`, …). */
328
+ op: string;
329
+ /** The op's arguments, in the verb table's declared shapes. */
330
+ args: Record<string, unknown>;
331
+ /** Abort signal threaded from the pipeline execution. */
332
+ signal?: AbortSignal;
333
+ }
334
+ /** Counts an edit reports back for result summaries. */
335
+ export interface EditSummary {
336
+ /** Items added (rows, columns, sheets…). */
337
+ added?: number;
338
+ /** Items removed. */
339
+ removed?: number;
340
+ /** Items modified. */
341
+ modified?: number;
342
+ /** Non-fatal notes surfaced to the model. */
343
+ warnings?: string[];
344
+ }
345
+ /** The settled result of an edit: the restructured bytes plus optional change counts. */
346
+ export interface EditResult extends EngineBytesResult {
347
+ /** Change counts for result summaries. */
348
+ summary?: EditSummary;
349
+ }
350
+ /**
351
+ * A structural document-editing capability group: every op in `ops` is applicable to every
352
+ * input matching `over`.
353
+ *
354
+ * @remarks
355
+ * Two engines may declare the same ops over the same patterns with different fidelity — e.g.
356
+ * an ExcelJS-backed editor preserves styling while a SheetJS CE-backed one strips it. The
357
+ * registry does not rank fidelity; supply order (or selection middleware) decides, which makes
358
+ * the trade-off the consumer's visible composition decision.
359
+ */
360
+ export interface EditCapability {
361
+ /** Input patterns this block edits. */
362
+ over: readonly MimePattern[];
363
+ /** Operation names supported (`sheet.update_cells`, `sheet.add_rows`, …). */
364
+ ops: readonly string[];
365
+ /**
366
+ * Apply one structural operation.
367
+ *
368
+ * @param request - The op, its args, and the input bytes.
369
+ * @returns The restructured bytes plus optional change counts.
370
+ */
371
+ edit(request: EditRequest): Promise<EditResult>;
372
+ }
297
373
  /**
298
374
  * A self-declaring media engine: an id for error messages plus the capabilities it provides.
299
375
  * At least one capability entry is required.
@@ -305,6 +381,8 @@ export interface MediaEngine {
305
381
  readonly converts?: readonly ConvertCapability[];
306
382
  /** Same-format content-transform capability groups. */
307
383
  readonly mutates?: readonly MutateCapability[];
384
+ /** Structural document-editing capability groups. */
385
+ readonly edits?: readonly EditCapability[];
308
386
  }
309
387
  /** `true` when `value` structurally implements {@link BinaryExecutor}. */
310
388
  export declare const implementsBinaryExecutor: (value: unknown) => value is BinaryExecutor;
@@ -314,6 +392,8 @@ export declare const implementsScratchWorkspace: (value: unknown) => value is Sc
314
392
  export declare const implementsConvertCapability: (value: unknown) => value is ConvertCapability;
315
393
  /** `true` when `value` structurally implements {@link MutateCapability}. */
316
394
  export declare const implementsMutateCapability: (value: unknown) => value is MutateCapability;
395
+ /** `true` when `value` structurally implements {@link EditCapability}. */
396
+ export declare const implementsEditCapability: (value: unknown) => value is EditCapability;
317
397
  /**
318
398
  * `true` when `value` structurally implements {@link MediaEngine}: a string id plus at least
319
399
  * one well-formed capability entry. Every declared entry must pass its capability guard.
@@ -1,4 +1,4 @@
1
- import { c as isObject } from "../../tool_registry-BaLUIzWz.mjs";
1
+ import { c as isObject } from "../../tool_registry-CZ55mIti.mjs";
2
2
  import "../../guards.mjs";
3
3
  //#region src/batteries/media/contracts.ts
4
4
  /**
@@ -10,12 +10,15 @@ import "../../guards.mjs";
10
10
  * @remarks
11
11
  * Engines are seams, not policies. An engine is a self-declaring capability provider: it
12
12
  * states exactly which transforms it supports — {@link ConvertCapability} edges (input MIME
13
- * patterns to output format tokens) and {@link MutateCapability} groups (same-format content
14
- * transforms) and the pipeline dispatches against those declarations. There are only two
15
- * capability shapes because there are only two things a media engine ever does: change the
16
- * format, or change the content. OCR is a convert (image to text). Transcription is a convert
17
- * (PCM to text). Decoding audio is a convert (container to PCM). Resizing is a mutate. A new
18
- * capability is a new edge in the data, never a new contract.
13
+ * patterns to output format tokens), {@link MutateCapability} groups (same-format content
14
+ * transforms), and {@link EditCapability} groups (structural document operations) and the
15
+ * pipeline dispatches against those declarations. There are only three capability shapes
16
+ * because there are only three things a media engine ever does: change the format, change the
17
+ * content, or restructure the document. OCR is a convert (image to text). Transcription is a
18
+ * convert (PCM to text). Decoding audio is a convert (container to PCM). Generating a blank
19
+ * file is a convert (from {@link EMPTY_MIME} — the source format happens to be nothing).
20
+ * Resizing is a mutate. Inserting a worksheet row is an edit. A new capability is a new edge
21
+ * in the data, never a new contract.
19
22
  *
20
23
  * Engines are supplied to `createMediaPipeline` as a flat ordered array and resolved eagerly
21
24
  * at construction (declarations drive verb narrowing, so they must be known up front).
@@ -45,6 +48,22 @@ import "../../guards.mjs";
45
48
  */
46
49
  var PCM_MIME = "audio/x-adk-pcm";
47
50
  /**
51
+ * The virtual source MIME type for media generation — the single seam through which new media
52
+ * comes into existence. An engine that can mint a blank/seed file declares
53
+ * `converts: [{ from: [EMPTY_MIME], to: [...] }]` and receives a {@link ConvertRequest} with
54
+ * zero bytes; the format token in `to` names what gets created.
55
+ *
56
+ * @remarks
57
+ * Generating an .xlsx, a blank canvas, or a second of silence IS media generation — it is
58
+ * *deterministic* generation (same inputs, same bytes). *Model-based semantic* generation
59
+ * (diffusion, TTS) is the same edge with different machinery: a BYO engine declares
60
+ * `from: [EMPTY_MIME]` and consumes a prompt from `request.options`. Both kinds ride one
61
+ * declaration shape, which is exactly why this is a MIME and not a special API. `EMPTY_MIME`
62
+ * can never become a conversion intermediate: no engine declares `to: 'empty'`, so the
63
+ * pathfinder only ever sees it as a source.
64
+ */
65
+ var EMPTY_MIME = "application/x-adk-empty";
66
+ /**
48
67
  * Pack PCM samples into transport bytes for a {@link ConvertOutput}.
49
68
  *
50
69
  * @remarks
@@ -86,6 +105,8 @@ var implementsScratchWorkspace = (value) => hasFns(value, [
86
105
  var implementsConvertCapability = (value) => hasFns(value, ["convert"]) && isStringArray(value.from) && isStringArray(value.to);
87
106
  /** `true` when `value` structurally implements {@link MutateCapability}. */
88
107
  var implementsMutateCapability = (value) => hasFns(value, ["mutate"]) && isStringArray(value.over) && isStringArray(value.ops) && isStringArray(value.encodes);
108
+ /** `true` when `value` structurally implements {@link EditCapability}. */
109
+ var implementsEditCapability = (value) => hasFns(value, ["edit"]) && isStringArray(value.over) && isStringArray(value.ops);
89
110
  /**
90
111
  * `true` when `value` structurally implements {@link MediaEngine}: a string id plus at least
91
112
  * one well-formed capability entry. Every declared entry must pass its capability guard.
@@ -96,15 +117,22 @@ var implementsMediaEngine = (value) => {
96
117
  if (typeof engine.id !== "string" || engine.id.length === 0) return false;
97
118
  const converts = engine.converts;
98
119
  const mutates = engine.mutates;
120
+ const edits = engine.edits;
99
121
  if (converts !== void 0) {
100
122
  if (!Array.isArray(converts) || !converts.every(implementsConvertCapability)) return false;
101
123
  }
102
124
  if (mutates !== void 0) {
103
125
  if (!Array.isArray(mutates) || !mutates.every(implementsMutateCapability)) return false;
104
126
  }
105
- return (Array.isArray(converts) ? converts.length : 0) + (Array.isArray(mutates) ? mutates.length : 0) > 0;
127
+ if (edits !== void 0) {
128
+ if (!Array.isArray(edits) || !edits.every(implementsEditCapability)) return false;
129
+ }
130
+ const convertCount = Array.isArray(converts) ? converts.length : 0;
131
+ const mutateCount = Array.isArray(mutates) ? mutates.length : 0;
132
+ const editCount = Array.isArray(edits) ? edits.length : 0;
133
+ return convertCount + mutateCount + editCount > 0;
106
134
  };
107
135
  //#endregion
108
- export { PCM_MIME, bytesToPcm, implementsBinaryExecutor, implementsConvertCapability, implementsMediaEngine, implementsMutateCapability, implementsScratchWorkspace, pcmToBytes };
136
+ export { EMPTY_MIME, PCM_MIME, bytesToPcm, implementsBinaryExecutor, implementsConvertCapability, implementsEditCapability, implementsMediaEngine, implementsMutateCapability, implementsScratchWorkspace, pcmToBytes };
109
137
 
110
138
  //# sourceMappingURL=contracts.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"contracts.mjs","names":[],"sources":["../../../src/batteries/media/contracts.ts"],"sourcesContent":["/**\n * Generic engine contracts for the media pipeline battery — the seams every implementation\n * (bundled or BYO) plugs into.\n *\n * @module @nhtio/adk/batteries/media/contracts\n *\n * @remarks\n * Engines are seams, not policies. An engine is a self-declaring capability provider: it\n * states exactly which transforms it supports — {@link ConvertCapability} edges (input MIME\n * patterns to output format tokens) and {@link MutateCapability} groups (same-format content\n * transforms) — and the pipeline dispatches against those declarations. There are only two\n * capability shapes because there are only two things a media engine ever does: change the\n * format, or change the content. OCR is a convert (image to text). Transcription is a convert\n * (PCM to text). Decoding audio is a convert (container to PCM). Resizing is a mutate. A new\n * capability is a new edge in the data, never a new contract.\n *\n * Engines are supplied to `createMediaPipeline` as a flat ordered array and resolved eagerly\n * at construction (declarations drive verb narrowing, so they must be known up front).\n * Bundled engines stay cheap to resolve: their heavy peer dependencies load lazily inside\n * the capability methods, on first actual use.\n *\n * All contracts are duck-typed: validation guards check structure, not class identity, so a\n * consumer can implement an interface from scratch or adapt an existing client. Contracts are\n * enforced at runtime — construction validates every engine and every declared capability and\n * throws `E_INVALID_MEDIA_PIPELINE_CONFIG` naming the offending index when a value fails.\n *\n * Two further contracts exist so that even \"run a binary\" and \"give a binary a file\" are\n * movable seams rather than Node assumptions:\n *\n * - {@link BinaryExecutor} — how an invocation runs. The bundled `execa_executor` wraps execa;\n * a browser/remote/sandbox executor satisfies the same contract.\n * - {@link ScratchWorkspace} — bytes ⇄ executor-visible paths. A sibling of `ByteStore`, NOT a\n * `ByteStore`: byte stores promise in-process readers, while binaries are foreign processes\n * that need real paths. The bundled `fs_workspace` uses `node:fs/promises` via an async\n * resolver; any implementation whose paths the chosen executor can see is valid — that\n * compatibility is the consumer's composition decision.\n */\n\nimport { isObject } from '@nhtio/adk/guards'\n\n// ── shared helper shapes ─────────────────────────────────────────────────────\n\n/**\n * A value-or-resolver: the canonical way to supply an engine. Resolvers may be sync or async\n * (dynamic import) and may resolve to the value directly or a `{ default: value }` module\n * namespace. Engine resolvers run eagerly at pipeline construction — the engine module itself\n * is cheap; heavy peer dependencies load lazily inside capability methods.\n */\nexport type EngineResolver<T = MediaEngine> =\n | T\n | (() => T | { default: T } | Promise<T | { default: T }>)\n\n/** Common result shape for engines that transform bytes to bytes. */\nexport interface EngineBytesResult {\n /** The output bytes. */\n bytes: Uint8Array\n /** The output MIME type. */\n mimeType: string\n}\n\n// ── process execution + scratch filesystem ──────────────────────────────────\n\n/** A single binary invocation handed to a {@link BinaryExecutor}. */\nexport interface BinaryInvocation {\n /** The command to run (an absolute path or a name the executor can resolve). */\n cmd: string\n /** Arguments, exec-style (no shell interpolation). */\n args: string[]\n /** Wall-clock timeout in milliseconds. */\n timeoutMs?: number\n /** Abort signal to cancel the invocation. */\n signal?: AbortSignal\n}\n\n/** The settled result of a {@link BinaryExecutor.exec} call. */\nexport interface BinaryExecResult {\n /** The process exit code (or -1 when the process failed to start). */\n exitCode: number\n /** Captured standard output. */\n stdout: string\n /** Captured standard error. */\n stderr: string\n /** `true` when the invocation failed (non-zero exit, spawn failure, timeout, abort). */\n failed: boolean\n}\n\n/**\n * Runs a binary invocation to completion. How and where it runs — local child process, remote\n * runner, sandbox, container, a browser-side WASI shim — is the implementation's business.\n */\nexport interface BinaryExecutor {\n /**\n * Run one invocation to completion and report the result. Implementations must not throw on\n * non-zero exits — report via `failed`/`exitCode` so callers map failures to readable errors.\n *\n * @param invocation - The command, args, and limits to run.\n * @returns The settled result.\n */\n exec(invocation: BinaryInvocation): Promise<BinaryExecResult>\n}\n\n/**\n * Bytes ⇄ executor-visible paths. The seam that lets binary-backed engines exchange files\n * with the process (or remote runner) that executes them.\n */\nexport interface ScratchWorkspace {\n /**\n * Write `bytes` into the workspace under `filename` and return the absolute path the\n * paired executor can open.\n *\n * @param bytes - The content to materialize.\n * @param filename - The basename to use (extension matters to format-sniffing binaries).\n * @returns The absolute path.\n */\n materialize(bytes: Uint8Array, filename: string): Promise<string>\n /**\n * Read a file the executor produced inside the workspace.\n *\n * @param path - The absolute path to read.\n * @returns The file bytes.\n */\n read(path: string): Promise<Uint8Array>\n /** The workspace root directory, for `--outdir`-style binary arguments. */\n dir(): string\n /** List the files currently in the workspace root (basenames). */\n list(): Promise<string[]>\n /** Remove the workspace and everything in it. Engines call this in `finally`. */\n dispose(): Promise<void>\n}\n\n/**\n * A factory for per-execution scratch workspaces. Engines mint one workspace per invocation\n * so concurrent executions never share a directory.\n */\nexport type ScratchWorkspaceFactory = () => ScratchWorkspace | Promise<ScratchWorkspace>\n\n// ── the format vocabulary ────────────────────────────────────────────────────\n\n/**\n * An input-matching pattern: an exact MIME type (`application/pdf`), a family wildcard\n * (`image/*`), or a virtual MIME such as {@link PCM_MIME}.\n */\nexport type MimePattern = string\n\n/**\n * The virtual MIME type for decoded mono PCM audio — the intermediate between an audio\n * container and a transcription. Bytes are little-endian Float32 samples in `[-1, 1]`;\n * a {@link ConvertOutput} carrying PCM must set `meta.sampleRate` (Hz).\n */\nexport const PCM_MIME = 'audio/x-adk-pcm'\n\n/**\n * Pack PCM samples into transport bytes for a {@link ConvertOutput}.\n *\n * @remarks\n * A `Float32Array` view over arbitrary `Uint8Array` bytes requires 4-byte alignment, which\n * sliced buffers do not guarantee — this helper (and {@link bytesToPcm}) copy-normalize so\n * neither side has to think about alignment.\n *\n * @param pcm - Mono PCM samples in `[-1, 1]`.\n * @returns The samples as little-endian Float32 bytes.\n */\nexport const pcmToBytes = (pcm: Float32Array): Uint8Array => {\n const copy = new Float32Array(pcm)\n return new Uint8Array(copy.buffer, 0, copy.byteLength)\n}\n\n/**\n * Read PCM samples back out of transport bytes.\n *\n * @param bytes - Little-endian Float32 bytes (as produced by {@link pcmToBytes}).\n * @returns The mono PCM samples.\n */\nexport const bytesToPcm = (bytes: Uint8Array): Float32Array => {\n const aligned = new Uint8Array(bytes.length)\n aligned.set(bytes)\n return new Float32Array(aligned.buffer, 0, Math.floor(bytes.length / 4))\n}\n\n// ── convert options (typed, augmentable) ─────────────────────────────────────\n\n/** Options understood by OCR-flavored converts (`image/*` → `txt`/`hocr`/`json`). */\nexport interface OcrConvertOptions {\n /** Recognition language hints (e.g. `['eng','deu']`). */\n languages?: readonly string[]\n}\n\n/** Options understood by transcription-flavored converts ({@link PCM_MIME} → `txt`/`srt`/`vtt`/`json`). */\nexport interface AsrConvertOptions {\n /** Source-language hint (BCP-47-ish). */\n lang?: string\n /** Translate the transcription to English. */\n translate?: boolean\n}\n\n/** Options understood by embedded-image extraction converts (`application/pdf` → `images`). */\nexport interface ImagesConvertOptions {\n /**\n * Preferred output encoding token (`jpg`, `png`, …). An extractor that can emit it natively\n * should; outputs in other encodings are re-encoded downstream by the requesting step.\n */\n format?: string\n}\n\n/**\n * The options bag carried by a {@link ConvertRequest} — one typed, augmentable interface\n * merging every documented convention.\n *\n * @remarks\n * Consumers add their own keys via declaration merging against this module:\n *\n * ```ts\n * declare module '@nhtio/adk/batteries/media/contracts' {\n * interface ConvertOptions {\n * watermark?: { text: string }\n * }\n * }\n * ```\n *\n * Typo'd keys become excess-property compile errors at literal call sites; the runtime stays\n * open — engines must ignore keys they don't understand (multi-hop conversion forwards one\n * bag to every hop). The namespace is flat and globally merged, so BYO keys should be named\n * to avoid collisions (prefix by engine where ambiguous).\n */\nexport interface ConvertOptions\n extends OcrConvertOptions, AsrConvertOptions, ImagesConvertOptions {}\n\n// ── the two capabilities ─────────────────────────────────────────────────────\n\n/** A format-changing request handed to a {@link ConvertCapability}. */\nexport interface ConvertRequest {\n /** The input content bytes. */\n bytes: Uint8Array\n /** The input MIME type. */\n mimeType: string\n /** The input filename (extension informs format sniffing). */\n filename: string\n /** The target format token (`pdf`, `docx`, `txt`, `pcm`, `images`, …). */\n to: string\n /** Capability-specific options — see {@link ConvertOptions}. */\n options?: ConvertOptions\n /** Abort signal threaded from the pipeline execution. */\n signal?: AbortSignal\n}\n\n/** One output of a convert — most converts yield exactly one; `images` yields many. */\nexport interface ConvertOutput {\n /** The output bytes. */\n bytes: Uint8Array\n /** The output MIME type (honest — native encoding, no silent re-encode). */\n mimeType: string\n /** Output metadata (e.g. `{ sampleRate: 44100 }` on {@link PCM_MIME} outputs). */\n meta?: Record<string, unknown>\n}\n\n/** The settled result of a convert. */\nexport interface ConvertResult {\n /** The outputs, in source order. */\n outputs: readonly ConvertOutput[]\n}\n\n/**\n * One uniform block of an engine's conversion matrix: every format token in `to` is\n * producible from every input matching `from`.\n *\n * @remarks\n * Declarations are plain data — the registry reads them without calling engine code. An\n * input-dependent matrix (LibreOffice: docx→pdf yes, docx→xlsx no, ods→xlsx yes) is expressed\n * as several capability groups, each a uniform from×to block.\n */\nexport interface ConvertCapability {\n /** Input patterns this block accepts. */\n from: readonly MimePattern[]\n /** Format tokens producible from every `from` member. */\n to: readonly string[]\n /**\n * Perform the conversion.\n *\n * @param request - The input bytes, target token, and options.\n * @returns The conversion outputs.\n */\n convert(request: ConvertRequest): Promise<ConvertResult>\n}\n\n/**\n * A same-format content transform handed to a {@link MutateCapability} — the fused image\n * request: adjacent `image.*` steps fold into ONE request so a resize→rotate→format chain\n * costs a single decode/encode.\n */\nexport interface MutateRequest {\n /** The input bytes. */\n bytes: Uint8Array\n /** The input MIME type. */\n mimeType: string\n /** Resize, when requested. */\n resize?: {\n width?: number\n height?: number\n fit?: 'cover' | 'contain' | 'fill' | 'inside' | 'outside'\n }\n /** Clockwise rotation in degrees. */\n rotate?: 90 | 180 | 270\n /** Flip axes. */\n flip?: { horizontal?: boolean; vertical?: boolean }\n /** Remove EXIF/ICC metadata. */\n stripMetadata?: boolean\n /** Re-encode target, when requested (rides the same fused call). */\n format?: { to: string; quality?: number }\n /** Abort signal threaded from the pipeline execution. */\n signal?: AbortSignal\n}\n\n/** A same-format content-transform capability group. */\nexport interface MutateCapability {\n /** Input patterns this block mutates. */\n over: readonly MimePattern[]\n /** Content operations supported (`resize`, `rotate`, `flip`, `strip_metadata`). */\n ops: readonly string[]\n /** Format tokens reachable via `request.format` in the same fused call. */\n encodes: readonly string[]\n /**\n * Apply the fused transform.\n *\n * @param request - The folded operations and input bytes.\n * @returns The transformed bytes.\n */\n mutate(request: MutateRequest): Promise<EngineBytesResult>\n}\n\n/**\n * A self-declaring media engine: an id for error messages plus the capabilities it provides.\n * At least one capability entry is required.\n */\nexport interface MediaEngine {\n /** Stable identifier used in config and dispatch error messages (`jimp`, `soffice`, …). */\n readonly id: string\n /** Format-changing capability groups. */\n readonly converts?: readonly ConvertCapability[]\n /** Same-format content-transform capability groups. */\n readonly mutates?: readonly MutateCapability[]\n}\n\n// ── duck-typed guards ────────────────────────────────────────────────────────\n\nconst hasFns = (value: unknown, names: readonly string[]): boolean =>\n isObject(value) && names.every((n) => typeof (value as Record<string, unknown>)[n] === 'function')\n\nconst isStringArray = (value: unknown): boolean =>\n Array.isArray(value) && value.every((v) => typeof v === 'string')\n\n/** `true` when `value` structurally implements {@link BinaryExecutor}. */\nexport const implementsBinaryExecutor = (value: unknown): value is BinaryExecutor =>\n hasFns(value, ['exec'])\n\n/** `true` when `value` structurally implements {@link ScratchWorkspace}. */\nexport const implementsScratchWorkspace = (value: unknown): value is ScratchWorkspace =>\n hasFns(value, ['materialize', 'read', 'dir', 'list', 'dispose'])\n\n/** `true` when `value` structurally implements {@link ConvertCapability}. */\nexport const implementsConvertCapability = (value: unknown): value is ConvertCapability =>\n hasFns(value, ['convert']) &&\n isStringArray((value as ConvertCapability).from) &&\n isStringArray((value as ConvertCapability).to)\n\n/** `true` when `value` structurally implements {@link MutateCapability}. */\nexport const implementsMutateCapability = (value: unknown): value is MutateCapability =>\n hasFns(value, ['mutate']) &&\n isStringArray((value as MutateCapability).over) &&\n isStringArray((value as MutateCapability).ops) &&\n isStringArray((value as MutateCapability).encodes)\n\n/**\n * `true` when `value` structurally implements {@link MediaEngine}: a string id plus at least\n * one well-formed capability entry. Every declared entry must pass its capability guard.\n */\nexport const implementsMediaEngine = (value: unknown): value is MediaEngine => {\n if (!isObject(value)) return false\n const engine = value as unknown as MediaEngine\n if (typeof engine.id !== 'string' || engine.id.length === 0) return false\n const converts = engine.converts\n const mutates = engine.mutates\n if (converts !== undefined) {\n if (!Array.isArray(converts) || !converts.every(implementsConvertCapability)) return false\n }\n if (mutates !== undefined) {\n if (!Array.isArray(mutates) || !mutates.every(implementsMutateCapability)) return false\n }\n const convertCount = Array.isArray(converts) ? converts.length : 0\n const mutateCount = Array.isArray(mutates) ? mutates.length : 0\n return convertCount + mutateCount > 0\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqJA,IAAa,WAAW;;;;;;;;;;;;AAaxB,IAAa,cAAc,QAAkC;CAC3D,MAAM,OAAO,IAAI,aAAa,GAAG;CACjC,OAAO,IAAI,WAAW,KAAK,QAAQ,GAAG,KAAK,UAAU;AACvD;;;;;;;AAQA,IAAa,cAAc,UAAoC;CAC7D,MAAM,UAAU,IAAI,WAAW,MAAM,MAAM;CAC3C,QAAQ,IAAI,KAAK;CACjB,OAAO,IAAI,aAAa,QAAQ,QAAQ,GAAG,KAAK,MAAM,MAAM,SAAS,CAAC,CAAC;AACzE;AAuKA,IAAM,UAAU,OAAgB,UAC9B,SAAS,KAAK,KAAK,MAAM,OAAO,MAAM,OAAQ,MAAkC,OAAO,UAAU;AAEnG,IAAM,iBAAiB,UACrB,MAAM,QAAQ,KAAK,KAAK,MAAM,OAAO,MAAM,OAAO,MAAM,QAAQ;;AAGlE,IAAa,4BAA4B,UACvC,OAAO,OAAO,CAAC,MAAM,CAAC;;AAGxB,IAAa,8BAA8B,UACzC,OAAO,OAAO;CAAC;CAAe;CAAQ;CAAO;CAAQ;AAAS,CAAC;;AAGjE,IAAa,+BAA+B,UAC1C,OAAO,OAAO,CAAC,SAAS,CAAC,KACzB,cAAe,MAA4B,IAAI,KAC/C,cAAe,MAA4B,EAAE;;AAG/C,IAAa,8BAA8B,UACzC,OAAO,OAAO,CAAC,QAAQ,CAAC,KACxB,cAAe,MAA2B,IAAI,KAC9C,cAAe,MAA2B,GAAG,KAC7C,cAAe,MAA2B,OAAO;;;;;AAMnD,IAAa,yBAAyB,UAAyC;CAC7E,IAAI,CAAC,SAAS,KAAK,GAAG,OAAO;CAC7B,MAAM,SAAS;CACf,IAAI,OAAO,OAAO,OAAO,YAAY,OAAO,GAAG,WAAW,GAAG,OAAO;CACpE,MAAM,WAAW,OAAO;CACxB,MAAM,UAAU,OAAO;CACvB,IAAI,aAAa,KAAA;MACX,CAAC,MAAM,QAAQ,QAAQ,KAAK,CAAC,SAAS,MAAM,2BAA2B,GAAG,OAAO;CAAA;CAEvF,IAAI,YAAY,KAAA;MACV,CAAC,MAAM,QAAQ,OAAO,KAAK,CAAC,QAAQ,MAAM,0BAA0B,GAAG,OAAO;CAAA;CAIpF,QAFqB,MAAM,QAAQ,QAAQ,IAAI,SAAS,SAAS,MAC7C,MAAM,QAAQ,OAAO,IAAI,QAAQ,SAAS,KAC1B;AACtC"}
1
+ {"version":3,"file":"contracts.mjs","names":[],"sources":["../../../src/batteries/media/contracts.ts"],"sourcesContent":["/**\n * Generic engine contracts for the media pipeline battery — the seams every implementation\n * (bundled or BYO) plugs into.\n *\n * @module @nhtio/adk/batteries/media/contracts\n *\n * @remarks\n * Engines are seams, not policies. An engine is a self-declaring capability provider: it\n * states exactly which transforms it supports — {@link ConvertCapability} edges (input MIME\n * patterns to output format tokens), {@link MutateCapability} groups (same-format content\n * transforms), and {@link EditCapability} groups (structural document operations) — and the\n * pipeline dispatches against those declarations. There are only three capability shapes\n * because there are only three things a media engine ever does: change the format, change the\n * content, or restructure the document. OCR is a convert (image to text). Transcription is a\n * convert (PCM to text). Decoding audio is a convert (container to PCM). Generating a blank\n * file is a convert (from {@link EMPTY_MIME} — the source format happens to be nothing).\n * Resizing is a mutate. Inserting a worksheet row is an edit. A new capability is a new edge\n * in the data, never a new contract.\n *\n * Engines are supplied to `createMediaPipeline` as a flat ordered array and resolved eagerly\n * at construction (declarations drive verb narrowing, so they must be known up front).\n * Bundled engines stay cheap to resolve: their heavy peer dependencies load lazily inside\n * the capability methods, on first actual use.\n *\n * All contracts are duck-typed: validation guards check structure, not class identity, so a\n * consumer can implement an interface from scratch or adapt an existing client. Contracts are\n * enforced at runtime — construction validates every engine and every declared capability and\n * throws `E_INVALID_MEDIA_PIPELINE_CONFIG` naming the offending index when a value fails.\n *\n * Two further contracts exist so that even \"run a binary\" and \"give a binary a file\" are\n * movable seams rather than Node assumptions:\n *\n * - {@link BinaryExecutor} — how an invocation runs. The bundled `execa_executor` wraps execa;\n * a browser/remote/sandbox executor satisfies the same contract.\n * - {@link ScratchWorkspace} — bytes ⇄ executor-visible paths. A sibling of `ByteStore`, NOT a\n * `ByteStore`: byte stores promise in-process readers, while binaries are foreign processes\n * that need real paths. The bundled `fs_workspace` uses `node:fs/promises` via an async\n * resolver; any implementation whose paths the chosen executor can see is valid — that\n * compatibility is the consumer's composition decision.\n */\n\nimport { isObject } from '@nhtio/adk/guards'\n\n// ── shared helper shapes ─────────────────────────────────────────────────────\n\n/**\n * A value-or-resolver: the canonical way to supply an engine. Resolvers may be sync or async\n * (dynamic import) and may resolve to the value directly or a `{ default: value }` module\n * namespace. Engine resolvers run eagerly at pipeline construction — the engine module itself\n * is cheap; heavy peer dependencies load lazily inside capability methods.\n */\nexport type EngineResolver<T = MediaEngine> =\n | T\n | (() => T | { default: T } | Promise<T | { default: T }>)\n\n/** Common result shape for engines that transform bytes to bytes. */\nexport interface EngineBytesResult {\n /** The output bytes. */\n bytes: Uint8Array\n /** The output MIME type. */\n mimeType: string\n}\n\n// ── process execution + scratch filesystem ──────────────────────────────────\n\n/** A single binary invocation handed to a {@link BinaryExecutor}. */\nexport interface BinaryInvocation {\n /** The command to run (an absolute path or a name the executor can resolve). */\n cmd: string\n /** Arguments, exec-style (no shell interpolation). */\n args: string[]\n /** Wall-clock timeout in milliseconds. */\n timeoutMs?: number\n /** Abort signal to cancel the invocation. */\n signal?: AbortSignal\n}\n\n/** The settled result of a {@link BinaryExecutor.exec} call. */\nexport interface BinaryExecResult {\n /** The process exit code (or -1 when the process failed to start). */\n exitCode: number\n /** Captured standard output. */\n stdout: string\n /** Captured standard error. */\n stderr: string\n /** `true` when the invocation failed (non-zero exit, spawn failure, timeout, abort). */\n failed: boolean\n}\n\n/**\n * Runs a binary invocation to completion. How and where it runs — local child process, remote\n * runner, sandbox, container, a browser-side WASI shim — is the implementation's business.\n */\nexport interface BinaryExecutor {\n /**\n * Run one invocation to completion and report the result. Implementations must not throw on\n * non-zero exits — report via `failed`/`exitCode` so callers map failures to readable errors.\n *\n * @param invocation - The command, args, and limits to run.\n * @returns The settled result.\n */\n exec(invocation: BinaryInvocation): Promise<BinaryExecResult>\n}\n\n/**\n * Bytes ⇄ executor-visible paths. The seam that lets binary-backed engines exchange files\n * with the process (or remote runner) that executes them.\n */\nexport interface ScratchWorkspace {\n /**\n * Write `bytes` into the workspace under `filename` and return the absolute path the\n * paired executor can open.\n *\n * @param bytes - The content to materialize.\n * @param filename - The basename to use (extension matters to format-sniffing binaries).\n * @returns The absolute path.\n */\n materialize(bytes: Uint8Array, filename: string): Promise<string>\n /**\n * Read a file the executor produced inside the workspace.\n *\n * @param path - The absolute path to read.\n * @returns The file bytes.\n */\n read(path: string): Promise<Uint8Array>\n /** The workspace root directory, for `--outdir`-style binary arguments. */\n dir(): string\n /** List the files currently in the workspace root (basenames). */\n list(): Promise<string[]>\n /** Remove the workspace and everything in it. Engines call this in `finally`. */\n dispose(): Promise<void>\n}\n\n/**\n * A factory for per-execution scratch workspaces. Engines mint one workspace per invocation\n * so concurrent executions never share a directory.\n */\nexport type ScratchWorkspaceFactory = () => ScratchWorkspace | Promise<ScratchWorkspace>\n\n// ── the format vocabulary ────────────────────────────────────────────────────\n\n/**\n * An input-matching pattern: an exact MIME type (`application/pdf`), a family wildcard\n * (`image/*`), or a virtual MIME such as {@link PCM_MIME}.\n */\nexport type MimePattern = string\n\n/**\n * The virtual MIME type for decoded mono PCM audio — the intermediate between an audio\n * container and a transcription. Bytes are little-endian Float32 samples in `[-1, 1]`;\n * a {@link ConvertOutput} carrying PCM must set `meta.sampleRate` (Hz).\n */\nexport const PCM_MIME = 'audio/x-adk-pcm'\n\n/**\n * The virtual source MIME type for media generation — the single seam through which new media\n * comes into existence. An engine that can mint a blank/seed file declares\n * `converts: [{ from: [EMPTY_MIME], to: [...] }]` and receives a {@link ConvertRequest} with\n * zero bytes; the format token in `to` names what gets created.\n *\n * @remarks\n * Generating an .xlsx, a blank canvas, or a second of silence IS media generation — it is\n * *deterministic* generation (same inputs, same bytes). *Model-based semantic* generation\n * (diffusion, TTS) is the same edge with different machinery: a BYO engine declares\n * `from: [EMPTY_MIME]` and consumes a prompt from `request.options`. Both kinds ride one\n * declaration shape, which is exactly why this is a MIME and not a special API. `EMPTY_MIME`\n * can never become a conversion intermediate: no engine declares `to: 'empty'`, so the\n * pathfinder only ever sees it as a source.\n */\nexport const EMPTY_MIME = 'application/x-adk-empty'\n\n/**\n * Pack PCM samples into transport bytes for a {@link ConvertOutput}.\n *\n * @remarks\n * A `Float32Array` view over arbitrary `Uint8Array` bytes requires 4-byte alignment, which\n * sliced buffers do not guarantee — this helper (and {@link bytesToPcm}) copy-normalize so\n * neither side has to think about alignment.\n *\n * @param pcm - Mono PCM samples in `[-1, 1]`.\n * @returns The samples as little-endian Float32 bytes.\n */\nexport const pcmToBytes = (pcm: Float32Array): Uint8Array => {\n const copy = new Float32Array(pcm)\n return new Uint8Array(copy.buffer, 0, copy.byteLength)\n}\n\n/**\n * Read PCM samples back out of transport bytes.\n *\n * @param bytes - Little-endian Float32 bytes (as produced by {@link pcmToBytes}).\n * @returns The mono PCM samples.\n */\nexport const bytesToPcm = (bytes: Uint8Array): Float32Array => {\n const aligned = new Uint8Array(bytes.length)\n aligned.set(bytes)\n return new Float32Array(aligned.buffer, 0, Math.floor(bytes.length / 4))\n}\n\n// ── convert options (typed, augmentable) ─────────────────────────────────────\n\n/** Options understood by OCR-flavored converts (`image/*` → `txt`/`hocr`/`json`). */\nexport interface OcrConvertOptions {\n /** Recognition language hints (e.g. `['eng','deu']`). */\n languages?: readonly string[]\n}\n\n/** Options understood by transcription-flavored converts ({@link PCM_MIME} → `txt`/`srt`/`vtt`/`json`). */\nexport interface AsrConvertOptions {\n /** Source-language hint (BCP-47-ish). */\n lang?: string\n /** Translate the transcription to English. */\n translate?: boolean\n}\n\n/** Options understood by embedded-image extraction converts (`application/pdf` → `images`). */\nexport interface ImagesConvertOptions {\n /**\n * Preferred output encoding token (`jpg`, `png`, …). An extractor that can emit it natively\n * should; outputs in other encodings are re-encoded downstream by the requesting step.\n */\n format?: string\n}\n\n/**\n * The options bag carried by a {@link ConvertRequest} — one typed, augmentable interface\n * merging every documented convention.\n *\n * @remarks\n * Consumers add their own keys via declaration merging against this module:\n *\n * ```ts\n * declare module '@nhtio/adk/batteries/media/contracts' {\n * interface ConvertOptions {\n * watermark?: { text: string }\n * }\n * }\n * ```\n *\n * Typo'd keys become excess-property compile errors at literal call sites; the runtime stays\n * open — engines must ignore keys they don't understand (multi-hop conversion forwards one\n * bag to every hop). The namespace is flat and globally merged, so BYO keys should be named\n * to avoid collisions (prefix by engine where ambiguous).\n */\nexport interface ConvertOptions\n extends OcrConvertOptions, AsrConvertOptions, ImagesConvertOptions {}\n\n// ── the three capabilities ───────────────────────────────────────────────────\n\n/** A format-changing request handed to a {@link ConvertCapability}. */\nexport interface ConvertRequest {\n /** The input content bytes. */\n bytes: Uint8Array\n /** The input MIME type. */\n mimeType: string\n /** The input filename (extension informs format sniffing). */\n filename: string\n /** The target format token (`pdf`, `docx`, `txt`, `pcm`, `images`, …). */\n to: string\n /** Capability-specific options — see {@link ConvertOptions}. */\n options?: ConvertOptions\n /** Abort signal threaded from the pipeline execution. */\n signal?: AbortSignal\n}\n\n/** One output of a convert — most converts yield exactly one; `images` yields many. */\nexport interface ConvertOutput {\n /** The output bytes. */\n bytes: Uint8Array\n /** The output MIME type (honest — native encoding, no silent re-encode). */\n mimeType: string\n /** Output metadata (e.g. `{ sampleRate: 44100 }` on {@link PCM_MIME} outputs). */\n meta?: Record<string, unknown>\n}\n\n/** The settled result of a convert. */\nexport interface ConvertResult {\n /** The outputs, in source order. */\n outputs: readonly ConvertOutput[]\n}\n\n/**\n * One uniform block of an engine's conversion matrix: every format token in `to` is\n * producible from every input matching `from`.\n *\n * @remarks\n * Declarations are plain data — the registry reads them without calling engine code. An\n * input-dependent matrix (LibreOffice: docx→pdf yes, docx→xlsx no, ods→xlsx yes) is expressed\n * as several capability groups, each a uniform from×to block.\n */\nexport interface ConvertCapability {\n /** Input patterns this block accepts. */\n from: readonly MimePattern[]\n /** Format tokens producible from every `from` member. */\n to: readonly string[]\n /**\n * Perform the conversion.\n *\n * @param request - The input bytes, target token, and options.\n * @returns The conversion outputs.\n */\n convert(request: ConvertRequest): Promise<ConvertResult>\n}\n\n/**\n * A same-format content transform handed to a {@link MutateCapability} — the fused image\n * request: adjacent `image.*` steps fold into ONE request so a resize→rotate→format chain\n * costs a single decode/encode.\n */\nexport interface MutateRequest {\n /** The input bytes. */\n bytes: Uint8Array\n /** The input MIME type. */\n mimeType: string\n /** Resize, when requested. */\n resize?: {\n width?: number\n height?: number\n fit?: 'cover' | 'contain' | 'fill' | 'inside' | 'outside'\n }\n /** Clockwise rotation in degrees. */\n rotate?: 90 | 180 | 270\n /** Flip axes. */\n flip?: { horizontal?: boolean; vertical?: boolean }\n /** Remove EXIF/ICC metadata. */\n stripMetadata?: boolean\n /** Re-encode target, when requested (rides the same fused call). */\n format?: { to: string; quality?: number }\n /** Abort signal threaded from the pipeline execution. */\n signal?: AbortSignal\n}\n\n/** A same-format content-transform capability group. */\nexport interface MutateCapability {\n /** Input patterns this block mutates. */\n over: readonly MimePattern[]\n /** Content operations supported (`resize`, `rotate`, `flip`, `strip_metadata`). */\n ops: readonly string[]\n /** Format tokens reachable via `request.format` in the same fused call. */\n encodes: readonly string[]\n /**\n * Apply the fused transform.\n *\n * @param request - The folded operations and input bytes.\n * @returns The transformed bytes.\n */\n mutate(request: MutateRequest): Promise<EngineBytesResult>\n}\n\n/**\n * A structural document operation handed to an {@link EditCapability}: one named op with its\n * verb-table args, applied to in-memory bytes. Unlike the fused {@link MutateRequest} (which\n * folds adjacent image transforms into one decode/encode), edits run one op per request — a\n * worksheet splice is not a pixel pass and gains nothing from fusion.\n */\nexport interface EditRequest {\n /** The input bytes. */\n bytes: Uint8Array\n /** The input MIME type. */\n mimeType: string\n /** The operation name, namespaced as in the verb table (`sheet.update_cells`, …). */\n op: string\n /** The op's arguments, in the verb table's declared shapes. */\n args: Record<string, unknown>\n /** Abort signal threaded from the pipeline execution. */\n signal?: AbortSignal\n}\n\n/** Counts an edit reports back for result summaries. */\nexport interface EditSummary {\n /** Items added (rows, columns, sheets…). */\n added?: number\n /** Items removed. */\n removed?: number\n /** Items modified. */\n modified?: number\n /** Non-fatal notes surfaced to the model. */\n warnings?: string[]\n}\n\n/** The settled result of an edit: the restructured bytes plus optional change counts. */\nexport interface EditResult extends EngineBytesResult {\n /** Change counts for result summaries. */\n summary?: EditSummary\n}\n\n/**\n * A structural document-editing capability group: every op in `ops` is applicable to every\n * input matching `over`.\n *\n * @remarks\n * Two engines may declare the same ops over the same patterns with different fidelity — e.g.\n * an ExcelJS-backed editor preserves styling while a SheetJS CE-backed one strips it. The\n * registry does not rank fidelity; supply order (or selection middleware) decides, which makes\n * the trade-off the consumer's visible composition decision.\n */\nexport interface EditCapability {\n /** Input patterns this block edits. */\n over: readonly MimePattern[]\n /** Operation names supported (`sheet.update_cells`, `sheet.add_rows`, …). */\n ops: readonly string[]\n /**\n * Apply one structural operation.\n *\n * @param request - The op, its args, and the input bytes.\n * @returns The restructured bytes plus optional change counts.\n */\n edit(request: EditRequest): Promise<EditResult>\n}\n\n/**\n * A self-declaring media engine: an id for error messages plus the capabilities it provides.\n * At least one capability entry is required.\n */\nexport interface MediaEngine {\n /** Stable identifier used in config and dispatch error messages (`jimp`, `soffice`, …). */\n readonly id: string\n /** Format-changing capability groups. */\n readonly converts?: readonly ConvertCapability[]\n /** Same-format content-transform capability groups. */\n readonly mutates?: readonly MutateCapability[]\n /** Structural document-editing capability groups. */\n readonly edits?: readonly EditCapability[]\n}\n\n// ── duck-typed guards ────────────────────────────────────────────────────────\n\nconst hasFns = (value: unknown, names: readonly string[]): boolean =>\n isObject(value) && names.every((n) => typeof (value as Record<string, unknown>)[n] === 'function')\n\nconst isStringArray = (value: unknown): boolean =>\n Array.isArray(value) && value.every((v) => typeof v === 'string')\n\n/** `true` when `value` structurally implements {@link BinaryExecutor}. */\nexport const implementsBinaryExecutor = (value: unknown): value is BinaryExecutor =>\n hasFns(value, ['exec'])\n\n/** `true` when `value` structurally implements {@link ScratchWorkspace}. */\nexport const implementsScratchWorkspace = (value: unknown): value is ScratchWorkspace =>\n hasFns(value, ['materialize', 'read', 'dir', 'list', 'dispose'])\n\n/** `true` when `value` structurally implements {@link ConvertCapability}. */\nexport const implementsConvertCapability = (value: unknown): value is ConvertCapability =>\n hasFns(value, ['convert']) &&\n isStringArray((value as ConvertCapability).from) &&\n isStringArray((value as ConvertCapability).to)\n\n/** `true` when `value` structurally implements {@link MutateCapability}. */\nexport const implementsMutateCapability = (value: unknown): value is MutateCapability =>\n hasFns(value, ['mutate']) &&\n isStringArray((value as MutateCapability).over) &&\n isStringArray((value as MutateCapability).ops) &&\n isStringArray((value as MutateCapability).encodes)\n\n/** `true` when `value` structurally implements {@link EditCapability}. */\nexport const implementsEditCapability = (value: unknown): value is EditCapability =>\n hasFns(value, ['edit']) &&\n isStringArray((value as EditCapability).over) &&\n isStringArray((value as EditCapability).ops)\n\n/**\n * `true` when `value` structurally implements {@link MediaEngine}: a string id plus at least\n * one well-formed capability entry. Every declared entry must pass its capability guard.\n */\nexport const implementsMediaEngine = (value: unknown): value is MediaEngine => {\n if (!isObject(value)) return false\n const engine = value as unknown as MediaEngine\n if (typeof engine.id !== 'string' || engine.id.length === 0) return false\n const converts = engine.converts\n const mutates = engine.mutates\n const edits = engine.edits\n if (converts !== undefined) {\n if (!Array.isArray(converts) || !converts.every(implementsConvertCapability)) return false\n }\n if (mutates !== undefined) {\n if (!Array.isArray(mutates) || !mutates.every(implementsMutateCapability)) return false\n }\n if (edits !== undefined) {\n if (!Array.isArray(edits) || !edits.every(implementsEditCapability)) return false\n }\n const convertCount = Array.isArray(converts) ? converts.length : 0\n const mutateCount = Array.isArray(mutates) ? mutates.length : 0\n const editCount = Array.isArray(edits) ? edits.length : 0\n return convertCount + mutateCount + editCount > 0\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwJA,IAAa,WAAW;;;;;;;;;;;;;;;;AAiBxB,IAAa,aAAa;;;;;;;;;;;;AAa1B,IAAa,cAAc,QAAkC;CAC3D,MAAM,OAAO,IAAI,aAAa,GAAG;CACjC,OAAO,IAAI,WAAW,KAAK,QAAQ,GAAG,KAAK,UAAU;AACvD;;;;;;;AAQA,IAAa,cAAc,UAAoC;CAC7D,MAAM,UAAU,IAAI,WAAW,MAAM,MAAM;CAC3C,QAAQ,IAAI,KAAK;CACjB,OAAO,IAAI,aAAa,QAAQ,QAAQ,GAAG,KAAK,MAAM,MAAM,SAAS,CAAC,CAAC;AACzE;AAsOA,IAAM,UAAU,OAAgB,UAC9B,SAAS,KAAK,KAAK,MAAM,OAAO,MAAM,OAAQ,MAAkC,OAAO,UAAU;AAEnG,IAAM,iBAAiB,UACrB,MAAM,QAAQ,KAAK,KAAK,MAAM,OAAO,MAAM,OAAO,MAAM,QAAQ;;AAGlE,IAAa,4BAA4B,UACvC,OAAO,OAAO,CAAC,MAAM,CAAC;;AAGxB,IAAa,8BAA8B,UACzC,OAAO,OAAO;CAAC;CAAe;CAAQ;CAAO;CAAQ;AAAS,CAAC;;AAGjE,IAAa,+BAA+B,UAC1C,OAAO,OAAO,CAAC,SAAS,CAAC,KACzB,cAAe,MAA4B,IAAI,KAC/C,cAAe,MAA4B,EAAE;;AAG/C,IAAa,8BAA8B,UACzC,OAAO,OAAO,CAAC,QAAQ,CAAC,KACxB,cAAe,MAA2B,IAAI,KAC9C,cAAe,MAA2B,GAAG,KAC7C,cAAe,MAA2B,OAAO;;AAGnD,IAAa,4BAA4B,UACvC,OAAO,OAAO,CAAC,MAAM,CAAC,KACtB,cAAe,MAAyB,IAAI,KAC5C,cAAe,MAAyB,GAAG;;;;;AAM7C,IAAa,yBAAyB,UAAyC;CAC7E,IAAI,CAAC,SAAS,KAAK,GAAG,OAAO;CAC7B,MAAM,SAAS;CACf,IAAI,OAAO,OAAO,OAAO,YAAY,OAAO,GAAG,WAAW,GAAG,OAAO;CACpE,MAAM,WAAW,OAAO;CACxB,MAAM,UAAU,OAAO;CACvB,MAAM,QAAQ,OAAO;CACrB,IAAI,aAAa,KAAA;MACX,CAAC,MAAM,QAAQ,QAAQ,KAAK,CAAC,SAAS,MAAM,2BAA2B,GAAG,OAAO;CAAA;CAEvF,IAAI,YAAY,KAAA;MACV,CAAC,MAAM,QAAQ,OAAO,KAAK,CAAC,QAAQ,MAAM,0BAA0B,GAAG,OAAO;CAAA;CAEpF,IAAI,UAAU,KAAA;MACR,CAAC,MAAM,QAAQ,KAAK,KAAK,CAAC,MAAM,MAAM,wBAAwB,GAAG,OAAO;CAAA;CAE9E,MAAM,eAAe,MAAM,QAAQ,QAAQ,IAAI,SAAS,SAAS;CACjE,MAAM,cAAc,MAAM,QAAQ,OAAO,IAAI,QAAQ,SAAS;CAC9D,MAAM,YAAY,MAAM,QAAQ,KAAK,IAAI,MAAM,SAAS;CACxD,OAAO,eAAe,cAAc,YAAY;AAClD"}
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  require("../../../chunk-Cek0wNdY.js");
3
- const require_tool_registry = require("../../../tool_registry-BdKlPuHS.js");
3
+ const require_tool_registry = require("../../../tool_registry-DwZ6pzN4.js");
4
4
  require("../../../guards.cjs");
5
5
  const require_batteries_media_contracts = require("../contracts.cjs");
6
6
  const require_exceptions = require("../../../exceptions-D79HIKeX.js");
@@ -13,11 +13,15 @@ const require_exceptions = require("../../../exceptions-D79HIKeX.js");
13
13
  * @module @nhtio/adk/batteries/media/engines/audio_decode
14
14
  *
15
15
  * @remarks
16
- * Declares one convert capability: audio containers to the virtual `pcm` token (mp3 /
17
- * m4a-aac / ogg-vorbis / opus / flac / wav), downmixed to mono. The PCM output reports the
18
- * SOURCE sample rate in `meta.sampleRate` the pipeline's transcribe step resamples to the
19
- * 16 kHz transcription engines expect. For exotic containers, compose an ffmpeg-backed
20
- * engine instead; the capability declaration is the seam.
16
+ * Declares two convert capabilities: audio containers to the virtual `pcm` token (mp3 /
17
+ * m4a-aac / ogg-vorbis / opus / flac / wav), downmixed to mono; and a generation edge
18
+ * `EMPTY_MIME` wav. Audio bytes are just bytes in a documented envelope: the generated
19
+ * seed is a canonical 44-byte RIFF/WAVE header plus one second of 16-bit mono silence at
20
+ * 44100 Hz, written by a dependency-free helper (generation never loads the decode peer).
21
+ * The PCM output reports the SOURCE sample rate in `meta.sampleRate` — the pipeline's
22
+ * transcribe step resamples to the 16 kHz transcription engines expect. For exotic
23
+ * containers, compose an ffmpeg-backed engine instead; the capability declaration is the
24
+ * seam.
21
25
  *
22
26
  * `audio-decode` is an optional peer dependency, lazily imported on first actual use.
23
27
  */
@@ -30,6 +34,35 @@ var channelsOf = (buffer) => {
30
34
  throw new Error("audio-decode returned an unrecognized buffer shape");
31
35
  };
32
36
  /**
37
+ * Mint one second of 16-bit mono silence at 44100 Hz as a canonical RIFF/WAVE file — the
38
+ * 44-byte header plus zeroed sample data. Pure bytes, zero dependencies.
39
+ */
40
+ var makeSilentWav = () => {
41
+ const sampleRate = 44100;
42
+ const numSamples = sampleRate;
43
+ const bytesPerSample = 2;
44
+ const dataSize = numSamples * bytesPerSample;
45
+ const bytes = new Uint8Array(88244);
46
+ const view = new DataView(bytes.buffer);
47
+ const writeAscii = (offset, text) => {
48
+ for (let i = 0; i < text.length; i++) bytes[offset + i] = text.charCodeAt(i);
49
+ };
50
+ writeAscii(0, "RIFF");
51
+ view.setUint32(4, 88236, true);
52
+ writeAscii(8, "WAVE");
53
+ writeAscii(12, "fmt ");
54
+ view.setUint32(16, 16, true);
55
+ view.setUint16(20, 1, true);
56
+ view.setUint16(22, 1, true);
57
+ view.setUint32(24, sampleRate, true);
58
+ view.setUint32(28, sampleRate * bytesPerSample, true);
59
+ view.setUint16(32, bytesPerSample, true);
60
+ view.setUint16(34, 16, true);
61
+ writeAscii(36, "data");
62
+ view.setUint32(40, dataSize, true);
63
+ return bytes;
64
+ };
65
+ /**
33
66
  * Construct the audio-decode-backed engine.
34
67
  *
35
68
  * @param options - Optional module resolver override.
@@ -83,6 +116,13 @@ var audioDecodeEngine = (options = {}) => {
83
116
  ],
84
117
  to: ["pcm"],
85
118
  convert
119
+ }, {
120
+ from: [require_batteries_media_contracts.EMPTY_MIME],
121
+ to: ["wav"],
122
+ convert: async () => ({ outputs: [{
123
+ bytes: makeSilentWav(),
124
+ mimeType: "audio/wav"
125
+ }] })
86
126
  }]
87
127
  };
88
128
  };
@@ -1 +1 @@
1
- {"version":3,"file":"audio_decode.cjs","names":[],"sources":["../../../../src/batteries/media/engines/audio_decode.ts"],"sourcesContent":["/**\n * A cross-environment audio-decoding {@link @nhtio/adk/batteries/media/contracts!MediaEngine}\n * backed by the `audio-decode` package (pure JS/WASM codecs — no ffmpeg, no native bindings;\n * works in Node and browsers).\n *\n * @module @nhtio/adk/batteries/media/engines/audio_decode\n *\n * @remarks\n * Declares one convert capability: audio containers to the virtual `pcm` token (mp3 /\n * m4a-aac / ogg-vorbis / opus / flac / wav), downmixed to mono. The PCM output reports the\n * SOURCE sample rate in `meta.sampleRate` — the pipeline's transcribe step resamples to the\n * 16 kHz transcription engines expect. For exotic containers, compose an ffmpeg-backed\n * engine instead; the capability declaration is the seam.\n *\n * `audio-decode` is an optional peer dependency, lazily imported on first actual use.\n */\n\nimport { isError } from '@nhtio/adk/guards'\nimport { pcmToBytes, PCM_MIME } from '../contracts'\nimport { E_INVALID_MEDIA_PIPELINE_CONFIG } from '../exceptions'\nimport type { MediaEngine, ConvertRequest, ConvertResult } from '../contracts'\n\n/**\n * The decoded shapes audio-decode resolves to. Some codecs return an AudioBuffer-compatible\n * object (`numberOfChannels` + `getChannelData`); others (e.g. the wav path in Node) return a\n * plain `{ channelData: Float32Array[], sampleRate }` record. The engine normalizes both.\n */\nexport interface AudioBufferLike {\n /** Channel count when the AudioBuffer-compatible shape is returned. */\n numberOfChannels?: number\n /** Sample rate of the decoded audio, in Hz. Present on both shapes. */\n sampleRate: number\n /** Per-channel sample accessor on the AudioBuffer-compatible shape. */\n getChannelData?(channel: number): Float32Array\n /** Raw per-channel sample arrays on the plain-record shape. */\n channelData?: Float32Array[]\n}\n\n/** The decode function shape the `audio-decode` package exports. */\nexport type AudioDecodeFn = (bytes: Uint8Array | ArrayBuffer) => Promise<AudioBufferLike>\n\nconst channelsOf = (buffer: AudioBufferLike): Float32Array[] => {\n if (Array.isArray(buffer.channelData)) return buffer.channelData\n if (typeof buffer.getChannelData === 'function') {\n const count = buffer.numberOfChannels ?? 1\n return Array.from({ length: count }, (_, c) => buffer.getChannelData!(c))\n }\n throw new Error('audio-decode returned an unrecognized buffer shape')\n}\n\n/** Options for {@link audioDecodeEngine}. */\nexport interface AudioDecodeEngineOptions {\n /** Override the module resolution. Default: `import('audio-decode')`. */\n audioDecode?: () =>\n | AudioDecodeFn\n | { default: AudioDecodeFn }\n | Promise<AudioDecodeFn | { default: AudioDecodeFn }>\n}\n\n/**\n * Construct the audio-decode-backed engine.\n *\n * @param options - Optional module resolver override.\n * @returns The engine.\n */\nexport const audioDecodeEngine = (options: AudioDecodeEngineOptions = {}): MediaEngine => {\n let fnPromise: Promise<AudioDecodeFn> | undefined\n const getDecode = (): Promise<AudioDecodeFn> => {\n fnPromise ??= Promise.resolve(\n options.audioDecode ? options.audioDecode() : import('audio-decode')\n )\n .then((mod) => {\n const fn = typeof mod === 'function' ? mod : (mod as { default: AudioDecodeFn }).default\n if (typeof fn !== 'function') {\n throw new Error('audio-decode did not resolve to a decode function')\n }\n return fn\n })\n .catch((err) => {\n const detail = isError(err) ? err.message : String(err)\n throw new E_INVALID_MEDIA_PIPELINE_CONFIG([\n `the audio-decode engine could not load its peer dependency \"audio-decode\": ${detail} — install it (pnpm add audio-decode)`,\n ])\n })\n return fnPromise\n }\n\n const convert = async (request: ConvertRequest): Promise<ConvertResult> => {\n const decode = await getDecode()\n const buffer = await decode(request.bytes)\n const channels = channelsOf(buffer)\n let pcm: Float32Array\n if (channels.length <= 1) {\n pcm = channels[0]\n } else {\n // Downmix to mono by averaging channels.\n const length = channels[0].length\n const mono = new Float32Array(length)\n for (const data of channels) {\n for (let i = 0; i < length; i++) mono[i] += data[i] / channels.length\n }\n pcm = mono\n }\n return {\n outputs: [\n { bytes: pcmToBytes(pcm), mimeType: PCM_MIME, meta: { sampleRate: buffer.sampleRate } },\n ],\n }\n }\n\n return {\n id: 'audio-decode',\n converts: [\n {\n from: [\n 'audio/mpeg',\n 'audio/mp3',\n 'audio/mp4',\n 'audio/aac',\n 'audio/x-m4a',\n 'audio/ogg',\n 'audio/opus',\n 'audio/flac',\n 'audio/x-flac',\n 'audio/wav',\n 'audio/x-wav',\n 'audio/wave',\n ],\n to: ['pcm'],\n convert,\n },\n ],\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAyCA,IAAM,cAAc,WAA4C;CAC9D,IAAI,MAAM,QAAQ,OAAO,WAAW,GAAG,OAAO,OAAO;CACrD,IAAI,OAAO,OAAO,mBAAmB,YAAY;EAC/C,MAAM,QAAQ,OAAO,oBAAoB;EACzC,OAAO,MAAM,KAAK,EAAE,QAAQ,MAAM,IAAI,GAAG,MAAM,OAAO,eAAgB,CAAC,CAAC;CAC1E;CACA,MAAM,IAAI,MAAM,oDAAoD;AACtE;;;;;;;AAiBA,IAAa,qBAAqB,UAAoC,CAAC,MAAmB;CACxF,IAAI;CACJ,MAAM,kBAA0C;EAC9C,cAAc,QAAQ,QACpB,QAAQ,cAAc,QAAQ,YAAY,IAAI,OAAO,eACvD,EACG,MAAM,QAAQ;GACb,MAAM,KAAK,OAAO,QAAQ,aAAa,MAAO,IAAmC;GACjF,IAAI,OAAO,OAAO,YAChB,MAAM,IAAI,MAAM,mDAAmD;GAErE,OAAO;EACT,CAAC,EACA,OAAO,QAAQ;GAEd,MAAM,IAAI,mBAAA,gCAAgC,CACxC,8EAFa,sBAAA,QAAQ,GAAG,IAAI,IAAI,UAAU,OAAO,GAAG,EAEiC,sCACvF,CAAC;EACH,CAAC;EACH,OAAO;CACT;CAEA,MAAM,UAAU,OAAO,YAAoD;EAEzE,MAAM,SAAS,OAAM,MADA,UAAU,GACH,QAAQ,KAAK;EACzC,MAAM,WAAW,WAAW,MAAM;EAClC,IAAI;EACJ,IAAI,SAAS,UAAU,GACrB,MAAM,SAAS;OACV;GAEL,MAAM,SAAS,SAAS,GAAG;GAC3B,MAAM,OAAO,IAAI,aAAa,MAAM;GACpC,KAAK,MAAM,QAAQ,UACjB,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,KAAK,KAAK,MAAM,KAAK,KAAK,SAAS;GAEjE,MAAM;EACR;EACA,OAAO,EACL,SAAS,CACP;GAAE,OAAO,kCAAA,WAAW,GAAG;GAAG,UAAU,kCAAA;GAAU,MAAM,EAAE,YAAY,OAAO,WAAW;EAAE,CACxF,EACF;CACF;CAEA,OAAO;EACL,IAAI;EACJ,UAAU,CACR;GACE,MAAM;IACJ;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF;GACA,IAAI,CAAC,KAAK;GACV;EACF,CACF;CACF;AACF"}
1
+ {"version":3,"file":"audio_decode.cjs","names":[],"sources":["../../../../src/batteries/media/engines/audio_decode.ts"],"sourcesContent":["/**\n * A cross-environment audio-decoding {@link @nhtio/adk/batteries/media/contracts!MediaEngine}\n * backed by the `audio-decode` package (pure JS/WASM codecs — no ffmpeg, no native bindings;\n * works in Node and browsers).\n *\n * @module @nhtio/adk/batteries/media/engines/audio_decode\n *\n * @remarks\n * Declares two convert capabilities: audio containers to the virtual `pcm` token (mp3 /\n * m4a-aac / ogg-vorbis / opus / flac / wav), downmixed to mono; and a generation edge —\n * `EMPTY_MIME` → wav. Audio bytes are just bytes in a documented envelope: the generated\n * seed is a canonical 44-byte RIFF/WAVE header plus one second of 16-bit mono silence at\n * 44100 Hz, written by a dependency-free helper (generation never loads the decode peer).\n * The PCM output reports the SOURCE sample rate in `meta.sampleRate` — the pipeline's\n * transcribe step resamples to the 16 kHz transcription engines expect. For exotic\n * containers, compose an ffmpeg-backed engine instead; the capability declaration is the\n * seam.\n *\n * `audio-decode` is an optional peer dependency, lazily imported on first actual use.\n */\n\nimport { isError } from '@nhtio/adk/guards'\nimport { pcmToBytes, PCM_MIME, EMPTY_MIME } from '../contracts'\nimport { E_INVALID_MEDIA_PIPELINE_CONFIG } from '../exceptions'\nimport type { MediaEngine, ConvertRequest, ConvertResult } from '../contracts'\n\n/**\n * The decoded shapes audio-decode resolves to. Some codecs return an AudioBuffer-compatible\n * object (`numberOfChannels` + `getChannelData`); others (e.g. the wav path in Node) return a\n * plain `{ channelData: Float32Array[], sampleRate }` record. The engine normalizes both.\n */\nexport interface AudioBufferLike {\n /** Channel count when the AudioBuffer-compatible shape is returned. */\n numberOfChannels?: number\n /** Sample rate of the decoded audio, in Hz. Present on both shapes. */\n sampleRate: number\n /** Per-channel sample accessor on the AudioBuffer-compatible shape. */\n getChannelData?(channel: number): Float32Array\n /** Raw per-channel sample arrays on the plain-record shape. */\n channelData?: Float32Array[]\n}\n\n/** The decode function shape the `audio-decode` package exports. */\nexport type AudioDecodeFn = (bytes: Uint8Array | ArrayBuffer) => Promise<AudioBufferLike>\n\nconst channelsOf = (buffer: AudioBufferLike): Float32Array[] => {\n if (Array.isArray(buffer.channelData)) return buffer.channelData\n if (typeof buffer.getChannelData === 'function') {\n const count = buffer.numberOfChannels ?? 1\n return Array.from({ length: count }, (_, c) => buffer.getChannelData!(c))\n }\n throw new Error('audio-decode returned an unrecognized buffer shape')\n}\n\n/**\n * Mint one second of 16-bit mono silence at 44100 Hz as a canonical RIFF/WAVE file — the\n * 44-byte header plus zeroed sample data. Pure bytes, zero dependencies.\n */\nconst makeSilentWav = (): Uint8Array => {\n const sampleRate = 44_100\n const numSamples = sampleRate // 1 second\n const bytesPerSample = 2 // 16-bit\n const dataSize = numSamples * bytesPerSample\n const bytes = new Uint8Array(44 + dataSize) // sample data stays zeroed = silence\n const view = new DataView(bytes.buffer)\n const writeAscii = (offset: number, text: string): void => {\n for (let i = 0; i < text.length; i++) bytes[offset + i] = text.charCodeAt(i)\n }\n writeAscii(0, 'RIFF')\n view.setUint32(4, 36 + dataSize, true)\n writeAscii(8, 'WAVE')\n writeAscii(12, 'fmt ')\n view.setUint32(16, 16, true) // fmt chunk size\n view.setUint16(20, 1, true) // PCM format\n view.setUint16(22, 1, true) // mono\n view.setUint32(24, sampleRate, true)\n view.setUint32(28, sampleRate * bytesPerSample, true) // byte rate\n view.setUint16(32, bytesPerSample, true) // block align\n view.setUint16(34, 16, true) // bits per sample\n writeAscii(36, 'data')\n view.setUint32(40, dataSize, true)\n return bytes\n}\n\n/** Options for {@link audioDecodeEngine}. */\nexport interface AudioDecodeEngineOptions {\n /** Override the module resolution. Default: `import('audio-decode')`. */\n audioDecode?: () =>\n | AudioDecodeFn\n | { default: AudioDecodeFn }\n | Promise<AudioDecodeFn | { default: AudioDecodeFn }>\n}\n\n/**\n * Construct the audio-decode-backed engine.\n *\n * @param options - Optional module resolver override.\n * @returns The engine.\n */\nexport const audioDecodeEngine = (options: AudioDecodeEngineOptions = {}): MediaEngine => {\n let fnPromise: Promise<AudioDecodeFn> | undefined\n const getDecode = (): Promise<AudioDecodeFn> => {\n fnPromise ??= Promise.resolve(\n options.audioDecode ? options.audioDecode() : import('audio-decode')\n )\n .then((mod) => {\n const fn = typeof mod === 'function' ? mod : (mod as { default: AudioDecodeFn }).default\n if (typeof fn !== 'function') {\n throw new Error('audio-decode did not resolve to a decode function')\n }\n return fn\n })\n .catch((err) => {\n const detail = isError(err) ? err.message : String(err)\n throw new E_INVALID_MEDIA_PIPELINE_CONFIG([\n `the audio-decode engine could not load its peer dependency \"audio-decode\": ${detail} — install it (pnpm add audio-decode)`,\n ])\n })\n return fnPromise\n }\n\n const convert = async (request: ConvertRequest): Promise<ConvertResult> => {\n const decode = await getDecode()\n const buffer = await decode(request.bytes)\n const channels = channelsOf(buffer)\n let pcm: Float32Array\n if (channels.length <= 1) {\n pcm = channels[0]\n } else {\n // Downmix to mono by averaging channels.\n const length = channels[0].length\n const mono = new Float32Array(length)\n for (const data of channels) {\n for (let i = 0; i < length; i++) mono[i] += data[i] / channels.length\n }\n pcm = mono\n }\n return {\n outputs: [\n { bytes: pcmToBytes(pcm), mimeType: PCM_MIME, meta: { sampleRate: buffer.sampleRate } },\n ],\n }\n }\n\n return {\n id: 'audio-decode',\n converts: [\n {\n from: [\n 'audio/mpeg',\n 'audio/mp3',\n 'audio/mp4',\n 'audio/aac',\n 'audio/x-m4a',\n 'audio/ogg',\n 'audio/opus',\n 'audio/flac',\n 'audio/x-flac',\n 'audio/wav',\n 'audio/x-wav',\n 'audio/wave',\n ],\n to: ['pcm'],\n convert,\n },\n {\n // Generation: silence in the engine's own input envelope (never loads the peer).\n from: [EMPTY_MIME],\n to: ['wav'],\n convert: async (): Promise<ConvertResult> => ({\n outputs: [{ bytes: makeSilentWav(), mimeType: 'audio/wav' }],\n }),\n },\n ],\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA6CA,IAAM,cAAc,WAA4C;CAC9D,IAAI,MAAM,QAAQ,OAAO,WAAW,GAAG,OAAO,OAAO;CACrD,IAAI,OAAO,OAAO,mBAAmB,YAAY;EAC/C,MAAM,QAAQ,OAAO,oBAAoB;EACzC,OAAO,MAAM,KAAK,EAAE,QAAQ,MAAM,IAAI,GAAG,MAAM,OAAO,eAAgB,CAAC,CAAC;CAC1E;CACA,MAAM,IAAI,MAAM,oDAAoD;AACtE;;;;;AAMA,IAAM,sBAAkC;CACtC,MAAM,aAAa;CACnB,MAAM,aAAa;CACnB,MAAM,iBAAiB;CACvB,MAAM,WAAW,aAAa;CAC9B,MAAM,QAAQ,IAAI,WAAW,KAAa;CAC1C,MAAM,OAAO,IAAI,SAAS,MAAM,MAAM;CACtC,MAAM,cAAc,QAAgB,SAAuB;EACzD,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,MAAM,SAAS,KAAK,KAAK,WAAW,CAAC;CAC7E;CACA,WAAW,GAAG,MAAM;CACpB,KAAK,UAAU,GAAG,OAAe,IAAI;CACrC,WAAW,GAAG,MAAM;CACpB,WAAW,IAAI,MAAM;CACrB,KAAK,UAAU,IAAI,IAAI,IAAI;CAC3B,KAAK,UAAU,IAAI,GAAG,IAAI;CAC1B,KAAK,UAAU,IAAI,GAAG,IAAI;CAC1B,KAAK,UAAU,IAAI,YAAY,IAAI;CACnC,KAAK,UAAU,IAAI,aAAa,gBAAgB,IAAI;CACpD,KAAK,UAAU,IAAI,gBAAgB,IAAI;CACvC,KAAK,UAAU,IAAI,IAAI,IAAI;CAC3B,WAAW,IAAI,MAAM;CACrB,KAAK,UAAU,IAAI,UAAU,IAAI;CACjC,OAAO;AACT;;;;;;;AAiBA,IAAa,qBAAqB,UAAoC,CAAC,MAAmB;CACxF,IAAI;CACJ,MAAM,kBAA0C;EAC9C,cAAc,QAAQ,QACpB,QAAQ,cAAc,QAAQ,YAAY,IAAI,OAAO,eACvD,EACG,MAAM,QAAQ;GACb,MAAM,KAAK,OAAO,QAAQ,aAAa,MAAO,IAAmC;GACjF,IAAI,OAAO,OAAO,YAChB,MAAM,IAAI,MAAM,mDAAmD;GAErE,OAAO;EACT,CAAC,EACA,OAAO,QAAQ;GAEd,MAAM,IAAI,mBAAA,gCAAgC,CACxC,8EAFa,sBAAA,QAAQ,GAAG,IAAI,IAAI,UAAU,OAAO,GAAG,EAEiC,sCACvF,CAAC;EACH,CAAC;EACH,OAAO;CACT;CAEA,MAAM,UAAU,OAAO,YAAoD;EAEzE,MAAM,SAAS,OAAM,MADA,UAAU,GACH,QAAQ,KAAK;EACzC,MAAM,WAAW,WAAW,MAAM;EAClC,IAAI;EACJ,IAAI,SAAS,UAAU,GACrB,MAAM,SAAS;OACV;GAEL,MAAM,SAAS,SAAS,GAAG;GAC3B,MAAM,OAAO,IAAI,aAAa,MAAM;GACpC,KAAK,MAAM,QAAQ,UACjB,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,KAAK,KAAK,MAAM,KAAK,KAAK,SAAS;GAEjE,MAAM;EACR;EACA,OAAO,EACL,SAAS,CACP;GAAE,OAAO,kCAAA,WAAW,GAAG;GAAG,UAAU,kCAAA;GAAU,MAAM,EAAE,YAAY,OAAO,WAAW;EAAE,CACxF,EACF;CACF;CAEA,OAAO;EACL,IAAI;EACJ,UAAU,CACR;GACE,MAAM;IACJ;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF;GACA,IAAI,CAAC,KAAK;GACV;EACF,GACA;GAEE,MAAM,CAAC,kCAAA,UAAU;GACjB,IAAI,CAAC,KAAK;GACV,SAAS,aAAqC,EAC5C,SAAS,CAAC;IAAE,OAAO,cAAc;IAAG,UAAU;GAAY,CAAC,EAC7D;EACF,CACF;CACF;AACF"}
@@ -6,11 +6,15 @@
6
6
  * @module @nhtio/adk/batteries/media/engines/audio_decode
7
7
  *
8
8
  * @remarks
9
- * Declares one convert capability: audio containers to the virtual `pcm` token (mp3 /
10
- * m4a-aac / ogg-vorbis / opus / flac / wav), downmixed to mono. The PCM output reports the
11
- * SOURCE sample rate in `meta.sampleRate` the pipeline's transcribe step resamples to the
12
- * 16 kHz transcription engines expect. For exotic containers, compose an ffmpeg-backed
13
- * engine instead; the capability declaration is the seam.
9
+ * Declares two convert capabilities: audio containers to the virtual `pcm` token (mp3 /
10
+ * m4a-aac / ogg-vorbis / opus / flac / wav), downmixed to mono; and a generation edge
11
+ * `EMPTY_MIME` wav. Audio bytes are just bytes in a documented envelope: the generated
12
+ * seed is a canonical 44-byte RIFF/WAVE header plus one second of 16-bit mono silence at
13
+ * 44100 Hz, written by a dependency-free helper (generation never loads the decode peer).
14
+ * The PCM output reports the SOURCE sample rate in `meta.sampleRate` — the pipeline's
15
+ * transcribe step resamples to the 16 kHz transcription engines expect. For exotic
16
+ * containers, compose an ffmpeg-backed engine instead; the capability declaration is the
17
+ * seam.
14
18
  *
15
19
  * `audio-decode` is an optional peer dependency, lazily imported on first actual use.
16
20
  */
@@ -1,6 +1,6 @@
1
- import { o as isError } from "../../../tool_registry-BaLUIzWz.mjs";
1
+ import { o as isError } from "../../../tool_registry-CZ55mIti.mjs";
2
2
  import "../../../guards.mjs";
3
- import { PCM_MIME, pcmToBytes } from "../contracts.mjs";
3
+ import { EMPTY_MIME, PCM_MIME, pcmToBytes } from "../contracts.mjs";
4
4
  import { t as E_INVALID_MEDIA_PIPELINE_CONFIG } from "../../../exceptions-BLTn4Ofs.mjs";
5
5
  //#region src/batteries/media/engines/audio_decode.ts
6
6
  /**
@@ -11,11 +11,15 @@ import { t as E_INVALID_MEDIA_PIPELINE_CONFIG } from "../../../exceptions-BLTn4O
11
11
  * @module @nhtio/adk/batteries/media/engines/audio_decode
12
12
  *
13
13
  * @remarks
14
- * Declares one convert capability: audio containers to the virtual `pcm` token (mp3 /
15
- * m4a-aac / ogg-vorbis / opus / flac / wav), downmixed to mono. The PCM output reports the
16
- * SOURCE sample rate in `meta.sampleRate` the pipeline's transcribe step resamples to the
17
- * 16 kHz transcription engines expect. For exotic containers, compose an ffmpeg-backed
18
- * engine instead; the capability declaration is the seam.
14
+ * Declares two convert capabilities: audio containers to the virtual `pcm` token (mp3 /
15
+ * m4a-aac / ogg-vorbis / opus / flac / wav), downmixed to mono; and a generation edge
16
+ * `EMPTY_MIME` wav. Audio bytes are just bytes in a documented envelope: the generated
17
+ * seed is a canonical 44-byte RIFF/WAVE header plus one second of 16-bit mono silence at
18
+ * 44100 Hz, written by a dependency-free helper (generation never loads the decode peer).
19
+ * The PCM output reports the SOURCE sample rate in `meta.sampleRate` — the pipeline's
20
+ * transcribe step resamples to the 16 kHz transcription engines expect. For exotic
21
+ * containers, compose an ffmpeg-backed engine instead; the capability declaration is the
22
+ * seam.
19
23
  *
20
24
  * `audio-decode` is an optional peer dependency, lazily imported on first actual use.
21
25
  */
@@ -28,6 +32,35 @@ var channelsOf = (buffer) => {
28
32
  throw new Error("audio-decode returned an unrecognized buffer shape");
29
33
  };
30
34
  /**
35
+ * Mint one second of 16-bit mono silence at 44100 Hz as a canonical RIFF/WAVE file — the
36
+ * 44-byte header plus zeroed sample data. Pure bytes, zero dependencies.
37
+ */
38
+ var makeSilentWav = () => {
39
+ const sampleRate = 44100;
40
+ const numSamples = sampleRate;
41
+ const bytesPerSample = 2;
42
+ const dataSize = numSamples * bytesPerSample;
43
+ const bytes = new Uint8Array(88244);
44
+ const view = new DataView(bytes.buffer);
45
+ const writeAscii = (offset, text) => {
46
+ for (let i = 0; i < text.length; i++) bytes[offset + i] = text.charCodeAt(i);
47
+ };
48
+ writeAscii(0, "RIFF");
49
+ view.setUint32(4, 88236, true);
50
+ writeAscii(8, "WAVE");
51
+ writeAscii(12, "fmt ");
52
+ view.setUint32(16, 16, true);
53
+ view.setUint16(20, 1, true);
54
+ view.setUint16(22, 1, true);
55
+ view.setUint32(24, sampleRate, true);
56
+ view.setUint32(28, sampleRate * bytesPerSample, true);
57
+ view.setUint16(32, bytesPerSample, true);
58
+ view.setUint16(34, 16, true);
59
+ writeAscii(36, "data");
60
+ view.setUint32(40, dataSize, true);
61
+ return bytes;
62
+ };
63
+ /**
31
64
  * Construct the audio-decode-backed engine.
32
65
  *
33
66
  * @param options - Optional module resolver override.
@@ -81,6 +114,13 @@ var audioDecodeEngine = (options = {}) => {
81
114
  ],
82
115
  to: ["pcm"],
83
116
  convert
117
+ }, {
118
+ from: [EMPTY_MIME],
119
+ to: ["wav"],
120
+ convert: async () => ({ outputs: [{
121
+ bytes: makeSilentWav(),
122
+ mimeType: "audio/wav"
123
+ }] })
84
124
  }]
85
125
  };
86
126
  };
@@ -1 +1 @@
1
- {"version":3,"file":"audio_decode.mjs","names":[],"sources":["../../../../src/batteries/media/engines/audio_decode.ts"],"sourcesContent":["/**\n * A cross-environment audio-decoding {@link @nhtio/adk/batteries/media/contracts!MediaEngine}\n * backed by the `audio-decode` package (pure JS/WASM codecs — no ffmpeg, no native bindings;\n * works in Node and browsers).\n *\n * @module @nhtio/adk/batteries/media/engines/audio_decode\n *\n * @remarks\n * Declares one convert capability: audio containers to the virtual `pcm` token (mp3 /\n * m4a-aac / ogg-vorbis / opus / flac / wav), downmixed to mono. The PCM output reports the\n * SOURCE sample rate in `meta.sampleRate` — the pipeline's transcribe step resamples to the\n * 16 kHz transcription engines expect. For exotic containers, compose an ffmpeg-backed\n * engine instead; the capability declaration is the seam.\n *\n * `audio-decode` is an optional peer dependency, lazily imported on first actual use.\n */\n\nimport { isError } from '@nhtio/adk/guards'\nimport { pcmToBytes, PCM_MIME } from '../contracts'\nimport { E_INVALID_MEDIA_PIPELINE_CONFIG } from '../exceptions'\nimport type { MediaEngine, ConvertRequest, ConvertResult } from '../contracts'\n\n/**\n * The decoded shapes audio-decode resolves to. Some codecs return an AudioBuffer-compatible\n * object (`numberOfChannels` + `getChannelData`); others (e.g. the wav path in Node) return a\n * plain `{ channelData: Float32Array[], sampleRate }` record. The engine normalizes both.\n */\nexport interface AudioBufferLike {\n /** Channel count when the AudioBuffer-compatible shape is returned. */\n numberOfChannels?: number\n /** Sample rate of the decoded audio, in Hz. Present on both shapes. */\n sampleRate: number\n /** Per-channel sample accessor on the AudioBuffer-compatible shape. */\n getChannelData?(channel: number): Float32Array\n /** Raw per-channel sample arrays on the plain-record shape. */\n channelData?: Float32Array[]\n}\n\n/** The decode function shape the `audio-decode` package exports. */\nexport type AudioDecodeFn = (bytes: Uint8Array | ArrayBuffer) => Promise<AudioBufferLike>\n\nconst channelsOf = (buffer: AudioBufferLike): Float32Array[] => {\n if (Array.isArray(buffer.channelData)) return buffer.channelData\n if (typeof buffer.getChannelData === 'function') {\n const count = buffer.numberOfChannels ?? 1\n return Array.from({ length: count }, (_, c) => buffer.getChannelData!(c))\n }\n throw new Error('audio-decode returned an unrecognized buffer shape')\n}\n\n/** Options for {@link audioDecodeEngine}. */\nexport interface AudioDecodeEngineOptions {\n /** Override the module resolution. Default: `import('audio-decode')`. */\n audioDecode?: () =>\n | AudioDecodeFn\n | { default: AudioDecodeFn }\n | Promise<AudioDecodeFn | { default: AudioDecodeFn }>\n}\n\n/**\n * Construct the audio-decode-backed engine.\n *\n * @param options - Optional module resolver override.\n * @returns The engine.\n */\nexport const audioDecodeEngine = (options: AudioDecodeEngineOptions = {}): MediaEngine => {\n let fnPromise: Promise<AudioDecodeFn> | undefined\n const getDecode = (): Promise<AudioDecodeFn> => {\n fnPromise ??= Promise.resolve(\n options.audioDecode ? options.audioDecode() : import('audio-decode')\n )\n .then((mod) => {\n const fn = typeof mod === 'function' ? mod : (mod as { default: AudioDecodeFn }).default\n if (typeof fn !== 'function') {\n throw new Error('audio-decode did not resolve to a decode function')\n }\n return fn\n })\n .catch((err) => {\n const detail = isError(err) ? err.message : String(err)\n throw new E_INVALID_MEDIA_PIPELINE_CONFIG([\n `the audio-decode engine could not load its peer dependency \"audio-decode\": ${detail} — install it (pnpm add audio-decode)`,\n ])\n })\n return fnPromise\n }\n\n const convert = async (request: ConvertRequest): Promise<ConvertResult> => {\n const decode = await getDecode()\n const buffer = await decode(request.bytes)\n const channels = channelsOf(buffer)\n let pcm: Float32Array\n if (channels.length <= 1) {\n pcm = channels[0]\n } else {\n // Downmix to mono by averaging channels.\n const length = channels[0].length\n const mono = new Float32Array(length)\n for (const data of channels) {\n for (let i = 0; i < length; i++) mono[i] += data[i] / channels.length\n }\n pcm = mono\n }\n return {\n outputs: [\n { bytes: pcmToBytes(pcm), mimeType: PCM_MIME, meta: { sampleRate: buffer.sampleRate } },\n ],\n }\n }\n\n return {\n id: 'audio-decode',\n converts: [\n {\n from: [\n 'audio/mpeg',\n 'audio/mp3',\n 'audio/mp4',\n 'audio/aac',\n 'audio/x-m4a',\n 'audio/ogg',\n 'audio/opus',\n 'audio/flac',\n 'audio/x-flac',\n 'audio/wav',\n 'audio/x-wav',\n 'audio/wave',\n ],\n to: ['pcm'],\n convert,\n },\n ],\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAyCA,IAAM,cAAc,WAA4C;CAC9D,IAAI,MAAM,QAAQ,OAAO,WAAW,GAAG,OAAO,OAAO;CACrD,IAAI,OAAO,OAAO,mBAAmB,YAAY;EAC/C,MAAM,QAAQ,OAAO,oBAAoB;EACzC,OAAO,MAAM,KAAK,EAAE,QAAQ,MAAM,IAAI,GAAG,MAAM,OAAO,eAAgB,CAAC,CAAC;CAC1E;CACA,MAAM,IAAI,MAAM,oDAAoD;AACtE;;;;;;;AAiBA,IAAa,qBAAqB,UAAoC,CAAC,MAAmB;CACxF,IAAI;CACJ,MAAM,kBAA0C;EAC9C,cAAc,QAAQ,QACpB,QAAQ,cAAc,QAAQ,YAAY,IAAI,OAAO,eACvD,EACG,MAAM,QAAQ;GACb,MAAM,KAAK,OAAO,QAAQ,aAAa,MAAO,IAAmC;GACjF,IAAI,OAAO,OAAO,YAChB,MAAM,IAAI,MAAM,mDAAmD;GAErE,OAAO;EACT,CAAC,EACA,OAAO,QAAQ;GAEd,MAAM,IAAI,gCAAgC,CACxC,8EAFa,QAAQ,GAAG,IAAI,IAAI,UAAU,OAAO,GAAG,EAEiC,sCACvF,CAAC;EACH,CAAC;EACH,OAAO;CACT;CAEA,MAAM,UAAU,OAAO,YAAoD;EAEzE,MAAM,SAAS,OAAM,MADA,UAAU,GACH,QAAQ,KAAK;EACzC,MAAM,WAAW,WAAW,MAAM;EAClC,IAAI;EACJ,IAAI,SAAS,UAAU,GACrB,MAAM,SAAS;OACV;GAEL,MAAM,SAAS,SAAS,GAAG;GAC3B,MAAM,OAAO,IAAI,aAAa,MAAM;GACpC,KAAK,MAAM,QAAQ,UACjB,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,KAAK,KAAK,MAAM,KAAK,KAAK,SAAS;GAEjE,MAAM;EACR;EACA,OAAO,EACL,SAAS,CACP;GAAE,OAAO,WAAW,GAAG;GAAG,UAAU;GAAU,MAAM,EAAE,YAAY,OAAO,WAAW;EAAE,CACxF,EACF;CACF;CAEA,OAAO;EACL,IAAI;EACJ,UAAU,CACR;GACE,MAAM;IACJ;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF;GACA,IAAI,CAAC,KAAK;GACV;EACF,CACF;CACF;AACF"}
1
+ {"version":3,"file":"audio_decode.mjs","names":[],"sources":["../../../../src/batteries/media/engines/audio_decode.ts"],"sourcesContent":["/**\n * A cross-environment audio-decoding {@link @nhtio/adk/batteries/media/contracts!MediaEngine}\n * backed by the `audio-decode` package (pure JS/WASM codecs — no ffmpeg, no native bindings;\n * works in Node and browsers).\n *\n * @module @nhtio/adk/batteries/media/engines/audio_decode\n *\n * @remarks\n * Declares two convert capabilities: audio containers to the virtual `pcm` token (mp3 /\n * m4a-aac / ogg-vorbis / opus / flac / wav), downmixed to mono; and a generation edge —\n * `EMPTY_MIME` → wav. Audio bytes are just bytes in a documented envelope: the generated\n * seed is a canonical 44-byte RIFF/WAVE header plus one second of 16-bit mono silence at\n * 44100 Hz, written by a dependency-free helper (generation never loads the decode peer).\n * The PCM output reports the SOURCE sample rate in `meta.sampleRate` — the pipeline's\n * transcribe step resamples to the 16 kHz transcription engines expect. For exotic\n * containers, compose an ffmpeg-backed engine instead; the capability declaration is the\n * seam.\n *\n * `audio-decode` is an optional peer dependency, lazily imported on first actual use.\n */\n\nimport { isError } from '@nhtio/adk/guards'\nimport { pcmToBytes, PCM_MIME, EMPTY_MIME } from '../contracts'\nimport { E_INVALID_MEDIA_PIPELINE_CONFIG } from '../exceptions'\nimport type { MediaEngine, ConvertRequest, ConvertResult } from '../contracts'\n\n/**\n * The decoded shapes audio-decode resolves to. Some codecs return an AudioBuffer-compatible\n * object (`numberOfChannels` + `getChannelData`); others (e.g. the wav path in Node) return a\n * plain `{ channelData: Float32Array[], sampleRate }` record. The engine normalizes both.\n */\nexport interface AudioBufferLike {\n /** Channel count when the AudioBuffer-compatible shape is returned. */\n numberOfChannels?: number\n /** Sample rate of the decoded audio, in Hz. Present on both shapes. */\n sampleRate: number\n /** Per-channel sample accessor on the AudioBuffer-compatible shape. */\n getChannelData?(channel: number): Float32Array\n /** Raw per-channel sample arrays on the plain-record shape. */\n channelData?: Float32Array[]\n}\n\n/** The decode function shape the `audio-decode` package exports. */\nexport type AudioDecodeFn = (bytes: Uint8Array | ArrayBuffer) => Promise<AudioBufferLike>\n\nconst channelsOf = (buffer: AudioBufferLike): Float32Array[] => {\n if (Array.isArray(buffer.channelData)) return buffer.channelData\n if (typeof buffer.getChannelData === 'function') {\n const count = buffer.numberOfChannels ?? 1\n return Array.from({ length: count }, (_, c) => buffer.getChannelData!(c))\n }\n throw new Error('audio-decode returned an unrecognized buffer shape')\n}\n\n/**\n * Mint one second of 16-bit mono silence at 44100 Hz as a canonical RIFF/WAVE file — the\n * 44-byte header plus zeroed sample data. Pure bytes, zero dependencies.\n */\nconst makeSilentWav = (): Uint8Array => {\n const sampleRate = 44_100\n const numSamples = sampleRate // 1 second\n const bytesPerSample = 2 // 16-bit\n const dataSize = numSamples * bytesPerSample\n const bytes = new Uint8Array(44 + dataSize) // sample data stays zeroed = silence\n const view = new DataView(bytes.buffer)\n const writeAscii = (offset: number, text: string): void => {\n for (let i = 0; i < text.length; i++) bytes[offset + i] = text.charCodeAt(i)\n }\n writeAscii(0, 'RIFF')\n view.setUint32(4, 36 + dataSize, true)\n writeAscii(8, 'WAVE')\n writeAscii(12, 'fmt ')\n view.setUint32(16, 16, true) // fmt chunk size\n view.setUint16(20, 1, true) // PCM format\n view.setUint16(22, 1, true) // mono\n view.setUint32(24, sampleRate, true)\n view.setUint32(28, sampleRate * bytesPerSample, true) // byte rate\n view.setUint16(32, bytesPerSample, true) // block align\n view.setUint16(34, 16, true) // bits per sample\n writeAscii(36, 'data')\n view.setUint32(40, dataSize, true)\n return bytes\n}\n\n/** Options for {@link audioDecodeEngine}. */\nexport interface AudioDecodeEngineOptions {\n /** Override the module resolution. Default: `import('audio-decode')`. */\n audioDecode?: () =>\n | AudioDecodeFn\n | { default: AudioDecodeFn }\n | Promise<AudioDecodeFn | { default: AudioDecodeFn }>\n}\n\n/**\n * Construct the audio-decode-backed engine.\n *\n * @param options - Optional module resolver override.\n * @returns The engine.\n */\nexport const audioDecodeEngine = (options: AudioDecodeEngineOptions = {}): MediaEngine => {\n let fnPromise: Promise<AudioDecodeFn> | undefined\n const getDecode = (): Promise<AudioDecodeFn> => {\n fnPromise ??= Promise.resolve(\n options.audioDecode ? options.audioDecode() : import('audio-decode')\n )\n .then((mod) => {\n const fn = typeof mod === 'function' ? mod : (mod as { default: AudioDecodeFn }).default\n if (typeof fn !== 'function') {\n throw new Error('audio-decode did not resolve to a decode function')\n }\n return fn\n })\n .catch((err) => {\n const detail = isError(err) ? err.message : String(err)\n throw new E_INVALID_MEDIA_PIPELINE_CONFIG([\n `the audio-decode engine could not load its peer dependency \"audio-decode\": ${detail} — install it (pnpm add audio-decode)`,\n ])\n })\n return fnPromise\n }\n\n const convert = async (request: ConvertRequest): Promise<ConvertResult> => {\n const decode = await getDecode()\n const buffer = await decode(request.bytes)\n const channels = channelsOf(buffer)\n let pcm: Float32Array\n if (channels.length <= 1) {\n pcm = channels[0]\n } else {\n // Downmix to mono by averaging channels.\n const length = channels[0].length\n const mono = new Float32Array(length)\n for (const data of channels) {\n for (let i = 0; i < length; i++) mono[i] += data[i] / channels.length\n }\n pcm = mono\n }\n return {\n outputs: [\n { bytes: pcmToBytes(pcm), mimeType: PCM_MIME, meta: { sampleRate: buffer.sampleRate } },\n ],\n }\n }\n\n return {\n id: 'audio-decode',\n converts: [\n {\n from: [\n 'audio/mpeg',\n 'audio/mp3',\n 'audio/mp4',\n 'audio/aac',\n 'audio/x-m4a',\n 'audio/ogg',\n 'audio/opus',\n 'audio/flac',\n 'audio/x-flac',\n 'audio/wav',\n 'audio/x-wav',\n 'audio/wave',\n ],\n to: ['pcm'],\n convert,\n },\n {\n // Generation: silence in the engine's own input envelope (never loads the peer).\n from: [EMPTY_MIME],\n to: ['wav'],\n convert: async (): Promise<ConvertResult> => ({\n outputs: [{ bytes: makeSilentWav(), mimeType: 'audio/wav' }],\n }),\n },\n ],\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA6CA,IAAM,cAAc,WAA4C;CAC9D,IAAI,MAAM,QAAQ,OAAO,WAAW,GAAG,OAAO,OAAO;CACrD,IAAI,OAAO,OAAO,mBAAmB,YAAY;EAC/C,MAAM,QAAQ,OAAO,oBAAoB;EACzC,OAAO,MAAM,KAAK,EAAE,QAAQ,MAAM,IAAI,GAAG,MAAM,OAAO,eAAgB,CAAC,CAAC;CAC1E;CACA,MAAM,IAAI,MAAM,oDAAoD;AACtE;;;;;AAMA,IAAM,sBAAkC;CACtC,MAAM,aAAa;CACnB,MAAM,aAAa;CACnB,MAAM,iBAAiB;CACvB,MAAM,WAAW,aAAa;CAC9B,MAAM,QAAQ,IAAI,WAAW,KAAa;CAC1C,MAAM,OAAO,IAAI,SAAS,MAAM,MAAM;CACtC,MAAM,cAAc,QAAgB,SAAuB;EACzD,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,MAAM,SAAS,KAAK,KAAK,WAAW,CAAC;CAC7E;CACA,WAAW,GAAG,MAAM;CACpB,KAAK,UAAU,GAAG,OAAe,IAAI;CACrC,WAAW,GAAG,MAAM;CACpB,WAAW,IAAI,MAAM;CACrB,KAAK,UAAU,IAAI,IAAI,IAAI;CAC3B,KAAK,UAAU,IAAI,GAAG,IAAI;CAC1B,KAAK,UAAU,IAAI,GAAG,IAAI;CAC1B,KAAK,UAAU,IAAI,YAAY,IAAI;CACnC,KAAK,UAAU,IAAI,aAAa,gBAAgB,IAAI;CACpD,KAAK,UAAU,IAAI,gBAAgB,IAAI;CACvC,KAAK,UAAU,IAAI,IAAI,IAAI;CAC3B,WAAW,IAAI,MAAM;CACrB,KAAK,UAAU,IAAI,UAAU,IAAI;CACjC,OAAO;AACT;;;;;;;AAiBA,IAAa,qBAAqB,UAAoC,CAAC,MAAmB;CACxF,IAAI;CACJ,MAAM,kBAA0C;EAC9C,cAAc,QAAQ,QACpB,QAAQ,cAAc,QAAQ,YAAY,IAAI,OAAO,eACvD,EACG,MAAM,QAAQ;GACb,MAAM,KAAK,OAAO,QAAQ,aAAa,MAAO,IAAmC;GACjF,IAAI,OAAO,OAAO,YAChB,MAAM,IAAI,MAAM,mDAAmD;GAErE,OAAO;EACT,CAAC,EACA,OAAO,QAAQ;GAEd,MAAM,IAAI,gCAAgC,CACxC,8EAFa,QAAQ,GAAG,IAAI,IAAI,UAAU,OAAO,GAAG,EAEiC,sCACvF,CAAC;EACH,CAAC;EACH,OAAO;CACT;CAEA,MAAM,UAAU,OAAO,YAAoD;EAEzE,MAAM,SAAS,OAAM,MADA,UAAU,GACH,QAAQ,KAAK;EACzC,MAAM,WAAW,WAAW,MAAM;EAClC,IAAI;EACJ,IAAI,SAAS,UAAU,GACrB,MAAM,SAAS;OACV;GAEL,MAAM,SAAS,SAAS,GAAG;GAC3B,MAAM,OAAO,IAAI,aAAa,MAAM;GACpC,KAAK,MAAM,QAAQ,UACjB,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,KAAK,KAAK,MAAM,KAAK,KAAK,SAAS;GAEjE,MAAM;EACR;EACA,OAAO,EACL,SAAS,CACP;GAAE,OAAO,WAAW,GAAG;GAAG,UAAU;GAAU,MAAM,EAAE,YAAY,OAAO,WAAW;EAAE,CACxF,EACF;CACF;CAEA,OAAO;EACL,IAAI;EACJ,UAAU,CACR;GACE,MAAM;IACJ;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF;GACA,IAAI,CAAC,KAAK;GACV;EACF,GACA;GAEE,MAAM,CAAC,UAAU;GACjB,IAAI,CAAC,KAAK;GACV,SAAS,aAAqC,EAC5C,SAAS,CAAC;IAAE,OAAO,cAAc;IAAG,UAAU;GAAY,CAAC,EAC7D;EACF,CACF;CACF;AACF"}