@bevel-software/platform-core-backend 0.11.1 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (443) hide show
  1. package/THIRD-PARTY-NOTICES.md +1163 -425
  2. package/dist/core/create-core-server.js +1 -1
  3. package/dist/core/create-core-server.js.map +1 -1
  4. package/dist/core/create-core-services.d.ts +3 -1
  5. package/dist/core/create-core-services.d.ts.map +1 -1
  6. package/dist/core/create-core-services.js +7 -2
  7. package/dist/core/create-core-services.js.map +1 -1
  8. package/dist/core-config.d.ts +7 -0
  9. package/dist/core-config.d.ts.map +1 -1
  10. package/dist/core-config.js +9 -0
  11. package/dist/core-config.js.map +1 -1
  12. package/dist/modules/access/access-control.service.d.ts +1 -253
  13. package/dist/modules/access/access-control.service.d.ts.map +1 -1
  14. package/dist/modules/access/access-control.service.js +3 -632
  15. package/dist/modules/access/access-control.service.js.map +1 -1
  16. package/dist/modules/access/access-declarations.d.ts +2 -2
  17. package/dist/modules/access/access-declarations.d.ts.map +1 -1
  18. package/dist/modules/access/access-declarations.js +2 -2
  19. package/dist/modules/access/access-declarations.js.map +1 -1
  20. package/dist/modules/access/access-mutation.service.d.ts +3 -3
  21. package/dist/modules/access/access-mutation.service.d.ts.map +1 -1
  22. package/dist/modules/access/access-mutation.service.js +3 -3
  23. package/dist/modules/access/access-mutation.service.js.map +1 -1
  24. package/dist/modules/access/access.routes.js +4 -4
  25. package/dist/modules/access/access.routes.js.map +1 -1
  26. package/dist/modules/access/admin-locked-commit.d.ts +2 -2
  27. package/dist/modules/access/admin-locked-commit.d.ts.map +1 -1
  28. package/dist/modules/access/admin-locked-commit.js +2 -2
  29. package/dist/modules/access/admin-locked-commit.js.map +1 -1
  30. package/dist/modules/access/admin-route-helpers.js +1 -1
  31. package/dist/modules/access/admin-route-helpers.js.map +1 -1
  32. package/dist/modules/access/capability-registry.js +1 -1
  33. package/dist/modules/access/capability-registry.js.map +1 -1
  34. package/dist/modules/access/creator-access.d.ts +1 -45
  35. package/dist/modules/access/creator-access.d.ts.map +1 -1
  36. package/dist/modules/access/creator-access.js +4 -24
  37. package/dist/modules/access/creator-access.js.map +1 -1
  38. package/dist/modules/access/groups-admin.routes.js +1 -1
  39. package/dist/modules/access/groups-admin.routes.js.map +1 -1
  40. package/dist/modules/access/groups-admin.service.d.ts +1 -1
  41. package/dist/modules/access/groups-admin.service.d.ts.map +1 -1
  42. package/dist/modules/access/groups-admin.service.js +6 -5
  43. package/dist/modules/access/groups-admin.service.js.map +1 -1
  44. package/dist/modules/access/groups-edit.js +2 -2
  45. package/dist/modules/access/groups-edit.js.map +1 -1
  46. package/dist/modules/access/reference-scan.js +1 -1
  47. package/dist/modules/access/reference-scan.js.map +1 -1
  48. package/dist/modules/access/roles-admin.service.d.ts +1 -1
  49. package/dist/modules/access/roles-admin.service.d.ts.map +1 -1
  50. package/dist/modules/access/roles-admin.service.js +8 -7
  51. package/dist/modules/access/roles-admin.service.js.map +1 -1
  52. package/dist/modules/access/roles-edit.js +1 -1
  53. package/dist/modules/access/roles-edit.js.map +1 -1
  54. package/dist/modules/access/synced-groups-committer.js +4 -4
  55. package/dist/modules/access/synced-groups-committer.js.map +1 -1
  56. package/dist/modules/access/synced-groups-writer.js +2 -2
  57. package/dist/modules/access/synced-groups-writer.js.map +1 -1
  58. package/dist/modules/access-model/access-errors.d.ts +34 -0
  59. package/dist/modules/access-model/access-errors.d.ts.map +1 -0
  60. package/dist/modules/access-model/access-errors.js +40 -0
  61. package/dist/modules/access-model/access-errors.js.map +1 -0
  62. package/dist/modules/access-model/access-grammar.d.ts +266 -0
  63. package/dist/modules/access-model/access-grammar.d.ts.map +1 -0
  64. package/dist/modules/access-model/access-grammar.js +644 -0
  65. package/dist/modules/access-model/access-grammar.js.map +1 -0
  66. package/dist/modules/access-model/access-splice.d.ts +140 -0
  67. package/dist/modules/access-model/access-splice.d.ts.map +1 -0
  68. package/dist/modules/access-model/access-splice.js +389 -0
  69. package/dist/modules/access-model/access-splice.js.map +1 -0
  70. package/dist/modules/access-model/creator.d.ts +54 -0
  71. package/dist/modules/access-model/creator.d.ts.map +1 -0
  72. package/dist/modules/access-model/creator.js +30 -0
  73. package/dist/modules/access-model/creator.js.map +1 -0
  74. package/dist/modules/access-model/group-files.d.ts +83 -0
  75. package/dist/modules/access-model/group-files.d.ts.map +1 -0
  76. package/dist/modules/access-model/group-files.js +167 -0
  77. package/dist/modules/access-model/group-files.js.map +1 -0
  78. package/dist/modules/access-model/kb-read-filter.d.ts +41 -0
  79. package/dist/modules/access-model/kb-read-filter.d.ts.map +1 -0
  80. package/dist/modules/access-model/kb-read-filter.js +60 -0
  81. package/dist/modules/access-model/kb-read-filter.js.map +1 -0
  82. package/dist/modules/access-model/render-roles-yaml.d.ts +22 -0
  83. package/dist/modules/access-model/render-roles-yaml.d.ts.map +1 -0
  84. package/dist/modules/access-model/render-roles-yaml.js +56 -0
  85. package/dist/modules/access-model/render-roles-yaml.js.map +1 -0
  86. package/dist/modules/access-model/roles-yaml-guard.d.ts +56 -0
  87. package/dist/modules/access-model/roles-yaml-guard.d.ts.map +1 -0
  88. package/dist/modules/access-model/roles-yaml-guard.js +79 -0
  89. package/dist/modules/access-model/roles-yaml-guard.js.map +1 -0
  90. package/dist/modules/admin/admin-access.service.d.ts.map +1 -1
  91. package/dist/modules/admin/admin-access.service.js +1 -1
  92. package/dist/modules/admin/admin-access.service.js.map +1 -1
  93. package/dist/modules/code-mode/code-mode.tool.d.ts.map +1 -1
  94. package/dist/modules/code-mode/code-mode.tool.js +7 -1
  95. package/dist/modules/code-mode/code-mode.tool.js.map +1 -1
  96. package/dist/modules/diff/diff.routes.js +4 -4
  97. package/dist/modules/diff/diff.routes.js.map +1 -1
  98. package/dist/modules/diff/diff.service.d.ts +1 -1
  99. package/dist/modules/diff/diff.service.d.ts.map +1 -1
  100. package/dist/modules/kb-fs/branch-name.d.ts +10 -0
  101. package/dist/modules/kb-fs/branch-name.d.ts.map +1 -0
  102. package/dist/modules/kb-fs/branch-name.js +76 -0
  103. package/dist/modules/kb-fs/branch-name.js.map +1 -0
  104. package/dist/modules/kb-fs/clone-config.d.ts +61 -0
  105. package/dist/modules/kb-fs/clone-config.d.ts.map +1 -0
  106. package/dist/modules/kb-fs/clone-config.js +69 -0
  107. package/dist/modules/kb-fs/clone-config.js.map +1 -0
  108. package/dist/modules/kb-fs/file-change-notifier.d.ts +38 -0
  109. package/dist/modules/kb-fs/file-change-notifier.d.ts.map +1 -0
  110. package/dist/modules/kb-fs/file-change-notifier.js +22 -0
  111. package/dist/modules/kb-fs/file-change-notifier.js.map +1 -0
  112. package/dist/modules/kb-fs/locking-filesystem.d.ts +137 -0
  113. package/dist/modules/kb-fs/locking-filesystem.d.ts.map +1 -0
  114. package/dist/modules/kb-fs/locking-filesystem.js +553 -0
  115. package/dist/modules/kb-fs/locking-filesystem.js.map +1 -0
  116. package/dist/modules/kb-fs/mutex.d.ts +11 -0
  117. package/dist/modules/kb-fs/mutex.d.ts.map +1 -0
  118. package/dist/modules/kb-fs/mutex.js +23 -0
  119. package/dist/modules/kb-fs/mutex.js.map +1 -0
  120. package/dist/modules/kb-fs/read-only-filesystem.d.ts +24 -0
  121. package/dist/modules/kb-fs/read-only-filesystem.d.ts.map +1 -0
  122. package/dist/modules/kb-fs/read-only-filesystem.js +39 -0
  123. package/dist/modules/kb-fs/read-only-filesystem.js.map +1 -0
  124. package/dist/modules/plugins/join-proposals.d.ts +1 -1
  125. package/dist/modules/plugins/join-proposals.d.ts.map +1 -1
  126. package/dist/modules/plugins/join-proposals.js +1 -1
  127. package/dist/modules/plugins/join-proposals.js.map +1 -1
  128. package/dist/modules/plugins/join-requests.service.d.ts.map +1 -1
  129. package/dist/modules/plugins/join-requests.service.js +1 -1
  130. package/dist/modules/plugins/join-requests.service.js.map +1 -1
  131. package/dist/modules/plugins/plugin-provision.service.js +3 -3
  132. package/dist/modules/plugins/plugin-provision.service.js.map +1 -1
  133. package/dist/modules/plugins/plugins.routes.js +2 -2
  134. package/dist/modules/plugins/plugins.routes.js.map +1 -1
  135. package/dist/modules/plugins/plugins.service.js +1 -1
  136. package/dist/modules/plugins/plugins.service.js.map +1 -1
  137. package/dist/modules/secrets-vault/secrets-vault.routes.js +1 -1
  138. package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -1
  139. package/dist/modules/skills/pending-skills.service.d.ts.map +1 -1
  140. package/dist/modules/skills/pending-skills.service.js +1 -1
  141. package/dist/modules/skills/pending-skills.service.js.map +1 -1
  142. package/dist/modules/skills/skills.service.js +1 -1
  143. package/dist/modules/skills/skills.service.js.map +1 -1
  144. package/dist/modules/tool-helpers/tool-context.d.ts +2 -2
  145. package/dist/modules/tool-helpers/tool-context.d.ts.map +1 -1
  146. package/dist/modules/tool-helpers/tool-context.js +4 -4
  147. package/dist/modules/tool-helpers/tool-context.js.map +1 -1
  148. package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts +1 -1
  149. package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts.map +1 -1
  150. package/dist/modules/tool-manuals/mcp-server-edit.service.js +1 -1
  151. package/dist/modules/tool-manuals/mcp-server-edit.service.js.map +1 -1
  152. package/dist/modules/tool-manuals/tool-manuals.routes.d.ts +1 -1
  153. package/dist/modules/tool-manuals/tool-manuals.routes.d.ts.map +1 -1
  154. package/dist/modules/tool-manuals/tool-manuals.routes.js +1 -1
  155. package/dist/modules/tool-manuals/tool-manuals.routes.js.map +1 -1
  156. package/dist/modules/tool-manuals/tool-manuals.service.js +1 -1
  157. package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
  158. package/dist/modules/tool-manuals/tool-manuals.tools.js +1 -1
  159. package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
  160. package/dist/modules/workflow/agent-tools/workflow.tools.js +1 -1
  161. package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
  162. package/dist/modules/workflow/file-lock.service.js +1 -1
  163. package/dist/modules/workflow/file-lock.service.js.map +1 -1
  164. package/dist/modules/workflow/git/git.service.d.ts +2 -2
  165. package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
  166. package/dist/modules/workflow/git/git.service.js +5 -5
  167. package/dist/modules/workflow/git/git.service.js.map +1 -1
  168. package/dist/modules/workflow/git/pull-request.service.js +1 -1
  169. package/dist/modules/workflow/git/pull-request.service.js.map +1 -1
  170. package/dist/modules/workflow/review-workflow/review-workflow.service.js +2 -2
  171. package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -1
  172. package/dist/modules/workflow/session-ontology.service.js +1 -1
  173. package/dist/modules/workflow/session-ontology.service.js.map +1 -1
  174. package/dist/modules/workflow/workflow.routes.js +2 -2
  175. package/dist/modules/workflow/workflow.routes.js.map +1 -1
  176. package/dist/modules/workflow/workflow.service.d.ts +1 -1
  177. package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
  178. package/dist/modules/workflow/workflow.service.js +4 -4
  179. package/dist/modules/workflow/workflow.service.js.map +1 -1
  180. package/dist/modules/workspace/file-readers/doc-extract.service.d.ts +71 -0
  181. package/dist/modules/workspace/file-readers/doc-extract.service.d.ts.map +1 -0
  182. package/dist/modules/workspace/file-readers/doc-extract.service.js +90 -0
  183. package/dist/modules/workspace/file-readers/doc-extract.service.js.map +1 -0
  184. package/dist/modules/workspace/file-readers/doc-extract.types.d.ts +55 -0
  185. package/dist/modules/workspace/file-readers/doc-extract.types.d.ts.map +1 -0
  186. package/dist/modules/workspace/file-readers/doc-extract.types.js +34 -0
  187. package/dist/modules/workspace/file-readers/doc-extract.types.js.map +1 -0
  188. package/dist/modules/workspace/file-readers/document-reader.d.ts +32 -0
  189. package/dist/modules/workspace/file-readers/document-reader.d.ts.map +1 -0
  190. package/dist/modules/workspace/file-readers/document-reader.js +59 -0
  191. package/dist/modules/workspace/file-readers/document-reader.js.map +1 -0
  192. package/dist/modules/workspace/file-readers/email-reader.d.ts +15 -0
  193. package/dist/modules/workspace/file-readers/email-reader.d.ts.map +1 -0
  194. package/dist/modules/workspace/file-readers/email-reader.js +19 -0
  195. package/dist/modules/workspace/file-readers/email-reader.js.map +1 -0
  196. package/dist/modules/workspace/file-readers/email-text.d.ts +51 -0
  197. package/dist/modules/workspace/file-readers/email-text.d.ts.map +1 -0
  198. package/dist/modules/workspace/file-readers/email-text.js +151 -0
  199. package/dist/modules/workspace/file-readers/email-text.js.map +1 -0
  200. package/dist/modules/workspace/file-readers/extract-docx.d.ts +13 -0
  201. package/dist/modules/workspace/file-readers/extract-docx.d.ts.map +1 -0
  202. package/dist/modules/workspace/file-readers/extract-docx.js +67 -0
  203. package/dist/modules/workspace/file-readers/extract-docx.js.map +1 -0
  204. package/dist/modules/workspace/file-readers/extract-eml.d.ts +18 -0
  205. package/dist/modules/workspace/file-readers/extract-eml.d.ts.map +1 -0
  206. package/dist/modules/workspace/file-readers/extract-eml.js +87 -0
  207. package/dist/modules/workspace/file-readers/extract-eml.js.map +1 -0
  208. package/dist/modules/workspace/file-readers/extract-msg.d.ts +17 -0
  209. package/dist/modules/workspace/file-readers/extract-msg.d.ts.map +1 -0
  210. package/dist/modules/workspace/file-readers/extract-msg.js +121 -0
  211. package/dist/modules/workspace/file-readers/extract-msg.js.map +1 -0
  212. package/dist/modules/workspace/file-readers/extract-odp.d.ts +13 -0
  213. package/dist/modules/workspace/file-readers/extract-odp.d.ts.map +1 -0
  214. package/dist/modules/workspace/file-readers/extract-odp.js +60 -0
  215. package/dist/modules/workspace/file-readers/extract-odp.js.map +1 -0
  216. package/dist/modules/workspace/file-readers/extract-ods.d.ts +10 -0
  217. package/dist/modules/workspace/file-readers/extract-ods.d.ts.map +1 -0
  218. package/dist/modules/workspace/file-readers/extract-ods.js +173 -0
  219. package/dist/modules/workspace/file-readers/extract-ods.js.map +1 -0
  220. package/dist/modules/workspace/file-readers/extract-odt.d.ts +17 -0
  221. package/dist/modules/workspace/file-readers/extract-odt.d.ts.map +1 -0
  222. package/dist/modules/workspace/file-readers/extract-odt.js +45 -0
  223. package/dist/modules/workspace/file-readers/extract-odt.js.map +1 -0
  224. package/dist/modules/workspace/file-readers/extract-pdf.d.ts +3 -0
  225. package/dist/modules/workspace/file-readers/extract-pdf.d.ts.map +1 -0
  226. package/dist/modules/workspace/file-readers/extract-pdf.js +176 -0
  227. package/dist/modules/workspace/file-readers/extract-pdf.js.map +1 -0
  228. package/dist/modules/workspace/file-readers/extract-pptx.d.ts +37 -0
  229. package/dist/modules/workspace/file-readers/extract-pptx.d.ts.map +1 -0
  230. package/dist/modules/workspace/file-readers/extract-pptx.js +288 -0
  231. package/dist/modules/workspace/file-readers/extract-pptx.js.map +1 -0
  232. package/dist/modules/workspace/file-readers/extract-xlsx.d.ts +10 -0
  233. package/dist/modules/workspace/file-readers/extract-xlsx.d.ts.map +1 -0
  234. package/dist/modules/workspace/file-readers/extract-xlsx.js +98 -0
  235. package/dist/modules/workspace/file-readers/extract-xlsx.js.map +1 -0
  236. package/dist/modules/workspace/file-readers/extraction-cache.d.ts +61 -0
  237. package/dist/modules/workspace/file-readers/extraction-cache.d.ts.map +1 -0
  238. package/dist/modules/workspace/file-readers/extraction-cache.js +135 -0
  239. package/dist/modules/workspace/file-readers/extraction-cache.js.map +1 -0
  240. package/dist/modules/workspace/file-readers/file-reader.d.ts +76 -0
  241. package/dist/modules/workspace/file-readers/file-reader.d.ts.map +1 -0
  242. package/dist/modules/workspace/file-readers/file-reader.js +55 -0
  243. package/dist/modules/workspace/file-readers/file-reader.js.map +1 -0
  244. package/dist/modules/workspace/file-readers/file-reader.registry.d.ts +13 -0
  245. package/dist/modules/workspace/file-readers/file-reader.registry.d.ts.map +1 -0
  246. package/dist/modules/workspace/file-readers/file-reader.registry.js +41 -0
  247. package/dist/modules/workspace/file-readers/file-reader.registry.js.map +1 -0
  248. package/dist/modules/workspace/file-readers/image-read.d.ts +35 -0
  249. package/dist/modules/workspace/file-readers/image-read.d.ts.map +1 -0
  250. package/dist/modules/workspace/file-readers/image-read.js +108 -0
  251. package/dist/modules/workspace/file-readers/image-read.js.map +1 -0
  252. package/dist/modules/workspace/file-readers/image-reader.d.ts +19 -0
  253. package/dist/modules/workspace/file-readers/image-reader.d.ts.map +1 -0
  254. package/dist/modules/workspace/file-readers/image-reader.js +30 -0
  255. package/dist/modules/workspace/file-readers/image-reader.js.map +1 -0
  256. package/dist/modules/workspace/file-readers/odf-text.d.ts +26 -0
  257. package/dist/modules/workspace/file-readers/odf-text.d.ts.map +1 -0
  258. package/dist/modules/workspace/file-readers/odf-text.js +116 -0
  259. package/dist/modules/workspace/file-readers/odf-text.js.map +1 -0
  260. package/dist/modules/workspace/file-readers/ooxml-text.d.ts +172 -0
  261. package/dist/modules/workspace/file-readers/ooxml-text.d.ts.map +1 -0
  262. package/dist/modules/workspace/file-readers/ooxml-text.js +439 -0
  263. package/dist/modules/workspace/file-readers/ooxml-text.js.map +1 -0
  264. package/dist/modules/workspace/file-readers/text-reader.d.ts +47 -0
  265. package/dist/modules/workspace/file-readers/text-reader.d.ts.map +1 -0
  266. package/dist/modules/workspace/file-readers/text-reader.js +117 -0
  267. package/dist/modules/workspace/file-readers/text-reader.js.map +1 -0
  268. package/dist/modules/workspace/startup/kb-startup-runner.js +1 -1
  269. package/dist/modules/workspace/startup/kb-startup-runner.js.map +1 -1
  270. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js +1 -1
  271. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js.map +1 -1
  272. package/dist/modules/workspace/startup/steps/roles-yaml.step.d.ts +1 -1
  273. package/dist/modules/workspace/startup/steps/roles-yaml.step.js +2 -2
  274. package/dist/modules/workspace/startup/steps/roles-yaml.step.js.map +1 -1
  275. package/dist/modules/workspace/startup/steps/seed-tree.js +1 -1
  276. package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
  277. package/dist/modules/workspace/workspace.routes.d.ts +1 -1
  278. package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
  279. package/dist/modules/workspace/workspace.routes.js +5 -4
  280. package/dist/modules/workspace/workspace.routes.js.map +1 -1
  281. package/dist/modules/workspace/workspace.service.d.ts +2 -9
  282. package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
  283. package/dist/modules/workspace/workspace.service.js +14 -15
  284. package/dist/modules/workspace/workspace.service.js.map +1 -1
  285. package/dist/modules/workspace/workspace.tools.d.ts +2 -1
  286. package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
  287. package/dist/modules/workspace/workspace.tools.js +161 -18
  288. package/dist/modules/workspace/workspace.tools.js.map +1 -1
  289. package/dist/shared/domain-errors.d.ts +202 -0
  290. package/dist/shared/domain-errors.d.ts.map +1 -0
  291. package/dist/shared/domain-errors.js +303 -0
  292. package/dist/shared/domain-errors.js.map +1 -0
  293. package/dist/shared/workspace-id.d.ts +27 -0
  294. package/dist/shared/workspace-id.d.ts.map +1 -0
  295. package/dist/shared/workspace-id.js +36 -0
  296. package/dist/shared/workspace-id.js.map +1 -0
  297. package/package.json +9 -4
  298. package/src/core/create-core-server.ts +1 -1
  299. package/src/core/create-core-services.ts +8 -2
  300. package/src/core-config.ts +9 -0
  301. package/src/modules/access/__tests__/access-control.service.test.ts +1 -1
  302. package/src/modules/access/__tests__/access-groups.test.ts +6 -5
  303. package/src/modules/access/__tests__/access-md-format.test.ts +3 -3
  304. package/src/modules/access/__tests__/access-mutation.service.test.ts +1 -1
  305. package/src/modules/access/__tests__/admin-locked-commit.test.ts +1 -1
  306. package/src/modules/access/__tests__/admin-route-helpers.test.ts +1 -1
  307. package/src/modules/access/__tests__/roles-admin.service.test.ts +2 -2
  308. package/src/modules/access/__tests__/roles-edit.test.ts +1 -1
  309. package/src/modules/access/__tests__/synced-groups-committer.test.ts +1 -1
  310. package/src/modules/access/__tests__/synced-groups-writer.test.ts +1 -1
  311. package/src/modules/access/access-control.service.ts +23 -768
  312. package/src/modules/access/access-declarations.ts +2 -2
  313. package/src/modules/access/access-mutation.service.ts +3 -3
  314. package/src/modules/access/access.routes.ts +5 -5
  315. package/src/modules/access/admin-locked-commit.ts +2 -2
  316. package/src/modules/access/admin-route-helpers.ts +1 -1
  317. package/src/modules/access/capability-registry.ts +1 -1
  318. package/src/modules/access/creator-access.ts +9 -72
  319. package/src/modules/access/groups-admin.routes.ts +1 -1
  320. package/src/modules/access/groups-admin.service.ts +6 -7
  321. package/src/modules/access/groups-edit.ts +2 -2
  322. package/src/modules/access/reference-scan.ts +1 -1
  323. package/src/modules/access/roles-admin.service.ts +8 -8
  324. package/src/modules/access/roles-edit.ts +1 -1
  325. package/src/modules/access/synced-groups-committer.ts +4 -4
  326. package/src/modules/access/synced-groups-writer.ts +2 -2
  327. package/src/modules/access-model/__tests__/access-grammar.test.ts +31 -0
  328. package/src/modules/{access → access-model}/__tests__/access-splice.test.ts +268 -268
  329. package/src/modules/{access → access-model}/access-errors.ts +1 -1
  330. package/src/modules/access-model/access-grammar.ts +778 -0
  331. package/src/modules/{access → access-model}/access-splice.ts +1 -1
  332. package/src/modules/access-model/creator.ts +79 -0
  333. package/src/modules/{access → access-model}/group-files.ts +1 -1
  334. package/src/modules/{access → access-model}/render-roles-yaml.ts +1 -1
  335. package/src/modules/{access → access-model}/roles-yaml-guard.ts +2 -2
  336. package/src/modules/admin/admin-access.service.ts +2 -1
  337. package/src/modules/code-mode/__tests__/code-mode.tool.test.ts +30 -0
  338. package/src/modules/code-mode/code-mode.tool.ts +7 -1
  339. package/src/modules/diff/__tests__/diff.routes.rejectPathsLocked.test.ts +2 -2
  340. package/src/modules/diff/__tests__/diff.service.seed-atomicity.test.ts +3 -2
  341. package/src/modules/diff/__tests__/diff.service.test.ts +3 -2
  342. package/src/modules/diff/diff.routes.ts +4 -4
  343. package/src/modules/diff/diff.service.ts +1 -1
  344. package/src/modules/{workflow/git → kb-fs}/__tests__/branch-name.test.ts +1 -1
  345. package/src/modules/{workflow → kb-fs}/__tests__/locking-filesystem.test.ts +1 -1
  346. package/src/modules/{workflow/git → kb-fs}/branch-name.ts +1 -1
  347. package/src/modules/{workflow → kb-fs}/locking-filesystem.ts +2 -2
  348. package/src/modules/plugins/__tests__/join-requests.service.test.ts +2 -4
  349. package/src/modules/plugins/__tests__/plugin-index.service.test.ts +1 -1
  350. package/src/modules/plugins/__tests__/plugins.routes.test.ts +1 -1
  351. package/src/modules/plugins/join-proposals.ts +1 -1
  352. package/src/modules/plugins/join-requests.service.ts +2 -1
  353. package/src/modules/plugins/plugin-provision.service.ts +3 -3
  354. package/src/modules/plugins/plugins.routes.ts +2 -2
  355. package/src/modules/plugins/plugins.service.ts +1 -1
  356. package/src/modules/secrets-vault/secrets-vault.routes.ts +1 -1
  357. package/src/modules/skills/__tests__/skills.service.test.ts +1 -1
  358. package/src/modules/skills/pending-skills.service.ts +2 -1
  359. package/src/modules/skills/skills.service.ts +1 -1
  360. package/src/modules/tool-helpers/__tests__/phase4-tools.test.ts +2 -1
  361. package/src/modules/tool-helpers/tool-context.ts +6 -5
  362. package/src/modules/tool-manuals/__tests__/mcp-server-edit.service.test.ts +1 -1
  363. package/src/modules/tool-manuals/__tests__/tool-manuals.archive.route.test.ts +1 -1
  364. package/src/modules/tool-manuals/__tests__/tool-manuals.detail.route.test.ts +1 -1
  365. package/src/modules/tool-manuals/__tests__/tool-manuals.mcp-oauth.test.ts +1 -1
  366. package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +1 -1
  367. package/src/modules/tool-manuals/mcp-server-edit.service.ts +2 -1
  368. package/src/modules/tool-manuals/tool-manuals.routes.ts +2 -1
  369. package/src/modules/tool-manuals/tool-manuals.service.ts +1 -1
  370. package/src/modules/tool-manuals/tool-manuals.tools.ts +1 -1
  371. package/src/modules/workflow/__tests__/preserve-roles-yaml.test.ts +1 -1
  372. package/src/modules/workflow/__tests__/workflow.service.commitFileWhileLocked.test.ts +1 -1
  373. package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +1 -1
  374. package/src/modules/workflow/__tests__/workflow.service.releaseLock.test.ts +1 -1
  375. package/src/modules/workflow/agent-tools/workflow.tools.ts +1 -1
  376. package/src/modules/workflow/file-lock.service.ts +1 -1
  377. package/src/modules/workflow/git/__tests__/git.service.accessGating.test.ts +1 -1
  378. package/src/modules/workflow/git/__tests__/git.service.deleteBranch.test.ts +1 -1
  379. package/src/modules/workflow/git/__tests__/git.service.diffFileAtCommit.test.ts +1 -1
  380. package/src/modules/workflow/git/__tests__/git.service.diffFileBetweenBranches.test.ts +1 -1
  381. package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +1 -1
  382. package/src/modules/workflow/git/git.service.ts +5 -5
  383. package/src/modules/workflow/git/pull-request.service.ts +1 -1
  384. package/src/modules/workflow/review-workflow/__tests__/cancel-pr.test.ts +1 -1
  385. package/src/modules/workflow/review-workflow/review-workflow.service.ts +2 -2
  386. package/src/modules/workflow/session-ontology.service.ts +1 -1
  387. package/src/modules/workflow/workflow.routes.ts +2 -2
  388. package/src/modules/workflow/workflow.service.ts +5 -5
  389. package/src/modules/workspace/__tests__/workspace.routes.create-grant.test.ts +1 -1
  390. package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +1 -1
  391. package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +2 -2
  392. package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +1 -1
  393. package/src/modules/workspace/__tests__/workspace.service.read-filter.test.ts +2 -5
  394. package/src/modules/workspace/__tests__/workspace.service.test.ts +13 -1
  395. package/src/modules/workspace/__tests__/workspace.tools.test.ts +501 -3
  396. package/src/modules/workspace/file-readers/__tests__/doc-extract.test.ts +1658 -0
  397. package/src/modules/workspace/file-readers/__tests__/email-extract.test.ts +485 -0
  398. package/src/modules/workspace/file-readers/__tests__/file-reader.registry.test.ts +97 -0
  399. package/src/modules/workspace/file-readers/__tests__/image-read.test.ts +100 -0
  400. package/src/modules/workspace/file-readers/doc-extract.service.ts +104 -0
  401. package/src/modules/workspace/file-readers/doc-extract.types.ts +63 -0
  402. package/src/modules/workspace/file-readers/document-reader.ts +64 -0
  403. package/src/modules/workspace/file-readers/email-reader.ts +21 -0
  404. package/src/modules/workspace/file-readers/email-text.ts +193 -0
  405. package/src/modules/workspace/file-readers/extract-docx.ts +67 -0
  406. package/src/modules/workspace/file-readers/extract-eml.ts +92 -0
  407. package/src/modules/workspace/file-readers/extract-msg.ts +134 -0
  408. package/src/modules/workspace/file-readers/extract-odp.ts +63 -0
  409. package/src/modules/workspace/file-readers/extract-ods.ts +182 -0
  410. package/src/modules/workspace/file-readers/extract-odt.ts +48 -0
  411. package/src/modules/workspace/file-readers/extract-pdf.ts +178 -0
  412. package/src/modules/workspace/file-readers/extract-pptx.ts +302 -0
  413. package/src/modules/workspace/file-readers/extract-xlsx.ts +96 -0
  414. package/src/modules/workspace/file-readers/extraction-cache.ts +142 -0
  415. package/src/modules/workspace/file-readers/file-reader.registry.ts +45 -0
  416. package/src/modules/workspace/file-readers/file-reader.ts +104 -0
  417. package/src/modules/workspace/file-readers/image-read.ts +122 -0
  418. package/src/modules/workspace/file-readers/image-reader.ts +39 -0
  419. package/src/modules/workspace/file-readers/odf-text.ts +123 -0
  420. package/src/modules/workspace/file-readers/ooxml-text.ts +477 -0
  421. package/src/modules/workspace/file-readers/text-reader.ts +131 -0
  422. package/src/modules/workspace/startup/kb-startup-runner.ts +1 -1
  423. package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +1 -1
  424. package/src/modules/workspace/startup/steps/groups-to-plugins.step.ts +1 -1
  425. package/src/modules/workspace/startup/steps/roles-yaml.step.ts +2 -2
  426. package/src/modules/workspace/startup/steps/seed-tree.ts +1 -1
  427. package/src/modules/workspace/workspace.routes.ts +6 -5
  428. package/src/modules/workspace/workspace.service.ts +14 -18
  429. package/src/modules/workspace/workspace.tools.ts +177 -15
  430. package/src/shared/__tests__/join-request.test.ts +1 -1
  431. package/src/shared/__tests__/workspace-id.test.ts +17 -0
  432. package/src/{modules/workflow/workflow.errors.ts → shared/domain-errors.ts} +6 -1
  433. package/src/shared/workspace-id.ts +36 -0
  434. /package/src/modules/{access → access-model}/__tests__/kb-read-filter.test.ts +0 -0
  435. /package/src/modules/{access → access-model}/__tests__/roles-yaml-guard.test.ts +0 -0
  436. /package/src/modules/{access → access-model}/kb-read-filter.ts +0 -0
  437. /package/src/modules/{workflow/git → kb-fs}/__tests__/clone-config.test.ts +0 -0
  438. /package/src/modules/{workflow → kb-fs}/__tests__/file-change-notifier.test.ts +0 -0
  439. /package/src/modules/{workflow/git → kb-fs}/__tests__/mutex.test.ts +0 -0
  440. /package/src/modules/{workflow/git → kb-fs}/clone-config.ts +0 -0
  441. /package/src/modules/{workflow → kb-fs}/file-change-notifier.ts +0 -0
  442. /package/src/modules/{workflow/git → kb-fs}/mutex.ts +0 -0
  443. /package/src/modules/{workflow → kb-fs}/read-only-filesystem.ts +0 -0
@@ -0,0 +1,104 @@
1
+ /**
2
+ * The FileReader contract: ONE interface every read-path consumer goes
3
+ * through, with per-extension implementations picked by a single registry
4
+ * lookup — the same shape as the frontend's renderer registry.
5
+ *
6
+ * Adding a file format is ONE new reader entry in `createFileReaderRegistry`
7
+ * (file-reader.registry.ts). The three consumers — `read_file`, `grep` and the
8
+ * write-refusal in workspace.tools.ts — dispatch through `readerFor` and never
9
+ * branch on extensions themselves.
10
+ */
11
+ import { fileExtension } from './doc-extract.types.js';
12
+
13
+ /**
14
+ * What reading a file produces, before the tool layer shapes it for MCP:
15
+ * text (the honest `[extracted text of …]` marker is ALREADY prepended for
16
+ * extractions, so line numbers match between read_file and grep), an image
17
+ * payload (the tool layer wraps it in the `McpImageResult` sentinel), or a
18
+ * refusal — legacy formats, oversized images, unreadable binary, corrupt
19
+ * documents — whose message is returned as the file's text content.
20
+ */
21
+ export type ReadResult =
22
+ | { kind: 'text'; text: string }
23
+ | { kind: 'image'; data: string; mimeType: string; note: string }
24
+ | { kind: 'refusal'; message: string };
25
+
26
+ /**
27
+ * `path` as interpolated into a ONE-LINE notice or refusal: CR/LF are shown
28
+ * as escapes rather than obeyed (the same rule as `extractionMarker`), so a
29
+ * filename cannot forge extra output lines.
30
+ */
31
+ export function displayPath(path: string): string {
32
+ return path.replace(/[\r\n]/g, (c) => (c === '\r' ? '\\r' : '\\n'));
33
+ }
34
+
35
+ /**
36
+ * Any text interpolated into a ONE-LINE notice — a path, or an extractor's
37
+ * own failure message, which quotes bytes from the file and can therefore
38
+ * carry CR/LF of the document's choosing.
39
+ */
40
+ export const oneLine = displayPath;
41
+
42
+ /** A per-format file reader. Register implementations in `createFileReaderRegistry`. */
43
+ export interface FileReader {
44
+ /** The extensions this reader owns — lowercase, with the dot. Empty for the default (fallback) reader. */
45
+ readonly extensions: readonly string[];
46
+ /** Read `bytes` (read at `path`) into what the read tools return. Must not throw for bad file content. */
47
+ read(bytes: Buffer, path: string): Promise<ReadResult>;
48
+ /**
49
+ * Text `grep` may search, or null when there is nothing (cheaply) searchable.
50
+ * Default readers: the decoded text content (TextReader; null on NUL bytes) /
51
+ * the CACHED extraction (DocumentReader — cold extraction is grep's call,
52
+ * under its per-walk budget) / absent = never greppable (ImageReader).
53
+ */
54
+ greppableText?(bytes: Buffer, path: string): Promise<string | null>;
55
+ /** May the agent TEXT-editing tools (write_file/write_files/edit_file) touch this file? */
56
+ readonly textEditable: boolean;
57
+ /**
58
+ * Format-specific copy for the write-refusal thrown when `textEditable` is
59
+ * false (see `assertNotDocumentEdit` in workspace.tools.ts). Absent = the
60
+ * generic extracted-text/round-trip explanation.
61
+ */
62
+ editRefusal?(path: string): string;
63
+ /**
64
+ * Refusal for OVERWRITING what this file ALREADY holds, or null to allow.
65
+ * Consulted only when `textEditable` is true: that answer covers the
66
+ * FORMAT, this one covers the CONTENT. The fallback reader needs it because
67
+ * an extensionless file may hold anything — `read_file` refuses binary
68
+ * content, and a write gate that did not ask would let an agent overwrite
69
+ * bytes it was never allowed to see.
70
+ */
71
+ editRefusalForExisting?(bytes: Buffer, path: string): string | null;
72
+ }
73
+
74
+ /**
75
+ * Extension → reader, with a default fallback (the text reader). Built once
76
+ * per deployment by `createFileReaderRegistry`; `readerFor` is the ONE lookup
77
+ * every consumer routes through.
78
+ */
79
+ export class FileReaderRegistry {
80
+ private readonly byExtension = new Map<string, FileReader>();
81
+
82
+ constructor(
83
+ readers: readonly FileReader[],
84
+ private readonly fallback: FileReader,
85
+ ) {
86
+ for (const reader of readers) {
87
+ for (const raw of reader.extensions) {
88
+ // Lookups arrive lowercased (`fileExtension`), so a key stored with any
89
+ // other casing could never match and its reader would silently fall back
90
+ // to the default — routing a .PDF-registered reader nowhere.
91
+ const ext = raw.toLowerCase();
92
+ // Duplicate claims fail LOUDLY: were the later registration to win
93
+ // silently, adding a format could disable an existing reader.
94
+ if (this.byExtension.has(ext)) throw new Error(`two file readers claim the extension "${ext}"`);
95
+ this.byExtension.set(ext, reader);
96
+ }
97
+ }
98
+ }
99
+
100
+ /** The reader owning `path`'s extension (lowercased by `fileExtension`), or the fallback. */
101
+ readerFor(path: string): FileReader {
102
+ return this.byExtension.get(fileExtension(path)) ?? this.fallback;
103
+ }
104
+ }
@@ -0,0 +1,122 @@
1
+ /**
2
+ * `read_file` image support: which files come back as a native MCP image
3
+ * content block instead of text, the size cap that keeps the encoded payload
4
+ * under what Claude-family clients accept, and the self-describing note that
5
+ * rides beside the image so the transcript still says what was read.
6
+ *
7
+ * Deliberately dependency-free: dimensions are parsed straight from the
8
+ * container headers (PNG/GIF/JPEG) rather than through a native image library.
9
+ * No downscaling in this increment — `sharp` is a heavy native dependency, so
10
+ * an oversized image gets an honest refusal telling the caller to downscale
11
+ * locally or upload a smaller export.
12
+ */
13
+ import { fileExtension } from './doc-extract.types.js';
14
+
15
+ /** The image types `read_file` returns as a native MCP image content block. `.svg` is TEXT and stays on the text path. */
16
+ const IMAGE_MIME_BY_EXT: Record<string, string> = {
17
+ '.png': 'image/png',
18
+ '.jpg': 'image/jpeg',
19
+ '.jpeg': 'image/jpeg',
20
+ // GIF passes through whole under the same cap; clients render the first frame.
21
+ '.gif': 'image/gif',
22
+ '.webp': 'image/webp',
23
+ };
24
+
25
+ /** The extensions the `ImageReader` registers for — exactly the map above. */
26
+ export const IMAGE_EXTENSIONS: readonly string[] = Object.keys(IMAGE_MIME_BY_EXT);
27
+
28
+ /** Is this a file `read_file` should return as an image? */
29
+ export function isImageFile(path: string): boolean {
30
+ return fileExtension(path) in IMAGE_MIME_BY_EXT;
31
+ }
32
+
33
+ /** The MCP mime type for an image path — call only after `isImageFile`. */
34
+ export function imageMimeType(path: string): string {
35
+ return IMAGE_MIME_BY_EXT[fileExtension(path)]!;
36
+ }
37
+
38
+ /**
39
+ * Cap on the RAW bytes of an image `read_file` will return.
40
+ *
41
+ * The arithmetic: Claude-family clients reject a base64 image payload around
42
+ * 5 MB encoded (5 * 1024 * 1024 = 5,242,880 chars). Base64 inflates by 4/3,
43
+ * so the raw ceiling for that limit is 5,242,880 * 3/4 = 3,932,160 bytes. We
44
+ * cap at 3.5 MiB raw = 3,670,016 bytes, which encodes to
45
+ * ceil(3,670,016 / 3) * 4 = 4,893,356 base64 chars (~4.67 MiB) — under the
46
+ * reject line with headroom for the JSON envelope the sentinel travels in.
47
+ */
48
+ export const IMAGE_MAX_RAW_BYTES = 3.5 * 1024 * 1024; // = 3,670,016
49
+
50
+ /** Width×height parsed from the container header, when the format makes that cheap. */
51
+ export interface ImageDimensions {
52
+ width: number;
53
+ height: number;
54
+ }
55
+
56
+ /**
57
+ * Best-effort dimensions straight from the header bytes: PNG (IHDR), GIF
58
+ * (logical screen descriptor), JPEG (SOFn scan). WebP is left dimensionless —
59
+ * its three sub-formats (VP8/VP8L/VP8X) each encode size differently, and the
60
+ * note is honest without it. Returns undefined on anything unexpected; never
61
+ * throws (a corrupt image must still be delivered or refused, not 500).
62
+ */
63
+ export function imageDimensions(bytes: Buffer): ImageDimensions | undefined {
64
+ try {
65
+ // PNG: 8-byte signature, then the IHDR chunk — width/height at 16/20 (BE).
66
+ if (bytes.length >= 24 && bytes.readUInt32BE(0) === 0x89504e47 && bytes.toString('latin1', 12, 16) === 'IHDR') {
67
+ return { width: bytes.readUInt32BE(16), height: bytes.readUInt32BE(20) };
68
+ }
69
+ // GIF: the FULL "GIF87a"/"GIF89a" signature, then the logical screen
70
+ // size at 6/8 (LE) — a bare "GIF" prefix is not a GIF header.
71
+ if (bytes.length >= 10) {
72
+ const sig = bytes.toString('latin1', 0, 6);
73
+ if (sig === 'GIF87a' || sig === 'GIF89a') {
74
+ return { width: bytes.readUInt16LE(6), height: bytes.readUInt16LE(8) };
75
+ }
76
+ }
77
+ // JPEG: walk the marker segments to the first SOFn frame header.
78
+ if (bytes.length >= 4 && bytes[0] === 0xff && bytes[1] === 0xd8) {
79
+ let i = 2;
80
+ while (i + 9 < bytes.length) {
81
+ if (bytes[i] !== 0xff) {
82
+ i += 1; // stray fill byte — resync
83
+ continue;
84
+ }
85
+ const marker = bytes[i + 1]!;
86
+ // Standalone markers (no length field): padding, restarts, SOI/EOI.
87
+ if (marker === 0xff) {
88
+ i += 1;
89
+ continue;
90
+ }
91
+ if (marker === 0x01 || (marker >= 0xd0 && marker <= 0xd9)) {
92
+ i += 2;
93
+ continue;
94
+ }
95
+ // SOFn (C0–CF except the non-frame C4/C8/CC): height at +5, width at +7.
96
+ if (marker >= 0xc0 && marker <= 0xcf && marker !== 0xc4 && marker !== 0xc8 && marker !== 0xcc) {
97
+ return { height: bytes.readUInt16BE(i + 5), width: bytes.readUInt16BE(i + 7) };
98
+ }
99
+ i += 2 + bytes.readUInt16BE(i + 2);
100
+ }
101
+ }
102
+ } catch {
103
+ // Truncated/corrupt header — the note simply omits dimensions.
104
+ }
105
+ return undefined;
106
+ }
107
+
108
+ /** The one-line note emitted as a text block beside the image, so the transcript names what it shows. */
109
+ export function imageNote(path: string, mime: string, sizeBytes: number, dims: ImageDimensions | undefined): string {
110
+ const dimsPart = dims ? `, ${dims.width}×${dims.height} px` : '';
111
+ return `[image: ${path} — ${mime}, ${sizeBytes} bytes${dimsPart}]`;
112
+ }
113
+
114
+ /** The honest refusal for an image over {@link IMAGE_MAX_RAW_BYTES} — returned as the file's text content. */
115
+ export function oversizedImageNotice(path: string, mime: string, sizeBytes: number): string {
116
+ return (
117
+ `[${path} is a ${mime} image of ${sizeBytes} bytes — too large to return over MCP. The cap is ` +
118
+ `${IMAGE_MAX_RAW_BYTES} bytes (3.5 MiB) of raw image data, because base64 encoding inflates it by 4/3 and ` +
119
+ 'Claude-family clients reject images near 5 MB encoded. Downscale the image locally or upload a smaller ' +
120
+ 'export, then read that file instead.]'
121
+ );
122
+ }
@@ -0,0 +1,39 @@
1
+ import type { FileReader, ReadResult } from './file-reader.js';
2
+ import {
3
+ IMAGE_EXTENSIONS,
4
+ IMAGE_MAX_RAW_BYTES,
5
+ imageDimensions,
6
+ imageMimeType,
7
+ imageNote,
8
+ oversizedImageNotice,
9
+ } from './image-read.js';
10
+
11
+ /**
12
+ * FileReader over the image types `read_file` returns as a native MCP image
13
+ * content block (`.svg` is text and stays on the text path). Within the raw
14
+ * cap the read yields the picture itself (base64 + mime + the self-describing
15
+ * note); over it, the honest downscale refusal — see image-read.ts for the
16
+ * cap arithmetic and header-parsing details.
17
+ *
18
+ * No `greppableText`: a picture is never text-searchable. And images stay
19
+ * `textEditable` — the write tools only refuse formats whose reads are lossy
20
+ * EXTRACTIONS (documents); an image read is the real bytes, and image writes
21
+ * were never gated.
22
+ */
23
+ export class ImageReader implements FileReader {
24
+ readonly extensions: readonly string[] = IMAGE_EXTENSIONS;
25
+ readonly textEditable = true;
26
+
27
+ async read(bytes: Buffer, path: string): Promise<ReadResult> {
28
+ const mime = imageMimeType(path);
29
+ if (bytes.length > IMAGE_MAX_RAW_BYTES) {
30
+ return { kind: 'refusal', message: oversizedImageNotice(path, mime, bytes.length) };
31
+ }
32
+ return {
33
+ kind: 'image',
34
+ data: bytes.toString('base64'),
35
+ mimeType: mime,
36
+ note: imageNote(path, mime, bytes.length, imageDimensions(bytes)),
37
+ };
38
+ }
39
+ }
@@ -0,0 +1,123 @@
1
+ import AdmZip from 'adm-zip';
2
+ import { Parser } from 'htmlparser2';
3
+ import { attrByLocalName, localElementBlocks, localName, zipEntryOversize } from './ooxml-text.js';
4
+
5
+ /**
6
+ * ODF (OpenDocument) text helpers shared by the odt/odp/ods extractors.
7
+ *
8
+ * Elements are matched by LOCAL name — `p`, not `text:p`. A prefix is the
9
+ * document's own choice, and naming `text:` literally meant an ODF file that
10
+ * defaulted the namespace, or bound it to any other prefix, extracted as
11
+ * empty. This module used to answer that by REWRITING every non-conventional
12
+ * prefix across the whole of `content.xml` before scanning it, a pass that had
13
+ * to be bounded against crafted alias lists and could still corrupt ordinary
14
+ * paragraph text that happened to look like an alias. Matching local names
15
+ * needs none of it.
16
+ */
17
+
18
+ /**
19
+ * The text of one ODF paragraph (`<text:p>` / `<text:h>` content). Character
20
+ * data is concatenated with NO separator — formatting runs (`<text:span>`)
21
+ * split words exactly like OOXML runs do, so ignoring the span tags joins them
22
+ * back. Three ODF whitespace elements are REAL characters and render as such:
23
+ *
24
+ * - `<text:tab/>` → a tab
25
+ * - `<text:line-break/>` → a newline
26
+ * - `<text:s text:c="N"/>` → N spaces (no `text:c` attribute = 1)
27
+ *
28
+ * Read through the parser, so entities decode, CDATA sections are the text
29
+ * they hold rather than markup, and a comment is not paragraph content.
30
+ */
31
+ /**
32
+ * Cap on the characters the whitespace ELEMENTS may add to ONE paragraph.
33
+ * Each `<text:s text:c="N"/>` is clamped on its own below, but nothing else
34
+ * bounds how many such elements a paragraph may hold — a content.xml well
35
+ * under the 50 MB part limit could still expand to gigabytes of spaces. The
36
+ * budget caps the SUM per paragraph; real layout whitespace is nowhere near it.
37
+ */
38
+ const MAX_PARAGRAPH_WHITESPACE_CHARS = 10_000;
39
+
40
+ export function odfParagraphText(paragraphXml: string): string {
41
+ let out = '';
42
+ let whitespaceBudget = MAX_PARAGRAPH_WHITESPACE_CHARS;
43
+ const parser = new Parser(
44
+ {
45
+ onopentag(name, attributes) {
46
+ const local = localName(name);
47
+ if (local === 'tab') {
48
+ if (whitespaceBudget > 0) {
49
+ out += '\t';
50
+ whitespaceBudget--;
51
+ }
52
+ } else if (local === 'line-break') {
53
+ if (whitespaceBudget > 0) {
54
+ out += '\n';
55
+ whitespaceBudget--;
56
+ }
57
+ } else if (local === 's') {
58
+ const raw = attrByLocalName(attributes, 'c');
59
+ const count = raw !== undefined ? parseInt(raw, 10) : 1;
60
+ // Bounded defensively — a corrupt attribute must not balloon the
61
+ // extraction — and again by the per-paragraph budget above.
62
+ const spaces = Math.min(Number.isFinite(count) ? Math.min(Math.max(count, 0), 1000) : 1, whitespaceBudget);
63
+ out += ' '.repeat(spaces);
64
+ whitespaceBudget -= spaces;
65
+ }
66
+ },
67
+ ontext(text) {
68
+ out += text;
69
+ },
70
+ },
71
+ { xmlMode: true, decodeEntities: true },
72
+ );
73
+ parser.write(paragraphXml);
74
+ parser.end();
75
+ return out;
76
+ }
77
+
78
+ /**
79
+ * The `<text:p>` / `<text:h>` paragraph bodies of an XML fragment, in DOCUMENT
80
+ * order (headings interleaved with paragraphs, as written). Self-closing
81
+ * elements (an empty paragraph) yield ''. `<text:page-number>` and friends do
82
+ * not count as paragraphs.
83
+ */
84
+ export function odfParagraphBlocks(xml: string): string[] {
85
+ return localElementBlocks(xml, ['p', 'h']).map((e) => e.body ?? '');
86
+ }
87
+
88
+
89
+ /** Non-empty paragraph texts of an ODF fragment — what odp slides/notes render. */
90
+ export function odfParagraphLines(xml: string): string[] {
91
+ const out: string[] = [];
92
+ for (const p of odfParagraphBlocks(xml)) {
93
+ const text = odfParagraphText(p);
94
+ if (text.trim() !== '') out.push(text);
95
+ }
96
+ return out;
97
+ }
98
+
99
+ /**
100
+ * `content.xml` of an ODF package or a typed could-not-parse failure message
101
+ * (`kind` names the extension for the message, e.g. '.odt').
102
+ *
103
+ * Bounded: the entry's DECLARED uncompressed size is checked against
104
+ * `MAX_DOC_PART_BYTES` (50 MB) before anything inflates, so a zip bomb is a
105
+ * typed refusal, never an allocation.
106
+ */
107
+ export function readOdfContentXml(
108
+ bytes: Buffer,
109
+ kind: '.odt' | '.odp' | '.ods',
110
+ ): { ok: true; xml: string } | { ok: false; message: string } {
111
+ try {
112
+ const zip = new AdmZip(bytes);
113
+ const entry = zip.getEntry('content.xml');
114
+ if (!entry) {
115
+ return { ok: false, message: `could not be parsed as a ${kind} (no content.xml inside the archive)` };
116
+ }
117
+ const oversize = zipEntryOversize(entry);
118
+ if (oversize) return { ok: false, message: `could not be extracted as a ${kind} (${oversize})` };
119
+ return { ok: true, xml: entry.getData().toString('utf8') };
120
+ } catch (err) {
121
+ return { ok: false, message: `could not be parsed as a ${kind} (${(err as Error).message})` };
122
+ }
123
+ }