@aopslab/domain-dm-docman 0.1.2

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 (528) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +6 -0
  3. package/README.md +26 -0
  4. package/dist/application/errors/AssetServiceError.d.ts +20 -0
  5. package/dist/application/errors/AssetServiceError.js +13 -0
  6. package/dist/application/errors/AssetVersionServiceError.d.ts +20 -0
  7. package/dist/application/errors/AssetVersionServiceError.js +13 -0
  8. package/dist/application/errors/DocumentGroupServiceError.d.ts +20 -0
  9. package/dist/application/errors/DocumentGroupServiceError.js +16 -0
  10. package/dist/application/errors/DocumentSectionLinkServiceError.d.ts +20 -0
  11. package/dist/application/errors/DocumentSectionLinkServiceError.js +16 -0
  12. package/dist/application/errors/DocumentServiceError.d.ts +20 -0
  13. package/dist/application/errors/DocumentServiceError.js +16 -0
  14. package/dist/application/errors/DocumentVersionServiceError.d.ts +20 -0
  15. package/dist/application/errors/DocumentVersionServiceError.js +16 -0
  16. package/dist/application/errors/EmbedServiceError.d.ts +20 -0
  17. package/dist/application/errors/EmbedServiceError.js +16 -0
  18. package/dist/application/errors/PageEmbedLinkServiceError.d.ts +20 -0
  19. package/dist/application/errors/PageEmbedLinkServiceError.js +16 -0
  20. package/dist/application/errors/PageServiceError.d.ts +20 -0
  21. package/dist/application/errors/PageServiceError.js +16 -0
  22. package/dist/application/errors/PageSnippetLinkServiceError.d.ts +20 -0
  23. package/dist/application/errors/PageSnippetLinkServiceError.js +16 -0
  24. package/dist/application/errors/PageVersionServiceError.d.ts +20 -0
  25. package/dist/application/errors/PageVersionServiceError.js +16 -0
  26. package/dist/application/errors/SectionPageLinkServiceError.d.ts +20 -0
  27. package/dist/application/errors/SectionPageLinkServiceError.js +16 -0
  28. package/dist/application/errors/SectionServiceError.d.ts +20 -0
  29. package/dist/application/errors/SectionServiceError.js +16 -0
  30. package/dist/application/errors/SnippetServiceError.d.ts +20 -0
  31. package/dist/application/errors/SnippetServiceError.js +16 -0
  32. package/dist/application/errors/index.d.ts +1 -0
  33. package/dist/application/errors/index.js +1 -0
  34. package/dist/application/factories/RepositoryFactoryAsset.d.ts +4 -0
  35. package/dist/application/factories/RepositoryFactoryAsset.js +7 -0
  36. package/dist/application/factories/RepositoryFactoryAssetVersion.d.ts +4 -0
  37. package/dist/application/factories/RepositoryFactoryAssetVersion.js +7 -0
  38. package/dist/application/factories/RepositoryFactoryDocument.d.ts +4 -0
  39. package/dist/application/factories/RepositoryFactoryDocument.js +7 -0
  40. package/dist/application/factories/RepositoryFactoryDocumentGroup.d.ts +4 -0
  41. package/dist/application/factories/RepositoryFactoryDocumentGroup.js +7 -0
  42. package/dist/application/factories/RepositoryFactoryDocumentIndexEntry.d.ts +4 -0
  43. package/dist/application/factories/RepositoryFactoryDocumentIndexEntry.js +7 -0
  44. package/dist/application/factories/RepositoryFactoryDocumentSectionLink.d.ts +4 -0
  45. package/dist/application/factories/RepositoryFactoryDocumentSectionLink.js +7 -0
  46. package/dist/application/factories/RepositoryFactoryDocumentVersion.d.ts +4 -0
  47. package/dist/application/factories/RepositoryFactoryDocumentVersion.js +7 -0
  48. package/dist/application/factories/RepositoryFactoryEmbed.d.ts +4 -0
  49. package/dist/application/factories/RepositoryFactoryEmbed.js +7 -0
  50. package/dist/application/factories/RepositoryFactoryPage.d.ts +4 -0
  51. package/dist/application/factories/RepositoryFactoryPage.js +7 -0
  52. package/dist/application/factories/RepositoryFactoryPageEmbedLink.d.ts +4 -0
  53. package/dist/application/factories/RepositoryFactoryPageEmbedLink.js +7 -0
  54. package/dist/application/factories/RepositoryFactoryPageSnippetLink.d.ts +4 -0
  55. package/dist/application/factories/RepositoryFactoryPageSnippetLink.js +7 -0
  56. package/dist/application/factories/RepositoryFactoryPageVersion.d.ts +4 -0
  57. package/dist/application/factories/RepositoryFactoryPageVersion.js +7 -0
  58. package/dist/application/factories/RepositoryFactorySection.d.ts +4 -0
  59. package/dist/application/factories/RepositoryFactorySection.js +7 -0
  60. package/dist/application/factories/RepositoryFactorySectionPageLink.d.ts +4 -0
  61. package/dist/application/factories/RepositoryFactorySectionPageLink.js +7 -0
  62. package/dist/application/factories/RepositoryFactorySnippet.d.ts +4 -0
  63. package/dist/application/factories/RepositoryFactorySnippet.js +7 -0
  64. package/dist/application/factories/ServiceAssetBuilder.d.ts +30 -0
  65. package/dist/application/factories/ServiceAssetBuilder.js +80 -0
  66. package/dist/application/factories/ServiceAssetFactory.d.ts +11 -0
  67. package/dist/application/factories/ServiceAssetFactory.js +15 -0
  68. package/dist/application/factories/ServiceAssetVersionBuilder.d.ts +30 -0
  69. package/dist/application/factories/ServiceAssetVersionBuilder.js +80 -0
  70. package/dist/application/factories/ServiceAssetVersionFactory.d.ts +11 -0
  71. package/dist/application/factories/ServiceAssetVersionFactory.js +15 -0
  72. package/dist/application/factories/ServiceDocumentBuilder.d.ts +30 -0
  73. package/dist/application/factories/ServiceDocumentBuilder.js +83 -0
  74. package/dist/application/factories/ServiceDocumentFactory.d.ts +11 -0
  75. package/dist/application/factories/ServiceDocumentFactory.js +15 -0
  76. package/dist/application/factories/ServiceDocumentGroupBuilder.d.ts +30 -0
  77. package/dist/application/factories/ServiceDocumentGroupBuilder.js +83 -0
  78. package/dist/application/factories/ServiceDocumentGroupFactory.d.ts +11 -0
  79. package/dist/application/factories/ServiceDocumentGroupFactory.js +15 -0
  80. package/dist/application/factories/ServiceDocumentSectionLinkBuilder.d.ts +30 -0
  81. package/dist/application/factories/ServiceDocumentSectionLinkBuilder.js +83 -0
  82. package/dist/application/factories/ServiceDocumentSectionLinkFactory.d.ts +11 -0
  83. package/dist/application/factories/ServiceDocumentSectionLinkFactory.js +15 -0
  84. package/dist/application/factories/ServiceDocumentVersionBuilder.d.ts +30 -0
  85. package/dist/application/factories/ServiceDocumentVersionBuilder.js +83 -0
  86. package/dist/application/factories/ServiceDocumentVersionFactory.d.ts +11 -0
  87. package/dist/application/factories/ServiceDocumentVersionFactory.js +15 -0
  88. package/dist/application/factories/ServiceEmbedBuilder.d.ts +30 -0
  89. package/dist/application/factories/ServiceEmbedBuilder.js +83 -0
  90. package/dist/application/factories/ServiceEmbedFactory.d.ts +11 -0
  91. package/dist/application/factories/ServiceEmbedFactory.js +15 -0
  92. package/dist/application/factories/ServicePageBuilder.d.ts +30 -0
  93. package/dist/application/factories/ServicePageBuilder.js +83 -0
  94. package/dist/application/factories/ServicePageEmbedLinkBuilder.d.ts +30 -0
  95. package/dist/application/factories/ServicePageEmbedLinkBuilder.js +83 -0
  96. package/dist/application/factories/ServicePageEmbedLinkFactory.d.ts +11 -0
  97. package/dist/application/factories/ServicePageEmbedLinkFactory.js +15 -0
  98. package/dist/application/factories/ServicePageFactory.d.ts +11 -0
  99. package/dist/application/factories/ServicePageFactory.js +15 -0
  100. package/dist/application/factories/ServicePageSnippetLinkBuilder.d.ts +30 -0
  101. package/dist/application/factories/ServicePageSnippetLinkBuilder.js +83 -0
  102. package/dist/application/factories/ServicePageSnippetLinkFactory.d.ts +11 -0
  103. package/dist/application/factories/ServicePageSnippetLinkFactory.js +15 -0
  104. package/dist/application/factories/ServicePageVersionBuilder.d.ts +34 -0
  105. package/dist/application/factories/ServicePageVersionBuilder.js +121 -0
  106. package/dist/application/factories/ServicePageVersionFactory.d.ts +11 -0
  107. package/dist/application/factories/ServicePageVersionFactory.js +15 -0
  108. package/dist/application/factories/ServiceSectionBuilder.d.ts +30 -0
  109. package/dist/application/factories/ServiceSectionBuilder.js +83 -0
  110. package/dist/application/factories/ServiceSectionFactory.d.ts +11 -0
  111. package/dist/application/factories/ServiceSectionFactory.js +15 -0
  112. package/dist/application/factories/ServiceSectionPageLinkBuilder.d.ts +30 -0
  113. package/dist/application/factories/ServiceSectionPageLinkBuilder.js +83 -0
  114. package/dist/application/factories/ServiceSectionPageLinkFactory.d.ts +11 -0
  115. package/dist/application/factories/ServiceSectionPageLinkFactory.js +15 -0
  116. package/dist/application/factories/ServiceSnippetBuilder.d.ts +30 -0
  117. package/dist/application/factories/ServiceSnippetBuilder.js +83 -0
  118. package/dist/application/factories/ServiceSnippetFactory.d.ts +11 -0
  119. package/dist/application/factories/ServiceSnippetFactory.js +15 -0
  120. package/dist/application/factories/drizzleDialect.d.ts +16 -0
  121. package/dist/application/factories/drizzleDialect.js +36 -0
  122. package/dist/application/factories/index.d.ts +43 -0
  123. package/dist/application/factories/index.js +44 -0
  124. package/dist/application/ports/inbound/IAssetServicePort.d.ts +14 -0
  125. package/dist/application/ports/inbound/IAssetServicePort.js +1 -0
  126. package/dist/application/ports/inbound/IAssetVersionServicePort.d.ts +14 -0
  127. package/dist/application/ports/inbound/IAssetVersionServicePort.js +1 -0
  128. package/dist/application/ports/inbound/IDocumentGroupServicePort.d.ts +14 -0
  129. package/dist/application/ports/inbound/IDocumentGroupServicePort.js +1 -0
  130. package/dist/application/ports/inbound/IDocumentSectionLinkServicePort.d.ts +16 -0
  131. package/dist/application/ports/inbound/IDocumentSectionLinkServicePort.js +1 -0
  132. package/dist/application/ports/inbound/IDocumentServicePort.d.ts +300 -0
  133. package/dist/application/ports/inbound/IDocumentServicePort.js +1 -0
  134. package/dist/application/ports/inbound/IDocumentVersionServicePort.d.ts +106 -0
  135. package/dist/application/ports/inbound/IDocumentVersionServicePort.js +1 -0
  136. package/dist/application/ports/inbound/IEmbedServicePort.d.ts +14 -0
  137. package/dist/application/ports/inbound/IEmbedServicePort.js +1 -0
  138. package/dist/application/ports/inbound/IPageEmbedLinkServicePort.d.ts +14 -0
  139. package/dist/application/ports/inbound/IPageEmbedLinkServicePort.js +1 -0
  140. package/dist/application/ports/inbound/IPageServicePort.d.ts +14 -0
  141. package/dist/application/ports/inbound/IPageServicePort.js +1 -0
  142. package/dist/application/ports/inbound/IPageSnippetLinkServicePort.d.ts +14 -0
  143. package/dist/application/ports/inbound/IPageSnippetLinkServicePort.js +1 -0
  144. package/dist/application/ports/inbound/IPageVersionServicePort.d.ts +14 -0
  145. package/dist/application/ports/inbound/IPageVersionServicePort.js +1 -0
  146. package/dist/application/ports/inbound/ISectionPageLinkServicePort.d.ts +14 -0
  147. package/dist/application/ports/inbound/ISectionPageLinkServicePort.js +1 -0
  148. package/dist/application/ports/inbound/ISectionServicePort.d.ts +14 -0
  149. package/dist/application/ports/inbound/ISectionServicePort.js +1 -0
  150. package/dist/application/ports/inbound/ISnippetServicePort.d.ts +14 -0
  151. package/dist/application/ports/inbound/ISnippetServicePort.js +1 -0
  152. package/dist/application/ports/inbound/index.d.ts +14 -0
  153. package/dist/application/ports/inbound/index.js +15 -0
  154. package/dist/application/ports/repository-ports/IRepositoryPortAsset.d.ts +5 -0
  155. package/dist/application/ports/repository-ports/IRepositoryPortAsset.js +1 -0
  156. package/dist/application/ports/repository-ports/IRepositoryPortAssetVersion.d.ts +5 -0
  157. package/dist/application/ports/repository-ports/IRepositoryPortAssetVersion.js +1 -0
  158. package/dist/application/ports/repository-ports/IRepositoryPortDocument.d.ts +10 -0
  159. package/dist/application/ports/repository-ports/IRepositoryPortDocument.js +1 -0
  160. package/dist/application/ports/repository-ports/IRepositoryPortDocumentGroup.d.ts +10 -0
  161. package/dist/application/ports/repository-ports/IRepositoryPortDocumentGroup.js +1 -0
  162. package/dist/application/ports/repository-ports/IRepositoryPortDocumentIndexEntry.d.ts +5 -0
  163. package/dist/application/ports/repository-ports/IRepositoryPortDocumentIndexEntry.js +1 -0
  164. package/dist/application/ports/repository-ports/IRepositoryPortDocumentSectionLink.d.ts +21 -0
  165. package/dist/application/ports/repository-ports/IRepositoryPortDocumentSectionLink.js +1 -0
  166. package/dist/application/ports/repository-ports/IRepositoryPortDocumentVersion.d.ts +10 -0
  167. package/dist/application/ports/repository-ports/IRepositoryPortDocumentVersion.js +1 -0
  168. package/dist/application/ports/repository-ports/IRepositoryPortEmbed.d.ts +10 -0
  169. package/dist/application/ports/repository-ports/IRepositoryPortEmbed.js +1 -0
  170. package/dist/application/ports/repository-ports/IRepositoryPortPage.d.ts +10 -0
  171. package/dist/application/ports/repository-ports/IRepositoryPortPage.js +1 -0
  172. package/dist/application/ports/repository-ports/IRepositoryPortPageEmbedLink.d.ts +10 -0
  173. package/dist/application/ports/repository-ports/IRepositoryPortPageEmbedLink.js +1 -0
  174. package/dist/application/ports/repository-ports/IRepositoryPortPageSnippetLink.d.ts +10 -0
  175. package/dist/application/ports/repository-ports/IRepositoryPortPageSnippetLink.js +1 -0
  176. package/dist/application/ports/repository-ports/IRepositoryPortPageVersion.d.ts +10 -0
  177. package/dist/application/ports/repository-ports/IRepositoryPortPageVersion.js +1 -0
  178. package/dist/application/ports/repository-ports/IRepositoryPortSection.d.ts +10 -0
  179. package/dist/application/ports/repository-ports/IRepositoryPortSection.js +1 -0
  180. package/dist/application/ports/repository-ports/IRepositoryPortSectionPageLink.d.ts +10 -0
  181. package/dist/application/ports/repository-ports/IRepositoryPortSectionPageLink.js +1 -0
  182. package/dist/application/ports/repository-ports/IRepositoryPortSnippet.d.ts +10 -0
  183. package/dist/application/ports/repository-ports/IRepositoryPortSnippet.js +1 -0
  184. package/dist/application/ports/repository-ports/index.d.ts +15 -0
  185. package/dist/application/ports/repository-ports/index.js +16 -0
  186. package/dist/application/services/documentCascadeDelete.d.ts +47 -0
  187. package/dist/application/services/documentCascadeDelete.js +202 -0
  188. package/dist/application/services/documentComposeSupport.d.ts +30 -0
  189. package/dist/application/services/documentComposeSupport.js +285 -0
  190. package/dist/application/services/documentIndexSupport.d.ts +7 -0
  191. package/dist/application/services/documentIndexSupport.js +28 -0
  192. package/dist/application/services/documentPublishSupport.d.ts +14 -0
  193. package/dist/application/services/documentPublishSupport.js +39 -0
  194. package/dist/application/services/documentRetrievalAiSupport.d.ts +21 -0
  195. package/dist/application/services/documentRetrievalAiSupport.js +114 -0
  196. package/dist/application/services/index.d.ts +14 -0
  197. package/dist/application/services/index.js +15 -0
  198. package/dist/application/services/service.asset.d.ts +25 -0
  199. package/dist/application/services/service.asset.js +58 -0
  200. package/dist/application/services/service.assetVersion.d.ts +25 -0
  201. package/dist/application/services/service.assetVersion.js +58 -0
  202. package/dist/application/services/service.document.d.ts +167 -0
  203. package/dist/application/services/service.document.js +2860 -0
  204. package/dist/application/services/service.documentGroup.d.ts +25 -0
  205. package/dist/application/services/service.documentGroup.js +58 -0
  206. package/dist/application/services/service.documentSectionLink.d.ts +27 -0
  207. package/dist/application/services/service.documentSectionLink.js +69 -0
  208. package/dist/application/services/service.documentVersion.d.ts +66 -0
  209. package/dist/application/services/service.documentVersion.js +715 -0
  210. package/dist/application/services/service.embed.d.ts +25 -0
  211. package/dist/application/services/service.embed.js +58 -0
  212. package/dist/application/services/service.page.d.ts +25 -0
  213. package/dist/application/services/service.page.js +58 -0
  214. package/dist/application/services/service.pageEmbedLink.d.ts +25 -0
  215. package/dist/application/services/service.pageEmbedLink.js +58 -0
  216. package/dist/application/services/service.pageSnippetLink.d.ts +25 -0
  217. package/dist/application/services/service.pageSnippetLink.js +58 -0
  218. package/dist/application/services/service.pageVersion.d.ts +31 -0
  219. package/dist/application/services/service.pageVersion.js +114 -0
  220. package/dist/application/services/service.section.d.ts +25 -0
  221. package/dist/application/services/service.section.js +58 -0
  222. package/dist/application/services/service.sectionPageLink.d.ts +25 -0
  223. package/dist/application/services/service.sectionPageLink.js +58 -0
  224. package/dist/application/services/service.snippet.d.ts +25 -0
  225. package/dist/application/services/service.snippet.js +58 -0
  226. package/dist/application/services/service.zod-validation.d.ts +11 -0
  227. package/dist/application/services/service.zod-validation.js +14 -0
  228. package/dist/domain/domain.d.ts +8 -0
  229. package/dist/domain/domain.js +9 -0
  230. package/dist/domain/dto/index.d.ts +1 -0
  231. package/dist/domain/dto/index.js +1 -0
  232. package/dist/domain/index.d.ts +4 -0
  233. package/dist/domain/index.js +4 -0
  234. package/dist/domain/models/asset/IbmAsset.d.ts +5 -0
  235. package/dist/domain/models/asset/IbmAsset.js +18 -0
  236. package/dist/domain/models/asset/index.d.ts +2 -0
  237. package/dist/domain/models/asset/index.js +2 -0
  238. package/dist/domain/models/asset/zod.schema.d.ts +39 -0
  239. package/dist/domain/models/asset/zod.schema.js +22 -0
  240. package/dist/domain/models/assetVersion/IbmAssetVersion.d.ts +6 -0
  241. package/dist/domain/models/assetVersion/IbmAssetVersion.js +25 -0
  242. package/dist/domain/models/assetVersion/index.d.ts +2 -0
  243. package/dist/domain/models/assetVersion/index.js +2 -0
  244. package/dist/domain/models/assetVersion/zod.schema.d.ts +71 -0
  245. package/dist/domain/models/assetVersion/zod.schema.js +37 -0
  246. package/dist/domain/models/document/IbmDocument.d.ts +8 -0
  247. package/dist/domain/models/document/IbmDocument.js +30 -0
  248. package/dist/domain/models/document/index.d.ts +2 -0
  249. package/dist/domain/models/document/index.js +2 -0
  250. package/dist/domain/models/document/zod.schema.d.ts +157 -0
  251. package/dist/domain/models/document/zod.schema.js +31 -0
  252. package/dist/domain/models/documentGroup/IbmDocumentGroup.d.ts +5 -0
  253. package/dist/domain/models/documentGroup/IbmDocumentGroup.js +17 -0
  254. package/dist/domain/models/documentGroup/index.d.ts +2 -0
  255. package/dist/domain/models/documentGroup/index.js +2 -0
  256. package/dist/domain/models/documentGroup/zod.schema.d.ts +27 -0
  257. package/dist/domain/models/documentGroup/zod.schema.js +22 -0
  258. package/dist/domain/models/documentIndexEntry/IbmDocumentIndexEntry.d.ts +6 -0
  259. package/dist/domain/models/documentIndexEntry/IbmDocumentIndexEntry.js +50 -0
  260. package/dist/domain/models/documentIndexEntry/index.d.ts +2 -0
  261. package/dist/domain/models/documentIndexEntry/index.js +2 -0
  262. package/dist/domain/models/documentIndexEntry/zod.schema.d.ts +112 -0
  263. package/dist/domain/models/documentIndexEntry/zod.schema.js +55 -0
  264. package/dist/domain/models/documentSectionLink/IbmDocumentSectionLink.d.ts +5 -0
  265. package/dist/domain/models/documentSectionLink/IbmDocumentSectionLink.js +23 -0
  266. package/dist/domain/models/documentSectionLink/index.d.ts +2 -0
  267. package/dist/domain/models/documentSectionLink/index.js +2 -0
  268. package/dist/domain/models/documentSectionLink/zod.schema.d.ts +65 -0
  269. package/dist/domain/models/documentSectionLink/zod.schema.js +80 -0
  270. package/dist/domain/models/documentVersion/IbmDocumentVersion.d.ts +8 -0
  271. package/dist/domain/models/documentVersion/IbmDocumentVersion.js +25 -0
  272. package/dist/domain/models/documentVersion/index.d.ts +2 -0
  273. package/dist/domain/models/documentVersion/index.js +2 -0
  274. package/dist/domain/models/documentVersion/zod.schema.d.ts +75 -0
  275. package/dist/domain/models/documentVersion/zod.schema.js +26 -0
  276. package/dist/domain/models/embed/IbmEmbed.d.ts +5 -0
  277. package/dist/domain/models/embed/IbmEmbed.js +19 -0
  278. package/dist/domain/models/embed/index.d.ts +2 -0
  279. package/dist/domain/models/embed/index.js +2 -0
  280. package/dist/domain/models/embed/zod.schema.d.ts +39 -0
  281. package/dist/domain/models/embed/zod.schema.js +24 -0
  282. package/dist/domain/models/index.d.ts +15 -0
  283. package/dist/domain/models/index.js +16 -0
  284. package/dist/domain/models/page/IbmPage.d.ts +8 -0
  285. package/dist/domain/models/page/IbmPage.js +20 -0
  286. package/dist/domain/models/page/index.d.ts +2 -0
  287. package/dist/domain/models/page/index.js +2 -0
  288. package/dist/domain/models/page/zod.schema.d.ts +57 -0
  289. package/dist/domain/models/page/zod.schema.js +21 -0
  290. package/dist/domain/models/pageEmbedLink/IbmPageEmbedLink.d.ts +5 -0
  291. package/dist/domain/models/pageEmbedLink/IbmPageEmbedLink.js +14 -0
  292. package/dist/domain/models/pageEmbedLink/index.d.ts +2 -0
  293. package/dist/domain/models/pageEmbedLink/index.js +2 -0
  294. package/dist/domain/models/pageEmbedLink/zod.schema.d.ts +21 -0
  295. package/dist/domain/models/pageEmbedLink/zod.schema.js +19 -0
  296. package/dist/domain/models/pageSnippetLink/IbmPageSnippetLink.d.ts +5 -0
  297. package/dist/domain/models/pageSnippetLink/IbmPageSnippetLink.js +15 -0
  298. package/dist/domain/models/pageSnippetLink/index.d.ts +2 -0
  299. package/dist/domain/models/pageSnippetLink/index.js +2 -0
  300. package/dist/domain/models/pageSnippetLink/zod.schema.d.ts +23 -0
  301. package/dist/domain/models/pageSnippetLink/zod.schema.js +20 -0
  302. package/dist/domain/models/pageVersion/IbmPageVersion.d.ts +8 -0
  303. package/dist/domain/models/pageVersion/IbmPageVersion.js +23 -0
  304. package/dist/domain/models/pageVersion/index.d.ts +2 -0
  305. package/dist/domain/models/pageVersion/index.js +2 -0
  306. package/dist/domain/models/pageVersion/zod.schema.d.ts +77 -0
  307. package/dist/domain/models/pageVersion/zod.schema.js +24 -0
  308. package/dist/domain/models/section/IbmSection.d.ts +8 -0
  309. package/dist/domain/models/section/IbmSection.js +20 -0
  310. package/dist/domain/models/section/index.d.ts +2 -0
  311. package/dist/domain/models/section/index.js +2 -0
  312. package/dist/domain/models/section/zod.schema.d.ts +57 -0
  313. package/dist/domain/models/section/zod.schema.js +21 -0
  314. package/dist/domain/models/sectionPageLink/IbmSectionPageLink.d.ts +5 -0
  315. package/dist/domain/models/sectionPageLink/IbmSectionPageLink.js +18 -0
  316. package/dist/domain/models/sectionPageLink/index.d.ts +2 -0
  317. package/dist/domain/models/sectionPageLink/index.js +2 -0
  318. package/dist/domain/models/sectionPageLink/zod.schema.d.ts +29 -0
  319. package/dist/domain/models/sectionPageLink/zod.schema.js +23 -0
  320. package/dist/domain/models/snippet/IbmSnippet.d.ts +5 -0
  321. package/dist/domain/models/snippet/IbmSnippet.js +17 -0
  322. package/dist/domain/models/snippet/index.d.ts +2 -0
  323. package/dist/domain/models/snippet/index.js +2 -0
  324. package/dist/domain/models/snippet/zod.schema.d.ts +27 -0
  325. package/dist/domain/models/snippet/zod.schema.js +22 -0
  326. package/dist/domain/types.d.ts +1 -0
  327. package/dist/domain/types.js +1 -0
  328. package/dist/index.d.ts +3 -0
  329. package/dist/index.js +3 -0
  330. package/dist/infrastructure/db/asset/drizzle/drizzle.mapper.asset.d.ts +31 -0
  331. package/dist/infrastructure/db/asset/drizzle/drizzle.mapper.asset.js +6 -0
  332. package/dist/infrastructure/db/asset/drizzle/drizzle.schema.asset.d.ts +250 -0
  333. package/dist/infrastructure/db/asset/drizzle/drizzle.schema.asset.js +24 -0
  334. package/dist/infrastructure/db/asset/drizzle/drizzle.schema.asset.sqlite.d.ts +272 -0
  335. package/dist/infrastructure/db/asset/drizzle/drizzle.schema.asset.sqlite.js +25 -0
  336. package/dist/infrastructure/db/asset/drizzle/index.d.ts +2 -0
  337. package/dist/infrastructure/db/asset/drizzle/index.js +2 -0
  338. package/dist/infrastructure/db/assetVersion/drizzle/drizzle.mapper.assetVersion.d.ts +45 -0
  339. package/dist/infrastructure/db/assetVersion/drizzle/drizzle.mapper.assetVersion.js +6 -0
  340. package/dist/infrastructure/db/assetVersion/drizzle/drizzle.schema.assetVersion.d.ts +371 -0
  341. package/dist/infrastructure/db/assetVersion/drizzle/drizzle.schema.assetVersion.js +30 -0
  342. package/dist/infrastructure/db/assetVersion/drizzle/drizzle.schema.assetVersion.sqlite.d.ts +397 -0
  343. package/dist/infrastructure/db/assetVersion/drizzle/drizzle.schema.assetVersion.sqlite.js +31 -0
  344. package/dist/infrastructure/db/assetVersion/drizzle/index.d.ts +2 -0
  345. package/dist/infrastructure/db/assetVersion/drizzle/index.js +2 -0
  346. package/dist/infrastructure/db/document/drizzle/drizzle.mapper.document.d.ts +98 -0
  347. package/dist/infrastructure/db/document/drizzle/drizzle.mapper.document.js +6 -0
  348. package/dist/infrastructure/db/document/drizzle/drizzle.schema.document.d.ts +394 -0
  349. package/dist/infrastructure/db/document/drizzle/drizzle.schema.document.js +34 -0
  350. package/dist/infrastructure/db/document/drizzle/drizzle.schema.document.sqlite.d.ts +424 -0
  351. package/dist/infrastructure/db/document/drizzle/drizzle.schema.document.sqlite.js +35 -0
  352. package/dist/infrastructure/db/document/drizzle/index.d.ts +2 -0
  353. package/dist/infrastructure/db/document/drizzle/index.js +2 -0
  354. package/dist/infrastructure/db/documentGroup/drizzle/drizzle.mapper.documentGroup.d.ts +29 -0
  355. package/dist/infrastructure/db/documentGroup/drizzle/drizzle.mapper.documentGroup.js +6 -0
  356. package/dist/infrastructure/db/documentGroup/drizzle/drizzle.schema.documentGroup.d.ts +233 -0
  357. package/dist/infrastructure/db/documentGroup/drizzle/drizzle.schema.documentGroup.js +22 -0
  358. package/dist/infrastructure/db/documentGroup/drizzle/drizzle.schema.documentGroup.sqlite.d.ts +253 -0
  359. package/dist/infrastructure/db/documentGroup/drizzle/drizzle.schema.documentGroup.sqlite.js +23 -0
  360. package/dist/infrastructure/db/documentGroup/drizzle/index.d.ts +2 -0
  361. package/dist/infrastructure/db/documentGroup/drizzle/index.js +2 -0
  362. package/dist/infrastructure/db/documentIndexEntry/drizzle/drizzle.mapper.documentIndexEntry.d.ts +95 -0
  363. package/dist/infrastructure/db/documentIndexEntry/drizzle/drizzle.mapper.documentIndexEntry.js +12 -0
  364. package/dist/infrastructure/db/documentIndexEntry/drizzle/drizzle.schema.documentIndexEntry.d.ts +792 -0
  365. package/dist/infrastructure/db/documentIndexEntry/drizzle/drizzle.schema.documentIndexEntry.js +57 -0
  366. package/dist/infrastructure/db/documentIndexEntry/drizzle/drizzle.schema.documentIndexEntry.sqlite.d.ts +854 -0
  367. package/dist/infrastructure/db/documentIndexEntry/drizzle/drizzle.schema.documentIndexEntry.sqlite.js +58 -0
  368. package/dist/infrastructure/db/documentIndexEntry/drizzle/index.d.ts +2 -0
  369. package/dist/infrastructure/db/documentIndexEntry/drizzle/index.js +2 -0
  370. package/dist/infrastructure/db/documentSectionLink/drizzle/drizzle.mapper.documentSectionLink.d.ts +41 -0
  371. package/dist/infrastructure/db/documentSectionLink/drizzle/drizzle.mapper.documentSectionLink.js +12 -0
  372. package/dist/infrastructure/db/documentSectionLink/drizzle/drizzle.schema.documentSectionLink.d.ts +335 -0
  373. package/dist/infrastructure/db/documentSectionLink/drizzle/drizzle.schema.documentSectionLink.js +29 -0
  374. package/dist/infrastructure/db/documentSectionLink/drizzle/drizzle.schema.documentSectionLink.sqlite.d.ts +357 -0
  375. package/dist/infrastructure/db/documentSectionLink/drizzle/drizzle.schema.documentSectionLink.sqlite.js +30 -0
  376. package/dist/infrastructure/db/documentSectionLink/drizzle/index.d.ts +2 -0
  377. package/dist/infrastructure/db/documentSectionLink/drizzle/index.js +2 -0
  378. package/dist/infrastructure/db/documentVersion/drizzle/drizzle.mapper.documentVersion.d.ts +54 -0
  379. package/dist/infrastructure/db/documentVersion/drizzle/drizzle.mapper.documentVersion.js +7 -0
  380. package/dist/infrastructure/db/documentVersion/drizzle/drizzle.schema.documentVersion.d.ts +301 -0
  381. package/dist/infrastructure/db/documentVersion/drizzle/drizzle.schema.documentVersion.js +30 -0
  382. package/dist/infrastructure/db/documentVersion/drizzle/drizzle.schema.documentVersion.sqlite.d.ts +323 -0
  383. package/dist/infrastructure/db/documentVersion/drizzle/drizzle.schema.documentVersion.sqlite.js +27 -0
  384. package/dist/infrastructure/db/documentVersion/drizzle/index.d.ts +2 -0
  385. package/dist/infrastructure/db/documentVersion/drizzle/index.js +2 -0
  386. package/dist/infrastructure/db/domain-naming.d.ts +2 -0
  387. package/dist/infrastructure/db/domain-naming.js +57 -0
  388. package/dist/infrastructure/db/embed/drizzle/drizzle.mapper.embed.d.ts +33 -0
  389. package/dist/infrastructure/db/embed/drizzle/drizzle.mapper.embed.js +5 -0
  390. package/dist/infrastructure/db/embed/drizzle/drizzle.schema.embed.d.ts +267 -0
  391. package/dist/infrastructure/db/embed/drizzle/drizzle.schema.embed.js +24 -0
  392. package/dist/infrastructure/db/embed/drizzle/drizzle.schema.embed.sqlite.d.ts +291 -0
  393. package/dist/infrastructure/db/embed/drizzle/drizzle.schema.embed.sqlite.js +25 -0
  394. package/dist/infrastructure/db/embed/drizzle/index.d.ts +2 -0
  395. package/dist/infrastructure/db/embed/drizzle/index.js +2 -0
  396. package/dist/infrastructure/db/page/drizzle/drizzle.mapper.page.d.ts +44 -0
  397. package/dist/infrastructure/db/page/drizzle/drizzle.mapper.page.js +5 -0
  398. package/dist/infrastructure/db/page/drizzle/drizzle.schema.page.d.ts +218 -0
  399. package/dist/infrastructure/db/page/drizzle/drizzle.schema.page.js +20 -0
  400. package/dist/infrastructure/db/page/drizzle/drizzle.schema.page.sqlite.d.ts +234 -0
  401. package/dist/infrastructure/db/page/drizzle/drizzle.schema.page.sqlite.js +21 -0
  402. package/dist/infrastructure/db/page/drizzle/index.d.ts +2 -0
  403. package/dist/infrastructure/db/page/drizzle/index.js +2 -0
  404. package/dist/infrastructure/db/pageEmbedLink/drizzle/drizzle.mapper.pageEmbedLink.d.ts +23 -0
  405. package/dist/infrastructure/db/pageEmbedLink/drizzle/drizzle.mapper.pageEmbedLink.js +5 -0
  406. package/dist/infrastructure/db/pageEmbedLink/drizzle/drizzle.schema.pageEmbedLink.d.ts +180 -0
  407. package/dist/infrastructure/db/pageEmbedLink/drizzle/drizzle.schema.pageEmbedLink.js +18 -0
  408. package/dist/infrastructure/db/pageEmbedLink/drizzle/drizzle.schema.pageEmbedLink.sqlite.d.ts +194 -0
  409. package/dist/infrastructure/db/pageEmbedLink/drizzle/drizzle.schema.pageEmbedLink.sqlite.js +19 -0
  410. package/dist/infrastructure/db/pageEmbedLink/drizzle/index.d.ts +2 -0
  411. package/dist/infrastructure/db/pageEmbedLink/drizzle/index.js +2 -0
  412. package/dist/infrastructure/db/pageSnippetLink/drizzle/drizzle.mapper.pageSnippetLink.d.ts +25 -0
  413. package/dist/infrastructure/db/pageSnippetLink/drizzle/drizzle.mapper.pageSnippetLink.js +7 -0
  414. package/dist/infrastructure/db/pageSnippetLink/drizzle/drizzle.schema.pageSnippetLink.d.ts +197 -0
  415. package/dist/infrastructure/db/pageSnippetLink/drizzle/drizzle.schema.pageSnippetLink.js +19 -0
  416. package/dist/infrastructure/db/pageSnippetLink/drizzle/drizzle.schema.pageSnippetLink.sqlite.d.ts +211 -0
  417. package/dist/infrastructure/db/pageSnippetLink/drizzle/drizzle.schema.pageSnippetLink.sqlite.js +20 -0
  418. package/dist/infrastructure/db/pageSnippetLink/drizzle/index.d.ts +2 -0
  419. package/dist/infrastructure/db/pageSnippetLink/drizzle/index.js +2 -0
  420. package/dist/infrastructure/db/pageVersion/drizzle/drizzle.mapper.pageVersion.d.ts +50 -0
  421. package/dist/infrastructure/db/pageVersion/drizzle/drizzle.mapper.pageVersion.js +6 -0
  422. package/dist/infrastructure/db/pageVersion/drizzle/drizzle.schema.pageVersion.d.ts +271 -0
  423. package/dist/infrastructure/db/pageVersion/drizzle/drizzle.schema.pageVersion.js +23 -0
  424. package/dist/infrastructure/db/pageVersion/drizzle/drizzle.schema.pageVersion.sqlite.d.ts +289 -0
  425. package/dist/infrastructure/db/pageVersion/drizzle/drizzle.schema.pageVersion.sqlite.js +24 -0
  426. package/dist/infrastructure/db/pageVersion/drizzle/index.d.ts +2 -0
  427. package/dist/infrastructure/db/pageVersion/drizzle/index.js +2 -0
  428. package/dist/infrastructure/db/section/drizzle/drizzle.mapper.section.d.ts +44 -0
  429. package/dist/infrastructure/db/section/drizzle/drizzle.mapper.section.js +5 -0
  430. package/dist/infrastructure/db/section/drizzle/drizzle.schema.section.d.ts +216 -0
  431. package/dist/infrastructure/db/section/drizzle/drizzle.schema.section.js +20 -0
  432. package/dist/infrastructure/db/section/drizzle/drizzle.schema.section.sqlite.d.ts +234 -0
  433. package/dist/infrastructure/db/section/drizzle/drizzle.schema.section.sqlite.js +21 -0
  434. package/dist/infrastructure/db/section/drizzle/index.d.ts +2 -0
  435. package/dist/infrastructure/db/section/drizzle/index.js +2 -0
  436. package/dist/infrastructure/db/sectionPageLink/drizzle/drizzle.mapper.sectionPageLink.d.ts +31 -0
  437. package/dist/infrastructure/db/sectionPageLink/drizzle/drizzle.mapper.sectionPageLink.js +7 -0
  438. package/dist/infrastructure/db/sectionPageLink/drizzle/drizzle.schema.sectionPageLink.d.ts +248 -0
  439. package/dist/infrastructure/db/sectionPageLink/drizzle/drizzle.schema.sectionPageLink.js +22 -0
  440. package/dist/infrastructure/db/sectionPageLink/drizzle/drizzle.schema.sectionPageLink.sqlite.d.ts +264 -0
  441. package/dist/infrastructure/db/sectionPageLink/drizzle/drizzle.schema.sectionPageLink.sqlite.js +23 -0
  442. package/dist/infrastructure/db/sectionPageLink/drizzle/index.d.ts +2 -0
  443. package/dist/infrastructure/db/sectionPageLink/drizzle/index.js +2 -0
  444. package/dist/infrastructure/db/snippet/drizzle/drizzle.mapper.snippet.d.ts +29 -0
  445. package/dist/infrastructure/db/snippet/drizzle/drizzle.mapper.snippet.js +5 -0
  446. package/dist/infrastructure/db/snippet/drizzle/drizzle.schema.snippet.d.ts +233 -0
  447. package/dist/infrastructure/db/snippet/drizzle/drizzle.schema.snippet.js +22 -0
  448. package/dist/infrastructure/db/snippet/drizzle/drizzle.schema.snippet.sqlite.d.ts +253 -0
  449. package/dist/infrastructure/db/snippet/drizzle/drizzle.schema.snippet.sqlite.js +23 -0
  450. package/dist/infrastructure/db/snippet/drizzle/index.d.ts +2 -0
  451. package/dist/infrastructure/db/snippet/drizzle/index.js +2 -0
  452. package/dist/infrastructure/repositories/asset/drizzle/AssetDrizzleRepo.d.ts +12 -0
  453. package/dist/infrastructure/repositories/asset/drizzle/AssetDrizzleRepo.js +8 -0
  454. package/dist/infrastructure/repositories/asset/drizzle/AssetDrizzleSqliteRepo.d.ts +12 -0
  455. package/dist/infrastructure/repositories/asset/drizzle/AssetDrizzleSqliteRepo.js +8 -0
  456. package/dist/infrastructure/repositories/assetVersion/drizzle/AssetVersionDrizzleRepo.d.ts +12 -0
  457. package/dist/infrastructure/repositories/assetVersion/drizzle/AssetVersionDrizzleRepo.js +8 -0
  458. package/dist/infrastructure/repositories/assetVersion/drizzle/AssetVersionDrizzleSqliteRepo.d.ts +12 -0
  459. package/dist/infrastructure/repositories/assetVersion/drizzle/AssetVersionDrizzleSqliteRepo.js +12 -0
  460. package/dist/infrastructure/repositories/document/drizzle/DocumentDrizzleRepo.d.ts +12 -0
  461. package/dist/infrastructure/repositories/document/drizzle/DocumentDrizzleRepo.js +8 -0
  462. package/dist/infrastructure/repositories/document/drizzle/DocumentDrizzleSqliteRepo.d.ts +12 -0
  463. package/dist/infrastructure/repositories/document/drizzle/DocumentDrizzleSqliteRepo.js +8 -0
  464. package/dist/infrastructure/repositories/documentGroup/drizzle/DocumentGroupDrizzleRepo.d.ts +12 -0
  465. package/dist/infrastructure/repositories/documentGroup/drizzle/DocumentGroupDrizzleRepo.js +8 -0
  466. package/dist/infrastructure/repositories/documentGroup/drizzle/DocumentGroupDrizzleSqliteRepo.d.ts +12 -0
  467. package/dist/infrastructure/repositories/documentGroup/drizzle/DocumentGroupDrizzleSqliteRepo.js +8 -0
  468. package/dist/infrastructure/repositories/documentIndexEntry/drizzle/DocumentIndexEntryDrizzleRepo.d.ts +12 -0
  469. package/dist/infrastructure/repositories/documentIndexEntry/drizzle/DocumentIndexEntryDrizzleRepo.js +12 -0
  470. package/dist/infrastructure/repositories/documentIndexEntry/drizzle/DocumentIndexEntryDrizzleSqliteRepo.d.ts +12 -0
  471. package/dist/infrastructure/repositories/documentIndexEntry/drizzle/DocumentIndexEntryDrizzleSqliteRepo.js +12 -0
  472. package/dist/infrastructure/repositories/documentSectionLink/drizzle/DocumentSectionLinkDrizzleRepo.d.ts +15 -0
  473. package/dist/infrastructure/repositories/documentSectionLink/drizzle/DocumentSectionLinkDrizzleRepo.js +41 -0
  474. package/dist/infrastructure/repositories/documentSectionLink/drizzle/DocumentSectionLinkDrizzleSqliteRepo.d.ts +15 -0
  475. package/dist/infrastructure/repositories/documentSectionLink/drizzle/DocumentSectionLinkDrizzleSqliteRepo.js +40 -0
  476. package/dist/infrastructure/repositories/documentVersion/drizzle/DocumentVersionDrizzleRepo.d.ts +12 -0
  477. package/dist/infrastructure/repositories/documentVersion/drizzle/DocumentVersionDrizzleRepo.js +8 -0
  478. package/dist/infrastructure/repositories/documentVersion/drizzle/DocumentVersionDrizzleSqliteRepo.d.ts +12 -0
  479. package/dist/infrastructure/repositories/documentVersion/drizzle/DocumentVersionDrizzleSqliteRepo.js +8 -0
  480. package/dist/infrastructure/repositories/embed/drizzle/EmbedDrizzleRepo.d.ts +12 -0
  481. package/dist/infrastructure/repositories/embed/drizzle/EmbedDrizzleRepo.js +8 -0
  482. package/dist/infrastructure/repositories/embed/drizzle/EmbedDrizzleSqliteRepo.d.ts +12 -0
  483. package/dist/infrastructure/repositories/embed/drizzle/EmbedDrizzleSqliteRepo.js +8 -0
  484. package/dist/infrastructure/repositories/index.d.ts +30 -0
  485. package/dist/infrastructure/repositories/index.js +31 -0
  486. package/dist/infrastructure/repositories/page/drizzle/PageDrizzleRepo.d.ts +12 -0
  487. package/dist/infrastructure/repositories/page/drizzle/PageDrizzleRepo.js +8 -0
  488. package/dist/infrastructure/repositories/page/drizzle/PageDrizzleSqliteRepo.d.ts +12 -0
  489. package/dist/infrastructure/repositories/page/drizzle/PageDrizzleSqliteRepo.js +8 -0
  490. package/dist/infrastructure/repositories/pageEmbedLink/drizzle/PageEmbedLinkDrizzleRepo.d.ts +12 -0
  491. package/dist/infrastructure/repositories/pageEmbedLink/drizzle/PageEmbedLinkDrizzleRepo.js +8 -0
  492. package/dist/infrastructure/repositories/pageEmbedLink/drizzle/PageEmbedLinkDrizzleSqliteRepo.d.ts +12 -0
  493. package/dist/infrastructure/repositories/pageEmbedLink/drizzle/PageEmbedLinkDrizzleSqliteRepo.js +8 -0
  494. package/dist/infrastructure/repositories/pageSnippetLink/drizzle/PageSnippetLinkDrizzleRepo.d.ts +12 -0
  495. package/dist/infrastructure/repositories/pageSnippetLink/drizzle/PageSnippetLinkDrizzleRepo.js +8 -0
  496. package/dist/infrastructure/repositories/pageSnippetLink/drizzle/PageSnippetLinkDrizzleSqliteRepo.d.ts +12 -0
  497. package/dist/infrastructure/repositories/pageSnippetLink/drizzle/PageSnippetLinkDrizzleSqliteRepo.js +8 -0
  498. package/dist/infrastructure/repositories/pageVersion/drizzle/PageVersionDrizzleRepo.d.ts +12 -0
  499. package/dist/infrastructure/repositories/pageVersion/drizzle/PageVersionDrizzleRepo.js +8 -0
  500. package/dist/infrastructure/repositories/pageVersion/drizzle/PageVersionDrizzleSqliteRepo.d.ts +12 -0
  501. package/dist/infrastructure/repositories/pageVersion/drizzle/PageVersionDrizzleSqliteRepo.js +8 -0
  502. package/dist/infrastructure/repositories/section/drizzle/SectionDrizzleRepo.d.ts +12 -0
  503. package/dist/infrastructure/repositories/section/drizzle/SectionDrizzleRepo.js +8 -0
  504. package/dist/infrastructure/repositories/section/drizzle/SectionDrizzleSqliteRepo.d.ts +12 -0
  505. package/dist/infrastructure/repositories/section/drizzle/SectionDrizzleSqliteRepo.js +8 -0
  506. package/dist/infrastructure/repositories/sectionPageLink/drizzle/SectionPageLinkDrizzleRepo.d.ts +12 -0
  507. package/dist/infrastructure/repositories/sectionPageLink/drizzle/SectionPageLinkDrizzleRepo.js +8 -0
  508. package/dist/infrastructure/repositories/sectionPageLink/drizzle/SectionPageLinkDrizzleSqliteRepo.d.ts +12 -0
  509. package/dist/infrastructure/repositories/sectionPageLink/drizzle/SectionPageLinkDrizzleSqliteRepo.js +8 -0
  510. package/dist/infrastructure/repositories/snippet/drizzle/SnippetDrizzleRepo.d.ts +12 -0
  511. package/dist/infrastructure/repositories/snippet/drizzle/SnippetDrizzleRepo.js +8 -0
  512. package/dist/infrastructure/repositories/snippet/drizzle/SnippetDrizzleSqliteRepo.d.ts +12 -0
  513. package/dist/infrastructure/repositories/snippet/drizzle/SnippetDrizzleSqliteRepo.js +8 -0
  514. package/dist/public/factories/index.d.ts +2 -0
  515. package/dist/public/factories/index.js +2 -0
  516. package/dist/public/index.d.ts +5 -0
  517. package/dist/public/index.js +5 -0
  518. package/dist/public/models/index.d.ts +3 -0
  519. package/dist/public/models/index.js +4 -0
  520. package/dist/public/ports/index.d.ts +1 -0
  521. package/dist/public/ports/index.js +2 -0
  522. package/dist/public/repository-ports/index.d.ts +1 -0
  523. package/dist/public/repository-ports/index.js +2 -0
  524. package/dist/public/services/index.d.ts +1 -0
  525. package/dist/public/services/index.js +2 -0
  526. package/dist/types.d.ts +1 -0
  527. package/dist/types.js +1 -0
  528. package/package.json +84 -0
@@ -0,0 +1,2860 @@
1
+ import { Effect } from 'effect';
2
+ import { pipe } from 'effect/Function';
3
+ import { createElement } from 'react';
4
+ import { renderToStaticMarkup } from 'react-dom/server';
5
+ import ReactMarkdown from 'react-markdown';
6
+ import remarkGfm from 'remark-gfm';
7
+ import { validateInput, XfErrorFactory, effectErrorInfo } from '@aopslab/xf-core';
8
+ import { documentZodSchemaInsert, } from '../../domain/models/index.js';
9
+ import { validateBmInputWithSchema } from './service.zod-validation.js';
10
+ import { mapDbError } from '@aopslab/xf-db';
11
+ import { listDocmanAssetReferenceTokens, normalizeDocmanComposeSourceContent, reduceDocmanComposeFormats, replaceDocmanAssetReferenceTokens, resolveDocmanComposeSourceFormat, splitDocmanComposeSourceContent, stripLeadingNumericPrefixForRender, } from './documentComposeSupport.js';
12
+ import { formatDocmanPublishTargets, resolveDocmanPublishTargetDescriptor, } from './documentPublishSupport.js';
13
+ import { DOCMAN_DOCUMENT_INDEX_BUILD_ACTOR, buildDocmanDocumentAnchor, buildDocmanDocumentIndexFingerprint, buildDocmanPageAnchor, buildDocmanSectionAnchor, normalizeDocmanDocumentIndexLocale, } from './documentIndexSupport.js';
14
+ import { deleteDocumentCascade, } from './documentCascadeDelete.js';
15
+ import { buildDocmanEmbeddingHash, cosineSimilarity, createDocmanLocalHashEmbeddingProvider, parseDocmanEmbeddingVector, resolveDocmanDefaultEmbeddingProvider, serializeDocmanEmbeddingVector, } from './documentRetrievalAiSupport.js';
16
+ const PAGE_BREAK_LINE_RE = /^\s*(?:<!--\s*(?:new\s*page|newpage|pagebreak)\s*-->|\[\[(?:new\s*page|newpage|pagebreak)\]\]|\[(?:new\s*page|newpage|pagebreak)\]|---\s*pagebreak\s*---|\\pagebreak)\s*$/i;
17
+ const HTML_LIKE_BLOCK_LINE_RE = /^<\/?[A-Za-z][A-Za-z0-9:_-]*(?:\s[^>]*)?>$/;
18
+ const HTML_COMMENT_LINE_RE = /^<!--[\s\S]*-->$/;
19
+ const DOCMAN_PAGEBREAK_HTML = '<hr data-docman-pagebreak="true" />';
20
+ const DOCMAN_PUBLISH_HTML_STYLES = `
21
+ :root {
22
+ color-scheme: light;
23
+ font-family: "IBM Plex Sans", "Segoe UI", sans-serif;
24
+ line-height: 1.6;
25
+ color: #16202a;
26
+ background: #f6f7f9;
27
+ }
28
+ body {
29
+ margin: 0;
30
+ padding: 32px 20px 48px;
31
+ background:
32
+ radial-gradient(circle at top left, rgba(15, 118, 110, 0.08), transparent 36%),
33
+ linear-gradient(180deg, #fbfcfd 0%, #f2f4f7 100%);
34
+ }
35
+ main {
36
+ max-width: 880px;
37
+ margin: 0 auto;
38
+ padding: 40px 48px;
39
+ background: rgba(255, 255, 255, 0.92);
40
+ border: 1px solid rgba(18, 24, 28, 0.08);
41
+ border-radius: 20px;
42
+ box-shadow: 0 18px 50px rgba(15, 23, 42, 0.08);
43
+ }
44
+ h1, h2, h3, h4, h5, h6 {
45
+ line-height: 1.2;
46
+ letter-spacing: -0.02em;
47
+ color: #0f172a;
48
+ }
49
+ h1 { font-size: 2.35rem; margin: 0 0 1.2rem; }
50
+ h2 { font-size: 1.72rem; margin-top: 2.2rem; }
51
+ h3 { font-size: 1.35rem; margin-top: 1.8rem; }
52
+ p, ul, ol, blockquote, table, pre {
53
+ margin-top: 0;
54
+ margin-bottom: 1rem;
55
+ }
56
+ a {
57
+ color: #0f766e;
58
+ }
59
+ blockquote {
60
+ margin-left: 0;
61
+ padding: 0.8rem 1rem;
62
+ border-left: 4px solid rgba(15, 118, 110, 0.45);
63
+ background: rgba(15, 118, 110, 0.06);
64
+ }
65
+ code {
66
+ font-family: "IBM Plex Mono", "SFMono-Regular", monospace;
67
+ font-size: 0.92em;
68
+ }
69
+ pre {
70
+ overflow-x: auto;
71
+ padding: 1rem 1.1rem;
72
+ border-radius: 14px;
73
+ background: #0f172a;
74
+ color: #e2e8f0;
75
+ }
76
+ table {
77
+ width: 100%;
78
+ border-collapse: collapse;
79
+ }
80
+ th, td {
81
+ padding: 0.65rem 0.8rem;
82
+ border: 1px solid rgba(15, 23, 42, 0.12);
83
+ text-align: left;
84
+ }
85
+ img {
86
+ max-width: 100%;
87
+ height: auto;
88
+ }
89
+ hr[data-docman-pagebreak="true"] {
90
+ margin: 2.5rem 0;
91
+ border: 0;
92
+ border-top: 2px dashed rgba(15, 23, 42, 0.18);
93
+ }
94
+ `;
95
+ const DOCMAN_DOCUMENT_SUMMARY_BUILD_ACTOR = 'docman:document-summary.build';
96
+ const activePersistedDocumentIndexBuilds = new Map();
97
+ export class DocumentService {
98
+ documentRepository;
99
+ assetRepository;
100
+ assetVersionRepository;
101
+ documentVersionRepository;
102
+ documentIndexEntryRepository;
103
+ documentSectionLinkRepository;
104
+ sectionRepository;
105
+ pageRepository;
106
+ pageVersionRepository;
107
+ sectionPageLinkRepository;
108
+ pageSnippetLinkRepository;
109
+ pageEmbedLinkRepository;
110
+ embeddingProvider;
111
+ logger;
112
+ locale;
113
+ constructor(options) {
114
+ const deps = options.serviceDependencies ?? {};
115
+ this.documentRepository = options.documentRepository;
116
+ this.assetRepository = deps.assetRepository;
117
+ this.assetVersionRepository = deps.assetVersionRepository;
118
+ this.documentVersionRepository = deps.documentVersionRepository;
119
+ this.documentIndexEntryRepository = deps.documentIndexEntryRepository;
120
+ this.documentSectionLinkRepository = deps.documentSectionLinkRepository;
121
+ this.sectionRepository = deps.sectionRepository;
122
+ this.pageRepository = deps.pageRepository;
123
+ this.pageVersionRepository = deps.pageVersionRepository;
124
+ this.sectionPageLinkRepository = deps.sectionPageLinkRepository;
125
+ this.pageSnippetLinkRepository = deps.pageSnippetLinkRepository;
126
+ this.pageEmbedLinkRepository = deps.pageEmbedLinkRepository;
127
+ this.embeddingProvider = deps.embeddingProvider ?? resolveDocmanDefaultEmbeddingProvider();
128
+ this.logger = options.logger?.child({ module: this.constructor.name });
129
+ this.locale = options.locale;
130
+ }
131
+ getById(id, options) {
132
+ const stage = 'DocumentService::getById';
133
+ const localeState = this.resolveLocaleOptions();
134
+ const queryOptions = this.withLocaleOptions(options, localeState, ['titleMl', 'summaryMl', 'descriptionMl']);
135
+ return pipe(validateInput(id, 'id', { stage }), Effect.flatMap((docId) => this.documentRepository.findById(docId, queryOptions).pipe(Effect.mapError(mapDbError({ stage, operation: 'findById', factory: XfErrorFactory.notFound })))), Effect.tapError((e) => Effect.sync(() => {
136
+ const info = effectErrorInfo(e);
137
+ this.logger?.error({ error: info.unwrapped, stage }, 'Error in getById');
138
+ })));
139
+ }
140
+ create(data) {
141
+ const stage = 'DocumentService::create';
142
+ return pipe(validateInput(data, 'data', { stage }), Effect.flatMap((payload) => validateBmInputWithSchema({
143
+ input: payload,
144
+ schema: documentZodSchemaInsert,
145
+ stage,
146
+ operation: 'DocumentService::create.documentZodSchemaInsert',
147
+ field: 'data',
148
+ })), Effect.flatMap((payload) => this.documentRepository.create(payload).pipe(Effect.mapError(mapDbError({ stage, operation: 'create', factory: XfErrorFactory.createFailed })))));
149
+ }
150
+ listDocuments(filter = {}, options = {}) {
151
+ const stage = 'DocumentService::listDocuments';
152
+ const includeVersionInfo = options?.includeVersionInfo === true;
153
+ const localeState = this.resolveLocaleOptions(options);
154
+ const documentQueryOptions = this.withLocaleOptions(this.stripDocumentListOptions(options), localeState, ['titleMl', 'summaryMl', 'descriptionMl']);
155
+ const versionQueryOptions = this.withLocaleOptions({
156
+ sort: [{ field: 'version', type: 'desc' }],
157
+ }, localeState, ['releaseNotesMl']) ?? {
158
+ sort: [{ field: 'version', type: 'desc' }],
159
+ };
160
+ return Effect.gen(this, function* () {
161
+ const filtered = yield* validateInput(filter, 'filter', { stage });
162
+ const documents = yield* this.documentRepository
163
+ .find({
164
+ matchEq: filtered,
165
+ options: documentQueryOptions,
166
+ })
167
+ .pipe(Effect.mapError(mapDbError({
168
+ stage,
169
+ operation: 'find',
170
+ factory: XfErrorFactory.notFound,
171
+ })));
172
+ if (!includeVersionInfo) {
173
+ return documents;
174
+ }
175
+ if (!this.documentVersionRepository) {
176
+ return yield* Effect.fail(XfErrorFactory.configurationError({
177
+ stage,
178
+ operation: 'listDocuments',
179
+ message: 'Missing dependency: documentVersionRepository',
180
+ }));
181
+ }
182
+ const documentsWithVersions = yield* Effect.all(documents.map((document) => this.documentVersionRepository
183
+ .find({
184
+ matchEq: { documentId: document.id },
185
+ options: versionQueryOptions,
186
+ })
187
+ .pipe(Effect.mapError(mapDbError({
188
+ stage,
189
+ operation: 'find',
190
+ factory: XfErrorFactory.notFound,
191
+ })), Effect.map((versions) => ({
192
+ ...document,
193
+ documentVersions: versions.sort((a, b) => Number(b.version ?? 0) - Number(a.version ?? 0)),
194
+ })))), { concurrency: 6 });
195
+ return documentsWithVersions;
196
+ }).pipe(Effect.tapError((e) => Effect.sync(() => {
197
+ const info = effectErrorInfo(e);
198
+ this.logger?.error({ error: info.unwrapped, stage }, 'Error in listDocuments');
199
+ })));
200
+ }
201
+ updateDocument(id, patch) {
202
+ const stage = 'DocumentService::updateDocument';
203
+ if (!patch || Object.keys(patch).length === 0) {
204
+ return Effect.fail(XfErrorFactory.inputRequired({ field: 'patch', stage }));
205
+ }
206
+ return pipe(validateInput(id, 'id', { stage }), Effect.flatMap((entityId) => validateBmInputWithSchema({
207
+ input: patch,
208
+ schema: documentZodSchemaInsert.partial().strict(),
209
+ stage,
210
+ operation: 'DocumentService::updateDocument.documentZodSchemaInsert.patch',
211
+ field: 'patch',
212
+ }).pipe(Effect.map(() => entityId))), Effect.flatMap((entityId) => this.documentRepository.patchById(entityId, patch).pipe(Effect.mapError(mapDbError({ stage, operation: 'patchById', factory: XfErrorFactory.upsertFailed })))), Effect.tapError((e) => Effect.sync(() => {
213
+ const info = effectErrorInfo(e);
214
+ this.logger?.error({ error: info.unwrapped, stage }, 'Error in updateDocument');
215
+ })));
216
+ }
217
+ removeDocument(id) {
218
+ const stage = 'DocumentService::removeDocument';
219
+ return pipe(validateInput(id, 'id', { stage }), Effect.flatMap((entityId) => this.documentRepository.deleteById(entityId).pipe(Effect.mapError(mapDbError({ stage, operation: 'deleteById', factory: XfErrorFactory.upsertFailed })))), Effect.map(() => undefined));
220
+ }
221
+ buildDocumentIndex(documentVersionId, options) {
222
+ const stage = 'DocumentService::buildDocumentIndex';
223
+ return Effect.gen(this, function* (_) {
224
+ const versionId = yield* _(validateInput(documentVersionId, 'documentVersionId', { stage }));
225
+ const resolved = yield* _(this.resolveComposedDocument(versionId, options, stage, 'buildDocumentIndex'));
226
+ return this.toDocumentComposeIndex(resolved);
227
+ }).pipe(Effect.tapError((e) => Effect.sync(() => {
228
+ const info = effectErrorInfo(e);
229
+ this.logger?.error({ error: info.unwrapped, stage }, 'Error in buildDocumentIndex');
230
+ })));
231
+ }
232
+ buildPersistedDocumentIndex(input) {
233
+ const stage = 'DocumentService::buildPersistedDocumentIndex';
234
+ return Effect.gen(this, function* (_) {
235
+ const payload = yield* _(validateInput(input, 'input', { stage }));
236
+ const versionId = yield* _(validateInput(payload.documentVersionId, 'documentVersionId', { stage }));
237
+ const localeKey = this.resolveDocumentIndexLocaleKey(payload);
238
+ const buildKey = this.buildPersistedDocumentIndexBuildKey(versionId, localeKey);
239
+ const activeBuild = activePersistedDocumentIndexBuilds.get(buildKey);
240
+ if (activeBuild) {
241
+ return yield* _(Effect.tryPromise({
242
+ try: () => activeBuild,
243
+ catch: (error) => error,
244
+ }));
245
+ }
246
+ const buildPromise = Effect.runPromise(this.buildPersistedDocumentIndexForResolvedInput(payload, versionId, localeKey, stage)).finally(() => {
247
+ if (activePersistedDocumentIndexBuilds.get(buildKey) === buildPromise) {
248
+ activePersistedDocumentIndexBuilds.delete(buildKey);
249
+ }
250
+ });
251
+ activePersistedDocumentIndexBuilds.set(buildKey, buildPromise);
252
+ return yield* _(Effect.tryPromise({
253
+ try: () => buildPromise,
254
+ catch: (error) => error,
255
+ }));
256
+ }).pipe(Effect.tapError((e) => Effect.sync(() => {
257
+ const info = effectErrorInfo(e);
258
+ this.logger?.error({ error: info.unwrapped, stage }, 'Error in buildPersistedDocumentIndex');
259
+ })));
260
+ }
261
+ buildPersistedDocumentIndexForResolvedInput(payload, versionId, localeKey, stage) {
262
+ return Effect.gen(this, function* (_) {
263
+ const repository = yield* _(this.requireDependency(this.documentIndexEntryRepository, 'documentIndexEntryRepository', stage, 'buildPersistedDocumentIndex'));
264
+ const resolved = yield* _(this.resolveComposedDocument(versionId, payload, stage, 'buildPersistedDocumentIndex'));
265
+ const rows = yield* _(this.populatePersistedDocumentIndexEmbeddings(this.toPersistedDocumentIndexRows(resolved, localeKey), stage, 'buildPersistedDocumentIndex.embed'));
266
+ const existingRows = yield* _(this.listPersistedDocumentIndexRows(repository, versionId, localeKey, stage, 'buildPersistedDocumentIndex.find'));
267
+ yield* _(this.deletePersistedDocumentIndexRows(repository, existingRows, stage, 'buildPersistedDocumentIndex.delete'));
268
+ const createdRows = yield* _(this.createPersistedDocumentIndexRows(repository, rows, versionId, localeKey, stage, 'buildPersistedDocumentIndex.create'));
269
+ return this.toPersistedDocumentIndexSnapshot(versionId, localeKey, createdRows);
270
+ });
271
+ }
272
+ getPersistedDocumentIndex(input) {
273
+ const stage = 'DocumentService::getPersistedDocumentIndex';
274
+ return Effect.gen(this, function* (_) {
275
+ const payload = yield* _(validateInput(input, 'input', { stage }));
276
+ const versionId = yield* _(validateInput(payload.documentVersionId, 'documentVersionId', { stage }));
277
+ const localeKey = this.resolveDocumentIndexLocaleKey(payload);
278
+ const repository = yield* _(this.requireDependency(this.documentIndexEntryRepository, 'documentIndexEntryRepository', stage, 'getPersistedDocumentIndex'));
279
+ const rows = yield* _(this.listPersistedDocumentIndexRows(repository, versionId, localeKey, stage, 'getPersistedDocumentIndex.find'));
280
+ return this.toPersistedDocumentIndexSnapshot(versionId, localeKey, rows);
281
+ }).pipe(Effect.tapError((e) => Effect.sync(() => {
282
+ const info = effectErrorInfo(e);
283
+ this.logger?.error({ error: info.unwrapped, stage }, 'Error in getPersistedDocumentIndex');
284
+ })));
285
+ }
286
+ buildPersistedDocumentSummary(input) {
287
+ const stage = 'DocumentService::buildPersistedDocumentSummary';
288
+ return Effect.gen(this, function* (_) {
289
+ const payload = yield* _(validateInput(input, 'input', { stage }));
290
+ const versionId = yield* _(validateInput(payload.documentVersionId, 'documentVersionId', { stage }));
291
+ const localeKey = this.resolveDocumentIndexLocaleKey(payload);
292
+ const repository = yield* _(this.requireDependency(this.documentIndexEntryRepository, 'documentIndexEntryRepository', stage, 'buildPersistedDocumentSummary'));
293
+ yield* _(this.buildPersistedDocumentIndex(payload));
294
+ const rows = yield* _(this.listPersistedDocumentIndexRows(repository, versionId, localeKey, stage, 'buildPersistedDocumentSummary.find'));
295
+ if (rows.length === 0) {
296
+ return this.toPersistedDocumentSummarySnapshot(versionId, localeKey, rows);
297
+ }
298
+ const authoredDocumentSummary = yield* _(this.resolvePersistedDocumentAuthoredSummaryText(rows, localeKey, stage, 'buildPersistedDocumentSummary.resolveAuthorSummary'));
299
+ const summaryPatches = this.toPersistedDocumentSummaryPatches(rows, authoredDocumentSummary);
300
+ yield* _(Effect.all(summaryPatches.map(({ id, patch }) => repository.patchById(id, patch).pipe(Effect.mapError(mapDbError({
301
+ stage,
302
+ operation: 'documentIndexEntryRepository.patchById',
303
+ factory: XfErrorFactory.upsertFailed,
304
+ })))), { concurrency: 6 }));
305
+ const updatedRows = yield* _(this.listPersistedDocumentIndexRows(repository, versionId, localeKey, stage, 'buildPersistedDocumentSummary.reload'));
306
+ yield* _(this.refreshPersistedDocumentIndexEmbeddings(repository, updatedRows, stage, 'buildPersistedDocumentSummary.embed'));
307
+ return this.toPersistedDocumentSummarySnapshot(versionId, localeKey, updatedRows);
308
+ }).pipe(Effect.tapError((e) => Effect.sync(() => {
309
+ const info = effectErrorInfo(e);
310
+ this.logger?.error({ error: info.unwrapped, stage }, 'Error in buildPersistedDocumentSummary');
311
+ })));
312
+ }
313
+ getPersistedDocumentSummary(input) {
314
+ const stage = 'DocumentService::getPersistedDocumentSummary';
315
+ return Effect.gen(this, function* (_) {
316
+ const payload = yield* _(validateInput(input, 'input', { stage }));
317
+ const versionId = yield* _(validateInput(payload.documentVersionId, 'documentVersionId', { stage }));
318
+ const localeKey = this.resolveDocumentIndexLocaleKey(payload);
319
+ const repository = yield* _(this.requireDependency(this.documentIndexEntryRepository, 'documentIndexEntryRepository', stage, 'getPersistedDocumentSummary'));
320
+ const rows = yield* _(this.listPersistedDocumentIndexRows(repository, versionId, localeKey, stage, 'getPersistedDocumentSummary.find'));
321
+ return this.toPersistedDocumentSummarySnapshot(versionId, localeKey, rows);
322
+ }).pipe(Effect.tapError((e) => Effect.sync(() => {
323
+ const info = effectErrorInfo(e);
324
+ this.logger?.error({ error: info.unwrapped, stage }, 'Error in getPersistedDocumentSummary');
325
+ })));
326
+ }
327
+ searchPersistedDocumentIndex(input) {
328
+ const stage = 'DocumentService::searchPersistedDocumentIndex';
329
+ return Effect.gen(this, function* (_) {
330
+ const payload = yield* _(validateInput(input, 'input', { stage }));
331
+ const versionId = yield* _(validateInput(payload.documentVersionId, 'documentVersionId', { stage }));
332
+ const q = this.normalizeNonEmpty(payload.q);
333
+ if (!q) {
334
+ return yield* _(Effect.fail(XfErrorFactory.inputRequired({ field: 'q', stage })));
335
+ }
336
+ const localeKey = this.resolveDocumentIndexLocaleKey(payload);
337
+ const repository = yield* _(this.requireDependency(this.documentIndexEntryRepository, 'documentIndexEntryRepository', stage, 'searchPersistedDocumentIndex'));
338
+ const rows = yield* _(this.listPersistedDocumentIndexRows(repository, versionId, localeKey, stage, 'searchPersistedDocumentIndex.find'));
339
+ return yield* _(this.searchPersistedDocumentIndexRows(versionId, localeKey, q, payload.limit, payload.retrievalStrategy, rows));
340
+ }).pipe(Effect.tapError((e) => Effect.sync(() => {
341
+ const info = effectErrorInfo(e);
342
+ this.logger?.error({ error: info.unwrapped, stage }, 'Error in searchPersistedDocumentIndex');
343
+ })));
344
+ }
345
+ searchScopePersistedDocumentIndex(input) {
346
+ const stage = 'DocumentService::searchScopePersistedDocumentIndex';
347
+ return Effect.gen(this, function* (_) {
348
+ const payload = yield* _(validateInput(input, 'input', { stage }));
349
+ const scopeId = yield* _(validateInput(payload.scopeId, 'scopeId', { stage }));
350
+ const q = this.normalizeNonEmpty(payload.q);
351
+ if (!q) {
352
+ return yield* _(Effect.fail(XfErrorFactory.inputRequired({ field: 'q', stage })));
353
+ }
354
+ const localeKey = this.resolveDocumentIndexLocaleKey(payload);
355
+ const repository = yield* _(this.requireDependency(this.documentIndexEntryRepository, 'documentIndexEntryRepository', stage, 'searchScopePersistedDocumentIndex'));
356
+ const documents = yield* _(this.listDocuments({ scopeId }, {
357
+ includeVersionInfo: true,
358
+ locale: localeKey.locale,
359
+ fallbackLocale: localeKey.fallbackLocale,
360
+ }));
361
+ const retrievalStrategy = this.resolveDocumentRetrievalStrategy(payload.retrievalStrategy);
362
+ const limit = this.resolveDocumentIndexSearchLimit(payload.limit);
363
+ const autoBuiltDocumentVersionIds = [];
364
+ const failures = [];
365
+ const aggregatedHits = [];
366
+ let searchedDocumentCount = 0;
367
+ for (const document of documents) {
368
+ const seed = this.resolveScopeSearchDocumentSeed(document);
369
+ if (!seed) {
370
+ failures.push({
371
+ documentId: this.normalizeNonEmpty(document.id),
372
+ documentTitle: this.normalizeNonEmpty(document.title),
373
+ stage: 'resolve-latest-version',
374
+ message: 'Latest document version could not be resolved.',
375
+ });
376
+ continue;
377
+ }
378
+ let rows = yield* _(this.listPersistedDocumentIndexRows(repository, seed.documentVersionId, localeKey, stage, 'searchScopePersistedDocumentIndex.find'));
379
+ if (rows.length === 0) {
380
+ const buildAttempt = yield* _(Effect.either(this.buildPersistedDocumentIndex({
381
+ documentVersionId: seed.documentVersionId,
382
+ locale: localeKey.locale,
383
+ fallbackLocale: localeKey.fallbackLocale,
384
+ })));
385
+ if (buildAttempt._tag === 'Left') {
386
+ failures.push({
387
+ documentId: seed.documentId,
388
+ documentTitle: seed.documentTitle,
389
+ documentVersionId: seed.documentVersionId,
390
+ stage: 'build-index',
391
+ message: this.describeScopeSearchError(buildAttempt.left),
392
+ });
393
+ continue;
394
+ }
395
+ autoBuiltDocumentVersionIds.push(seed.documentVersionId);
396
+ rows = yield* _(this.listPersistedDocumentIndexRows(repository, seed.documentVersionId, localeKey, stage, 'searchScopePersistedDocumentIndex.reload'));
397
+ }
398
+ const searchResult = yield* _(Effect.either(this.searchPersistedDocumentIndexRows(seed.documentVersionId, localeKey, q, limit, retrievalStrategy, rows)));
399
+ if (searchResult._tag === 'Left') {
400
+ failures.push({
401
+ documentId: seed.documentId,
402
+ documentTitle: seed.documentTitle,
403
+ documentVersionId: seed.documentVersionId,
404
+ stage: 'search',
405
+ message: this.describeScopeSearchError(searchResult.left),
406
+ });
407
+ continue;
408
+ }
409
+ searchedDocumentCount += 1;
410
+ aggregatedHits.push(...searchResult.right.hits.map((hit) => this.toScopeDocumentSearchHit(seed, hit)));
411
+ }
412
+ const hits = [...aggregatedHits]
413
+ .sort((left, right) => {
414
+ const scoreDelta = right.score - left.score;
415
+ if (scoreDelta !== 0)
416
+ return scoreDelta;
417
+ const pageDelta = (left.pageNumberStart ?? Number.MAX_SAFE_INTEGER) - (right.pageNumberStart ?? Number.MAX_SAFE_INTEGER);
418
+ if (pageDelta !== 0)
419
+ return pageDelta;
420
+ const titleDelta = left.documentTitle.localeCompare(right.documentTitle);
421
+ if (titleDelta !== 0)
422
+ return titleDelta;
423
+ const breadcrumbDelta = left.breadcrumb.localeCompare(right.breadcrumb);
424
+ if (breadcrumbDelta !== 0)
425
+ return breadcrumbDelta;
426
+ return left.anchor.localeCompare(right.anchor);
427
+ })
428
+ .slice(0, limit);
429
+ return {
430
+ scopeId,
431
+ locale: localeKey.locale,
432
+ fallbackLocale: localeKey.fallbackLocale,
433
+ q,
434
+ hits,
435
+ provenance: {
436
+ strategy: this.toDocumentSearchStrategy(retrievalStrategy),
437
+ retrievalStrategy,
438
+ totalDocumentCount: documents.length,
439
+ searchedDocumentCount,
440
+ autoBuiltDocumentCount: autoBuiltDocumentVersionIds.length,
441
+ failedDocumentCount: failures.length,
442
+ },
443
+ buildReport: {
444
+ autoBuiltDocumentVersionIds,
445
+ failures,
446
+ },
447
+ };
448
+ }).pipe(Effect.tapError((e) => Effect.sync(() => {
449
+ const info = effectErrorInfo(e);
450
+ this.logger?.error({ error: info.unwrapped, stage }, 'Error in searchScopePersistedDocumentIndex');
451
+ })));
452
+ }
453
+ getDocumentAnswerPack(input) {
454
+ const stage = 'DocumentService::getDocumentAnswerPack';
455
+ return Effect.gen(this, function* (_) {
456
+ const payload = yield* _(validateInput(input, 'input', { stage }));
457
+ const versionId = yield* _(validateInput(payload.documentVersionId, 'documentVersionId', { stage }));
458
+ const q = this.normalizeNonEmpty(payload.q);
459
+ if (!q) {
460
+ return yield* _(Effect.fail(XfErrorFactory.inputRequired({ field: 'q', stage })));
461
+ }
462
+ const localeKey = this.resolveDocumentIndexLocaleKey(payload);
463
+ const repository = yield* _(this.requireDependency(this.documentIndexEntryRepository, 'documentIndexEntryRepository', stage, 'getDocumentAnswerPack'));
464
+ const rows = yield* _(this.listPersistedDocumentIndexRows(repository, versionId, localeKey, stage, 'getDocumentAnswerPack.find'));
465
+ return yield* _(this.buildDocumentAnswerPack(versionId, localeKey, q, payload.limit, payload.retrievalStrategy, rows));
466
+ }).pipe(Effect.tapError((e) => Effect.sync(() => {
467
+ const info = effectErrorInfo(e);
468
+ this.logger?.error({ error: info.unwrapped, stage }, 'Error in getDocumentAnswerPack');
469
+ })));
470
+ }
471
+ fetchComposedFragment(input) {
472
+ const stage = 'DocumentService::fetchComposedFragment';
473
+ return Effect.gen(this, function* (_) {
474
+ const payload = yield* _(validateInput(input, 'input', { stage }));
475
+ const versionId = yield* _(validateInput(payload.documentVersionId, 'documentVersionId', { stage }));
476
+ const resolved = yield* _(this.resolveComposedDocument(versionId, payload, stage, 'fetchComposedFragment'));
477
+ if (payload.pageNumber !== undefined) {
478
+ const pageNumber = Number(payload.pageNumber);
479
+ if (!Number.isInteger(pageNumber) || pageNumber <= 0) {
480
+ return yield* _(Effect.fail(XfErrorFactory.inputRequired({
481
+ field: 'pageNumber',
482
+ stage,
483
+ message: 'pageNumber must be a positive integer.',
484
+ })));
485
+ }
486
+ const page = resolved.pages.find((item) => item.pageNumber === pageNumber);
487
+ if (!page) {
488
+ return yield* _(Effect.fail(XfErrorFactory.notFound({
489
+ stage,
490
+ operation: 'fetchComposedFragment',
491
+ message: 'Composed page not found.',
492
+ identifier: { documentVersionId: versionId, pageNumber },
493
+ })));
494
+ }
495
+ const bundle = this.renderPageSourceBundle(resolved.title, page);
496
+ return {
497
+ documentVersionId: versionId,
498
+ kind: 'page',
499
+ pageNumber,
500
+ ...bundle,
501
+ };
502
+ }
503
+ const targetPageVersionId = this.normalizeNonEmpty(payload.pageVersionId);
504
+ if (targetPageVersionId) {
505
+ const pageItem = resolved.items.find((item) => item.kind === 'page' && item.pageVersionId === targetPageVersionId);
506
+ if (!pageItem) {
507
+ return yield* _(Effect.fail(XfErrorFactory.notFound({
508
+ stage,
509
+ operation: 'fetchComposedFragment',
510
+ message: 'Page not found in composed document.',
511
+ identifier: { documentVersionId: versionId, pageVersionId: targetPageVersionId },
512
+ })));
513
+ }
514
+ const bundle = this.renderSinglePageItemSourceBundle(resolved.title, pageItem);
515
+ return {
516
+ documentVersionId: versionId,
517
+ kind: 'page',
518
+ pageVersionId: pageItem.pageVersionId,
519
+ ...bundle,
520
+ };
521
+ }
522
+ const targetSectionId = this.normalizeNonEmpty(payload.sectionId);
523
+ if (targetSectionId) {
524
+ const sectionItem = resolved.items.find((item) => item.kind === 'section' && item.sectionId === targetSectionId);
525
+ if (!sectionItem) {
526
+ return yield* _(Effect.fail(XfErrorFactory.notFound({
527
+ stage,
528
+ operation: 'fetchComposedFragment',
529
+ message: 'Section not found in composed document.',
530
+ identifier: { documentVersionId: versionId, sectionId: targetSectionId },
531
+ })));
532
+ }
533
+ const bundle = this.renderSectionSourceBundleFromSection(resolved.title, resolved, sectionItem);
534
+ return {
535
+ documentVersionId: versionId,
536
+ kind: 'section',
537
+ sectionId: sectionItem.sectionId,
538
+ ...bundle,
539
+ };
540
+ }
541
+ const bundle = this.renderDocumentSourceBundleFromResolved(resolved);
542
+ return {
543
+ documentVersionId: versionId,
544
+ kind: 'document',
545
+ ...bundle,
546
+ };
547
+ }).pipe(Effect.tapError((e) => Effect.sync(() => {
548
+ const info = effectErrorInfo(e);
549
+ this.logger?.error({ error: info.unwrapped, stage }, 'Error in fetchComposedFragment');
550
+ })));
551
+ }
552
+ materializePublishedFragment(input) {
553
+ const stage = 'DocumentService::materializePublishedFragment';
554
+ return Effect.gen(this, function* (_) {
555
+ const payload = yield* _(validateInput(input, 'input', { stage }));
556
+ const targetDescriptor = resolveDocmanPublishTargetDescriptor(payload.target);
557
+ if (!targetDescriptor) {
558
+ return yield* _(Effect.fail(XfErrorFactory.inputRequired({
559
+ field: 'target',
560
+ stage,
561
+ message: `target must be one of: ${formatDocmanPublishTargets()}`,
562
+ })));
563
+ }
564
+ const composed = yield* _(this.fetchComposedFragment({
565
+ documentVersionId: payload.documentVersionId,
566
+ sectionId: payload.sectionId,
567
+ pageVersionId: payload.pageVersionId,
568
+ pageNumber: payload.pageNumber,
569
+ locale: payload.locale,
570
+ fallbackLocale: payload.fallbackLocale,
571
+ }));
572
+ const materialized = this.materializePublishContent(composed, targetDescriptor);
573
+ return {
574
+ ...composed,
575
+ target: targetDescriptor.target,
576
+ mediaType: materialized.mediaType,
577
+ content: materialized.content,
578
+ warnings: materialized.warnings,
579
+ };
580
+ }).pipe(Effect.tapError((e) => Effect.sync(() => {
581
+ const info = effectErrorInfo(e);
582
+ this.logger?.error({ error: info.unwrapped, stage }, 'Error in materializePublishedFragment');
583
+ })));
584
+ }
585
+ removeDocumentSafe(id, confirmName) {
586
+ const stage = 'DocumentService::removeDocumentSafe';
587
+ return Effect.gen(this, function* (_) {
588
+ const entityId = yield* _(validateInput(id, 'id', { stage }));
589
+ const confirmation = String(confirmName ?? '').trim();
590
+ if (!confirmation) {
591
+ return yield* _(Effect.fail(XfErrorFactory.inputRequired({ field: 'confirmName', stage })));
592
+ }
593
+ const document = yield* _(this.documentRepository.findById(entityId).pipe(Effect.mapError(mapDbError({ stage, operation: 'findById', factory: XfErrorFactory.notFound }))));
594
+ const expected = String(document?.title ?? '').trim();
595
+ if (confirmation !== expected) {
596
+ return yield* _(Effect.fail(XfErrorFactory.upsertFailed({
597
+ stage,
598
+ operation: 'confirmName',
599
+ message: 'Document title confirmation mismatch.',
600
+ data: { expected, received: confirmation },
601
+ })));
602
+ }
603
+ const dependencies = yield* _(this.resolveCascadeDependencies(stage, 'removeDocumentSafe'));
604
+ return yield* _((deleteDocumentCascade(dependencies, entityId, stage).pipe(Effect.mapError((error) => this.normalizeCascadeError(stage, 'removeDocumentSafe.cascade', error)))));
605
+ }).pipe(Effect.tapError((e) => Effect.sync(() => {
606
+ const info = effectErrorInfo(e);
607
+ this.logger?.error({ error: info.unwrapped, stage }, 'Error in removeDocumentSafe');
608
+ })));
609
+ }
610
+ resolveComposedDocument(documentVersionId, options, stage, operation) {
611
+ return Effect.gen(this, function* (_) {
612
+ const localeState = this.resolveLocaleOptions(options);
613
+ const documentVersionRepository = yield* _(this.requireDependency(this.documentVersionRepository, 'documentVersionRepository', stage, operation));
614
+ const documentSectionLinkRepository = yield* _(this.requireDependency(this.documentSectionLinkRepository, 'documentSectionLinkRepository', stage, operation));
615
+ const sectionRepository = yield* _(this.requireDependency(this.sectionRepository, 'sectionRepository', stage, operation));
616
+ const pageVersionRepository = yield* _(this.requireDependency(this.pageVersionRepository, 'pageVersionRepository', stage, operation));
617
+ const documentVersionQueryOptions = this.withLocaleOptions(undefined, localeState, ['releaseNotesMl']);
618
+ const sectionQueryOptions = this.withLocaleOptions(undefined, localeState, ['titleMl']);
619
+ const pageQueryOptions = this.withLocaleOptions(undefined, localeState, ['titleMl']);
620
+ const pageVersionQueryOptions = this.withLocaleOptions(undefined, localeState, ['contentMl']);
621
+ const documentVersion = yield* _(documentVersionRepository.findById(documentVersionId, documentVersionQueryOptions).pipe(Effect.mapError(mapDbError({ stage, operation: 'documentVersionRepository.findById', factory: XfErrorFactory.notFound }))));
622
+ if (!documentVersion?.id) {
623
+ return yield* _(Effect.fail(XfErrorFactory.notFound({
624
+ stage,
625
+ operation: 'documentVersionRepository.findById',
626
+ message: 'Document version not found.',
627
+ identifier: { documentVersionId },
628
+ })));
629
+ }
630
+ const documentQueryOptions = this.withLocaleOptions(undefined, localeState, ['titleMl', 'summaryMl', 'descriptionMl']);
631
+ const document = yield* _(this.documentRepository.findById(documentVersion.documentId, documentQueryOptions).pipe(Effect.mapError(mapDbError({ stage, operation: 'documentRepository.findById', factory: XfErrorFactory.notFound }))));
632
+ if (!document?.id) {
633
+ return yield* _(Effect.fail(XfErrorFactory.notFound({
634
+ stage,
635
+ operation: 'documentRepository.findById',
636
+ message: 'Document not found.',
637
+ identifier: { documentId: documentVersion.documentId },
638
+ })));
639
+ }
640
+ const links = yield* _(documentSectionLinkRepository
641
+ .find({
642
+ matchEq: { documentVersionId },
643
+ options: { sort: [{ field: 'position', type: 'asc' }] },
644
+ })
645
+ .pipe(Effect.mapError(mapDbError({
646
+ stage,
647
+ operation: 'documentSectionLinkRepository.find',
648
+ factory: XfErrorFactory.notFound,
649
+ }))));
650
+ const traversed = this.traverseLinks(links, undefined, 0);
651
+ const items = [];
652
+ for (const node of traversed) {
653
+ const linkId = node.link.id;
654
+ if (!linkId)
655
+ continue;
656
+ if (node.link.kind === 'section') {
657
+ const sectionId = this.normalizeNonEmpty(node.link.sectionId);
658
+ if (!sectionId)
659
+ continue;
660
+ const section = yield* _(sectionRepository.findById(sectionId, sectionQueryOptions).pipe(Effect.mapError(mapDbError({
661
+ stage,
662
+ operation: 'sectionRepository.findById',
663
+ factory: XfErrorFactory.notFound,
664
+ }))));
665
+ if (!section)
666
+ continue;
667
+ items.push({
668
+ linkId,
669
+ kind: 'section',
670
+ sectionId,
671
+ sectionUid: this.normalizeNonEmpty(section.sectionUid),
672
+ sectionSlug: this.normalizeNonEmpty(section.slug),
673
+ number: node.number,
674
+ depth: node.depth,
675
+ position: node.link.position,
676
+ title: this.resolveSectionTitle(section, node.link.titleOverride, localeState),
677
+ parentLinkId: this.normalizeNonEmpty(node.link.parentLinkId),
678
+ titleVisible: node.link.titleVisible !== false,
679
+ pageBreakBefore: node.link.pageBreakBefore === true,
680
+ pageBreakAfter: node.link.pageBreakAfter === true,
681
+ directives: node.link.directives,
682
+ });
683
+ continue;
684
+ }
685
+ if (node.link.kind === 'page') {
686
+ const pageVersionId = this.normalizeNonEmpty(node.link.pageVersionId);
687
+ if (!pageVersionId)
688
+ continue;
689
+ const pageVersion = yield* _(pageVersionRepository.findById(pageVersionId, pageVersionQueryOptions).pipe(Effect.mapError(mapDbError({
690
+ stage,
691
+ operation: 'pageVersionRepository.findById',
692
+ factory: XfErrorFactory.notFound,
693
+ }))));
694
+ if (!pageVersion?.id)
695
+ continue;
696
+ const resolvedPageSource = yield* _(this.resolveComposePageSource(pageVersion, localeState, stage, operation));
697
+ let page = null;
698
+ if (this.pageRepository) {
699
+ page = yield* _(this.pageRepository.findById(pageVersion.pageId, pageQueryOptions).pipe(Effect.mapError(mapDbError({
700
+ stage,
701
+ operation: 'pageRepository.findById',
702
+ factory: XfErrorFactory.notFound,
703
+ }))));
704
+ }
705
+ items.push({
706
+ linkId,
707
+ kind: 'page',
708
+ pageVersionId,
709
+ pageId: pageVersion.pageId,
710
+ pageUid: this.normalizeNonEmpty(page?.pageUid),
711
+ format: resolvedPageSource.format,
712
+ modulePreamble: resolvedPageSource.modulePreamble,
713
+ number: node.number,
714
+ depth: node.depth,
715
+ position: node.link.position,
716
+ title: this.resolvePageTitle(page, pageVersion, node.link.titleOverride, localeState),
717
+ parentLinkId: this.normalizeNonEmpty(node.link.parentLinkId),
718
+ titleVisible: node.link.titleVisible !== false,
719
+ pageBreakBefore: node.link.pageBreakBefore === true,
720
+ pageBreakAfter: node.link.pageBreakAfter === true,
721
+ directives: node.link.directives ?? pageVersion.directives,
722
+ contentParts: this.splitContentByPageBreakMarkers(resolvedPageSource.content),
723
+ assetRefs: resolvedPageSource.assets,
724
+ });
725
+ }
726
+ }
727
+ return {
728
+ document: document,
729
+ documentVersion: documentVersion,
730
+ title: this.resolveDocumentTitle(document, documentVersion, localeState),
731
+ documentReleaseNotes: this.resolveVersionReleaseNotes(documentVersion, localeState),
732
+ items,
733
+ pages: this.composeDynamicPages(items),
734
+ };
735
+ });
736
+ }
737
+ composeDynamicPages(items) {
738
+ const pages = [this.createEmptyPage(1)];
739
+ let currentPage = pages[0];
740
+ const hasPageContent = (page) => page.chunks.length > 0;
741
+ const startNewPage = (onlyIfCurrentHasContent = true) => {
742
+ if (onlyIfCurrentHasContent && !hasPageContent(currentPage))
743
+ return;
744
+ currentPage = this.createEmptyPage(pages.length + 1);
745
+ pages.push(currentPage);
746
+ };
747
+ for (const item of items) {
748
+ if (item.pageBreakBefore) {
749
+ startNewPage(true);
750
+ }
751
+ this.pushUnique(currentPage.itemNumbers, item.number);
752
+ if (item.kind !== 'page') {
753
+ if (item.pageBreakAfter) {
754
+ startNewPage(true);
755
+ }
756
+ continue;
757
+ }
758
+ const parts = item.contentParts.length > 0 ? item.contentParts : [''];
759
+ for (let i = 0; i < parts.length; i++) {
760
+ const content = parts[i] ?? '';
761
+ this.pushUnique(currentPage.itemNumbers, item.number);
762
+ this.pushUnique(currentPage.pageVersionIds, item.pageVersionId);
763
+ currentPage.chunks.push({
764
+ linkId: item.linkId,
765
+ number: item.number,
766
+ depth: item.depth,
767
+ title: item.title,
768
+ titleVisible: item.titleVisible,
769
+ pageVersionId: item.pageVersionId,
770
+ pageId: item.pageId,
771
+ format: item.format,
772
+ modulePreamble: item.modulePreamble,
773
+ content,
774
+ assets: item.assetRefs,
775
+ chunkIndex: i + 1,
776
+ chunkCount: parts.length,
777
+ });
778
+ if (i < parts.length - 1) {
779
+ startNewPage(true);
780
+ }
781
+ }
782
+ if (item.pageBreakAfter) {
783
+ startNewPage(true);
784
+ }
785
+ }
786
+ if (pages.length > 1 && !hasPageContent(pages[pages.length - 1])) {
787
+ pages.pop();
788
+ }
789
+ return pages.map((page, index) => ({
790
+ ...page,
791
+ pageNumber: index + 1,
792
+ ...reduceDocmanComposeFormats(page.chunks.map((chunk) => chunk.format)),
793
+ modulePreambles: this.uniqueModulePreambles(page.chunks.map((chunk) => chunk.modulePreamble)),
794
+ assets: this.uniqueAssetRefs(page.chunks.flatMap((chunk) => chunk.assets)),
795
+ }));
796
+ }
797
+ createEmptyPage(pageNumber) {
798
+ return {
799
+ pageNumber,
800
+ format: 'md',
801
+ formats: ['md'],
802
+ itemNumbers: [],
803
+ pageVersionIds: [],
804
+ chunks: [],
805
+ modulePreambles: [],
806
+ assets: [],
807
+ };
808
+ }
809
+ toDocumentComposeIndex(resolved) {
810
+ const items = resolved.items.map((item) => {
811
+ if (item.kind === 'section') {
812
+ return {
813
+ kind: 'section',
814
+ linkId: item.linkId,
815
+ sectionId: item.sectionId,
816
+ number: item.number,
817
+ depth: item.depth,
818
+ position: item.position,
819
+ title: item.title,
820
+ pageBreakBefore: item.pageBreakBefore,
821
+ pageBreakAfter: item.pageBreakAfter,
822
+ parentLinkId: item.parentLinkId,
823
+ directives: item.directives,
824
+ };
825
+ }
826
+ return {
827
+ kind: 'page',
828
+ linkId: item.linkId,
829
+ pageVersionId: item.pageVersionId,
830
+ pageId: item.pageId,
831
+ format: item.format,
832
+ number: item.number,
833
+ depth: item.depth,
834
+ position: item.position,
835
+ title: item.title,
836
+ titleVisible: item.titleVisible,
837
+ pageBreakBefore: item.pageBreakBefore,
838
+ pageBreakAfter: item.pageBreakAfter,
839
+ parentLinkId: item.parentLinkId,
840
+ directives: item.directives,
841
+ };
842
+ });
843
+ return {
844
+ documentId: resolved.document.id,
845
+ documentVersionId: resolved.documentVersion.id,
846
+ title: resolved.title,
847
+ items,
848
+ pages: resolved.pages.map((page) => ({
849
+ pageNumber: page.pageNumber,
850
+ format: page.format,
851
+ formats: [...page.formats],
852
+ itemNumbers: [...page.itemNumbers],
853
+ pageVersionIds: [...page.pageVersionIds],
854
+ })),
855
+ };
856
+ }
857
+ resolveDocumentIndexLocaleKey(options) {
858
+ const localeState = this.resolveLocaleOptions(options);
859
+ const locale = normalizeDocmanDocumentIndexLocale(localeState.locale);
860
+ const fallbackLocale = normalizeDocmanDocumentIndexLocale(localeState.fallbackLocale);
861
+ return {
862
+ ...(locale ? { locale } : {}),
863
+ ...(fallbackLocale ? { fallbackLocale } : {}),
864
+ };
865
+ }
866
+ listPersistedDocumentIndexRows(repository, documentVersionId, localeKey, stage, operation) {
867
+ return repository
868
+ .find({
869
+ matchEq: {
870
+ documentVersionId,
871
+ locale: localeKey.locale ?? '',
872
+ fallbackLocale: localeKey.fallbackLocale ?? '',
873
+ },
874
+ options: {
875
+ sort: [
876
+ { field: 'sortOrder', type: 'asc' },
877
+ { field: 'position', type: 'asc' },
878
+ ],
879
+ },
880
+ })
881
+ .pipe(Effect.map((rows) => [...rows].sort((left, right) => {
882
+ const sortDelta = Number(left.sortOrder ?? 0) - Number(right.sortOrder ?? 0);
883
+ if (sortDelta !== 0)
884
+ return sortDelta;
885
+ return String(left.id ?? '').localeCompare(String(right.id ?? ''));
886
+ })), Effect.mapError(mapDbError({
887
+ stage,
888
+ operation,
889
+ factory: XfErrorFactory.notFound,
890
+ })));
891
+ }
892
+ deletePersistedDocumentIndexRows(repository, rows, stage, operation) {
893
+ if (!rows.length)
894
+ return Effect.succeed(undefined);
895
+ return Effect.all(rows
896
+ .map((row) => this.normalizeNonEmpty(row.id))
897
+ .filter((id) => Boolean(id))
898
+ .map((id) => repository.deleteById(id).pipe(Effect.mapError(mapDbError({
899
+ stage,
900
+ operation,
901
+ factory: XfErrorFactory.upsertFailed,
902
+ })))), { concurrency: 6 }).pipe(Effect.asVoid);
903
+ }
904
+ createPersistedDocumentIndexRows(repository, rows, documentVersionId, localeKey, stage, operation) {
905
+ if (rows.length === 0)
906
+ return Effect.succeed([]);
907
+ const createRows = Effect.all(rows.map((row) => repository.create(row).pipe(Effect.mapError(mapDbError({
908
+ stage,
909
+ operation: 'documentIndexEntryRepository.create',
910
+ factory: XfErrorFactory.createFailed,
911
+ })))), { concurrency: 6 });
912
+ return createRows.pipe(Effect.catchAll((error) => this.listPersistedDocumentIndexRows(repository, documentVersionId, localeKey, stage, `${operation}.reload`).pipe(Effect.flatMap((currentRows) => this.matchesPersistedDocumentIndexRows(rows, currentRows)
913
+ ? Effect.succeed(currentRows)
914
+ : Effect.fail(error)), Effect.catchAll(() => Effect.fail(error)))));
915
+ }
916
+ matchesPersistedDocumentIndexRows(expectedRows, actualRows) {
917
+ if (expectedRows.length !== actualRows.length)
918
+ return false;
919
+ const actualByKey = new Map(actualRows.map((row) => [this.persistedDocumentIndexRowKey(row), row]));
920
+ for (const expectedRow of expectedRows) {
921
+ const actualRow = actualByKey.get(this.persistedDocumentIndexRowKey(expectedRow));
922
+ if (!actualRow || !this.matchesPersistedDocumentIndexRow(expectedRow, actualRow)) {
923
+ return false;
924
+ }
925
+ }
926
+ return true;
927
+ }
928
+ matchesPersistedDocumentIndexRow(expectedRow, actualRow) {
929
+ const fields = [
930
+ 'documentVersionId',
931
+ 'documentId',
932
+ 'locale',
933
+ 'fallbackLocale',
934
+ 'itemKind',
935
+ 'sortOrder',
936
+ 'buildFingerprint',
937
+ 'linkId',
938
+ 'parentLinkId',
939
+ 'anchor',
940
+ 'parentAnchor',
941
+ 'number',
942
+ 'depth',
943
+ 'position',
944
+ 'title',
945
+ 'breadcrumb',
946
+ 'titleVisible',
947
+ 'pageBreakBefore',
948
+ 'pageBreakAfter',
949
+ 'sectionId',
950
+ 'sectionUid',
951
+ 'sectionSlug',
952
+ 'pageId',
953
+ 'pageUid',
954
+ 'pageVersionId',
955
+ 'format',
956
+ 'pageNumberStart',
957
+ 'pageNumberEnd',
958
+ 'bodyText',
959
+ 'searchText',
960
+ ];
961
+ return fields.every((field) => this.normalizePersistedDocumentIndexComparableValue(expectedRow[field]) ===
962
+ this.normalizePersistedDocumentIndexComparableValue(actualRow[field]));
963
+ }
964
+ persistedDocumentIndexRowKey(row) {
965
+ return `${Number(row.sortOrder ?? -1)}::${this.normalizeNonEmpty(row.anchor) ?? ''}`;
966
+ }
967
+ normalizePersistedDocumentIndexComparableValue(value) {
968
+ if (value === null || value === undefined)
969
+ return '';
970
+ if (typeof value === 'boolean')
971
+ return value ? 'true' : 'false';
972
+ return String(value);
973
+ }
974
+ buildPersistedDocumentIndexBuildKey(documentVersionId, localeKey) {
975
+ return `${documentVersionId}::${localeKey.locale ?? ''}::${localeKey.fallbackLocale ?? ''}`;
976
+ }
977
+ toPersistedDocumentIndexRows(resolved, localeKey) {
978
+ const pageRangesByLinkId = this.buildPersistedDocumentPageRanges(resolved.pages);
979
+ const fingerprint = buildDocmanDocumentIndexFingerprint({
980
+ documentVersionId: resolved.documentVersion.id,
981
+ title: resolved.title,
982
+ locale: localeKey.locale ?? '',
983
+ fallbackLocale: localeKey.fallbackLocale ?? '',
984
+ items: resolved.items.map((item) => item.kind === 'section'
985
+ ? {
986
+ kind: item.kind,
987
+ linkId: item.linkId,
988
+ parentLinkId: item.parentLinkId,
989
+ number: item.number,
990
+ depth: item.depth,
991
+ position: item.position,
992
+ title: item.title,
993
+ sectionId: item.sectionId,
994
+ sectionUid: item.sectionUid,
995
+ sectionSlug: item.sectionSlug,
996
+ }
997
+ : {
998
+ kind: item.kind,
999
+ linkId: item.linkId,
1000
+ parentLinkId: item.parentLinkId,
1001
+ number: item.number,
1002
+ depth: item.depth,
1003
+ position: item.position,
1004
+ title: item.title,
1005
+ pageId: item.pageId,
1006
+ pageUid: item.pageUid,
1007
+ pageVersionId: item.pageVersionId,
1008
+ format: item.format,
1009
+ content: item.contentParts,
1010
+ assets: item.assetRefs.map((assetRef) => assetRef.assetVersionId),
1011
+ }),
1012
+ pages: resolved.pages.map((page) => ({
1013
+ pageNumber: page.pageNumber,
1014
+ linkIds: page.chunks.map((chunk) => chunk.linkId),
1015
+ })),
1016
+ });
1017
+ const documentAnchor = buildDocmanDocumentAnchor(resolved.document.documentUid ?? resolved.document.id);
1018
+ const anchorByLinkId = new Map();
1019
+ const breadcrumbByLinkId = new Map();
1020
+ const rows = [];
1021
+ rows.push({
1022
+ documentVersionId: resolved.documentVersion.id,
1023
+ documentId: resolved.document.id,
1024
+ locale: localeKey.locale ?? '',
1025
+ fallbackLocale: localeKey.fallbackLocale ?? '',
1026
+ itemKind: 'document',
1027
+ sortOrder: 0,
1028
+ buildFingerprint: fingerprint,
1029
+ anchor: documentAnchor,
1030
+ depth: 0,
1031
+ position: 0,
1032
+ title: resolved.title,
1033
+ breadcrumb: resolved.title,
1034
+ titleVisible: true,
1035
+ pageBreakBefore: false,
1036
+ pageBreakAfter: false,
1037
+ createdBy: DOCMAN_DOCUMENT_INDEX_BUILD_ACTOR,
1038
+ updatedBy: DOCMAN_DOCUMENT_INDEX_BUILD_ACTOR,
1039
+ searchText: this.normalizeDocumentIndexSearchText([resolved.title, this.normalizeNonEmpty(resolved.documentReleaseNotes)].filter(Boolean).join('\n')),
1040
+ });
1041
+ for (const [index, item] of resolved.items.entries()) {
1042
+ const parentAnchor = item.parentLinkId
1043
+ ? anchorByLinkId.get(item.parentLinkId) ?? documentAnchor
1044
+ : documentAnchor;
1045
+ const breadcrumb = this.buildDocumentIndexBreadcrumb(item.title, item.parentLinkId, breadcrumbByLinkId, resolved.title);
1046
+ if (item.kind === 'section') {
1047
+ const anchor = buildDocmanSectionAnchor(item.sectionSlug ?? item.sectionUid ?? item.sectionId, item.linkId);
1048
+ anchorByLinkId.set(item.linkId, anchor);
1049
+ breadcrumbByLinkId.set(item.linkId, breadcrumb);
1050
+ rows.push({
1051
+ documentVersionId: resolved.documentVersion.id,
1052
+ documentId: resolved.document.id,
1053
+ locale: localeKey.locale ?? '',
1054
+ fallbackLocale: localeKey.fallbackLocale ?? '',
1055
+ itemKind: 'section',
1056
+ sortOrder: index + 1,
1057
+ buildFingerprint: fingerprint,
1058
+ linkId: item.linkId,
1059
+ parentLinkId: item.parentLinkId,
1060
+ anchor,
1061
+ parentAnchor,
1062
+ number: item.number,
1063
+ depth: item.depth,
1064
+ position: item.position,
1065
+ title: item.title,
1066
+ breadcrumb,
1067
+ titleVisible: item.titleVisible,
1068
+ pageBreakBefore: item.pageBreakBefore,
1069
+ pageBreakAfter: item.pageBreakAfter,
1070
+ sectionId: item.sectionId,
1071
+ sectionUid: item.sectionUid,
1072
+ sectionSlug: item.sectionSlug,
1073
+ createdBy: DOCMAN_DOCUMENT_INDEX_BUILD_ACTOR,
1074
+ updatedBy: DOCMAN_DOCUMENT_INDEX_BUILD_ACTOR,
1075
+ searchText: this.normalizeDocumentIndexSearchText([item.number, item.title, breadcrumb].filter(Boolean).join('\n')),
1076
+ });
1077
+ continue;
1078
+ }
1079
+ const anchor = buildDocmanPageAnchor(item.pageUid ?? item.pageId, item.linkId);
1080
+ const pageRange = pageRangesByLinkId.get(item.linkId);
1081
+ const bodyText = this.extractDocumentIndexBodyText(item);
1082
+ anchorByLinkId.set(item.linkId, anchor);
1083
+ breadcrumbByLinkId.set(item.linkId, breadcrumb);
1084
+ rows.push({
1085
+ documentVersionId: resolved.documentVersion.id,
1086
+ documentId: resolved.document.id,
1087
+ locale: localeKey.locale ?? '',
1088
+ fallbackLocale: localeKey.fallbackLocale ?? '',
1089
+ itemKind: 'page',
1090
+ sortOrder: index + 1,
1091
+ buildFingerprint: fingerprint,
1092
+ linkId: item.linkId,
1093
+ parentLinkId: item.parentLinkId,
1094
+ anchor,
1095
+ parentAnchor,
1096
+ number: item.number,
1097
+ depth: item.depth,
1098
+ position: item.position,
1099
+ title: item.title,
1100
+ breadcrumb,
1101
+ titleVisible: item.titleVisible,
1102
+ pageBreakBefore: item.pageBreakBefore,
1103
+ pageBreakAfter: item.pageBreakAfter,
1104
+ pageId: item.pageId,
1105
+ pageUid: item.pageUid,
1106
+ pageVersionId: item.pageVersionId,
1107
+ format: item.format,
1108
+ pageNumberStart: pageRange?.start,
1109
+ pageNumberEnd: pageRange?.end,
1110
+ bodyText,
1111
+ searchText: this.normalizeDocumentIndexSearchText([item.number, item.title, breadcrumb, bodyText].filter(Boolean).join('\n')),
1112
+ createdBy: DOCMAN_DOCUMENT_INDEX_BUILD_ACTOR,
1113
+ updatedBy: DOCMAN_DOCUMENT_INDEX_BUILD_ACTOR,
1114
+ });
1115
+ }
1116
+ return rows;
1117
+ }
1118
+ toPersistedDocumentIndexSnapshot(documentVersionId, localeKey, rows) {
1119
+ const sorted = [...rows].sort((left, right) => {
1120
+ const sortDelta = Number(left.sortOrder ?? 0) - Number(right.sortOrder ?? 0);
1121
+ if (sortDelta !== 0)
1122
+ return sortDelta;
1123
+ return String(left.id ?? '').localeCompare(String(right.id ?? ''));
1124
+ });
1125
+ const documentRow = sorted.find((row) => row.itemKind === 'document');
1126
+ const entries = sorted
1127
+ .filter((row) => row.itemKind === 'section' || row.itemKind === 'page')
1128
+ .map((row) => this.toPersistedDocumentIndexSnapshotEntry(row));
1129
+ return {
1130
+ documentId: this.normalizeNonEmpty(documentRow?.documentId) ?? this.normalizeNonEmpty(sorted[0]?.documentId),
1131
+ documentVersionId,
1132
+ title: this.normalizeNonEmpty(documentRow?.title),
1133
+ locale: this.normalizeNonEmpty(documentRow?.locale) ?? localeKey.locale,
1134
+ fallbackLocale: this.normalizeNonEmpty(documentRow?.fallbackLocale) ?? localeKey.fallbackLocale,
1135
+ built: sorted.length > 0,
1136
+ buildFingerprint: this.normalizeNonEmpty(documentRow?.buildFingerprint) ??
1137
+ this.normalizeNonEmpty(sorted[0]?.buildFingerprint),
1138
+ documentAnchor: this.normalizeNonEmpty(documentRow?.anchor),
1139
+ entries,
1140
+ counts: {
1141
+ sections: entries.filter((entry) => entry.itemKind === 'section').length,
1142
+ pages: entries.filter((entry) => entry.itemKind === 'page').length,
1143
+ },
1144
+ };
1145
+ }
1146
+ toPersistedDocumentSummarySnapshot(documentVersionId, localeKey, rows) {
1147
+ const sorted = [...rows].sort((left, right) => {
1148
+ const sortDelta = Number(left.sortOrder ?? 0) - Number(right.sortOrder ?? 0);
1149
+ if (sortDelta !== 0)
1150
+ return sortDelta;
1151
+ return String(left.id ?? '').localeCompare(String(right.id ?? ''));
1152
+ });
1153
+ const documentRow = sorted.find((row) => row.itemKind === 'document');
1154
+ const entries = sorted
1155
+ .filter((row) => Boolean(this.normalizeNonEmpty(row.summaryText)))
1156
+ .map((row) => this.toPersistedDocumentSummarySnapshotEntry(row));
1157
+ return {
1158
+ documentId: this.normalizeNonEmpty(documentRow?.documentId) ?? this.normalizeNonEmpty(sorted[0]?.documentId),
1159
+ documentVersionId,
1160
+ title: this.normalizeNonEmpty(documentRow?.title),
1161
+ locale: this.normalizeNonEmpty(documentRow?.locale) ?? localeKey.locale,
1162
+ fallbackLocale: this.normalizeNonEmpty(documentRow?.fallbackLocale) ?? localeKey.fallbackLocale,
1163
+ built: entries.length > 0,
1164
+ buildFingerprint: this.normalizeNonEmpty(documentRow?.buildFingerprint) ??
1165
+ this.normalizeNonEmpty(sorted[0]?.buildFingerprint),
1166
+ documentAnchor: this.normalizeNonEmpty(documentRow?.anchor),
1167
+ entries,
1168
+ counts: {
1169
+ documents: entries.filter((entry) => entry.itemKind === 'document').length,
1170
+ sections: entries.filter((entry) => entry.itemKind === 'section').length,
1171
+ pages: entries.filter((entry) => entry.itemKind === 'page').length,
1172
+ },
1173
+ };
1174
+ }
1175
+ toPersistedDocumentIndexSnapshotEntry(row) {
1176
+ return {
1177
+ itemKind: row.itemKind === 'section' ? 'section' : 'page',
1178
+ linkId: this.normalizeNonEmpty(row.linkId),
1179
+ parentLinkId: this.normalizeNonEmpty(row.parentLinkId),
1180
+ anchor: row.anchor,
1181
+ parentAnchor: this.normalizeNonEmpty(row.parentAnchor),
1182
+ number: this.normalizeNonEmpty(row.number),
1183
+ depth: Number(row.depth ?? 0),
1184
+ position: Number(row.position ?? 0),
1185
+ title: row.title,
1186
+ breadcrumb: row.breadcrumb,
1187
+ titleVisible: this.toDocumentIndexBoolean(row.titleVisible),
1188
+ pageBreakBefore: this.toDocumentIndexBoolean(row.pageBreakBefore),
1189
+ pageBreakAfter: this.toDocumentIndexBoolean(row.pageBreakAfter),
1190
+ sectionId: this.normalizeNonEmpty(row.sectionId),
1191
+ sectionUid: this.normalizeNonEmpty(row.sectionUid),
1192
+ sectionSlug: this.normalizeNonEmpty(row.sectionSlug),
1193
+ pageId: this.normalizeNonEmpty(row.pageId),
1194
+ pageUid: this.normalizeNonEmpty(row.pageUid),
1195
+ pageVersionId: this.normalizeNonEmpty(row.pageVersionId),
1196
+ format: row.format,
1197
+ pageNumberStart: Number.isInteger(Number(row.pageNumberStart)) ? Number(row.pageNumberStart) : undefined,
1198
+ pageNumberEnd: Number.isInteger(Number(row.pageNumberEnd)) ? Number(row.pageNumberEnd) : undefined,
1199
+ };
1200
+ }
1201
+ toPersistedDocumentSummarySnapshotEntry(row) {
1202
+ const itemKind = row.itemKind === 'document'
1203
+ ? 'document'
1204
+ : row.itemKind === 'section'
1205
+ ? 'section'
1206
+ : 'page';
1207
+ return {
1208
+ itemKind,
1209
+ linkId: this.normalizeNonEmpty(row.linkId),
1210
+ parentLinkId: this.normalizeNonEmpty(row.parentLinkId),
1211
+ anchor: row.anchor,
1212
+ parentAnchor: this.normalizeNonEmpty(row.parentAnchor),
1213
+ number: this.normalizeNonEmpty(row.number),
1214
+ depth: Number(row.depth ?? 0),
1215
+ position: Number(row.position ?? 0),
1216
+ title: row.title,
1217
+ breadcrumb: row.breadcrumb,
1218
+ titleVisible: this.toDocumentIndexBoolean(row.titleVisible),
1219
+ pageBreakBefore: this.toDocumentIndexBoolean(row.pageBreakBefore),
1220
+ pageBreakAfter: this.toDocumentIndexBoolean(row.pageBreakAfter),
1221
+ sectionId: this.normalizeNonEmpty(row.sectionId),
1222
+ sectionUid: this.normalizeNonEmpty(row.sectionUid),
1223
+ sectionSlug: this.normalizeNonEmpty(row.sectionSlug),
1224
+ pageId: this.normalizeNonEmpty(row.pageId),
1225
+ pageUid: this.normalizeNonEmpty(row.pageUid),
1226
+ pageVersionId: this.normalizeNonEmpty(row.pageVersionId),
1227
+ format: row.format,
1228
+ pageNumberStart: Number.isInteger(Number(row.pageNumberStart)) ? Number(row.pageNumberStart) : undefined,
1229
+ pageNumberEnd: Number.isInteger(Number(row.pageNumberEnd)) ? Number(row.pageNumberEnd) : undefined,
1230
+ summaryText: this.normalizeNonEmpty(row.summaryText) ?? row.title,
1231
+ sourceCharCount: this.toNonNegativeInteger(row.sourceCharCount),
1232
+ sourceWordCount: this.toNonNegativeInteger(row.sourceWordCount),
1233
+ summaryCharCount: this.toNonNegativeInteger(row.summaryCharCount),
1234
+ summaryWordCount: this.toNonNegativeInteger(row.summaryWordCount),
1235
+ };
1236
+ }
1237
+ buildDocumentAnswerPack(documentVersionId, localeKey, q, limitRaw, retrievalStrategyRaw, rows) {
1238
+ const retrievalStrategy = this.resolveDocumentRetrievalStrategy(retrievalStrategyRaw);
1239
+ const normalizedQuery = this.normalizeDocumentIndexSearchText(q);
1240
+ const queryTokens = this.tokenizeDocumentIndexSearchTokens(normalizedQuery);
1241
+ const limit = this.resolveDocumentAnswerPackLimit(limitRaw);
1242
+ const sorted = [...rows].sort((left, right) => {
1243
+ const sortDelta = Number(left.sortOrder ?? 0) - Number(right.sortOrder ?? 0);
1244
+ if (sortDelta !== 0)
1245
+ return sortDelta;
1246
+ return String(left.id ?? '').localeCompare(String(right.id ?? ''));
1247
+ });
1248
+ const documentRow = sorted.find((row) => row.itemKind === 'document');
1249
+ if (sorted.length === 0 || !normalizedQuery || queryTokens.length === 0) {
1250
+ return Effect.succeed({
1251
+ documentVersionId,
1252
+ locale: this.normalizeNonEmpty(documentRow?.locale) ?? localeKey.locale,
1253
+ fallbackLocale: this.normalizeNonEmpty(documentRow?.fallbackLocale) ?? localeKey.fallbackLocale,
1254
+ q,
1255
+ built: sorted.length > 0,
1256
+ buildFingerprint: this.normalizeNonEmpty(documentRow?.buildFingerprint) ??
1257
+ this.normalizeNonEmpty(sorted[0]?.buildFingerprint),
1258
+ answer: '',
1259
+ answerSource: 'none',
1260
+ citations: [],
1261
+ provenance: {
1262
+ strategy: this.toDocumentAnswerPackProvenanceStrategy(retrievalStrategy),
1263
+ retrievalStrategy,
1264
+ citationCount: 0,
1265
+ primaryMatchedBy: [],
1266
+ vectorAvailable: false,
1267
+ },
1268
+ });
1269
+ }
1270
+ return Effect.gen(this, function* (_) {
1271
+ const vectorState = yield* _(this.resolveDocumentVectorState(sorted, normalizedQuery, retrievalStrategy, 'buildDocumentAnswerPack.vector'));
1272
+ const citations = this.rankDocumentAnswerPackRows(sorted, normalizedQuery, queryTokens, vectorState)
1273
+ .map((match) => this.toDocumentAnswerPackCitation(match.row, match, queryTokens))
1274
+ .sort((left, right) => {
1275
+ const scoreDelta = right.score - left.score;
1276
+ if (scoreDelta !== 0)
1277
+ return scoreDelta;
1278
+ const kindPriority = this.resolveDocumentAnswerPackKindPriority(left.itemKind) -
1279
+ this.resolveDocumentAnswerPackKindPriority(right.itemKind);
1280
+ if (kindPriority !== 0)
1281
+ return kindPriority;
1282
+ const pageDelta = (left.pageNumberStart ?? Number.MAX_SAFE_INTEGER) - (right.pageNumberStart ?? Number.MAX_SAFE_INTEGER);
1283
+ if (pageDelta !== 0)
1284
+ return pageDelta;
1285
+ return left.breadcrumb.localeCompare(right.breadcrumb);
1286
+ })
1287
+ .slice(0, limit);
1288
+ const primary = citations[0];
1289
+ const answerSelection = this.selectDocumentAnswerPackAnswer(primary);
1290
+ return {
1291
+ documentVersionId,
1292
+ locale: this.normalizeNonEmpty(documentRow?.locale) ?? localeKey.locale,
1293
+ fallbackLocale: this.normalizeNonEmpty(documentRow?.fallbackLocale) ?? localeKey.fallbackLocale,
1294
+ q,
1295
+ built: sorted.length > 0,
1296
+ buildFingerprint: this.normalizeNonEmpty(documentRow?.buildFingerprint) ??
1297
+ this.normalizeNonEmpty(sorted[0]?.buildFingerprint),
1298
+ answer: answerSelection.answer,
1299
+ answerSource: answerSelection.answerSource,
1300
+ citations,
1301
+ provenance: {
1302
+ strategy: this.toDocumentAnswerPackProvenanceStrategy(retrievalStrategy),
1303
+ retrievalStrategy,
1304
+ citationCount: citations.length,
1305
+ selectedAnchor: primary?.anchor,
1306
+ selectedItemKind: primary?.itemKind,
1307
+ primaryMatchedBy: primary?.matchedBy ?? [],
1308
+ vectorAvailable: vectorState.vectorAvailable,
1309
+ vectorProvider: vectorState.vectorProvider,
1310
+ vectorModel: vectorState.vectorModel,
1311
+ },
1312
+ };
1313
+ });
1314
+ }
1315
+ toDocumentAnswerPackCitation(row, match, queryTokens) {
1316
+ const itemKind = row.itemKind === 'document'
1317
+ ? 'document'
1318
+ : row.itemKind === 'section'
1319
+ ? 'section'
1320
+ : 'page';
1321
+ const summaryText = this.normalizeNonEmpty(row.summaryText)
1322
+ ? this.normalizeDocumentSummarySourceText(row.summaryText)
1323
+ : undefined;
1324
+ return {
1325
+ itemKind,
1326
+ anchor: row.anchor,
1327
+ parentAnchor: this.normalizeNonEmpty(row.parentAnchor),
1328
+ number: this.normalizeNonEmpty(row.number),
1329
+ depth: Number(row.depth ?? 0),
1330
+ title: row.title,
1331
+ breadcrumb: row.breadcrumb,
1332
+ sectionId: this.normalizeNonEmpty(row.sectionId),
1333
+ sectionUid: this.normalizeNonEmpty(row.sectionUid),
1334
+ sectionSlug: this.normalizeNonEmpty(row.sectionSlug),
1335
+ pageId: this.normalizeNonEmpty(row.pageId),
1336
+ pageUid: this.normalizeNonEmpty(row.pageUid),
1337
+ pageVersionId: this.normalizeNonEmpty(row.pageVersionId),
1338
+ format: row.format,
1339
+ pageNumberStart: Number.isInteger(Number(row.pageNumberStart)) ? Number(row.pageNumberStart) : undefined,
1340
+ pageNumberEnd: Number.isInteger(Number(row.pageNumberEnd)) ? Number(row.pageNumberEnd) : undefined,
1341
+ score: match.score,
1342
+ excerpt: this.buildDocumentAnswerPackExcerpt(row, queryTokens, summaryText),
1343
+ matchedBy: [...match.matchedBy],
1344
+ lexicalScore: match.lexicalScore,
1345
+ ...(match.semanticScore !== undefined ? { semanticScore: match.semanticScore } : {}),
1346
+ ...(summaryText ? { summaryText } : {}),
1347
+ };
1348
+ }
1349
+ analyzeDocumentAnswerPackRow(row, normalizedQuery, queryTokens) {
1350
+ const title = this.normalizeDocumentIndexSearchText(row.title);
1351
+ const breadcrumb = this.normalizeDocumentIndexSearchText(row.breadcrumb);
1352
+ const bodyText = this.normalizeDocumentIndexSearchText(row.bodyText ?? '');
1353
+ const numberText = this.normalizeDocumentIndexSearchText(row.number ?? '');
1354
+ const summaryText = this.normalizeDocumentIndexSearchText(row.summaryText ?? '');
1355
+ const haystack = this.normalizeDocumentIndexSearchText([this.normalizeNonEmpty(row.searchText), summaryText].filter(Boolean).join('\n'));
1356
+ if (!haystack) {
1357
+ return { score: 0, lexicalScore: 0, matchedBy: [] };
1358
+ }
1359
+ let lexicalScore = 0;
1360
+ const matchedBy = [];
1361
+ const pushMatch = (field) => {
1362
+ if (!matchedBy.includes(field))
1363
+ matchedBy.push(field);
1364
+ };
1365
+ if (title.includes(normalizedQuery)) {
1366
+ lexicalScore += 48;
1367
+ pushMatch('title');
1368
+ }
1369
+ if (breadcrumb.includes(normalizedQuery)) {
1370
+ lexicalScore += 20;
1371
+ pushMatch('breadcrumb');
1372
+ }
1373
+ if (numberText && numberText.includes(normalizedQuery)) {
1374
+ lexicalScore += 18;
1375
+ pushMatch('number');
1376
+ }
1377
+ if (summaryText && summaryText.includes(normalizedQuery)) {
1378
+ lexicalScore += row.itemKind === 'document' ? 40 : 26;
1379
+ pushMatch('summaryText');
1380
+ }
1381
+ if (bodyText && bodyText.includes(normalizedQuery)) {
1382
+ lexicalScore += 10;
1383
+ pushMatch('bodyText');
1384
+ }
1385
+ const allTokensPresent = queryTokens.every((token) => haystack.includes(token));
1386
+ if (!allTokensPresent && lexicalScore === 0) {
1387
+ return { score: 0, lexicalScore: 0, matchedBy: [] };
1388
+ }
1389
+ if (allTokensPresent)
1390
+ lexicalScore += 12;
1391
+ for (const token of queryTokens) {
1392
+ if (title.includes(token)) {
1393
+ lexicalScore += 12;
1394
+ pushMatch('title');
1395
+ }
1396
+ if (breadcrumb.includes(token)) {
1397
+ lexicalScore += 6;
1398
+ pushMatch('breadcrumb');
1399
+ }
1400
+ if (numberText && numberText.includes(token)) {
1401
+ lexicalScore += 5;
1402
+ pushMatch('number');
1403
+ }
1404
+ if (summaryText && summaryText.includes(token)) {
1405
+ lexicalScore += 8;
1406
+ pushMatch('summaryText');
1407
+ }
1408
+ if (bodyText && bodyText.includes(token)) {
1409
+ lexicalScore += 2;
1410
+ pushMatch('bodyText');
1411
+ }
1412
+ }
1413
+ return { score: lexicalScore, lexicalScore, matchedBy };
1414
+ }
1415
+ buildDocumentAnswerPackExcerpt(row, queryTokens, summaryText) {
1416
+ const bodyText = this.normalizeNonEmpty(row.bodyText);
1417
+ if (bodyText) {
1418
+ const excerpt = this.extractExcerpt(bodyText, queryTokens);
1419
+ if (excerpt)
1420
+ return excerpt;
1421
+ }
1422
+ if (summaryText) {
1423
+ const excerpt = this.extractExcerpt(summaryText, queryTokens);
1424
+ if (excerpt)
1425
+ return excerpt;
1426
+ }
1427
+ const breadcrumbExcerpt = this.extractExcerpt(row.breadcrumb, queryTokens);
1428
+ if (breadcrumbExcerpt)
1429
+ return breadcrumbExcerpt;
1430
+ return row.title;
1431
+ }
1432
+ resolveDocumentAnswerPackKindPriority(itemKind) {
1433
+ if (itemKind === 'page')
1434
+ return 0;
1435
+ if (itemKind === 'section')
1436
+ return 1;
1437
+ return 2;
1438
+ }
1439
+ selectDocumentAnswerPackAnswer(citation) {
1440
+ if (!citation) {
1441
+ return { answer: '', answerSource: 'none' };
1442
+ }
1443
+ const summaryText = this.normalizeNonEmpty(citation.summaryText);
1444
+ const excerpt = this.normalizeNonEmpty(citation.excerpt);
1445
+ if (citation.itemKind === 'page' && excerpt && excerpt !== citation.title) {
1446
+ return { answer: excerpt, answerSource: 'excerpt' };
1447
+ }
1448
+ if (summaryText) {
1449
+ return { answer: summaryText, answerSource: 'summary' };
1450
+ }
1451
+ if (excerpt) {
1452
+ return { answer: excerpt, answerSource: 'excerpt' };
1453
+ }
1454
+ const title = this.normalizeNonEmpty(citation.title);
1455
+ if (title) {
1456
+ return { answer: title, answerSource: 'title' };
1457
+ }
1458
+ return { answer: '', answerSource: 'none' };
1459
+ }
1460
+ searchPersistedDocumentIndexRows(documentVersionId, localeKey, q, limitRaw, retrievalStrategyRaw, rows) {
1461
+ const retrievalStrategy = this.resolveDocumentRetrievalStrategy(retrievalStrategyRaw);
1462
+ const normalizedQuery = this.normalizeDocumentIndexSearchText(q);
1463
+ const queryTokens = this.tokenizeDocumentIndexSearchTokens(normalizedQuery);
1464
+ const limit = this.resolveDocumentIndexSearchLimit(limitRaw);
1465
+ const sorted = [...rows].sort((left, right) => {
1466
+ const sortDelta = Number(left.sortOrder ?? 0) - Number(right.sortOrder ?? 0);
1467
+ if (sortDelta !== 0)
1468
+ return sortDelta;
1469
+ return String(left.id ?? '').localeCompare(String(right.id ?? ''));
1470
+ });
1471
+ const documentRow = sorted.find((row) => row.itemKind === 'document');
1472
+ if (sorted.length === 0 || !normalizedQuery || queryTokens.length === 0) {
1473
+ return Effect.succeed({
1474
+ documentVersionId,
1475
+ locale: localeKey.locale,
1476
+ fallbackLocale: localeKey.fallbackLocale,
1477
+ q,
1478
+ built: sorted.length > 0,
1479
+ buildFingerprint: this.normalizeNonEmpty(documentRow?.buildFingerprint) ??
1480
+ this.normalizeNonEmpty(sorted[0]?.buildFingerprint),
1481
+ hits: [],
1482
+ provenance: {
1483
+ strategy: 'lexical-search-v1',
1484
+ retrievalStrategy,
1485
+ vectorAvailable: false,
1486
+ },
1487
+ });
1488
+ }
1489
+ return Effect.gen(this, function* (_) {
1490
+ const vectorState = yield* _(this.resolveDocumentVectorState(sorted, normalizedQuery, retrievalStrategy, 'searchPersistedDocumentIndexRows.vector'));
1491
+ const hits = this.rankPersistedDocumentSearchRows(sorted, normalizedQuery, queryTokens, vectorState)
1492
+ .map((match) => this.toPersistedDocumentSearchHit(match.row, match, queryTokens))
1493
+ .sort((left, right) => {
1494
+ const scoreDelta = right.score - left.score;
1495
+ if (scoreDelta !== 0)
1496
+ return scoreDelta;
1497
+ const pageDelta = (left.pageNumberStart ?? Number.MAX_SAFE_INTEGER) - (right.pageNumberStart ?? Number.MAX_SAFE_INTEGER);
1498
+ if (pageDelta !== 0)
1499
+ return pageDelta;
1500
+ return left.breadcrumb.localeCompare(right.breadcrumb);
1501
+ })
1502
+ .slice(0, limit);
1503
+ return {
1504
+ documentVersionId,
1505
+ locale: this.normalizeNonEmpty(documentRow?.locale) ?? localeKey.locale,
1506
+ fallbackLocale: this.normalizeNonEmpty(documentRow?.fallbackLocale) ?? localeKey.fallbackLocale,
1507
+ q,
1508
+ built: sorted.length > 0,
1509
+ buildFingerprint: this.normalizeNonEmpty(documentRow?.buildFingerprint) ??
1510
+ this.normalizeNonEmpty(sorted[0]?.buildFingerprint),
1511
+ hits,
1512
+ provenance: this.toDocumentSearchProvenance(retrievalStrategy, vectorState),
1513
+ };
1514
+ });
1515
+ }
1516
+ toPersistedDocumentSearchHit(row, match, queryTokens) {
1517
+ return {
1518
+ itemKind: row.itemKind === 'section' ? 'section' : 'page',
1519
+ anchor: row.anchor,
1520
+ parentAnchor: this.normalizeNonEmpty(row.parentAnchor),
1521
+ number: this.normalizeNonEmpty(row.number),
1522
+ depth: Number(row.depth ?? 0),
1523
+ title: row.title,
1524
+ breadcrumb: row.breadcrumb,
1525
+ sectionId: this.normalizeNonEmpty(row.sectionId),
1526
+ sectionUid: this.normalizeNonEmpty(row.sectionUid),
1527
+ sectionSlug: this.normalizeNonEmpty(row.sectionSlug),
1528
+ pageId: this.normalizeNonEmpty(row.pageId),
1529
+ pageUid: this.normalizeNonEmpty(row.pageUid),
1530
+ pageVersionId: this.normalizeNonEmpty(row.pageVersionId),
1531
+ format: row.format,
1532
+ pageNumberStart: Number.isInteger(Number(row.pageNumberStart)) ? Number(row.pageNumberStart) : undefined,
1533
+ pageNumberEnd: Number.isInteger(Number(row.pageNumberEnd)) ? Number(row.pageNumberEnd) : undefined,
1534
+ score: match.score,
1535
+ excerpt: this.buildPersistedDocumentIndexExcerpt(row, queryTokens),
1536
+ matchedBy: [...match.matchedBy],
1537
+ lexicalScore: match.lexicalScore,
1538
+ ...(match.semanticScore !== undefined ? { semanticScore: match.semanticScore } : {}),
1539
+ };
1540
+ }
1541
+ toDocumentIndexBoolean(value) {
1542
+ return value === true || value === 1 || value === '1';
1543
+ }
1544
+ toNonNegativeInteger(value) {
1545
+ const parsed = Number(value);
1546
+ if (!Number.isInteger(parsed) || parsed < 0)
1547
+ return 0;
1548
+ return parsed;
1549
+ }
1550
+ scorePersistedDocumentIndexRow(row, normalizedQuery, queryTokens) {
1551
+ const haystack = this.normalizeNonEmpty(row.searchText);
1552
+ if (!haystack) {
1553
+ return { row, score: 0, lexicalScore: 0, matchedBy: [] };
1554
+ }
1555
+ let lexicalScore = 0;
1556
+ const title = this.normalizeDocumentIndexSearchText(row.title);
1557
+ const breadcrumb = this.normalizeDocumentIndexSearchText(row.breadcrumb);
1558
+ const bodyText = this.normalizeDocumentIndexSearchText(row.bodyText ?? '');
1559
+ const numberText = this.normalizeDocumentIndexSearchText(row.number ?? '');
1560
+ const matchedBy = [];
1561
+ const pushMatch = (field) => {
1562
+ if (!matchedBy.includes(field))
1563
+ matchedBy.push(field);
1564
+ };
1565
+ if (haystack.includes(normalizedQuery))
1566
+ lexicalScore += 24;
1567
+ if (title.includes(normalizedQuery)) {
1568
+ lexicalScore += 48;
1569
+ pushMatch('title');
1570
+ }
1571
+ if (breadcrumb.includes(normalizedQuery)) {
1572
+ lexicalScore += 20;
1573
+ pushMatch('breadcrumb');
1574
+ }
1575
+ if (numberText && numberText.includes(normalizedQuery)) {
1576
+ lexicalScore += 18;
1577
+ pushMatch('number');
1578
+ }
1579
+ if (bodyText && bodyText.includes(normalizedQuery)) {
1580
+ lexicalScore += 8;
1581
+ pushMatch('bodyText');
1582
+ }
1583
+ const allTokensPresent = queryTokens.every((token) => haystack.includes(token));
1584
+ if (!allTokensPresent && lexicalScore === 0) {
1585
+ return { row, score: 0, lexicalScore: 0, matchedBy: [] };
1586
+ }
1587
+ if (allTokensPresent)
1588
+ lexicalScore += 12;
1589
+ for (const token of queryTokens) {
1590
+ if (title.includes(token)) {
1591
+ lexicalScore += 12;
1592
+ pushMatch('title');
1593
+ }
1594
+ if (breadcrumb.includes(token)) {
1595
+ lexicalScore += 6;
1596
+ pushMatch('breadcrumb');
1597
+ }
1598
+ if (numberText && numberText.includes(token)) {
1599
+ lexicalScore += 5;
1600
+ pushMatch('number');
1601
+ }
1602
+ if (bodyText && bodyText.includes(token)) {
1603
+ lexicalScore += 2;
1604
+ pushMatch('bodyText');
1605
+ }
1606
+ }
1607
+ return { row, score: lexicalScore, lexicalScore, matchedBy };
1608
+ }
1609
+ resolveDocumentRetrievalStrategy(value) {
1610
+ return value === 'hybrid' || value === 'semantic' ? value : 'lexical';
1611
+ }
1612
+ toDocumentAnswerPackProvenanceStrategy(retrievalStrategy) {
1613
+ return retrievalStrategy === 'semantic'
1614
+ ? 'semantic-answer-pack-v1'
1615
+ : retrievalStrategy === 'hybrid'
1616
+ ? 'hybrid-answer-pack-v1'
1617
+ : 'deterministic-answer-pack-v1';
1618
+ }
1619
+ toDocumentSearchProvenance(retrievalStrategy, vectorState) {
1620
+ return {
1621
+ strategy: this.toDocumentSearchStrategy(retrievalStrategy),
1622
+ retrievalStrategy,
1623
+ vectorAvailable: vectorState.vectorAvailable,
1624
+ vectorProvider: vectorState.vectorProvider,
1625
+ vectorModel: vectorState.vectorModel,
1626
+ };
1627
+ }
1628
+ toDocumentSearchStrategy(retrievalStrategy) {
1629
+ return retrievalStrategy === 'semantic'
1630
+ ? 'semantic-search-v1'
1631
+ : retrievalStrategy === 'hybrid'
1632
+ ? 'hybrid-search-v1'
1633
+ : 'lexical-search-v1';
1634
+ }
1635
+ resolveScopeSearchDocumentSeed(document) {
1636
+ const versions = Array.isArray(document.documentVersions) ? [...document.documentVersions] : [];
1637
+ // Prefer the row marked isCurrent=true (set via docman.document-version.set-current).
1638
+ // Fall back to highest numeric version when no current row exists — keeps backward
1639
+ // compatibility for documents that pre-date the set-current invariant.
1640
+ const currentVersion = versions.find((version) => version.isCurrent === true && this.normalizeNonEmpty(version.id));
1641
+ const sortedVersions = versions
1642
+ .slice()
1643
+ .sort((left, right) => Number(right.version ?? 0) - Number(left.version ?? 0));
1644
+ const latestVersion = currentVersion ?? sortedVersions.find((version) => this.normalizeNonEmpty(version.id));
1645
+ const documentId = this.normalizeNonEmpty(document.id);
1646
+ const documentTitle = this.normalizeNonEmpty(document.title);
1647
+ const documentVersionId = this.normalizeNonEmpty(latestVersion?.id);
1648
+ if (!documentId || !documentTitle || !documentVersionId)
1649
+ return null;
1650
+ return {
1651
+ documentId,
1652
+ documentTitle,
1653
+ documentSlug: this.normalizeNonEmpty(document.slug),
1654
+ documentVersionId,
1655
+ documentVersionTitle: this.normalizeNonEmpty(latestVersion?.title) ?? `v${Number(latestVersion?.version ?? 0) || '?'}`,
1656
+ documentVersionNumber: Number.isFinite(Number(latestVersion?.version))
1657
+ ? Number(latestVersion?.version)
1658
+ : undefined,
1659
+ };
1660
+ }
1661
+ toScopeDocumentSearchHit(seed, hit) {
1662
+ return {
1663
+ ...hit,
1664
+ documentId: seed.documentId,
1665
+ documentTitle: seed.documentTitle,
1666
+ documentSlug: seed.documentSlug,
1667
+ documentVersionId: seed.documentVersionId,
1668
+ documentVersionTitle: seed.documentVersionTitle,
1669
+ documentVersionNumber: seed.documentVersionNumber,
1670
+ };
1671
+ }
1672
+ describeScopeSearchError(error) {
1673
+ const info = effectErrorInfo(error);
1674
+ if (error instanceof Error && this.normalizeNonEmpty(error.message)) {
1675
+ return error.message;
1676
+ }
1677
+ if (typeof info.unwrapped === 'string' && this.normalizeNonEmpty(info.unwrapped)) {
1678
+ return info.unwrapped;
1679
+ }
1680
+ if (info.unwrapped instanceof Error && this.normalizeNonEmpty(info.unwrapped.message)) {
1681
+ return info.unwrapped.message;
1682
+ }
1683
+ try {
1684
+ return JSON.stringify(info.unwrapped) ?? 'Unknown scope search error.';
1685
+ }
1686
+ catch {
1687
+ return 'Unknown scope search error.';
1688
+ }
1689
+ }
1690
+ rankPersistedDocumentSearchRows(rows, normalizedQuery, queryTokens, vectorState) {
1691
+ return rows
1692
+ .filter((row) => row.itemKind === 'section' || row.itemKind === 'page')
1693
+ .map((row) => {
1694
+ const lexical = this.scorePersistedDocumentIndexRow(row, normalizedQuery, queryTokens);
1695
+ return this.applySemanticScoreToMatch(lexical, row, vectorState);
1696
+ })
1697
+ .filter((match) => match.score > 0);
1698
+ }
1699
+ rankDocumentAnswerPackRows(rows, normalizedQuery, queryTokens, vectorState) {
1700
+ return rows
1701
+ .map((row) => {
1702
+ const lexical = this.analyzeDocumentAnswerPackRow(row, normalizedQuery, queryTokens);
1703
+ return this.applySemanticScoreToMatch({ row, ...lexical }, row, vectorState);
1704
+ })
1705
+ .filter((match) => match.score > 0);
1706
+ }
1707
+ applySemanticScoreToMatch(baseMatch, row, vectorState) {
1708
+ const semanticScore = this.resolvePersistedDocumentSemanticScore(row, vectorState);
1709
+ const matchedBy = [...baseMatch.matchedBy];
1710
+ if (semanticScore > 0.12 && !matchedBy.includes('semanticVector')) {
1711
+ matchedBy.push('semanticVector');
1712
+ }
1713
+ const score = vectorState.retrievalStrategy === 'semantic'
1714
+ ? semanticScore > 0.12
1715
+ ? semanticScore * 100 + baseMatch.lexicalScore * 0.2
1716
+ : 0
1717
+ : vectorState.retrievalStrategy === 'hybrid'
1718
+ ? baseMatch.lexicalScore + semanticScore * 48
1719
+ : baseMatch.lexicalScore;
1720
+ return {
1721
+ ...baseMatch,
1722
+ matchedBy,
1723
+ semanticScore: semanticScore > 0 ? Number(semanticScore.toFixed(6)) : undefined,
1724
+ score: Number(score.toFixed(6)),
1725
+ };
1726
+ }
1727
+ resolvePersistedDocumentSemanticScore(row, vectorState) {
1728
+ if (!vectorState.vectorAvailable || !vectorState.queryVector?.length)
1729
+ return 0;
1730
+ if (this.normalizeNonEmpty(row.embeddingProvider) !== this.normalizeNonEmpty(vectorState.vectorProvider))
1731
+ return 0;
1732
+ if (this.normalizeNonEmpty(row.embeddingModel) !== this.normalizeNonEmpty(vectorState.vectorModel))
1733
+ return 0;
1734
+ const storedVector = parseDocmanEmbeddingVector(row.embeddingVector);
1735
+ if (!storedVector?.length)
1736
+ return 0;
1737
+ return Math.max(0, cosineSimilarity(vectorState.queryVector, storedVector));
1738
+ }
1739
+ resolveDocumentVectorState(rows, normalizedQuery, retrievalStrategy, stage) {
1740
+ if (retrievalStrategy === 'lexical') {
1741
+ return Effect.succeed({
1742
+ retrievalStrategy,
1743
+ vectorAvailable: false,
1744
+ });
1745
+ }
1746
+ const embeddedRow = rows.find((row) => Boolean(parseDocmanEmbeddingVector(row.embeddingVector)?.length) &&
1747
+ Boolean(this.normalizeNonEmpty(row.embeddingProvider)) &&
1748
+ Boolean(this.normalizeNonEmpty(row.embeddingModel)));
1749
+ if (!embeddedRow) {
1750
+ return Effect.succeed({
1751
+ retrievalStrategy,
1752
+ vectorAvailable: false,
1753
+ });
1754
+ }
1755
+ const embeddedProvider = this.normalizeNonEmpty(embeddedRow.embeddingProvider);
1756
+ const embeddedModel = this.normalizeNonEmpty(embeddedRow.embeddingModel);
1757
+ const provider = embeddedProvider === this.embeddingProvider.provider && embeddedModel === this.embeddingProvider.model
1758
+ ? this.embeddingProvider
1759
+ : (() => {
1760
+ const localProvider = createDocmanLocalHashEmbeddingProvider();
1761
+ return embeddedProvider === localProvider.provider && embeddedModel === localProvider.model
1762
+ ? localProvider
1763
+ : undefined;
1764
+ })();
1765
+ if (!provider) {
1766
+ return Effect.succeed({
1767
+ retrievalStrategy,
1768
+ vectorAvailable: false,
1769
+ });
1770
+ }
1771
+ return Effect.tryPromise({
1772
+ try: async () => {
1773
+ const result = await provider.embedMany({ texts: [normalizedQuery] });
1774
+ const queryVector = result.vectors[0];
1775
+ return {
1776
+ retrievalStrategy,
1777
+ vectorAvailable: Array.isArray(queryVector) && queryVector.length > 0,
1778
+ vectorProvider: result.provider,
1779
+ vectorModel: result.model,
1780
+ queryVector,
1781
+ };
1782
+ },
1783
+ catch: (error) => XfErrorFactory.upsertFailed({
1784
+ stage,
1785
+ operation: 'embeddingProvider.embedMany',
1786
+ message: 'Failed to build document retrieval query vector.',
1787
+ cause: error,
1788
+ }),
1789
+ });
1790
+ }
1791
+ buildPersistedDocumentIndexExcerpt(row, queryTokens) {
1792
+ const bodyText = this.normalizeNonEmpty(row.bodyText);
1793
+ if (bodyText) {
1794
+ const excerpt = this.extractExcerpt(bodyText, queryTokens);
1795
+ if (excerpt)
1796
+ return excerpt;
1797
+ }
1798
+ const breadcrumbExcerpt = this.extractExcerpt(row.breadcrumb, queryTokens);
1799
+ if (breadcrumbExcerpt)
1800
+ return breadcrumbExcerpt;
1801
+ return row.title;
1802
+ }
1803
+ extractExcerpt(source, queryTokens) {
1804
+ const normalizedSource = String(source ?? '').replace(/\s+/g, ' ').trim();
1805
+ if (!normalizedSource)
1806
+ return '';
1807
+ const loweredSource = normalizedSource.toLowerCase();
1808
+ let matchIndex = -1;
1809
+ let matchedToken = '';
1810
+ for (const token of queryTokens) {
1811
+ const candidate = loweredSource.indexOf(token.toLowerCase());
1812
+ if (candidate === -1)
1813
+ continue;
1814
+ if (matchIndex === -1 || candidate < matchIndex) {
1815
+ matchIndex = candidate;
1816
+ matchedToken = token;
1817
+ }
1818
+ }
1819
+ if (matchIndex === -1) {
1820
+ return normalizedSource.slice(0, 160);
1821
+ }
1822
+ const start = Math.max(0, matchIndex - 48);
1823
+ const end = Math.min(normalizedSource.length, matchIndex + Math.max(matchedToken.length, 24) + 80);
1824
+ const prefix = start > 0 ? '...' : '';
1825
+ const suffix = end < normalizedSource.length ? '...' : '';
1826
+ return `${prefix}${normalizedSource.slice(start, end).trim()}${suffix}`;
1827
+ }
1828
+ resolveDocumentIndexSearchLimit(value) {
1829
+ const parsed = Number(value);
1830
+ if (!Number.isInteger(parsed) || parsed <= 0)
1831
+ return 8;
1832
+ return Math.max(1, Math.min(20, parsed));
1833
+ }
1834
+ resolveDocumentAnswerPackLimit(value) {
1835
+ const parsed = Number(value);
1836
+ if (!Number.isInteger(parsed) || parsed <= 0)
1837
+ return 3;
1838
+ return Math.max(1, Math.min(8, parsed));
1839
+ }
1840
+ tokenizeDocumentIndexSearchTokens(value) {
1841
+ return [...new Set(value.split(/\s+/).map((token) => token.trim()).filter(Boolean))];
1842
+ }
1843
+ normalizeDocumentIndexSearchText(value) {
1844
+ return String(value ?? '')
1845
+ .toLowerCase()
1846
+ .replace(/<!--[\s\S]*?-->/g, ' ')
1847
+ .replace(/<\/?[^>]+>/g, ' ')
1848
+ .replace(/[`*_~>#=+|]/g, ' ')
1849
+ .replace(/\[[^\]]*]\(([^)]+)\)/g, ' ')
1850
+ .replace(/[^\p{L}\p{N}.\- ]+/gu, ' ')
1851
+ .replace(/\s+/g, ' ')
1852
+ .trim();
1853
+ }
1854
+ buildPersistedDocumentIndexEmbeddingText(row) {
1855
+ return this.normalizeDocumentIndexSearchText([
1856
+ this.normalizeNonEmpty(row.number),
1857
+ this.normalizeNonEmpty(row.title),
1858
+ this.normalizeNonEmpty(row.breadcrumb),
1859
+ this.normalizeNonEmpty(row.summaryText),
1860
+ this.normalizeNonEmpty(row.bodyText),
1861
+ ]
1862
+ .filter(Boolean)
1863
+ .join('\n'));
1864
+ }
1865
+ populatePersistedDocumentIndexEmbeddings(rows, stage, operation) {
1866
+ if (rows.length === 0)
1867
+ return Effect.succeed([]);
1868
+ const embeddingTexts = rows.map((row) => this.buildPersistedDocumentIndexEmbeddingText(row));
1869
+ return Effect.tryPromise({
1870
+ try: async () => {
1871
+ let result;
1872
+ try {
1873
+ result = await this.embeddingProvider.embedMany({ texts: embeddingTexts });
1874
+ }
1875
+ catch (error) {
1876
+ if (this.embeddingProvider.provider === createDocmanLocalHashEmbeddingProvider().provider) {
1877
+ throw error;
1878
+ }
1879
+ this.logger?.warn({
1880
+ stage,
1881
+ operation,
1882
+ provider: this.embeddingProvider.provider,
1883
+ model: this.embeddingProvider.model,
1884
+ }, 'Embedding provider failed; falling back to local hash embeddings.');
1885
+ result = await createDocmanLocalHashEmbeddingProvider().embedMany({ texts: embeddingTexts });
1886
+ }
1887
+ return rows.map((row, index) => {
1888
+ const embeddingText = embeddingTexts[index];
1889
+ const vector = result.vectors[index];
1890
+ if (!embeddingText || !Array.isArray(vector) || vector.length === 0) {
1891
+ return {
1892
+ ...row,
1893
+ embeddingProvider: undefined,
1894
+ embeddingModel: undefined,
1895
+ embeddingHash: undefined,
1896
+ embeddingDimensions: undefined,
1897
+ embeddingVector: undefined,
1898
+ };
1899
+ }
1900
+ return {
1901
+ ...row,
1902
+ embeddingProvider: result.provider,
1903
+ embeddingModel: result.model,
1904
+ embeddingHash: buildDocmanEmbeddingHash(embeddingText),
1905
+ embeddingDimensions: result.dimensions || vector.length,
1906
+ embeddingVector: serializeDocmanEmbeddingVector(vector),
1907
+ };
1908
+ });
1909
+ },
1910
+ catch: (error) => XfErrorFactory.upsertFailed({
1911
+ stage,
1912
+ operation,
1913
+ message: 'Failed to populate document retrieval embeddings.',
1914
+ cause: error,
1915
+ }),
1916
+ });
1917
+ }
1918
+ refreshPersistedDocumentIndexEmbeddings(repository, rows, stage, operation) {
1919
+ if (rows.length === 0)
1920
+ return Effect.succeed(rows);
1921
+ return Effect.gen(this, function* (_) {
1922
+ const embeddedRows = yield* _(this.populatePersistedDocumentIndexEmbeddings(rows, stage, operation));
1923
+ const patches = [];
1924
+ for (const row of embeddedRows) {
1925
+ const id = this.normalizeNonEmpty(row.id);
1926
+ if (!id)
1927
+ continue;
1928
+ patches.push({
1929
+ id,
1930
+ patch: {
1931
+ embeddingProvider: row.embeddingProvider,
1932
+ embeddingModel: row.embeddingModel,
1933
+ embeddingHash: row.embeddingHash,
1934
+ embeddingDimensions: row.embeddingDimensions,
1935
+ embeddingVector: row.embeddingVector,
1936
+ },
1937
+ });
1938
+ }
1939
+ yield* _(Effect.all(patches.map(({ id, patch }) => repository.patchById(id, patch).pipe(Effect.mapError(mapDbError({
1940
+ stage,
1941
+ operation: 'documentIndexEntryRepository.patchById',
1942
+ factory: XfErrorFactory.upsertFailed,
1943
+ })))), { concurrency: 6 }));
1944
+ return embeddedRows;
1945
+ });
1946
+ }
1947
+ extractDocumentIndexBodyText(item) {
1948
+ const source = item.contentParts.join('\n\n');
1949
+ if (!source.trim())
1950
+ return '';
1951
+ return String(source)
1952
+ .replace(/<!--[\s\S]*?-->/g, ' ')
1953
+ .replace(/```[\s\S]*?```/g, (block) => block.replace(/```[^\n]*\n?/g, '').replace(/```/g, ' '))
1954
+ .replace(/`([^`]+)`/g, '$1')
1955
+ .replace(/!\[([^\]]*)]\(([^)]+)\)/g, '$1')
1956
+ .replace(/\[([^\]]+)]\(([^)]+)\)/g, '$1')
1957
+ .replace(/<\/?[^>]+>/g, ' ')
1958
+ .replace(/^[ \t]*[-*+]\s+/gm, '')
1959
+ .replace(/^[ \t]*\d+\.\s+/gm, '')
1960
+ .replace(/^[ \t]*#{1,6}\s+/gm, '')
1961
+ .replace(/\s+/g, ' ')
1962
+ .trim();
1963
+ }
1964
+ resolvePersistedDocumentAuthoredSummaryText(rows, localeKey, stage, operation) {
1965
+ const documentRow = rows.find((row) => row.itemKind === 'document');
1966
+ const documentId = this.normalizeNonEmpty(documentRow?.documentId);
1967
+ const documentVersionId = this.normalizeNonEmpty(documentRow?.documentVersionId);
1968
+ if (!documentId || !documentVersionId)
1969
+ return Effect.succeed(undefined);
1970
+ const localeState = {
1971
+ locale: localeKey.locale,
1972
+ fallbackLocale: localeKey.fallbackLocale,
1973
+ };
1974
+ const documentQueryOptions = this.withLocaleOptions(undefined, localeState, ['titleMl', 'summaryMl', 'descriptionMl']);
1975
+ return Effect.gen(this, function* (_) {
1976
+ const document = yield* _(this.documentRepository.findById(documentId, documentQueryOptions).pipe(Effect.mapError(mapDbError({
1977
+ stage,
1978
+ operation: `${operation}.documentRepository.findById`,
1979
+ factory: XfErrorFactory.notFound,
1980
+ }))));
1981
+ const version = this.documentVersionRepository
1982
+ ? yield* _(this.documentVersionRepository.findById(documentVersionId).pipe(Effect.mapError(mapDbError({
1983
+ stage,
1984
+ operation: `${operation}.documentVersionRepository.findById`,
1985
+ factory: XfErrorFactory.notFound,
1986
+ }))))
1987
+ : null;
1988
+ const versionSummary = this.normalizeNonEmpty(version?.summary);
1989
+ if (versionSummary)
1990
+ return versionSummary;
1991
+ const localizedDocumentSummary = document
1992
+ ? this.normalizeNonEmpty(this.resolveLocalizedValue(document.summaryMl, localeState))
1993
+ : undefined;
1994
+ if (localizedDocumentSummary)
1995
+ return localizedDocumentSummary;
1996
+ return this.normalizeNonEmpty(document?.summary);
1997
+ });
1998
+ }
1999
+ toPersistedDocumentSummaryPatches(rows, authoredDocumentSummary) {
2000
+ const metricsByAnchor = this.buildPersistedDocumentSummaryMetrics(rows, authoredDocumentSummary);
2001
+ const patches = [];
2002
+ for (const row of rows) {
2003
+ const id = this.normalizeNonEmpty(row.id);
2004
+ if (!id)
2005
+ continue;
2006
+ const metrics = metricsByAnchor.get(row.anchor);
2007
+ if (!metrics)
2008
+ continue;
2009
+ patches.push({
2010
+ id,
2011
+ patch: {
2012
+ summaryText: metrics.summaryText,
2013
+ sourceCharCount: metrics.sourceCharCount,
2014
+ sourceWordCount: metrics.sourceWordCount,
2015
+ summaryCharCount: metrics.summaryCharCount,
2016
+ summaryWordCount: metrics.summaryWordCount,
2017
+ updatedBy: DOCMAN_DOCUMENT_SUMMARY_BUILD_ACTOR,
2018
+ },
2019
+ });
2020
+ }
2021
+ return patches;
2022
+ }
2023
+ buildPersistedDocumentSummaryMetrics(rows, authoredDocumentSummary) {
2024
+ const sorted = [...rows].sort((left, right) => {
2025
+ const sortDelta = Number(left.sortOrder ?? 0) - Number(right.sortOrder ?? 0);
2026
+ if (sortDelta !== 0)
2027
+ return sortDelta;
2028
+ return String(left.id ?? '').localeCompare(String(right.id ?? ''));
2029
+ });
2030
+ const childrenByParentAnchor = new Map();
2031
+ for (const row of sorted) {
2032
+ const parentAnchor = this.normalizeNonEmpty(row.parentAnchor);
2033
+ if (!parentAnchor)
2034
+ continue;
2035
+ const siblings = childrenByParentAnchor.get(parentAnchor) ?? [];
2036
+ siblings.push(row);
2037
+ childrenByParentAnchor.set(parentAnchor, siblings);
2038
+ }
2039
+ const metricsByAnchor = new Map();
2040
+ const visit = (row) => {
2041
+ const existing = metricsByAnchor.get(row.anchor);
2042
+ if (existing)
2043
+ return existing;
2044
+ const childMetrics = (childrenByParentAnchor.get(row.anchor) ?? []).map((child) => visit(child));
2045
+ const ownBodyText = row.itemKind === 'page' ? this.normalizeDocumentSummarySourceText(row.bodyText) : '';
2046
+ const sourceText = this.normalizeDocumentSummarySourceText([
2047
+ row.title,
2048
+ ownBodyText,
2049
+ childMetrics.map((entry) => entry.sourceText).filter(Boolean).join('\n\n'),
2050
+ ]
2051
+ .filter(Boolean)
2052
+ .join('\n\n')) || row.title;
2053
+ const authoredSummary = row.itemKind === 'document' ? this.normalizeDocumentSummarySourceText(authoredDocumentSummary) : '';
2054
+ const summaryText = authoredSummary
2055
+ ? this.truncateDocumentSummaryText(authoredSummary, 220)
2056
+ : this.buildDeterministicDocumentSummaryText(sourceText, row.title);
2057
+ const metrics = {
2058
+ sourceText,
2059
+ summaryText,
2060
+ sourceCharCount: sourceText.length,
2061
+ sourceWordCount: this.countDocumentSummaryWords(sourceText),
2062
+ summaryCharCount: summaryText.length,
2063
+ summaryWordCount: this.countDocumentSummaryWords(summaryText),
2064
+ };
2065
+ metricsByAnchor.set(row.anchor, metrics);
2066
+ return metrics;
2067
+ };
2068
+ for (const row of sorted) {
2069
+ visit(row);
2070
+ }
2071
+ return metricsByAnchor;
2072
+ }
2073
+ normalizeDocumentSummarySourceText(value) {
2074
+ return String(value ?? '')
2075
+ .replace(/<!--[\s\S]*?-->/g, ' ')
2076
+ .replace(/\|\s*[-:]{3,}\s*(?=\||$)/g, ' ')
2077
+ .replace(/\|/g, ' / ')
2078
+ .replace(/\s+/g, ' ')
2079
+ .trim();
2080
+ }
2081
+ buildDeterministicDocumentSummaryText(sourceText, fallbackText) {
2082
+ const fallback = this.normalizeDocumentSummarySourceText(fallbackText);
2083
+ const normalized = this.normalizeDocumentSummarySourceText(sourceText);
2084
+ if (!normalized)
2085
+ return fallback;
2086
+ const sentences = normalized
2087
+ .split(/(?<=[.!?])\s+/)
2088
+ .map((entry) => entry.trim())
2089
+ .filter(Boolean);
2090
+ if (sentences.length === 0) {
2091
+ return this.truncateDocumentSummaryText(normalized, 220);
2092
+ }
2093
+ const selected = [];
2094
+ let length = 0;
2095
+ for (const sentence of sentences) {
2096
+ const nextLength = length === 0 ? sentence.length : length + 1 + sentence.length;
2097
+ if (selected.length > 0 && nextLength > 220)
2098
+ break;
2099
+ selected.push(sentence);
2100
+ length = nextLength;
2101
+ if (length >= 96)
2102
+ break;
2103
+ }
2104
+ const summary = selected.join(' ').trim();
2105
+ if (summary)
2106
+ return this.truncateDocumentSummaryText(summary, 220);
2107
+ return this.truncateDocumentSummaryText(normalized, 220);
2108
+ }
2109
+ truncateDocumentSummaryText(value, maxLength) {
2110
+ const normalized = this.normalizeDocumentSummarySourceText(value);
2111
+ if (normalized.length <= maxLength)
2112
+ return normalized;
2113
+ return `${normalized.slice(0, Math.max(0, maxLength - 3)).trimEnd()}...`;
2114
+ }
2115
+ countDocumentSummaryWords(value) {
2116
+ const normalized = this.normalizeDocumentSummarySourceText(value);
2117
+ if (!normalized)
2118
+ return 0;
2119
+ return normalized.split(/\s+/).filter(Boolean).length;
2120
+ }
2121
+ buildPersistedDocumentPageRanges(pages) {
2122
+ const ranges = new Map();
2123
+ for (const page of pages) {
2124
+ for (const chunk of page.chunks) {
2125
+ const existing = ranges.get(chunk.linkId);
2126
+ if (!existing) {
2127
+ ranges.set(chunk.linkId, { start: page.pageNumber, end: page.pageNumber });
2128
+ continue;
2129
+ }
2130
+ existing.start = Math.min(existing.start, page.pageNumber);
2131
+ existing.end = Math.max(existing.end, page.pageNumber);
2132
+ }
2133
+ }
2134
+ return ranges;
2135
+ }
2136
+ buildDocumentIndexBreadcrumb(title, parentLinkId, breadcrumbByLinkId, documentTitle) {
2137
+ const parentBreadcrumb = parentLinkId ? breadcrumbByLinkId.get(parentLinkId) : undefined;
2138
+ if (parentBreadcrumb)
2139
+ return `${parentBreadcrumb} / ${title}`;
2140
+ return `${documentTitle} / ${title}`;
2141
+ }
2142
+ renderDocumentSourceBundleFromResolved(resolved) {
2143
+ const lines = [];
2144
+ this.prependModulePreambles(lines, this.uniqueModulePreambles(resolved.items
2145
+ .filter((item) => item.kind === 'page')
2146
+ .map((item) => item.modulePreamble)));
2147
+ lines.push(`# ${resolved.title}`);
2148
+ const documentReleaseNotes = this.normalizeNonEmpty(resolved.documentReleaseNotes);
2149
+ if (documentReleaseNotes) {
2150
+ lines.push('');
2151
+ lines.push(`_Release Notes:_ ${documentReleaseNotes}`);
2152
+ }
2153
+ for (const item of resolved.items) {
2154
+ if (item.pageBreakBefore) {
2155
+ lines.push('', '<!-- pagebreak -->');
2156
+ }
2157
+ lines.push('');
2158
+ if (item.kind === 'section') {
2159
+ lines.push(...this.renderSectionHeading(item));
2160
+ }
2161
+ else {
2162
+ lines.push(...this.renderPageBody(item, 2 + item.depth));
2163
+ }
2164
+ if (item.pageBreakAfter) {
2165
+ lines.push('', '<!-- pagebreak -->');
2166
+ }
2167
+ }
2168
+ return {
2169
+ ...reduceDocmanComposeFormats(resolved.items
2170
+ .filter((item) => item.kind === 'page')
2171
+ .map((item) => item.format)),
2172
+ content: this.joinMarkdown(lines),
2173
+ assets: this.uniqueAssetRefs(resolved.items.flatMap((item) => (item.kind === 'page' ? item.assetRefs : []))),
2174
+ };
2175
+ }
2176
+ renderSectionSourceBundleFromSection(documentTitle, resolved, sectionItem) {
2177
+ const scopedItems = this.collectSubtreeItems(resolved.items, sectionItem.linkId);
2178
+ const lines = [];
2179
+ this.prependModulePreambles(lines, this.uniqueModulePreambles(scopedItems
2180
+ .filter((item) => item.kind === 'page')
2181
+ .map((item) => item.modulePreamble)));
2182
+ lines.push(`# ${documentTitle}`);
2183
+ for (const item of scopedItems) {
2184
+ lines.push('');
2185
+ if (item.pageBreakBefore) {
2186
+ lines.push('<!-- pagebreak -->', '');
2187
+ }
2188
+ if (item.kind === 'section') {
2189
+ lines.push(...this.renderSectionHeading(item));
2190
+ }
2191
+ else {
2192
+ lines.push(...this.renderPageBody(item, 2 + item.depth));
2193
+ }
2194
+ if (item.pageBreakAfter) {
2195
+ lines.push('', '<!-- pagebreak -->');
2196
+ }
2197
+ }
2198
+ return {
2199
+ ...reduceDocmanComposeFormats(scopedItems
2200
+ .filter((item) => item.kind === 'page')
2201
+ .map((item) => item.format)),
2202
+ content: this.joinMarkdown(lines),
2203
+ assets: this.uniqueAssetRefs(scopedItems.flatMap((item) => (item.kind === 'page' ? item.assetRefs : []))),
2204
+ };
2205
+ }
2206
+ renderSinglePageItemSourceBundle(documentTitle, pageItem) {
2207
+ const lines = [];
2208
+ this.prependModulePreambles(lines, this.uniqueModulePreambles([pageItem.modulePreamble]));
2209
+ lines.push(`# ${documentTitle}`, '');
2210
+ lines.push(...this.renderPageBody(pageItem, 2 + pageItem.depth));
2211
+ return {
2212
+ format: pageItem.format,
2213
+ formats: [pageItem.format],
2214
+ content: this.joinMarkdown(lines),
2215
+ assets: this.uniqueAssetRefs(pageItem.assetRefs),
2216
+ };
2217
+ }
2218
+ renderPageSourceBundle(documentTitle, page) {
2219
+ const lines = [];
2220
+ this.prependModulePreambles(lines, page.modulePreambles);
2221
+ lines.push(`# ${documentTitle}`, '', `## Page ${page.pageNumber}`);
2222
+ if (page.chunks.length === 0) {
2223
+ lines.push('', '_No content for this composed page._');
2224
+ return {
2225
+ format: page.format,
2226
+ formats: [...page.formats],
2227
+ content: this.joinMarkdown(lines),
2228
+ assets: [],
2229
+ };
2230
+ }
2231
+ let currentLinkId = null;
2232
+ for (const chunk of page.chunks) {
2233
+ if (currentLinkId !== chunk.linkId) {
2234
+ const headingPrefix = '#'.repeat(Math.max(2, Math.min(6, chunk.depth + 2)));
2235
+ lines.push('');
2236
+ if (chunk.titleVisible) {
2237
+ lines.push(`${headingPrefix} ${chunk.number} ${stripLeadingNumericPrefixForRender(chunk.title, chunk.number)}`);
2238
+ }
2239
+ else {
2240
+ lines.push(`<!-- ${chunk.number} (title hidden) -->`);
2241
+ }
2242
+ currentLinkId = chunk.linkId;
2243
+ }
2244
+ const body = chunk.content.trim();
2245
+ if (body.length > 0) {
2246
+ lines.push('', body);
2247
+ }
2248
+ }
2249
+ return {
2250
+ format: page.format,
2251
+ formats: [...page.formats],
2252
+ content: this.joinMarkdown(lines),
2253
+ assets: this.uniqueAssetRefs(page.assets),
2254
+ };
2255
+ }
2256
+ materializePublishContent(fragment, targetDescriptor) {
2257
+ if (targetDescriptor.target === 'markdown') {
2258
+ return this.materializeMarkdownPublishContent(fragment, targetDescriptor);
2259
+ }
2260
+ return this.materializeHtmlPublishContent(fragment, targetDescriptor);
2261
+ }
2262
+ materializeMarkdownPublishContent(fragment, targetDescriptor) {
2263
+ return {
2264
+ mediaType: targetDescriptor.mediaType,
2265
+ content: fragment.content,
2266
+ warnings: [],
2267
+ };
2268
+ }
2269
+ materializeHtmlPublishContent(fragment, targetDescriptor) {
2270
+ const warnings = [];
2271
+ const sourceParts = splitDocmanComposeSourceContent(fragment.format, fragment.content);
2272
+ if (this.normalizeNonEmpty(sourceParts.modulePreamble)) {
2273
+ this.pushPublishWarning(warnings, 'mdx_module_preamble_omitted', 'MDX import/export preamble is omitted from HTML materialization and is not executed.');
2274
+ }
2275
+ const blocks = this.tokenizeHtmlMaterializationBlocks(sourceParts.body, warnings);
2276
+ const renderedBody = blocks
2277
+ .map((block) => block.kind === 'markdown' ? this.renderMarkdownBlockToHtml(block.content) : block.content)
2278
+ .filter((entry) => this.normalizeNonEmpty(entry))
2279
+ .join('\n');
2280
+ return {
2281
+ mediaType: targetDescriptor.mediaType,
2282
+ content: this.buildPublishedHtmlDocument(this.toPublishedHtmlTitle(fragment), renderedBody),
2283
+ warnings,
2284
+ };
2285
+ }
2286
+ tokenizeHtmlMaterializationBlocks(source, warnings) {
2287
+ const lines = String(source ?? '').split(/\r?\n/);
2288
+ const blocks = [];
2289
+ let markdownLines = [];
2290
+ const flushMarkdown = () => {
2291
+ const content = markdownLines.join('\n').trim();
2292
+ markdownLines = [];
2293
+ if (!content)
2294
+ return;
2295
+ blocks.push({ kind: 'markdown', content });
2296
+ };
2297
+ for (let index = 0; index < lines.length; index += 1) {
2298
+ const line = String(lines[index] ?? '');
2299
+ const trimmed = line.trim();
2300
+ if (!trimmed) {
2301
+ markdownLines.push('');
2302
+ continue;
2303
+ }
2304
+ if (PAGE_BREAK_LINE_RE.test(line)) {
2305
+ flushMarkdown();
2306
+ blocks.push({ kind: 'raw', content: DOCMAN_PAGEBREAK_HTML });
2307
+ this.pushPublishWarning(warnings, 'pagebreak_rendered', 'Compose pagebreak markers are rendered as horizontal separators in HTML output.');
2308
+ continue;
2309
+ }
2310
+ if (!this.isHtmlLikeStandaloneLine(trimmed)) {
2311
+ markdownLines.push(line);
2312
+ continue;
2313
+ }
2314
+ flushMarkdown();
2315
+ const rawLines = [line];
2316
+ while (index + 1 < lines.length) {
2317
+ const nextLine = String(lines[index + 1] ?? '');
2318
+ const nextTrimmed = nextLine.trim();
2319
+ if (!nextTrimmed || !this.isHtmlLikeStandaloneLine(nextTrimmed) || PAGE_BREAK_LINE_RE.test(nextLine)) {
2320
+ break;
2321
+ }
2322
+ rawLines.push(nextLine);
2323
+ index += 1;
2324
+ }
2325
+ const rawBlock = rawLines.join('\n').trim();
2326
+ if (!rawBlock)
2327
+ continue;
2328
+ blocks.push({ kind: 'raw', content: rawBlock });
2329
+ if (!HTML_COMMENT_LINE_RE.test(trimmed)) {
2330
+ this.pushPublishWarning(warnings, 'raw_html_block_preserved', 'Standalone HTML/JSX blocks are preserved literally in HTML materialization.');
2331
+ }
2332
+ }
2333
+ flushMarkdown();
2334
+ return blocks;
2335
+ }
2336
+ renderMarkdownBlockToHtml(markdown) {
2337
+ const content = String(markdown ?? '').trim();
2338
+ if (!content)
2339
+ return '';
2340
+ return renderToStaticMarkup(createElement(ReactMarkdown, {
2341
+ remarkPlugins: [remarkGfm],
2342
+ }, content));
2343
+ }
2344
+ buildPublishedHtmlDocument(title, bodyHtml) {
2345
+ const body = this.normalizeNonEmpty(bodyHtml) ?? '<p><em>No content to materialize.</em></p>';
2346
+ return [
2347
+ '<!doctype html>',
2348
+ '<html lang="en">',
2349
+ '<head>',
2350
+ ' <meta charset="utf-8" />',
2351
+ ' <meta name="viewport" content="width=device-width, initial-scale=1" />',
2352
+ ` <title>${this.escapeHtml(title)}</title>`,
2353
+ ` <style>${DOCMAN_PUBLISH_HTML_STYLES}</style>`,
2354
+ '</head>',
2355
+ '<body>',
2356
+ ' <main>',
2357
+ body
2358
+ .split('\n')
2359
+ .map((line) => ` ${line}`)
2360
+ .join('\n'),
2361
+ ' </main>',
2362
+ '</body>',
2363
+ '</html>',
2364
+ ].join('\n');
2365
+ }
2366
+ toPublishedHtmlTitle(fragment) {
2367
+ const lines = String(fragment.content ?? '').split(/\r?\n/);
2368
+ for (const line of lines) {
2369
+ const trimmed = line.trim();
2370
+ if (trimmed.startsWith('# ')) {
2371
+ return trimmed.slice(2).trim() || 'Docman Document';
2372
+ }
2373
+ }
2374
+ if (fragment.kind === 'page' && fragment.pageNumber) {
2375
+ return `Document page ${fragment.pageNumber}`;
2376
+ }
2377
+ if (fragment.kind === 'section' && fragment.sectionId) {
2378
+ return `Document section ${fragment.sectionId}`;
2379
+ }
2380
+ if (fragment.kind === 'page' && fragment.pageVersionId) {
2381
+ return `Document page ${fragment.pageVersionId}`;
2382
+ }
2383
+ return 'Docman Document';
2384
+ }
2385
+ isHtmlLikeStandaloneLine(value) {
2386
+ const normalized = String(value ?? '').trim();
2387
+ if (!normalized)
2388
+ return false;
2389
+ return HTML_COMMENT_LINE_RE.test(normalized) || HTML_LIKE_BLOCK_LINE_RE.test(normalized);
2390
+ }
2391
+ pushPublishWarning(warnings, code, message) {
2392
+ if (warnings.some((warning) => warning.code === code))
2393
+ return;
2394
+ warnings.push({ code, message });
2395
+ }
2396
+ renderSectionHeading(item) {
2397
+ const headingLevel = Math.max(2, Math.min(6, 2 + item.depth));
2398
+ const headingPrefix = '#'.repeat(headingLevel);
2399
+ return [`${headingPrefix} ${item.number} ${stripLeadingNumericPrefixForRender(item.title, item.number)}`];
2400
+ }
2401
+ renderPageBody(item, headingLevel) {
2402
+ const lines = [];
2403
+ const headingPrefix = '#'.repeat(Math.max(2, Math.min(6, headingLevel)));
2404
+ if (item.titleVisible) {
2405
+ lines.push(`${headingPrefix} ${item.number} ${stripLeadingNumericPrefixForRender(item.title, item.number)}`);
2406
+ }
2407
+ else {
2408
+ lines.push(`<!-- ${item.number} (title hidden) -->`);
2409
+ }
2410
+ const parts = item.contentParts.length > 0 ? item.contentParts : [''];
2411
+ for (let i = 0; i < parts.length; i++) {
2412
+ const body = (parts[i] ?? '').trim();
2413
+ if (body.length > 0) {
2414
+ lines.push('', body);
2415
+ }
2416
+ if (i < parts.length - 1) {
2417
+ lines.push('', '<!-- pagebreak -->');
2418
+ }
2419
+ }
2420
+ return lines;
2421
+ }
2422
+ splitContentByPageBreakMarkers(content) {
2423
+ const text = String(content ?? '');
2424
+ if (!text)
2425
+ return [''];
2426
+ const lines = text.split(/\r?\n/);
2427
+ const parts = [];
2428
+ let current = [];
2429
+ for (const line of lines) {
2430
+ if (PAGE_BREAK_LINE_RE.test(line)) {
2431
+ parts.push(current.join('\n').trim());
2432
+ current = [];
2433
+ }
2434
+ else {
2435
+ current.push(line);
2436
+ }
2437
+ }
2438
+ parts.push(current.join('\n').trim());
2439
+ return parts.length > 0 ? parts : [''];
2440
+ }
2441
+ collectSubtreeItems(items, rootLinkId) {
2442
+ const byId = new Map();
2443
+ for (const item of items) {
2444
+ byId.set(item.linkId, item);
2445
+ }
2446
+ const isDescendant = (candidate) => {
2447
+ if (candidate.linkId === rootLinkId)
2448
+ return true;
2449
+ let cursor = candidate.parentLinkId;
2450
+ while (cursor) {
2451
+ if (cursor === rootLinkId)
2452
+ return true;
2453
+ const parent = byId.get(cursor);
2454
+ if (!parent)
2455
+ return false;
2456
+ cursor = parent.parentLinkId;
2457
+ }
2458
+ return false;
2459
+ };
2460
+ return items.filter((item) => isDescendant(item));
2461
+ }
2462
+ resolveDocumentTitle(document, documentVersion, localeState) {
2463
+ const versionTitle = this.normalizeNonEmpty(documentVersion.title);
2464
+ if (versionTitle)
2465
+ return versionTitle;
2466
+ return this.resolveLocalizedValue(document.titleMl, localeState, document.title);
2467
+ }
2468
+ resolveSectionTitle(section, titleOverride, localeState) {
2469
+ const override = this.normalizeNonEmpty(titleOverride);
2470
+ if (override)
2471
+ return override;
2472
+ const title = this.resolveLocalizedValue(section.titleMl, localeState, section.title);
2473
+ if (title)
2474
+ return title;
2475
+ return 'Section';
2476
+ }
2477
+ resolvePageTitle(page, pageVersion, titleOverride, localeState) {
2478
+ const override = this.normalizeNonEmpty(titleOverride);
2479
+ if (override)
2480
+ return override;
2481
+ const versionTitle = this.normalizeNonEmpty(pageVersion.title);
2482
+ if (versionTitle)
2483
+ return versionTitle;
2484
+ if (page) {
2485
+ const pageTitle = this.resolveLocalizedValue(page.titleMl, localeState, page.title);
2486
+ if (pageTitle)
2487
+ return pageTitle;
2488
+ }
2489
+ return `Page ${pageVersion.version}`;
2490
+ }
2491
+ resolvePageContent(pageVersion, localeState) {
2492
+ return this.resolveLocalizedValue(pageVersion.contentMl, localeState, pageVersion.content ?? '');
2493
+ }
2494
+ resolveComposePageSource(pageVersion, localeState, stage, operation) {
2495
+ return Effect.gen(this, function* (_) {
2496
+ const format = resolveDocmanComposeSourceFormat(pageVersion.format);
2497
+ if (!format) {
2498
+ return yield* _(Effect.fail(XfErrorFactory.upsertFailed({
2499
+ stage,
2500
+ operation,
2501
+ message: 'unsupported_compose_source_format',
2502
+ data: {
2503
+ pageVersionId: pageVersion.id,
2504
+ format: pageVersion.format,
2505
+ supportedFormats: ['md', 'mdx'],
2506
+ },
2507
+ })));
2508
+ }
2509
+ const source = normalizeDocmanComposeSourceContent(format, this.resolvePageContent(pageVersion, localeState));
2510
+ const assetTokens = listDocmanAssetReferenceTokens(source);
2511
+ if (assetTokens.length === 0) {
2512
+ const sourceParts = splitDocmanComposeSourceContent(format, source);
2513
+ return {
2514
+ format,
2515
+ modulePreamble: sourceParts.modulePreamble,
2516
+ content: sourceParts.body,
2517
+ assets: [],
2518
+ };
2519
+ }
2520
+ const assetRefs = yield* _(Effect.all(assetTokens.map((token) => this.resolveAssetReferenceToken(token, stage, `${operation}.asset`))));
2521
+ const byToken = new Map(assetRefs.map((assetRef) => [assetRef.token, assetRef]));
2522
+ const resolvedSource = replaceDocmanAssetReferenceTokens(source, (token) => byToken.get(token.token)?.href ?? token.token);
2523
+ const sourceParts = splitDocmanComposeSourceContent(format, resolvedSource);
2524
+ return {
2525
+ format,
2526
+ modulePreamble: sourceParts.modulePreamble,
2527
+ content: sourceParts.body,
2528
+ assets: this.uniqueAssetRefs(assetRefs),
2529
+ };
2530
+ });
2531
+ }
2532
+ resolveVersionReleaseNotes(version, localeState) {
2533
+ const fallback = this.normalizeNonEmpty(version.releaseNotes);
2534
+ const localized = this.resolveLocalizedValue((version.releaseNotesMl ?? undefined), localeState, fallback ?? '');
2535
+ return this.normalizeNonEmpty(localized) ?? fallback;
2536
+ }
2537
+ resolveAssetReferenceToken(token, stage, operation) {
2538
+ return Effect.gen(this, function* (_) {
2539
+ const assetRepository = yield* _(this.requireDependency(this.assetRepository, 'assetRepository', stage, operation));
2540
+ const assetVersionRepository = yield* _(this.requireDependency(this.assetVersionRepository, 'assetVersionRepository', stage, operation));
2541
+ let asset = null;
2542
+ const assetsByUid = yield* _(assetRepository
2543
+ .find({
2544
+ matchEq: { assetUid: token.ref },
2545
+ options: { limit: 1 },
2546
+ })
2547
+ .pipe(Effect.mapError(mapDbError({ stage, operation: 'assetRepository.find(assetUid)', factory: XfErrorFactory.notFound }))));
2548
+ asset = assetsByUid[0] ?? null;
2549
+ if (!asset) {
2550
+ const assetsBySlug = yield* _(assetRepository
2551
+ .find({
2552
+ matchEq: { slug: token.ref },
2553
+ options: { limit: 1 },
2554
+ })
2555
+ .pipe(Effect.mapError(mapDbError({ stage, operation: 'assetRepository.find(slug)', factory: XfErrorFactory.notFound }))));
2556
+ asset = assetsBySlug[0] ?? null;
2557
+ }
2558
+ if (!asset?.id) {
2559
+ return yield* _(Effect.fail(XfErrorFactory.notFound({
2560
+ stage,
2561
+ operation,
2562
+ message: 'Asset reference could not be resolved.',
2563
+ identifier: { token: token.token, ref: token.ref },
2564
+ })));
2565
+ }
2566
+ let assetVersion = null;
2567
+ if (token.version !== null) {
2568
+ const versions = yield* _(assetVersionRepository
2569
+ .find({
2570
+ matchEq: { assetId: asset.id, version: token.version },
2571
+ options: { limit: 1 },
2572
+ })
2573
+ .pipe(Effect.mapError(mapDbError({
2574
+ stage,
2575
+ operation: 'assetVersionRepository.find(version)',
2576
+ factory: XfErrorFactory.notFound,
2577
+ }))));
2578
+ assetVersion = versions[0] ?? null;
2579
+ }
2580
+ else if (this.normalizeNonEmpty(asset.currentVersionId)) {
2581
+ assetVersion = yield* _(assetVersionRepository.findById(String(asset.currentVersionId)).pipe(Effect.mapError(mapDbError({
2582
+ stage,
2583
+ operation: 'assetVersionRepository.findById(currentVersionId)',
2584
+ factory: XfErrorFactory.notFound,
2585
+ }))));
2586
+ }
2587
+ if (!assetVersion) {
2588
+ const versions = yield* _(assetVersionRepository
2589
+ .find({
2590
+ matchEq: { assetId: asset.id },
2591
+ options: { sort: [{ field: 'version', type: 'desc' }], limit: 50 },
2592
+ })
2593
+ .pipe(Effect.mapError(mapDbError({
2594
+ stage,
2595
+ operation: 'assetVersionRepository.find(latest)',
2596
+ factory: XfErrorFactory.notFound,
2597
+ }))));
2598
+ assetVersion =
2599
+ versions.find((entry) => this.normalizeNonEmpty(entry.status) === 'ready') ??
2600
+ versions[0] ??
2601
+ null;
2602
+ }
2603
+ if (!assetVersion?.id) {
2604
+ return yield* _(Effect.fail(XfErrorFactory.notFound({
2605
+ stage,
2606
+ operation,
2607
+ message: 'Asset version could not be resolved.',
2608
+ identifier: { token: token.token, assetId: asset.id, version: token.version ?? asset.currentVersionId },
2609
+ })));
2610
+ }
2611
+ const href = this.resolveAssetHref(assetVersion);
2612
+ if (!href) {
2613
+ return yield* _(Effect.fail(XfErrorFactory.upsertFailed({
2614
+ stage,
2615
+ operation,
2616
+ message: 'asset_reference_missing_source_url',
2617
+ data: { token: token.token, assetId: asset.id, assetVersionId: assetVersion.id },
2618
+ })));
2619
+ }
2620
+ return {
2621
+ token: token.token,
2622
+ ref: token.ref,
2623
+ assetId: asset.id,
2624
+ assetVersionId: assetVersion.id,
2625
+ assetVersion: Number(assetVersion.version) || 0,
2626
+ assetUid: this.normalizeNonEmpty(asset.assetUid),
2627
+ slug: this.normalizeNonEmpty(asset.slug),
2628
+ title: this.normalizeNonEmpty(asset.title),
2629
+ altText: this.normalizeNonEmpty(asset.altText),
2630
+ kind: String(asset.kind ?? ''),
2631
+ mime: String(assetVersion.mime ?? ''),
2632
+ href,
2633
+ width: Number.isFinite(Number(assetVersion.width)) ? Number(assetVersion.width) : undefined,
2634
+ height: Number.isFinite(Number(assetVersion.height)) ? Number(assetVersion.height) : undefined,
2635
+ };
2636
+ });
2637
+ }
2638
+ resolveAssetHref(assetVersion) {
2639
+ return this.normalizeNonEmpty(assetVersion.sourceUrl);
2640
+ }
2641
+ uniqueAssetRefs(assetRefs) {
2642
+ const unique = new Map();
2643
+ for (const assetRef of assetRefs) {
2644
+ const key = `${assetRef.assetVersionId}:${assetRef.token}`;
2645
+ if (!unique.has(key))
2646
+ unique.set(key, assetRef);
2647
+ }
2648
+ return [...unique.values()];
2649
+ }
2650
+ uniqueModulePreambles(values) {
2651
+ const unique = new Map();
2652
+ for (const value of values) {
2653
+ const normalized = this.normalizeNonEmpty(value);
2654
+ if (!normalized || unique.has(normalized))
2655
+ continue;
2656
+ unique.set(normalized, normalized);
2657
+ }
2658
+ return [...unique.values()];
2659
+ }
2660
+ prependModulePreambles(lines, modulePreambles) {
2661
+ if (modulePreambles.length === 0)
2662
+ return;
2663
+ lines.push(modulePreambles.join('\n\n'), '');
2664
+ }
2665
+ stripDocumentListOptions(options) {
2666
+ const { includeVersionInfo: _includeVersionInfo, locale: _locale, fallbackLocale: _fallbackLocale, ...query } = options ?? {};
2667
+ return Object.keys(query).length > 0 ? query : undefined;
2668
+ }
2669
+ resolveLocaleOptions(options) {
2670
+ return {
2671
+ locale: this.normalizeLocale(options?.locale ?? this.locale),
2672
+ fallbackLocale: this.normalizeLocale(options?.fallbackLocale),
2673
+ };
2674
+ }
2675
+ normalizeLocale(value) {
2676
+ if (typeof value !== 'string')
2677
+ return undefined;
2678
+ const normalized = value.trim().toLowerCase();
2679
+ return normalized.length > 0 ? normalized : undefined;
2680
+ }
2681
+ normalizeNonEmpty(value) {
2682
+ if (typeof value !== 'string')
2683
+ return undefined;
2684
+ const trimmed = value.trim();
2685
+ return trimmed.length > 0 ? trimmed : undefined;
2686
+ }
2687
+ escapeHtml(value) {
2688
+ return String(value ?? '')
2689
+ .replaceAll('&', '&amp;')
2690
+ .replaceAll('<', '&lt;')
2691
+ .replaceAll('>', '&gt;')
2692
+ .replaceAll('"', '&quot;')
2693
+ .replaceAll("'", '&#39;');
2694
+ }
2695
+ resolveLocalizedValue(value, localeState, fallbackText = '') {
2696
+ if (!value)
2697
+ return fallbackText;
2698
+ const locale = this.normalizeLocale(localeState.locale);
2699
+ if (locale) {
2700
+ const exact = value[locale];
2701
+ if (typeof exact === 'string' && exact.trim().length > 0)
2702
+ return exact;
2703
+ }
2704
+ const fallbackLocale = this.normalizeLocale(localeState.fallbackLocale);
2705
+ if (fallbackLocale) {
2706
+ const fallback = value[fallbackLocale];
2707
+ if (typeof fallback === 'string' && fallback.trim().length > 0)
2708
+ return fallback;
2709
+ }
2710
+ const first = Object.values(value).find((entry) => typeof entry === 'string' && entry.trim().length > 0);
2711
+ return first ?? fallbackText;
2712
+ }
2713
+ buildLanguageCandidates(localeState) {
2714
+ const candidates = [];
2715
+ const push = (value) => {
2716
+ const normalized = this.normalizeLocale(value);
2717
+ if (!normalized)
2718
+ return;
2719
+ if (!candidates.includes(normalized))
2720
+ candidates.push(normalized);
2721
+ };
2722
+ push(localeState.locale);
2723
+ push(localeState.fallbackLocale);
2724
+ return candidates;
2725
+ }
2726
+ withLocaleOptions(options, localeState, mlgFields) {
2727
+ const projectionOptions = { ...(options?.projectionOptions ?? {}) };
2728
+ const hasLanguages = Array.isArray(projectionOptions.languages) && projectionOptions.languages.length > 0;
2729
+ const languages = this.buildLanguageCandidates(localeState);
2730
+ if (!hasLanguages && languages.length > 0) {
2731
+ projectionOptions.languages = languages;
2732
+ }
2733
+ const hasDefaultLocaleSelection = hasLanguages || languages.length > 0;
2734
+ if (!options && !hasDefaultLocaleSelection) {
2735
+ return undefined;
2736
+ }
2737
+ const mlgFieldsResolved = options?.mlgFields && options.mlgFields.length > 0 ? options.mlgFields : [...mlgFields];
2738
+ return {
2739
+ ...(options ?? {}),
2740
+ mlgFields: mlgFieldsResolved,
2741
+ projectionOptions,
2742
+ };
2743
+ }
2744
+ traverseLinks(links, parentNumber, baseDepth) {
2745
+ if (!Array.isArray(links) || links.length === 0)
2746
+ return [];
2747
+ const byId = new Map();
2748
+ for (const link of links) {
2749
+ if (link.id) {
2750
+ byId.set(link.id, link);
2751
+ }
2752
+ }
2753
+ const childrenByParent = new Map();
2754
+ const roots = [];
2755
+ for (const link of links) {
2756
+ const parentId = this.normalizeNonEmpty(link.parentLinkId);
2757
+ if (!parentId || !byId.has(parentId)) {
2758
+ roots.push(link);
2759
+ continue;
2760
+ }
2761
+ const arr = childrenByParent.get(parentId) ?? [];
2762
+ arr.push(link);
2763
+ childrenByParent.set(parentId, arr);
2764
+ }
2765
+ const sortByPosition = (values) => [...values].sort((a, b) => {
2766
+ const posDelta = Number(a.position ?? 0) - Number(b.position ?? 0);
2767
+ if (posDelta !== 0)
2768
+ return posDelta;
2769
+ return String(a.id).localeCompare(String(b.id));
2770
+ });
2771
+ const visited = new Set();
2772
+ const result = [];
2773
+ const walk = (nodes, nodeParentNumber, depth) => {
2774
+ let ordinal = 1;
2775
+ for (const node of sortByPosition(nodes)) {
2776
+ const nodeId = node.id;
2777
+ if (!nodeId)
2778
+ continue;
2779
+ if (visited.has(nodeId))
2780
+ continue;
2781
+ visited.add(nodeId);
2782
+ const number = this.resolveNumber(nodeParentNumber, node.numbering, ordinal);
2783
+ const effectiveDepth = Number.isInteger(node.depth) ? Number(node.depth) : depth;
2784
+ result.push({
2785
+ link: node,
2786
+ depth: effectiveDepth,
2787
+ number,
2788
+ });
2789
+ const children = childrenByParent.get(nodeId) ?? [];
2790
+ walk(children, number, depth + 1);
2791
+ ordinal += 1;
2792
+ }
2793
+ };
2794
+ walk(roots, parentNumber, baseDepth);
2795
+ for (const link of sortByPosition(links)) {
2796
+ if (!link.id || visited.has(link.id))
2797
+ continue;
2798
+ walk([link], parentNumber, baseDepth);
2799
+ }
2800
+ return result;
2801
+ }
2802
+ resolveNumber(parentNumber, explicit, ordinal) {
2803
+ const normalized = this.normalizeNonEmpty(explicit);
2804
+ if (normalized) {
2805
+ if (!parentNumber)
2806
+ return normalized;
2807
+ if (normalized.startsWith(`${parentNumber}.`)) {
2808
+ return normalized;
2809
+ }
2810
+ return `${parentNumber}.${normalized}`;
2811
+ }
2812
+ const segment = String(ordinal);
2813
+ return parentNumber ? `${parentNumber}.${segment}` : segment;
2814
+ }
2815
+ pushUnique(target, value) {
2816
+ if (!target.includes(value)) {
2817
+ target.push(value);
2818
+ }
2819
+ }
2820
+ joinMarkdown(lines) {
2821
+ const merged = lines.join('\n').replace(/\n{3,}/g, '\n\n').trim();
2822
+ return merged.length > 0 ? `${merged}\n` : '';
2823
+ }
2824
+ resolveCascadeDependencies(stage, operation) {
2825
+ return Effect.gen(this, function* (_) {
2826
+ return {
2827
+ documentRepository: this.documentRepository,
2828
+ documentVersionRepository: yield* _(this.requireDependency(this.documentVersionRepository, 'documentVersionRepository', stage, operation)),
2829
+ documentSectionLinkRepository: yield* _(this.requireDependency(this.documentSectionLinkRepository, 'documentSectionLinkRepository', stage, operation)),
2830
+ sectionRepository: yield* _(this.requireDependency(this.sectionRepository, 'sectionRepository', stage, operation)),
2831
+ pageRepository: yield* _(this.requireDependency(this.pageRepository, 'pageRepository', stage, operation)),
2832
+ pageVersionRepository: yield* _(this.requireDependency(this.pageVersionRepository, 'pageVersionRepository', stage, operation)),
2833
+ sectionPageLinkRepository: yield* _(this.requireDependency(this.sectionPageLinkRepository, 'sectionPageLinkRepository', stage, operation)),
2834
+ pageSnippetLinkRepository: yield* _(this.requireDependency(this.pageSnippetLinkRepository, 'pageSnippetLinkRepository', stage, operation)),
2835
+ pageEmbedLinkRepository: yield* _(this.requireDependency(this.pageEmbedLinkRepository, 'pageEmbedLinkRepository', stage, operation)),
2836
+ };
2837
+ });
2838
+ }
2839
+ requireDependency(dependency, name, stage, operation) {
2840
+ if (dependency)
2841
+ return Effect.succeed(dependency);
2842
+ return Effect.fail(XfErrorFactory.configurationError({
2843
+ stage,
2844
+ operation,
2845
+ message: `Missing dependency: ${name}`,
2846
+ debug: { dependency: name },
2847
+ }));
2848
+ }
2849
+ normalizeCascadeError(stage, operation, cause) {
2850
+ if (cause && typeof cause === 'object' && '_tag' in cause) {
2851
+ return cause;
2852
+ }
2853
+ return XfErrorFactory.upsertFailed({
2854
+ stage,
2855
+ operation,
2856
+ message: 'document_cascade_delete_failed',
2857
+ cause,
2858
+ });
2859
+ }
2860
+ }