@open-mercato/documents 0.6.8-develop.7016.1.4ed7b1e49d

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 (602) hide show
  1. package/README.md +88 -0
  2. package/build.mjs +23 -0
  3. package/dist/index.js +5 -0
  4. package/dist/index.js.map +7 -0
  5. package/dist/modules/documents/acl.js +15 -0
  6. package/dist/modules/documents/acl.js.map +7 -0
  7. package/dist/modules/documents/api/[id]/archive/route.js +116 -0
  8. package/dist/modules/documents/api/[id]/archive/route.js.map +7 -0
  9. package/dist/modules/documents/api/[id]/attachments/[attachmentId]/route.js +155 -0
  10. package/dist/modules/documents/api/[id]/attachments/[attachmentId]/route.js.map +7 -0
  11. package/dist/modules/documents/api/[id]/attachments/route.js +153 -0
  12. package/dist/modules/documents/api/[id]/attachments/route.js.map +7 -0
  13. package/dist/modules/documents/api/[id]/collab-token/route.js +180 -0
  14. package/dist/modules/documents/api/[id]/collab-token/route.js.map +7 -0
  15. package/dist/modules/documents/api/[id]/comments/access-check/route.js +86 -0
  16. package/dist/modules/documents/api/[id]/comments/access-check/route.js.map +7 -0
  17. package/dist/modules/documents/api/[id]/comments/route.js +387 -0
  18. package/dist/modules/documents/api/[id]/comments/route.js.map +7 -0
  19. package/dist/modules/documents/api/[id]/content/route.js +143 -0
  20. package/dist/modules/documents/api/[id]/content/route.js.map +7 -0
  21. package/dist/modules/documents/api/[id]/duplicate/route.js +200 -0
  22. package/dist/modules/documents/api/[id]/duplicate/route.js.map +7 -0
  23. package/dist/modules/documents/api/[id]/export/route.js +421 -0
  24. package/dist/modules/documents/api/[id]/export/route.js.map +7 -0
  25. package/dist/modules/documents/api/[id]/favorite/route.js +96 -0
  26. package/dist/modules/documents/api/[id]/favorite/route.js.map +7 -0
  27. package/dist/modules/documents/api/[id]/links/[linkId]/route.js +85 -0
  28. package/dist/modules/documents/api/[id]/links/[linkId]/route.js.map +7 -0
  29. package/dist/modules/documents/api/[id]/links/route.js +257 -0
  30. package/dist/modules/documents/api/[id]/links/route.js.map +7 -0
  31. package/dist/modules/documents/api/[id]/principals/route.js +299 -0
  32. package/dist/modules/documents/api/[id]/principals/route.js.map +7 -0
  33. package/dist/modules/documents/api/[id]/route.js +246 -0
  34. package/dist/modules/documents/api/[id]/route.js.map +7 -0
  35. package/dist/modules/documents/api/[id]/shares/route.js +335 -0
  36. package/dist/modules/documents/api/[id]/shares/route.js.map +7 -0
  37. package/dist/modules/documents/api/[id]/unarchive/route.js +39 -0
  38. package/dist/modules/documents/api/[id]/unarchive/route.js.map +7 -0
  39. package/dist/modules/documents/api/[id]/versions/[versionId]/restore/route.js +133 -0
  40. package/dist/modules/documents/api/[id]/versions/[versionId]/restore/route.js.map +7 -0
  41. package/dist/modules/documents/api/[id]/versions/[versionId]/route.js +93 -0
  42. package/dist/modules/documents/api/[id]/versions/[versionId]/route.js.map +7 -0
  43. package/dist/modules/documents/api/[id]/versions/route.js +193 -0
  44. package/dist/modules/documents/api/[id]/versions/route.js.map +7 -0
  45. package/dist/modules/documents/api/[id]/watch/route.js +97 -0
  46. package/dist/modules/documents/api/[id]/watch/route.js.map +7 -0
  47. package/dist/modules/documents/api/_commands.js +34 -0
  48. package/dist/modules/documents/api/_commands.js.map +7 -0
  49. package/dist/modules/documents/api/_shared.js +490 -0
  50. package/dist/modules/documents/api/_shared.js.map +7 -0
  51. package/dist/modules/documents/api/folders/route.js +256 -0
  52. package/dist/modules/documents/api/folders/route.js.map +7 -0
  53. package/dist/modules/documents/api/instantiate/route.js +108 -0
  54. package/dist/modules/documents/api/instantiate/route.js.map +7 -0
  55. package/dist/modules/documents/api/route.js +322 -0
  56. package/dist/modules/documents/api/route.js.map +7 -0
  57. package/dist/modules/documents/api/templates/[templateId]/preview/route.js +94 -0
  58. package/dist/modules/documents/api/templates/[templateId]/preview/route.js.map +7 -0
  59. package/dist/modules/documents/api/templates/[templateId]/route.js +82 -0
  60. package/dist/modules/documents/api/templates/[templateId]/route.js.map +7 -0
  61. package/dist/modules/documents/api/templates/route.js +344 -0
  62. package/dist/modules/documents/api/templates/route.js.map +7 -0
  63. package/dist/modules/documents/backend/documents/DocumentsPageClient.js +160 -0
  64. package/dist/modules/documents/backend/documents/DocumentsPageClient.js.map +7 -0
  65. package/dist/modules/documents/backend/documents/DocumentsTable.js +143 -0
  66. package/dist/modules/documents/backend/documents/DocumentsTable.js.map +7 -0
  67. package/dist/modules/documents/backend/documents/FolderDialog.js +55 -0
  68. package/dist/modules/documents/backend/documents/FolderDialog.js.map +7 -0
  69. package/dist/modules/documents/backend/documents/FolderTree.js +40 -0
  70. package/dist/modules/documents/backend/documents/FolderTree.js.map +7 -0
  71. package/dist/modules/documents/backend/documents/MoveDocumentDialog.js +64 -0
  72. package/dist/modules/documents/backend/documents/MoveDocumentDialog.js.map +7 -0
  73. package/dist/modules/documents/backend/documents/[id]/CommentAnchorNavigation.js +77 -0
  74. package/dist/modules/documents/backend/documents/[id]/CommentAnchorNavigation.js.map +7 -0
  75. package/dist/modules/documents/backend/documents/[id]/CommentComposer.js +126 -0
  76. package/dist/modules/documents/backend/documents/[id]/CommentComposer.js.map +7 -0
  77. package/dist/modules/documents/backend/documents/[id]/CommentThreadList.js +88 -0
  78. package/dist/modules/documents/backend/documents/[id]/CommentThreadList.js.map +7 -0
  79. package/dist/modules/documents/backend/documents/[id]/CommentsRail.js +123 -0
  80. package/dist/modules/documents/backend/documents/[id]/CommentsRail.js.map +7 -0
  81. package/dist/modules/documents/backend/documents/[id]/DocumentCanvas.js +91 -0
  82. package/dist/modules/documents/backend/documents/[id]/DocumentCanvas.js.map +7 -0
  83. package/dist/modules/documents/backend/documents/[id]/DocumentEditorErrorBoundary.js +27 -0
  84. package/dist/modules/documents/backend/documents/[id]/DocumentEditorErrorBoundary.js.map +7 -0
  85. package/dist/modules/documents/backend/documents/[id]/DocumentEditorIsland.js +50 -0
  86. package/dist/modules/documents/backend/documents/[id]/DocumentEditorIsland.js.map +7 -0
  87. package/dist/modules/documents/backend/documents/[id]/DocumentEditorSurface.js +168 -0
  88. package/dist/modules/documents/backend/documents/[id]/DocumentEditorSurface.js.map +7 -0
  89. package/dist/modules/documents/backend/documents/[id]/DocumentNavigator.js +129 -0
  90. package/dist/modules/documents/backend/documents/[id]/DocumentNavigator.js.map +7 -0
  91. package/dist/modules/documents/backend/documents/[id]/DocumentPageClient.js +429 -0
  92. package/dist/modules/documents/backend/documents/[id]/DocumentPageClient.js.map +7 -0
  93. package/dist/modules/documents/backend/documents/[id]/EditorColorTools.js +68 -0
  94. package/dist/modules/documents/backend/documents/[id]/EditorColorTools.js.map +7 -0
  95. package/dist/modules/documents/backend/documents/[id]/EditorStatusPresence.js +69 -0
  96. package/dist/modules/documents/backend/documents/[id]/EditorStatusPresence.js.map +7 -0
  97. package/dist/modules/documents/backend/documents/[id]/EditorToolbar.js +151 -0
  98. package/dist/modules/documents/backend/documents/[id]/EditorToolbar.js.map +7 -0
  99. package/dist/modules/documents/backend/documents/[id]/ExportMenu.js +121 -0
  100. package/dist/modules/documents/backend/documents/[id]/ExportMenu.js.map +7 -0
  101. package/dist/modules/documents/backend/documents/[id]/MentionPicker.js +114 -0
  102. package/dist/modules/documents/backend/documents/[id]/MentionPicker.js.map +7 -0
  103. package/dist/modules/documents/backend/documents/[id]/OutlinePane.js +70 -0
  104. package/dist/modules/documents/backend/documents/[id]/OutlinePane.js.map +7 -0
  105. package/dist/modules/documents/backend/documents/[id]/RecordFieldsDialog.js +157 -0
  106. package/dist/modules/documents/backend/documents/[id]/RecordFieldsDialog.js.map +7 -0
  107. package/dist/modules/documents/backend/documents/[id]/RelatedRecordsPanel.js +266 -0
  108. package/dist/modules/documents/backend/documents/[id]/RelatedRecordsPanel.js.map +7 -0
  109. package/dist/modules/documents/backend/documents/[id]/VersionHistoryPanel.js +227 -0
  110. package/dist/modules/documents/backend/documents/[id]/VersionHistoryPanel.js.map +7 -0
  111. package/dist/modules/documents/backend/documents/[id]/VersionPreviewDialog.js +157 -0
  112. package/dist/modules/documents/backend/documents/[id]/VersionPreviewDialog.js.map +7 -0
  113. package/dist/modules/documents/backend/documents/[id]/commentTypes.js +150 -0
  114. package/dist/modules/documents/backend/documents/[id]/commentTypes.js.map +7 -0
  115. package/dist/modules/documents/backend/documents/[id]/componentCapabilities.js +13 -0
  116. package/dist/modules/documents/backend/documents/[id]/componentCapabilities.js.map +7 -0
  117. package/dist/modules/documents/backend/documents/[id]/documentPagination.js +295 -0
  118. package/dist/modules/documents/backend/documents/[id]/documentPagination.js.map +7 -0
  119. package/dist/modules/documents/backend/documents/[id]/editorPresenceStyles.js +81 -0
  120. package/dist/modules/documents/backend/documents/[id]/editorPresenceStyles.js.map +7 -0
  121. package/dist/modules/documents/backend/documents/[id]/editorTypes.js +52 -0
  122. package/dist/modules/documents/backend/documents/[id]/editorTypes.js.map +7 -0
  123. package/dist/modules/documents/backend/documents/[id]/page.js +10 -0
  124. package/dist/modules/documents/backend/documents/[id]/page.js.map +7 -0
  125. package/dist/modules/documents/backend/documents/[id]/page.meta.js +16 -0
  126. package/dist/modules/documents/backend/documents/[id]/page.meta.js.map +7 -0
  127. package/dist/modules/documents/backend/documents/[id]/relatedRecordModel.js +38 -0
  128. package/dist/modules/documents/backend/documents/[id]/relatedRecordModel.js.map +7 -0
  129. package/dist/modules/documents/backend/documents/[id]/restoreVersion.js +33 -0
  130. package/dist/modules/documents/backend/documents/[id]/restoreVersion.js.map +7 -0
  131. package/dist/modules/documents/backend/documents/[id]/useDocumentCollaboration.js +392 -0
  132. package/dist/modules/documents/backend/documents/[id]/useDocumentCollaboration.js.map +7 -0
  133. package/dist/modules/documents/backend/documents/[id]/useDocumentComments.js +253 -0
  134. package/dist/modules/documents/backend/documents/[id]/useDocumentComments.js.map +7 -0
  135. package/dist/modules/documents/backend/documents/[id]/useDocumentEditor.js +149 -0
  136. package/dist/modules/documents/backend/documents/[id]/useDocumentEditor.js.map +7 -0
  137. package/dist/modules/documents/backend/documents/[id]/useDocumentTitle.js +82 -0
  138. package/dist/modules/documents/backend/documents/[id]/useDocumentTitle.js.map +7 -0
  139. package/dist/modules/documents/backend/documents/[id]/useEditorInsertions.js +144 -0
  140. package/dist/modules/documents/backend/documents/[id]/useEditorInsertions.js.map +7 -0
  141. package/dist/modules/documents/backend/documents/[id]/useFallbackContentPersistence.js +237 -0
  142. package/dist/modules/documents/backend/documents/[id]/useFallbackContentPersistence.js.map +7 -0
  143. package/dist/modules/documents/backend/documents/[id]/useMentionPicker.js +216 -0
  144. package/dist/modules/documents/backend/documents/[id]/useMentionPicker.js.map +7 -0
  145. package/dist/modules/documents/backend/documents/[id]/versionHistoryModel.js +25 -0
  146. package/dist/modules/documents/backend/documents/[id]/versionHistoryModel.js.map +7 -0
  147. package/dist/modules/documents/backend/documents/components/EntityPicker.js +156 -0
  148. package/dist/modules/documents/backend/documents/components/EntityPicker.js.map +7 -0
  149. package/dist/modules/documents/backend/documents/components/EntityPickerResults.js +46 -0
  150. package/dist/modules/documents/backend/documents/components/EntityPickerResults.js.map +7 -0
  151. package/dist/modules/documents/backend/documents/components/NewFromTemplateDialog.js +127 -0
  152. package/dist/modules/documents/backend/documents/components/NewFromTemplateDialog.js.map +7 -0
  153. package/dist/modules/documents/backend/documents/components/PrincipalPicker.js +84 -0
  154. package/dist/modules/documents/backend/documents/components/PrincipalPicker.js.map +7 -0
  155. package/dist/modules/documents/backend/documents/components/ShareDialog.js +77 -0
  156. package/dist/modules/documents/backend/documents/components/ShareDialog.js.map +7 -0
  157. package/dist/modules/documents/backend/documents/components/ShareDialogAddForm.js +93 -0
  158. package/dist/modules/documents/backend/documents/components/ShareDialogAddForm.js.map +7 -0
  159. package/dist/modules/documents/backend/documents/components/ShareDialogList.js +82 -0
  160. package/dist/modules/documents/backend/documents/components/ShareDialogList.js.map +7 -0
  161. package/dist/modules/documents/backend/documents/components/TemplateBodyEditor.js +96 -0
  162. package/dist/modules/documents/backend/documents/components/TemplateBodyEditor.js.map +7 -0
  163. package/dist/modules/documents/backend/documents/components/TemplateEditorDialog.js +169 -0
  164. package/dist/modules/documents/backend/documents/components/TemplateEditorDialog.js.map +7 -0
  165. package/dist/modules/documents/backend/documents/components/TemplatePreview.js +38 -0
  166. package/dist/modules/documents/backend/documents/components/TemplatePreview.js.map +7 -0
  167. package/dist/modules/documents/backend/documents/components/TemplateSlotFields.js +69 -0
  168. package/dist/modules/documents/backend/documents/components/TemplateSlotFields.js.map +7 -0
  169. package/dist/modules/documents/backend/documents/components/TemplateSlotsEditor.js +71 -0
  170. package/dist/modules/documents/backend/documents/components/TemplateSlotsEditor.js.map +7 -0
  171. package/dist/modules/documents/backend/documents/components/principalPickerModel.js +63 -0
  172. package/dist/modules/documents/backend/documents/components/principalPickerModel.js.map +7 -0
  173. package/dist/modules/documents/backend/documents/components/shareDialogModel.js +68 -0
  174. package/dist/modules/documents/backend/documents/components/shareDialogModel.js.map +7 -0
  175. package/dist/modules/documents/backend/documents/components/templateUi.js +89 -0
  176. package/dist/modules/documents/backend/documents/components/templateUi.js.map +7 -0
  177. package/dist/modules/documents/backend/documents/components/useAvailableEntityRegistry.js +30 -0
  178. package/dist/modules/documents/backend/documents/components/useAvailableEntityRegistry.js.map +7 -0
  179. package/dist/modules/documents/backend/documents/components/useEntitySearch.js +191 -0
  180. package/dist/modules/documents/backend/documents/components/useEntitySearch.js.map +7 -0
  181. package/dist/modules/documents/backend/documents/components/usePrincipalPicker.js +221 -0
  182. package/dist/modules/documents/backend/documents/components/usePrincipalPicker.js.map +7 -0
  183. package/dist/modules/documents/backend/documents/components/useShareDialog.js +236 -0
  184. package/dist/modules/documents/backend/documents/components/useShareDialog.js.map +7 -0
  185. package/dist/modules/documents/backend/documents/components/useTemplateDetail.js +49 -0
  186. package/dist/modules/documents/backend/documents/components/useTemplateDetail.js.map +7 -0
  187. package/dist/modules/documents/backend/documents/components/useTemplateInstantiation.js +234 -0
  188. package/dist/modules/documents/backend/documents/components/useTemplateInstantiation.js.map +7 -0
  189. package/dist/modules/documents/backend/documents/documentUi.js +106 -0
  190. package/dist/modules/documents/backend/documents/documentUi.js.map +7 -0
  191. package/dist/modules/documents/backend/documents/documentsListTypes.js +132 -0
  192. package/dist/modules/documents/backend/documents/documentsListTypes.js.map +7 -0
  193. package/dist/modules/documents/backend/documents/page.js +9 -0
  194. package/dist/modules/documents/backend/documents/page.js.map +7 -0
  195. package/dist/modules/documents/backend/documents/page.meta.js +26 -0
  196. package/dist/modules/documents/backend/documents/page.meta.js.map +7 -0
  197. package/dist/modules/documents/backend/documents/templates/TemplatesPageClient.js +91 -0
  198. package/dist/modules/documents/backend/documents/templates/TemplatesPageClient.js.map +7 -0
  199. package/dist/modules/documents/backend/documents/templates/TemplatesTable.js +58 -0
  200. package/dist/modules/documents/backend/documents/templates/TemplatesTable.js.map +7 -0
  201. package/dist/modules/documents/backend/documents/templates/page.js +9 -0
  202. package/dist/modules/documents/backend/documents/templates/page.js.map +7 -0
  203. package/dist/modules/documents/backend/documents/templates/page.meta.js +34 -0
  204. package/dist/modules/documents/backend/documents/templates/page.meta.js.map +7 -0
  205. package/dist/modules/documents/backend/documents/templates/useTemplatesPage.js +139 -0
  206. package/dist/modules/documents/backend/documents/templates/useTemplatesPage.js.map +7 -0
  207. package/dist/modules/documents/backend/documents/useDocumentsList.js +314 -0
  208. package/dist/modules/documents/backend/documents/useDocumentsList.js.map +7 -0
  209. package/dist/modules/documents/ce.js +8 -0
  210. package/dist/modules/documents/ce.js.map +7 -0
  211. package/dist/modules/documents/commands/aggregate.js +99 -0
  212. package/dist/modules/documents/commands/aggregate.js.map +7 -0
  213. package/dist/modules/documents/commands/attachments.js +252 -0
  214. package/dist/modules/documents/commands/attachments.js.map +7 -0
  215. package/dist/modules/documents/commands/comments.js +782 -0
  216. package/dist/modules/documents/commands/comments.js.map +7 -0
  217. package/dist/modules/documents/commands/content.js +238 -0
  218. package/dist/modules/documents/commands/content.js.map +7 -0
  219. package/dist/modules/documents/commands/document-crud.js +505 -0
  220. package/dist/modules/documents/commands/document-crud.js.map +7 -0
  221. package/dist/modules/documents/commands/documents.js +473 -0
  222. package/dist/modules/documents/commands/documents.js.map +7 -0
  223. package/dist/modules/documents/commands/duplicate.js +640 -0
  224. package/dist/modules/documents/commands/duplicate.js.map +7 -0
  225. package/dist/modules/documents/commands/favorites.js +150 -0
  226. package/dist/modules/documents/commands/favorites.js.map +7 -0
  227. package/dist/modules/documents/commands/folders.js +420 -0
  228. package/dist/modules/documents/commands/folders.js.map +7 -0
  229. package/dist/modules/documents/commands/index.js +64 -0
  230. package/dist/modules/documents/commands/index.js.map +7 -0
  231. package/dist/modules/documents/commands/interceptors.js +300 -0
  232. package/dist/modules/documents/commands/interceptors.js.map +7 -0
  233. package/dist/modules/documents/commands/lifecycle.js +249 -0
  234. package/dist/modules/documents/commands/lifecycle.js.map +7 -0
  235. package/dist/modules/documents/commands/links.js +410 -0
  236. package/dist/modules/documents/commands/links.js.map +7 -0
  237. package/dist/modules/documents/commands/mutation-helpers.js +42 -0
  238. package/dist/modules/documents/commands/mutation-helpers.js.map +7 -0
  239. package/dist/modules/documents/commands/projection-types.js +1 -0
  240. package/dist/modules/documents/commands/projection-types.js.map +7 -0
  241. package/dist/modules/documents/commands/shared.js +143 -0
  242. package/dist/modules/documents/commands/shared.js.map +7 -0
  243. package/dist/modules/documents/commands/shares.js +527 -0
  244. package/dist/modules/documents/commands/shares.js.map +7 -0
  245. package/dist/modules/documents/commands/side-effects.js +103 -0
  246. package/dist/modules/documents/commands/side-effects.js.map +7 -0
  247. package/dist/modules/documents/commands/templates.js +461 -0
  248. package/dist/modules/documents/commands/templates.js.map +7 -0
  249. package/dist/modules/documents/commands/versions.js +432 -0
  250. package/dist/modules/documents/commands/versions.js.map +7 -0
  251. package/dist/modules/documents/commands/watchers.js +164 -0
  252. package/dist/modules/documents/commands/watchers.js.map +7 -0
  253. package/dist/modules/documents/data/entities.js +653 -0
  254. package/dist/modules/documents/data/entities.js.map +7 -0
  255. package/dist/modules/documents/data/extensions.js +44 -0
  256. package/dist/modules/documents/data/extensions.js.map +7 -0
  257. package/dist/modules/documents/data/validators.js +219 -0
  258. package/dist/modules/documents/data/validators.js.map +7 -0
  259. package/dist/modules/documents/di.js +35 -0
  260. package/dist/modules/documents/di.js.map +7 -0
  261. package/dist/modules/documents/encryption.js +12 -0
  262. package/dist/modules/documents/encryption.js.map +7 -0
  263. package/dist/modules/documents/events.js +27 -0
  264. package/dist/modules/documents/events.js.map +7 -0
  265. package/dist/modules/documents/index.js +19 -0
  266. package/dist/modules/documents/index.js.map +7 -0
  267. package/dist/modules/documents/lib/attachmentServicePort.js +30 -0
  268. package/dist/modules/documents/lib/attachmentServicePort.js.map +7 -0
  269. package/dist/modules/documents/lib/capabilities.js +32 -0
  270. package/dist/modules/documents/lib/capabilities.js.map +7 -0
  271. package/dist/modules/documents/lib/collabCloseEvents.js +17 -0
  272. package/dist/modules/documents/lib/collabCloseEvents.js.map +7 -0
  273. package/dist/modules/documents/lib/collabEndpoint.js +22 -0
  274. package/dist/modules/documents/lib/collabEndpoint.js.map +7 -0
  275. package/dist/modules/documents/lib/collabMaterializer.js +107 -0
  276. package/dist/modules/documents/lib/collabMaterializer.js.map +7 -0
  277. package/dist/modules/documents/lib/collabToken.js +120 -0
  278. package/dist/modules/documents/lib/collabToken.js.map +7 -0
  279. package/dist/modules/documents/lib/collaborationAwareness.js +68 -0
  280. package/dist/modules/documents/lib/collaborationAwareness.js.map +7 -0
  281. package/dist/modules/documents/lib/commentAnchors.js +15 -0
  282. package/dist/modules/documents/lib/commentAnchors.js.map +7 -0
  283. package/dist/modules/documents/lib/constants.js +25 -0
  284. package/dist/modules/documents/lib/constants.js.map +7 -0
  285. package/dist/modules/documents/lib/contentService.js +248 -0
  286. package/dist/modules/documents/lib/contentService.js.map +7 -0
  287. package/dist/modules/documents/lib/displayLabels.js +23 -0
  288. package/dist/modules/documents/lib/displayLabels.js.map +7 -0
  289. package/dist/modules/documents/lib/docxRenderer.js +601 -0
  290. package/dist/modules/documents/lib/docxRenderer.js.map +7 -0
  291. package/dist/modules/documents/lib/duplicateContent.js +22 -0
  292. package/dist/modules/documents/lib/duplicateContent.js.map +7 -0
  293. package/dist/modules/documents/lib/editorConfig.js +287 -0
  294. package/dist/modules/documents/lib/editorConfig.js.map +7 -0
  295. package/dist/modules/documents/lib/entityLinks.js +148 -0
  296. package/dist/modules/documents/lib/entityLinks.js.map +7 -0
  297. package/dist/modules/documents/lib/entityRegistry.js +258 -0
  298. package/dist/modules/documents/lib/entityRegistry.js.map +7 -0
  299. package/dist/modules/documents/lib/entityRegistry.server.js +189 -0
  300. package/dist/modules/documents/lib/entityRegistry.server.js.map +7 -0
  301. package/dist/modules/documents/lib/entityRegistryAvailability.server.js +9 -0
  302. package/dist/modules/documents/lib/entityRegistryAvailability.server.js.map +7 -0
  303. package/dist/modules/documents/lib/entitySuggestion.js +50 -0
  304. package/dist/modules/documents/lib/entitySuggestion.js.map +7 -0
  305. package/dist/modules/documents/lib/favorites.js +22 -0
  306. package/dist/modules/documents/lib/favorites.js.map +7 -0
  307. package/dist/modules/documents/lib/folderHierarchySerialization.js +19 -0
  308. package/dist/modules/documents/lib/folderHierarchySerialization.js.map +7 -0
  309. package/dist/modules/documents/lib/historyLimits.js +100 -0
  310. package/dist/modules/documents/lib/historyLimits.js.map +7 -0
  311. package/dist/modules/documents/lib/historyPolicy.js +14 -0
  312. package/dist/modules/documents/lib/historyPolicy.js.map +7 -0
  313. package/dist/modules/documents/lib/organizationAccess.js +17 -0
  314. package/dist/modules/documents/lib/organizationAccess.js.map +7 -0
  315. package/dist/modules/documents/lib/pdfHtml.js +62 -0
  316. package/dist/modules/documents/lib/pdfHtml.js.map +7 -0
  317. package/dist/modules/documents/lib/pdfRenderer.js +284 -0
  318. package/dist/modules/documents/lib/pdfRenderer.js.map +7 -0
  319. package/dist/modules/documents/lib/permissions.js +222 -0
  320. package/dist/modules/documents/lib/permissions.js.map +7 -0
  321. package/dist/modules/documents/lib/platformServices.js +35 -0
  322. package/dist/modules/documents/lib/platformServices.js.map +7 -0
  323. package/dist/modules/documents/lib/recordFieldInsertion.js +58 -0
  324. package/dist/modules/documents/lib/recordFieldInsertion.js.map +7 -0
  325. package/dist/modules/documents/lib/requestBody.js +80 -0
  326. package/dist/modules/documents/lib/requestBody.js.map +7 -0
  327. package/dist/modules/documents/lib/resourceLimits.js +62 -0
  328. package/dist/modules/documents/lib/resourceLimits.js.map +7 -0
  329. package/dist/modules/documents/lib/shareCounts.js +25 -0
  330. package/dist/modules/documents/lib/shareCounts.js.map +7 -0
  331. package/dist/modules/documents/lib/templateFill.js +84 -0
  332. package/dist/modules/documents/lib/templateFill.js.map +7 -0
  333. package/dist/modules/documents/lib/templateInstantiation.js +147 -0
  334. package/dist/modules/documents/lib/templateInstantiation.js.map +7 -0
  335. package/dist/modules/documents/lib/templatePreviewDigest.js +82 -0
  336. package/dist/modules/documents/lib/templatePreviewDigest.js.map +7 -0
  337. package/dist/modules/documents/lib/templateSeeds.js +201 -0
  338. package/dist/modules/documents/lib/templateSeeds.js.map +7 -0
  339. package/dist/modules/documents/lib/userLabels.js +37 -0
  340. package/dist/modules/documents/lib/userLabels.js.map +7 -0
  341. package/dist/modules/documents/lib/versionContent.js +131 -0
  342. package/dist/modules/documents/lib/versionContent.js.map +7 -0
  343. package/dist/modules/documents/lib/versionLabels.js +12 -0
  344. package/dist/modules/documents/lib/versionLabels.js.map +7 -0
  345. package/dist/modules/documents/lib/versioning.js +14 -0
  346. package/dist/modules/documents/lib/versioning.js.map +7 -0
  347. package/dist/modules/documents/lib/visibility.js +422 -0
  348. package/dist/modules/documents/lib/visibility.js.map +7 -0
  349. package/dist/modules/documents/lib/watchers.js +106 -0
  350. package/dist/modules/documents/lib/watchers.js.map +7 -0
  351. package/dist/modules/documents/migrations/Migration20260708163836_documents.js +44 -0
  352. package/dist/modules/documents/migrations/Migration20260708163836_documents.js.map +7 -0
  353. package/dist/modules/documents/migrations/Migration20260709164720_documents.js +17 -0
  354. package/dist/modules/documents/migrations/Migration20260709164720_documents.js.map +7 -0
  355. package/dist/modules/documents/migrations/Migration20260710002318_documents.js +32 -0
  356. package/dist/modules/documents/migrations/Migration20260710002318_documents.js.map +7 -0
  357. package/dist/modules/documents/migrations/Migration20260710071003_documents.js +13 -0
  358. package/dist/modules/documents/migrations/Migration20260710071003_documents.js.map +7 -0
  359. package/dist/modules/documents/migrations/Migration20260712120000_documents.js +13 -0
  360. package/dist/modules/documents/migrations/Migration20260712120000_documents.js.map +7 -0
  361. package/dist/modules/documents/migrations/Migration20260712183000_documents.js +14 -0
  362. package/dist/modules/documents/migrations/Migration20260712183000_documents.js.map +7 -0
  363. package/dist/modules/documents/migrations/Migration20260713092156_documents.js +29 -0
  364. package/dist/modules/documents/migrations/Migration20260713092156_documents.js.map +7 -0
  365. package/dist/modules/documents/migrations/Migration20260717000000_documents.js +37 -0
  366. package/dist/modules/documents/migrations/Migration20260717000000_documents.js.map +7 -0
  367. package/dist/modules/documents/notifications.js +47 -0
  368. package/dist/modules/documents/notifications.js.map +7 -0
  369. package/dist/modules/documents/search.js +101 -0
  370. package/dist/modules/documents/search.js.map +7 -0
  371. package/dist/modules/documents/setup.js +19 -0
  372. package/dist/modules/documents/setup.js.map +7 -0
  373. package/dist/modules/documents/widgets/injection/related-documents/LinkDocumentDialog.js +214 -0
  374. package/dist/modules/documents/widgets/injection/related-documents/LinkDocumentDialog.js.map +7 -0
  375. package/dist/modules/documents/widgets/injection/related-documents/context.js +84 -0
  376. package/dist/modules/documents/widgets/injection/related-documents/context.js.map +7 -0
  377. package/dist/modules/documents/widgets/injection/related-documents/useRelatedDocuments.js +98 -0
  378. package/dist/modules/documents/widgets/injection/related-documents/useRelatedDocuments.js.map +7 -0
  379. package/dist/modules/documents/widgets/injection/related-documents/widget.client.js +83 -0
  380. package/dist/modules/documents/widgets/injection/related-documents/widget.client.js.map +7 -0
  381. package/dist/modules/documents/widgets/injection/related-documents/widget.js +19 -0
  382. package/dist/modules/documents/widgets/injection/related-documents/widget.js.map +7 -0
  383. package/dist/modules/documents/widgets/injection-table.js +21 -0
  384. package/dist/modules/documents/widgets/injection-table.js.map +7 -0
  385. package/dist/server/documents-collab-server.js +2260 -0
  386. package/dist/server/documents-collab-server.js.map +7 -0
  387. package/generated/entities/document/index.ts +12 -0
  388. package/generated/entities/document_attachment/index.ts +9 -0
  389. package/generated/entities/document_comment/index.ts +14 -0
  390. package/generated/entities/document_content/index.ts +11 -0
  391. package/generated/entities/document_entity_link/index.ts +19 -0
  392. package/generated/entities/document_favorite/index.ts +7 -0
  393. package/generated/entities/document_folder/index.ts +9 -0
  394. package/generated/entities/document_share/index.ts +11 -0
  395. package/generated/entities/document_template/index.ts +13 -0
  396. package/generated/entities/document_version/index.ts +9 -0
  397. package/generated/entities/document_watcher/index.ts +7 -0
  398. package/generated/entities.ids.generated.ts +21 -0
  399. package/generated/entity-fields-registry.ts +151 -0
  400. package/jest.config.cjs +34 -0
  401. package/package.json +149 -0
  402. package/server/documents-collab-server.ts +3416 -0
  403. package/src/index.ts +1 -0
  404. package/src/modules/documents/acl.ts +11 -0
  405. package/src/modules/documents/api/[id]/archive/route.ts +132 -0
  406. package/src/modules/documents/api/[id]/attachments/[attachmentId]/route.ts +166 -0
  407. package/src/modules/documents/api/[id]/attachments/route.ts +173 -0
  408. package/src/modules/documents/api/[id]/collab-token/route.ts +203 -0
  409. package/src/modules/documents/api/[id]/comments/access-check/route.ts +96 -0
  410. package/src/modules/documents/api/[id]/comments/route.ts +442 -0
  411. package/src/modules/documents/api/[id]/content/route.ts +156 -0
  412. package/src/modules/documents/api/[id]/duplicate/route.ts +231 -0
  413. package/src/modules/documents/api/[id]/export/route.ts +535 -0
  414. package/src/modules/documents/api/[id]/favorite/route.ts +110 -0
  415. package/src/modules/documents/api/[id]/links/[linkId]/route.ts +92 -0
  416. package/src/modules/documents/api/[id]/links/route.ts +301 -0
  417. package/src/modules/documents/api/[id]/principals/route.ts +375 -0
  418. package/src/modules/documents/api/[id]/route.ts +267 -0
  419. package/src/modules/documents/api/[id]/shares/route.ts +372 -0
  420. package/src/modules/documents/api/[id]/unarchive/route.ts +42 -0
  421. package/src/modules/documents/api/[id]/versions/[versionId]/restore/route.ts +152 -0
  422. package/src/modules/documents/api/[id]/versions/[versionId]/route.ts +100 -0
  423. package/src/modules/documents/api/[id]/versions/route.ts +207 -0
  424. package/src/modules/documents/api/[id]/watch/route.ts +111 -0
  425. package/src/modules/documents/api/_commands.ts +53 -0
  426. package/src/modules/documents/api/_shared.ts +676 -0
  427. package/src/modules/documents/api/folders/route.ts +287 -0
  428. package/src/modules/documents/api/instantiate/route.ts +114 -0
  429. package/src/modules/documents/api/route.ts +362 -0
  430. package/src/modules/documents/api/templates/[templateId]/preview/route.ts +97 -0
  431. package/src/modules/documents/api/templates/[templateId]/route.ts +92 -0
  432. package/src/modules/documents/api/templates/route.ts +370 -0
  433. package/src/modules/documents/backend/documents/DocumentsPageClient.tsx +150 -0
  434. package/src/modules/documents/backend/documents/DocumentsTable.tsx +168 -0
  435. package/src/modules/documents/backend/documents/FolderDialog.tsx +62 -0
  436. package/src/modules/documents/backend/documents/FolderTree.tsx +72 -0
  437. package/src/modules/documents/backend/documents/MoveDocumentDialog.tsx +70 -0
  438. package/src/modules/documents/backend/documents/[id]/CommentAnchorNavigation.ts +88 -0
  439. package/src/modules/documents/backend/documents/[id]/CommentComposer.tsx +136 -0
  440. package/src/modules/documents/backend/documents/[id]/CommentThreadList.tsx +131 -0
  441. package/src/modules/documents/backend/documents/[id]/CommentsRail.tsx +138 -0
  442. package/src/modules/documents/backend/documents/[id]/DocumentCanvas.tsx +111 -0
  443. package/src/modules/documents/backend/documents/[id]/DocumentEditorErrorBoundary.tsx +39 -0
  444. package/src/modules/documents/backend/documents/[id]/DocumentEditorIsland.tsx +50 -0
  445. package/src/modules/documents/backend/documents/[id]/DocumentEditorSurface.tsx +162 -0
  446. package/src/modules/documents/backend/documents/[id]/DocumentNavigator.tsx +129 -0
  447. package/src/modules/documents/backend/documents/[id]/DocumentPageClient.tsx +463 -0
  448. package/src/modules/documents/backend/documents/[id]/EditorColorTools.tsx +71 -0
  449. package/src/modules/documents/backend/documents/[id]/EditorStatusPresence.tsx +78 -0
  450. package/src/modules/documents/backend/documents/[id]/EditorToolbar.tsx +108 -0
  451. package/src/modules/documents/backend/documents/[id]/ExportMenu.tsx +144 -0
  452. package/src/modules/documents/backend/documents/[id]/MentionPicker.tsx +137 -0
  453. package/src/modules/documents/backend/documents/[id]/OutlinePane.tsx +98 -0
  454. package/src/modules/documents/backend/documents/[id]/RecordFieldsDialog.tsx +185 -0
  455. package/src/modules/documents/backend/documents/[id]/RelatedRecordsPanel.tsx +320 -0
  456. package/src/modules/documents/backend/documents/[id]/VersionHistoryPanel.tsx +244 -0
  457. package/src/modules/documents/backend/documents/[id]/VersionPreviewDialog.tsx +197 -0
  458. package/src/modules/documents/backend/documents/[id]/commentTypes.ts +192 -0
  459. package/src/modules/documents/backend/documents/[id]/componentCapabilities.ts +17 -0
  460. package/src/modules/documents/backend/documents/[id]/documentPagination.ts +352 -0
  461. package/src/modules/documents/backend/documents/[id]/editorPresenceStyles.ts +77 -0
  462. package/src/modules/documents/backend/documents/[id]/editorTypes.ts +84 -0
  463. package/src/modules/documents/backend/documents/[id]/page.meta.ts +12 -0
  464. package/src/modules/documents/backend/documents/[id]/page.tsx +6 -0
  465. package/src/modules/documents/backend/documents/[id]/relatedRecordModel.ts +49 -0
  466. package/src/modules/documents/backend/documents/[id]/restoreVersion.ts +49 -0
  467. package/src/modules/documents/backend/documents/[id]/useDocumentCollaboration.ts +458 -0
  468. package/src/modules/documents/backend/documents/[id]/useDocumentComments.ts +286 -0
  469. package/src/modules/documents/backend/documents/[id]/useDocumentEditor.ts +157 -0
  470. package/src/modules/documents/backend/documents/[id]/useDocumentTitle.ts +77 -0
  471. package/src/modules/documents/backend/documents/[id]/useEditorInsertions.ts +147 -0
  472. package/src/modules/documents/backend/documents/[id]/useFallbackContentPersistence.ts +277 -0
  473. package/src/modules/documents/backend/documents/[id]/useMentionPicker.ts +259 -0
  474. package/src/modules/documents/backend/documents/[id]/versionHistoryModel.ts +29 -0
  475. package/src/modules/documents/backend/documents/components/EntityPicker.tsx +171 -0
  476. package/src/modules/documents/backend/documents/components/EntityPickerResults.tsx +64 -0
  477. package/src/modules/documents/backend/documents/components/NewFromTemplateDialog.tsx +136 -0
  478. package/src/modules/documents/backend/documents/components/PrincipalPicker.tsx +90 -0
  479. package/src/modules/documents/backend/documents/components/ShareDialog.tsx +88 -0
  480. package/src/modules/documents/backend/documents/components/ShareDialogAddForm.tsx +113 -0
  481. package/src/modules/documents/backend/documents/components/ShareDialogList.tsx +117 -0
  482. package/src/modules/documents/backend/documents/components/TemplateBodyEditor.tsx +96 -0
  483. package/src/modules/documents/backend/documents/components/TemplateEditorDialog.tsx +171 -0
  484. package/src/modules/documents/backend/documents/components/TemplatePreview.tsx +43 -0
  485. package/src/modules/documents/backend/documents/components/TemplateSlotFields.tsx +72 -0
  486. package/src/modules/documents/backend/documents/components/TemplateSlotsEditor.tsx +72 -0
  487. package/src/modules/documents/backend/documents/components/principalPickerModel.ts +86 -0
  488. package/src/modules/documents/backend/documents/components/shareDialogModel.ts +94 -0
  489. package/src/modules/documents/backend/documents/components/templateUi.ts +128 -0
  490. package/src/modules/documents/backend/documents/components/useAvailableEntityRegistry.ts +43 -0
  491. package/src/modules/documents/backend/documents/components/useEntitySearch.ts +197 -0
  492. package/src/modules/documents/backend/documents/components/usePrincipalPicker.ts +165 -0
  493. package/src/modules/documents/backend/documents/components/useShareDialog.ts +267 -0
  494. package/src/modules/documents/backend/documents/components/useTemplateDetail.ts +54 -0
  495. package/src/modules/documents/backend/documents/components/useTemplateInstantiation.ts +252 -0
  496. package/src/modules/documents/backend/documents/documentUi.ts +134 -0
  497. package/src/modules/documents/backend/documents/documentsListTypes.ts +163 -0
  498. package/src/modules/documents/backend/documents/page.meta.ts +24 -0
  499. package/src/modules/documents/backend/documents/page.tsx +5 -0
  500. package/src/modules/documents/backend/documents/templates/TemplatesPageClient.tsx +93 -0
  501. package/src/modules/documents/backend/documents/templates/TemplatesTable.tsx +71 -0
  502. package/src/modules/documents/backend/documents/templates/page.meta.ts +32 -0
  503. package/src/modules/documents/backend/documents/templates/page.tsx +5 -0
  504. package/src/modules/documents/backend/documents/templates/useTemplatesPage.tsx +148 -0
  505. package/src/modules/documents/backend/documents/useDocumentsList.tsx +305 -0
  506. package/src/modules/documents/ce.ts +6 -0
  507. package/src/modules/documents/commands/aggregate.ts +128 -0
  508. package/src/modules/documents/commands/attachments.ts +309 -0
  509. package/src/modules/documents/commands/comments.ts +966 -0
  510. package/src/modules/documents/commands/content.ts +294 -0
  511. package/src/modules/documents/commands/document-crud.ts +609 -0
  512. package/src/modules/documents/commands/documents.ts +581 -0
  513. package/src/modules/documents/commands/duplicate.ts +772 -0
  514. package/src/modules/documents/commands/favorites.ts +171 -0
  515. package/src/modules/documents/commands/folders.ts +506 -0
  516. package/src/modules/documents/commands/index.ts +33 -0
  517. package/src/modules/documents/commands/interceptors.ts +402 -0
  518. package/src/modules/documents/commands/lifecycle.ts +333 -0
  519. package/src/modules/documents/commands/links.ts +486 -0
  520. package/src/modules/documents/commands/mutation-helpers.ts +48 -0
  521. package/src/modules/documents/commands/projection-types.ts +77 -0
  522. package/src/modules/documents/commands/shared.ts +219 -0
  523. package/src/modules/documents/commands/shares.ts +612 -0
  524. package/src/modules/documents/commands/side-effects.ts +130 -0
  525. package/src/modules/documents/commands/templates.ts +522 -0
  526. package/src/modules/documents/commands/versions.ts +537 -0
  527. package/src/modules/documents/commands/watchers.ts +181 -0
  528. package/src/modules/documents/data/entities.ts +631 -0
  529. package/src/modules/documents/data/extensions.ts +42 -0
  530. package/src/modules/documents/data/validators.ts +248 -0
  531. package/src/modules/documents/di.ts +33 -0
  532. package/src/modules/documents/encryption.ts +14 -0
  533. package/src/modules/documents/events.ts +23 -0
  534. package/src/modules/documents/i18n/.hardcoded-allowlist.json +9 -0
  535. package/src/modules/documents/i18n/de.json +522 -0
  536. package/src/modules/documents/i18n/en.json +522 -0
  537. package/src/modules/documents/i18n/es.json +522 -0
  538. package/src/modules/documents/i18n/ko.json +522 -0
  539. package/src/modules/documents/i18n/pl.json +522 -0
  540. package/src/modules/documents/index.ts +17 -0
  541. package/src/modules/documents/lib/attachmentServicePort.ts +94 -0
  542. package/src/modules/documents/lib/capabilities.ts +57 -0
  543. package/src/modules/documents/lib/collabCloseEvents.ts +26 -0
  544. package/src/modules/documents/lib/collabEndpoint.ts +43 -0
  545. package/src/modules/documents/lib/collabMaterializer.ts +140 -0
  546. package/src/modules/documents/lib/collabToken.ts +170 -0
  547. package/src/modules/documents/lib/collaborationAwareness.ts +102 -0
  548. package/src/modules/documents/lib/commentAnchors.ts +19 -0
  549. package/src/modules/documents/lib/constants.ts +39 -0
  550. package/src/modules/documents/lib/contentService.ts +340 -0
  551. package/src/modules/documents/lib/displayLabels.ts +31 -0
  552. package/src/modules/documents/lib/docxRenderer.ts +685 -0
  553. package/src/modules/documents/lib/duplicateContent.ts +32 -0
  554. package/src/modules/documents/lib/editorConfig.ts +371 -0
  555. package/src/modules/documents/lib/entityLinks.ts +250 -0
  556. package/src/modules/documents/lib/entityRegistry.server.ts +267 -0
  557. package/src/modules/documents/lib/entityRegistry.ts +345 -0
  558. package/src/modules/documents/lib/entityRegistryAvailability.server.ts +19 -0
  559. package/src/modules/documents/lib/entitySuggestion.ts +61 -0
  560. package/src/modules/documents/lib/favorites.ts +35 -0
  561. package/src/modules/documents/lib/folderHierarchySerialization.ts +34 -0
  562. package/src/modules/documents/lib/historyLimits.ts +154 -0
  563. package/src/modules/documents/lib/historyPolicy.ts +7 -0
  564. package/src/modules/documents/lib/organizationAccess.ts +44 -0
  565. package/src/modules/documents/lib/pdfHtml.ts +65 -0
  566. package/src/modules/documents/lib/pdfRenderer.ts +367 -0
  567. package/src/modules/documents/lib/permissions.ts +299 -0
  568. package/src/modules/documents/lib/platformServices.ts +70 -0
  569. package/src/modules/documents/lib/recordFieldInsertion.ts +72 -0
  570. package/src/modules/documents/lib/requestBody.ts +97 -0
  571. package/src/modules/documents/lib/resourceLimits.ts +94 -0
  572. package/src/modules/documents/lib/shareCounts.ts +44 -0
  573. package/src/modules/documents/lib/templateFill.ts +138 -0
  574. package/src/modules/documents/lib/templateInstantiation.ts +206 -0
  575. package/src/modules/documents/lib/templatePreviewDigest.ts +122 -0
  576. package/src/modules/documents/lib/templateSeeds.ts +248 -0
  577. package/src/modules/documents/lib/userLabels.ts +58 -0
  578. package/src/modules/documents/lib/versionContent.ts +163 -0
  579. package/src/modules/documents/lib/versionLabels.ts +9 -0
  580. package/src/modules/documents/lib/versioning.ts +29 -0
  581. package/src/modules/documents/lib/visibility.ts +610 -0
  582. package/src/modules/documents/lib/watchers.ts +129 -0
  583. package/src/modules/documents/migrations/.snapshot-open-mercato.json +2892 -0
  584. package/src/modules/documents/migrations/Migration20260708163836_documents.ts +50 -0
  585. package/src/modules/documents/migrations/Migration20260709164720_documents.ts +19 -0
  586. package/src/modules/documents/migrations/Migration20260710002318_documents.ts +35 -0
  587. package/src/modules/documents/migrations/Migration20260710071003_documents.ts +13 -0
  588. package/src/modules/documents/migrations/Migration20260712120000_documents.ts +13 -0
  589. package/src/modules/documents/migrations/Migration20260712183000_documents.ts +12 -0
  590. package/src/modules/documents/migrations/Migration20260713092156_documents.ts +43 -0
  591. package/src/modules/documents/migrations/Migration20260717000000_documents.ts +40 -0
  592. package/src/modules/documents/notifications.ts +45 -0
  593. package/src/modules/documents/search.ts +118 -0
  594. package/src/modules/documents/setup.ts +19 -0
  595. package/src/modules/documents/widgets/injection/related-documents/LinkDocumentDialog.tsx +224 -0
  596. package/src/modules/documents/widgets/injection/related-documents/context.ts +101 -0
  597. package/src/modules/documents/widgets/injection/related-documents/useRelatedDocuments.ts +112 -0
  598. package/src/modules/documents/widgets/injection/related-documents/widget.client.tsx +76 -0
  599. package/src/modules/documents/widgets/injection/related-documents/widget.ts +19 -0
  600. package/src/modules/documents/widgets/injection-table.ts +20 -0
  601. package/tsconfig.json +9 -0
  602. package/watch.mjs +6 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/documents/lib/pdfRenderer.ts"],
4
+ "sourcesContent": ["import puppeteer from 'puppeteer-core'\n\nexport const PDF_RENDER_MAX_CONCURRENCY = 2\nexport const PDF_RENDER_MAX_QUEUE = 4\nexport const PDF_RENDER_ACQUIRE_TIMEOUT_MS = 2_000\nexport const PDF_RENDER_TIMEOUT_MS = 20_000\nexport const PDF_PAGE_CLOSE_TIMEOUT_MS = 1_000\nexport const PDF_MAX_OUTPUT_BYTES = 25 * 1024 * 1024\n\nconst PDF_OVERLOADED_MARKER = Symbol.for('open-mercato.documents.pdfRenderer.overloaded')\nconst PDF_TIMEOUT_MARKER = Symbol.for('open-mercato.documents.pdfRenderer.timeout')\nconst PDF_OUTPUT_TOO_LARGE_MARKER = Symbol.for('open-mercato.documents.pdfRenderer.outputTooLarge')\n\nexport class PdfRenderOverloadedError extends Error {\n readonly [PDF_OVERLOADED_MARKER] = true\n constructor() {\n super('PDF renderer is at capacity')\n this.name = 'PdfRenderOverloadedError'\n }\n}\n\nexport class PdfRenderTimeoutError extends Error {\n readonly [PDF_TIMEOUT_MARKER] = true\n constructor() {\n super('PDF renderer timed out')\n this.name = 'PdfRenderTimeoutError'\n }\n}\n\nexport class PdfRenderOutputTooLargeError extends Error {\n readonly [PDF_OUTPUT_TOO_LARGE_MARKER] = true\n constructor() {\n super('PDF renderer output exceeded its byte limit')\n this.name = 'PdfRenderOutputTooLargeError'\n }\n}\n\nfunction hasErrorMarker(error: unknown, marker: symbol): boolean {\n return Boolean(error && typeof error === 'object' && (error as Record<symbol, unknown>)[marker] === true)\n}\n\nexport const isPdfRenderOverloadedError = (error: unknown): boolean => (\n error instanceof PdfRenderOverloadedError || hasErrorMarker(error, PDF_OVERLOADED_MARKER)\n)\nexport const isPdfRenderTimeoutError = (error: unknown): boolean => (\n error instanceof PdfRenderTimeoutError || hasErrorMarker(error, PDF_TIMEOUT_MARKER)\n)\nexport const isPdfRenderOutputTooLargeError = (error: unknown): boolean => (\n error instanceof PdfRenderOutputTooLargeError || hasErrorMarker(error, PDF_OUTPUT_TOO_LARGE_MARKER)\n)\n\nexport type PdfAssetRequest = {\n url: () => string\n resourceType: () => string\n isNavigationRequest: () => boolean\n continue: () => Promise<unknown>\n abort: () => Promise<unknown>\n}\n\nexport type PdfPage = {\n setJavaScriptEnabled: (enabled: boolean) => Promise<unknown>\n setRequestInterception: (enabled: boolean) => Promise<unknown>\n on: (event: 'request', listener: (request: PdfAssetRequest) => void) => unknown\n setContent: (html: string, options: { waitUntil: 'load' }) => Promise<unknown>\n createPDFStream: (options: { format: 'A4'; printBackground: true; preferCSSPageSize: true }) => Promise<ReadableStream<Uint8Array>>\n close: () => Promise<unknown>\n}\n\nexport type PdfBrowser = {\n connected?: boolean\n newPage: () => Promise<PdfPage>\n close: () => Promise<unknown>\n on: (event: 'disconnected', listener: () => void) => unknown\n process?: () => { kill: (signal?: NodeJS.Signals) => unknown } | null\n}\n\nexport type PdfBrowserLauncher = (options: {\n executablePath: string\n headless: true\n args: string[]\n timeout: number\n protocolTimeout: number\n}) => Promise<PdfBrowser>\n\ntype PdfRendererOptions = {\n maxConcurrency?: number\n maxQueue?: number\n acquireTimeoutMs?: number\n renderTimeoutMs?: number\n pageCloseTimeoutMs?: number\n maxOutputBytes?: number\n}\n\nclass BoundedSemaphore {\n private active = 0\n private readonly waiters: Array<{\n resolve: (release: () => void) => void\n reject: (error: Error) => void\n timer: ReturnType<typeof setTimeout>\n }> = []\n\n constructor(\n private readonly capacity: number,\n private readonly maxQueue: number,\n private readonly acquireTimeoutMs: number,\n ) {}\n\n async acquire(): Promise<() => void> {\n if (this.active < this.capacity) return this.grant()\n if (this.waiters.length >= this.maxQueue) throw new PdfRenderOverloadedError()\n\n return await new Promise<() => void>((resolve, reject) => {\n const waiter = {\n resolve,\n reject,\n timer: setTimeout(() => {\n const index = this.waiters.indexOf(waiter)\n if (index >= 0) this.waiters.splice(index, 1)\n reject(new PdfRenderOverloadedError())\n }, this.acquireTimeoutMs),\n }\n this.waiters.push(waiter)\n })\n }\n\n private grant(): () => void {\n this.active += 1\n let released = false\n return () => {\n if (released) return\n released = true\n this.active -= 1\n const waiter = this.waiters.shift()\n if (!waiter) return\n clearTimeout(waiter.timer)\n waiter.resolve(this.grant())\n }\n }\n}\n\nfunction withTimeout<T>(\n promise: Promise<T>,\n timeoutMs: number,\n onTimeout: () => void,\n): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n let settled = false\n const timer = setTimeout(() => {\n if (settled) return\n settled = true\n onTimeout()\n reject(new PdfRenderTimeoutError())\n }, timeoutMs)\n promise.then(\n (value) => {\n if (settled) return\n settled = true\n clearTimeout(timer)\n resolve(value)\n },\n (error: unknown) => {\n if (settled) return\n settled = true\n clearTimeout(timer)\n reject(error)\n },\n )\n })\n}\n\nasync function readBoundedPdfStream(\n stream: ReadableStream<Uint8Array>,\n maxOutputBytes: number,\n): Promise<Uint8Array> {\n const reader = stream.getReader()\n const chunks: Uint8Array[] = []\n let totalBytes = 0\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n if (!value || value.byteLength === 0) continue\n if (totalBytes + value.byteLength > maxOutputBytes) {\n await reader.cancel('PDF output exceeded its byte limit').catch(() => undefined)\n throw new PdfRenderOutputTooLargeError()\n }\n totalBytes += value.byteLength\n chunks.push(value)\n }\n } finally {\n reader.releaseLock()\n }\n\n const output = new Uint8Array(totalBytes)\n let offset = 0\n for (const chunk of chunks) {\n output.set(chunk, offset)\n offset += chunk.byteLength\n }\n return output\n}\n\n/**\n * A process-local, bounded Chromium supervisor. The browser is reused between\n * requests, discarded after a crash/timeout, and never launched with sandbox-\n * disabling flags. Capacity is deliberately small so PDF work cannot starve\n * the application server.\n */\nexport function createPdfRenderer(\n launch: PdfBrowserLauncher,\n options: PdfRendererOptions = {},\n) {\n const renderTimeoutMs = options.renderTimeoutMs ?? PDF_RENDER_TIMEOUT_MS\n const pageCloseTimeoutMs = options.pageCloseTimeoutMs ?? PDF_PAGE_CLOSE_TIMEOUT_MS\n const maxOutputBytes = options.maxOutputBytes ?? PDF_MAX_OUTPUT_BYTES\n const semaphore = new BoundedSemaphore(\n options.maxConcurrency ?? PDF_RENDER_MAX_CONCURRENCY,\n options.maxQueue ?? PDF_RENDER_MAX_QUEUE,\n options.acquireTimeoutMs ?? PDF_RENDER_ACQUIRE_TIMEOUT_MS,\n )\n let browserState: {\n executablePath: string\n promise: Promise<PdfBrowser>\n browser?: PdfBrowser\n } | null = null\n\n const terminateBrowser = async (browser: PdfBrowser): Promise<void> => {\n let closed = false\n const closePromise = browser.close().then(\n () => { closed = true },\n () => undefined,\n )\n let closeTimer: ReturnType<typeof setTimeout> | undefined\n const closeTimeout = new Promise<void>((resolve) => {\n closeTimer = setTimeout(resolve, 1_000)\n })\n await Promise.race([\n closePromise,\n closeTimeout,\n ])\n if (closeTimer) clearTimeout(closeTimer)\n if (!closed) browser.process?.()?.kill('SIGKILL')\n }\n\n const invalidateBrowser = (browser?: PdfBrowser): void => {\n const state = browserState\n if (!state) return\n if (browser && state.browser && state.browser !== browser) return\n if (browser && !state.browser) {\n void state.promise.then((resolved) => {\n if (resolved !== browser) return\n if (browserState?.promise === state.promise) browserState = null\n return terminateBrowser(resolved)\n }).catch(() => undefined)\n return\n }\n browserState = null\n void state.promise.then((resolved) => {\n if (!browser || browser === resolved) return terminateBrowser(resolved)\n }).catch(() => undefined)\n }\n\n const getBrowser = async (executablePath: string): Promise<PdfBrowser> => {\n if (browserState && browserState.executablePath !== executablePath) {\n invalidateBrowser()\n }\n if (!browserState) {\n const promise = launch({\n executablePath,\n headless: true,\n args: [],\n timeout: Math.min(renderTimeoutMs, 10_000),\n protocolTimeout: renderTimeoutMs,\n })\n browserState = { executablePath, promise }\n promise.then((browser) => {\n if (browserState?.promise === promise) browserState.browser = browser\n browser.on('disconnected', () => {\n if (browserState?.promise === promise) browserState = null\n })\n }).catch(() => {\n if (browserState?.promise === promise) browserState = null\n })\n }\n const browser = await browserState.promise\n if (browser.connected === false) {\n invalidateBrowser(browser)\n return getBrowser(executablePath)\n }\n return browser\n }\n\n const render = async (input: {\n html: string\n executablePath: string\n isAllowedRequest: (request: PdfAssetRequest) => boolean\n }): Promise<Uint8Array> => {\n const release = await semaphore.acquire()\n let browser: PdfBrowser | undefined\n let page: PdfPage | undefined\n try {\n const renderOperation = (async () => {\n browser = await getBrowser(input.executablePath)\n page = await browser.newPage()\n await page.setJavaScriptEnabled(false)\n await page.setRequestInterception(true)\n page.on('request', (request) => {\n // A page can disappear between interception and resolution (timeout,\n // overflow, browser crash). Never turn that normal teardown race into\n // an unhandled rejection in the app process.\n void Promise.resolve()\n .then(() => (input.isAllowedRequest(request) ? request.continue() : request.abort()))\n .catch(() => undefined)\n })\n await page.setContent(input.html, { waitUntil: 'load' })\n const pdfStream = await page.createPDFStream({\n format: 'A4',\n printBackground: true,\n preferCSSPageSize: true,\n })\n return readBoundedPdfStream(pdfStream, maxOutputBytes)\n })()\n try {\n return await withTimeout(renderOperation, renderTimeoutMs, () => invalidateBrowser(browser))\n } catch (error) {\n if (isPdfRenderOutputTooLargeError(error)) invalidateBrowser(browser)\n throw error\n }\n } finally {\n if (page) {\n let closeSettled = false\n let closeTimer: ReturnType<typeof setTimeout> | undefined\n const closePromise = page.close().then(\n () => { closeSettled = true },\n () => { closeSettled = true },\n )\n const closeTimeout = new Promise<void>((resolve) => {\n closeTimer = setTimeout(resolve, pageCloseTimeoutMs)\n })\n await Promise.race([closePromise, closeTimeout])\n if (closeTimer) clearTimeout(closeTimer)\n if (!closeSettled) invalidateBrowser(browser)\n }\n release()\n }\n }\n\n return {\n render,\n dispose: async () => {\n const state = browserState\n browserState = null\n if (state) await state.promise.then(terminateBrowser).catch(() => undefined)\n },\n }\n}\n\nconst PDF_RENDERER_KEY = Symbol.for('open-mercato.documents.pdfRenderer')\nconst globalStore = globalThis as typeof globalThis & {\n [PDF_RENDERER_KEY]?: ReturnType<typeof createPdfRenderer>\n}\nconst processPdfRenderer = globalStore[PDF_RENDERER_KEY]\n ?? (globalStore[PDF_RENDERER_KEY] = createPdfRenderer(\n (options) => puppeteer.launch(options) as unknown as Promise<PdfBrowser>,\n ))\n\nexport const renderPdfWithChromium = processPdfRenderer.render\n"],
5
+ "mappings": "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",
6
+ "names": ["browser"]
7
+ }
@@ -0,0 +1,222 @@
1
+ import { hasAllFeatures } from "@open-mercato/shared/lib/auth/featureMatch";
2
+ import { forbidden } from "@open-mercato/shared/lib/crud/errors";
3
+ import { findOneWithDecryption, findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
4
+ import { Document, DocumentShare } from "../data/entities.js";
5
+ import {
6
+ resolveApiKeyPrincipalService,
7
+ resolveAuthPrincipalService,
8
+ resolveDocumentsRbacService
9
+ } from "./platformServices.js";
10
+ const TIER_RANK = {
11
+ owner: 3,
12
+ editor: 2,
13
+ commenter: 1,
14
+ viewer: 0
15
+ };
16
+ function resolveUserId(ctx) {
17
+ if (!ctx) return null;
18
+ if (typeof ctx.userId === "string" && ctx.userId.trim().length > 0) return ctx.userId;
19
+ if (typeof ctx.sub === "string" && ctx.sub.trim().length > 0 && !ctx.sub.startsWith("api_key:")) return ctx.sub;
20
+ return null;
21
+ }
22
+ function resolveOrganizationId(ctx) {
23
+ if (typeof ctx.organizationId === "string" && ctx.organizationId.trim().length > 0) {
24
+ return ctx.organizationId;
25
+ }
26
+ if (typeof ctx.orgId === "string" && ctx.orgId.trim().length > 0) return ctx.orgId;
27
+ return null;
28
+ }
29
+ function normalizeStrings(values) {
30
+ if (!Array.isArray(values)) return [];
31
+ return Array.from(
32
+ new Set(
33
+ values.map((value) => typeof value === "string" ? value.trim() : "").filter((value) => value.length > 0)
34
+ )
35
+ );
36
+ }
37
+ function hasDocumentsManage(ctx) {
38
+ if (ctx.isSuperAdmin === true) return true;
39
+ const features = normalizeStrings(ctx.features);
40
+ return hasAllFeatures(["documents.manage"], features);
41
+ }
42
+ async function resolveActiveUserRoleIds(container, scope, userId) {
43
+ const service = resolveAuthPrincipalService(container);
44
+ if (!service) return [];
45
+ return normalizeStrings(await service.resolveActiveUserRoleIds(userId, scope));
46
+ }
47
+ async function resolveActiveSubjectRoleIds(container, scope, subject) {
48
+ if (!subject.startsWith("api_key:")) {
49
+ return resolveActiveUserRoleIds(container, scope, subject);
50
+ }
51
+ const apiKeyId = subject.slice("api_key:".length).trim();
52
+ if (!apiKeyId) return [];
53
+ const apiKeyService = resolveApiKeyPrincipalService(container);
54
+ const authService = resolveAuthPrincipalService(container);
55
+ if (!apiKeyService || !authService) return [];
56
+ const assigned = normalizeStrings(await apiKeyService.resolveAssignedRoleIds(apiKeyId, scope));
57
+ if (assigned.length === 0) return [];
58
+ return normalizeStrings(await authService.filterActiveRoleIds(assigned, scope));
59
+ }
60
+ function maxShareTier(shares) {
61
+ let best = null;
62
+ for (const share of shares) {
63
+ const permission = share.permission;
64
+ if (permission !== "viewer" && permission !== "commenter" && permission !== "editor") continue;
65
+ if (!best || TIER_RANK[permission] > TIER_RANK[best]) best = permission;
66
+ }
67
+ return best;
68
+ }
69
+ async function resolvePermission(em, documentId, ctx) {
70
+ if (!ctx || !ctx.tenantId) return null;
71
+ const permissionCtx = ctx;
72
+ const organizationId = resolveOrganizationId(permissionCtx);
73
+ if (!organizationId) return null;
74
+ const document = await findOneWithDecryption(
75
+ em,
76
+ Document,
77
+ {
78
+ id: documentId,
79
+ tenantId: ctx.tenantId,
80
+ organizationId,
81
+ deletedAt: null
82
+ },
83
+ void 0,
84
+ { tenantId: ctx.tenantId, organizationId }
85
+ );
86
+ if (!document) return null;
87
+ const userId = resolveUserId(ctx);
88
+ if (userId && document.ownerUserId === userId || hasDocumentsManage(permissionCtx)) {
89
+ return "owner";
90
+ }
91
+ if (!userId) return null;
92
+ const roleIds = normalizeStrings(permissionCtx.resolvedRoleIds);
93
+ const principals = [
94
+ { principalType: "user", principalId: userId }
95
+ ];
96
+ if (roleIds.length > 0) {
97
+ principals.push({ principalType: "role", principalId: { $in: roleIds } });
98
+ }
99
+ const shares = await findWithDecryption(
100
+ em,
101
+ DocumentShare,
102
+ {
103
+ documentId,
104
+ tenantId: ctx.tenantId,
105
+ organizationId,
106
+ deletedAt: null,
107
+ $or: principals
108
+ },
109
+ void 0,
110
+ { tenantId: ctx.tenantId, organizationId }
111
+ );
112
+ return maxShareTier(shares);
113
+ }
114
+ async function loadScopedDocument(em, documentId, scope) {
115
+ return findOneWithDecryption(
116
+ em,
117
+ Document,
118
+ {
119
+ id: documentId,
120
+ tenantId: scope.tenantId,
121
+ organizationId: scope.organizationId,
122
+ deletedAt: null
123
+ },
124
+ void 0,
125
+ scope
126
+ );
127
+ }
128
+ async function resolveLoadedDocumentUserAccess(em, document, scope, userId, container) {
129
+ if (document.ownerUserId === userId) return "owner";
130
+ if (await resolveDocumentsRbacService(container)?.userHasAllFeatures(
131
+ userId,
132
+ ["documents.manage"],
133
+ scope
134
+ )) return "owner";
135
+ const roleIds = await resolveActiveUserRoleIds(container, scope, userId);
136
+ const principals = [
137
+ { principalType: "user", principalId: userId }
138
+ ];
139
+ if (roleIds.length > 0) {
140
+ principals.push({ principalType: "role", principalId: { $in: roleIds } });
141
+ }
142
+ const shares = await findWithDecryption(
143
+ em,
144
+ DocumentShare,
145
+ {
146
+ documentId: document.id,
147
+ tenantId: scope.tenantId,
148
+ organizationId: scope.organizationId,
149
+ deletedAt: null,
150
+ $or: principals
151
+ },
152
+ void 0,
153
+ scope
154
+ );
155
+ return maxShareTier(shares);
156
+ }
157
+ async function resolveUserAccess(em, documentId, scope, userId, container) {
158
+ const document = await loadScopedDocument(em, documentId, scope);
159
+ if (!document) return null;
160
+ return resolveLoadedDocumentUserAccess(em, document, scope, userId, container);
161
+ }
162
+ async function resolveSubjectAccess(em, documentId, scope, identity, container) {
163
+ const document = await findOneWithDecryption(
164
+ em,
165
+ Document,
166
+ {
167
+ id: documentId,
168
+ tenantId: scope.tenantId,
169
+ organizationId: scope.organizationId,
170
+ deletedAt: null
171
+ },
172
+ void 0,
173
+ scope
174
+ );
175
+ if (!document) return null;
176
+ if (document.ownerUserId === identity.userId) return "owner";
177
+ const roleIds = await resolveActiveSubjectRoleIds(container, scope, identity.subject);
178
+ const principals = [
179
+ { principalType: "user", principalId: identity.userId }
180
+ ];
181
+ if (roleIds.length > 0) {
182
+ principals.push({ principalType: "role", principalId: { $in: roleIds } });
183
+ }
184
+ const shares = await findWithDecryption(
185
+ em,
186
+ DocumentShare,
187
+ {
188
+ documentId,
189
+ tenantId: scope.tenantId,
190
+ organizationId: scope.organizationId,
191
+ deletedAt: null,
192
+ $or: principals
193
+ },
194
+ void 0,
195
+ scope
196
+ );
197
+ return maxShareTier(shares);
198
+ }
199
+ function hasTier(tier, required) {
200
+ if (!tier) return false;
201
+ return TIER_RANK[tier] >= TIER_RANK[required];
202
+ }
203
+ async function assertTier(em, documentId, ctx, required) {
204
+ const tier = await resolvePermission(em, documentId, ctx);
205
+ if (!tier || !hasTier(tier, required)) {
206
+ throw forbidden("Forbidden");
207
+ }
208
+ return tier;
209
+ }
210
+ export {
211
+ TIER_RANK,
212
+ assertTier,
213
+ hasTier,
214
+ loadScopedDocument,
215
+ resolveActiveSubjectRoleIds,
216
+ resolveActiveUserRoleIds,
217
+ resolveLoadedDocumentUserAccess,
218
+ resolvePermission,
219
+ resolveSubjectAccess,
220
+ resolveUserAccess
221
+ };
222
+ //# sourceMappingURL=permissions.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/documents/lib/permissions.ts"],
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { FilterQuery } from '@mikro-orm/core'\nimport type { AuthContext } from '@open-mercato/shared/lib/auth/server'\nimport { hasAllFeatures } from '@open-mercato/shared/lib/auth/featureMatch'\nimport { forbidden } from '@open-mercato/shared/lib/crud/errors'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { Document, DocumentShare } from '../data/entities'\nimport {\n resolveApiKeyPrincipalService,\n resolveAuthPrincipalService,\n resolveDocumentsRbacService,\n type DocumentsServiceContainer,\n} from './platformServices'\n\nexport type DocumentTier = 'owner' | 'editor' | 'commenter' | 'viewer'\n\nexport const TIER_RANK: Record<DocumentTier, number> = {\n owner: 3,\n editor: 2,\n commenter: 1,\n viewer: 0,\n}\n\ntype DocumentsPermissionContext = NonNullable<AuthContext> & {\n features?: string[]\n roleIds?: string[]\n resolvedRoleIds?: string[]\n organizationId?: string | null\n}\n\nfunction resolveUserId(ctx: AuthContext): string | null {\n if (!ctx) return null\n if (typeof ctx.userId === 'string' && ctx.userId.trim().length > 0) return ctx.userId\n if (typeof ctx.sub === 'string' && ctx.sub.trim().length > 0 && !ctx.sub.startsWith('api_key:')) return ctx.sub\n return null\n}\n\nfunction resolveOrganizationId(ctx: DocumentsPermissionContext): string | null {\n if (typeof ctx.organizationId === 'string' && ctx.organizationId.trim().length > 0) {\n return ctx.organizationId\n }\n if (typeof ctx.orgId === 'string' && ctx.orgId.trim().length > 0) return ctx.orgId\n return null\n}\n\nfunction normalizeStrings(values: unknown): string[] {\n if (!Array.isArray(values)) return []\n return Array.from(\n new Set(\n values\n .map((value) => (typeof value === 'string' ? value.trim() : ''))\n .filter((value) => value.length > 0),\n ),\n )\n}\n\nfunction hasDocumentsManage(ctx: DocumentsPermissionContext): boolean {\n if (ctx.isSuperAdmin === true) return true\n const features = normalizeStrings(ctx.features)\n return hasAllFeatures(['documents.manage'], features)\n}\n\nexport async function resolveActiveUserRoleIds(\n container: DocumentsServiceContainer | null | undefined,\n scope: { tenantId: string; organizationId: string },\n userId: string,\n): Promise<string[]> {\n const service = resolveAuthPrincipalService(container)\n if (!service) return []\n return normalizeStrings(await service.resolveActiveUserRoleIds(userId, scope))\n}\n\nexport async function resolveActiveSubjectRoleIds(\n container: DocumentsServiceContainer | null | undefined,\n scope: { tenantId: string; organizationId: string },\n subject: string,\n): Promise<string[]> {\n if (!subject.startsWith('api_key:')) {\n return resolveActiveUserRoleIds(container, scope, subject)\n }\n\n const apiKeyId = subject.slice('api_key:'.length).trim()\n if (!apiKeyId) return []\n const apiKeyService = resolveApiKeyPrincipalService(container)\n const authService = resolveAuthPrincipalService(container)\n if (!apiKeyService || !authService) return []\n const assigned = normalizeStrings(await apiKeyService.resolveAssignedRoleIds(apiKeyId, scope))\n if (assigned.length === 0) return []\n return normalizeStrings(await authService.filterActiveRoleIds(assigned, scope))\n}\n\nfunction maxShareTier(shares: DocumentShare[]): DocumentTier | null {\n let best: DocumentTier | null = null\n for (const share of shares) {\n const permission = share.permission\n if (permission !== 'viewer' && permission !== 'commenter' && permission !== 'editor') continue\n if (!best || TIER_RANK[permission] > TIER_RANK[best]) best = permission\n }\n return best\n}\n\nexport async function resolvePermission(\n em: EntityManager,\n documentId: string,\n ctx: AuthContext,\n): Promise<DocumentTier | null> {\n if (!ctx || !ctx.tenantId) return null\n const permissionCtx = ctx as DocumentsPermissionContext\n const organizationId = resolveOrganizationId(permissionCtx)\n if (!organizationId) return null\n\n const document = await findOneWithDecryption(\n em,\n Document,\n {\n id: documentId,\n tenantId: ctx.tenantId,\n organizationId,\n deletedAt: null,\n },\n undefined,\n { tenantId: ctx.tenantId, organizationId },\n )\n if (!document) return null\n\n const userId = resolveUserId(ctx)\n if ((userId && document.ownerUserId === userId) || hasDocumentsManage(permissionCtx)) {\n return 'owner'\n }\n if (!userId) return null\n\n // Never trust role ids/names carried by the request token. Role shares are\n // resolved from active UserRole links to active roles in the current tenant.\n // Only consume the server-projected role set. Raw token roleIds/roles are\n // authentication hints and can outlive an assignment revocation.\n const roleIds = normalizeStrings(permissionCtx.resolvedRoleIds)\n const principals: Array<FilterQuery<DocumentShare>> = [\n { principalType: 'user', principalId: userId },\n ]\n if (roleIds.length > 0) {\n principals.push({ principalType: 'role', principalId: { $in: roleIds } } as FilterQuery<DocumentShare>)\n }\n\n const shares = await findWithDecryption(\n em,\n DocumentShare,\n {\n documentId,\n tenantId: ctx.tenantId,\n organizationId,\n deletedAt: null,\n $or: principals,\n } as FilterQuery<DocumentShare>,\n undefined,\n { tenantId: ctx.tenantId, organizationId },\n )\n\n return maxShareTier(shares)\n}\n\nexport async function loadScopedDocument(\n em: EntityManager,\n documentId: string,\n scope: { tenantId: string; organizationId: string },\n): Promise<Document | null> {\n return findOneWithDecryption(\n em,\n Document,\n {\n id: documentId,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n}\n\n/**\n * Resolve one user's tier against an already scoped document. Callers that\n * evaluate many users for the same document load it once and reuse it here\n * instead of re-reading the identical row per user.\n */\nexport async function resolveLoadedDocumentUserAccess(\n em: EntityManager,\n document: Document,\n scope: { tenantId: string; organizationId: string },\n userId: string,\n container?: DocumentsServiceContainer | null,\n): Promise<DocumentTier | null> {\n if (document.ownerUserId === userId) return 'owner'\n if (await resolveDocumentsRbacService(container)?.userHasAllFeatures(\n userId,\n ['documents.manage'],\n scope,\n )) return 'owner'\n\n const roleIds = await resolveActiveUserRoleIds(container, scope, userId)\n\n const principals: Array<FilterQuery<DocumentShare>> = [\n { principalType: 'user', principalId: userId },\n ]\n if (roleIds.length > 0) {\n principals.push({ principalType: 'role', principalId: { $in: roleIds } } as FilterQuery<DocumentShare>)\n }\n\n const shares = await findWithDecryption(\n em,\n DocumentShare,\n {\n documentId: document.id,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n $or: principals,\n } as FilterQuery<DocumentShare>,\n undefined,\n scope,\n )\n\n return maxShareTier(shares)\n}\n\nexport async function resolveUserAccess(\n em: EntityManager,\n documentId: string,\n scope: { tenantId: string; organizationId: string },\n userId: string,\n container?: DocumentsServiceContainer | null,\n): Promise<DocumentTier | null> {\n const document = await loadScopedDocument(em, documentId, scope)\n if (!document) return null\n return resolveLoadedDocumentUserAccess(em, document, scope, userId, container)\n}\n\nexport async function resolveSubjectAccess(\n em: EntityManager,\n documentId: string,\n scope: { tenantId: string; organizationId: string },\n identity: { subject: string; userId: string },\n container?: DocumentsServiceContainer | null,\n): Promise<DocumentTier | null> {\n const document = await findOneWithDecryption(\n em,\n Document,\n {\n id: documentId,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n if (!document) return null\n if (document.ownerUserId === identity.userId) return 'owner'\n\n const roleIds = await resolveActiveSubjectRoleIds(container, scope, identity.subject)\n const principals: Array<FilterQuery<DocumentShare>> = [\n { principalType: 'user', principalId: identity.userId },\n ]\n if (roleIds.length > 0) {\n principals.push({ principalType: 'role', principalId: { $in: roleIds } } as FilterQuery<DocumentShare>)\n }\n\n const shares = await findWithDecryption(\n em,\n DocumentShare,\n {\n documentId,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n $or: principals,\n } as FilterQuery<DocumentShare>,\n undefined,\n scope,\n )\n return maxShareTier(shares)\n}\n\nexport function hasTier(tier: DocumentTier | null, required: DocumentTier): boolean {\n if (!tier) return false\n return TIER_RANK[tier] >= TIER_RANK[required]\n}\n\nexport async function assertTier(\n em: EntityManager,\n documentId: string,\n ctx: AuthContext,\n required: DocumentTier,\n): Promise<DocumentTier> {\n const tier = await resolvePermission(em, documentId, ctx)\n if (!tier || !hasTier(tier, required)) {\n throw forbidden('Forbidden')\n }\n return tier\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,35 @@
1
+ function tryResolve(container, name) {
2
+ if (!container) return null;
3
+ try {
4
+ return container.resolve(name);
5
+ } catch {
6
+ return null;
7
+ }
8
+ }
9
+ function resolveAuthPrincipalService(container) {
10
+ const service = tryResolve(container, "authPrincipalService");
11
+ if (!service || typeof service.principalExists !== "function" || typeof service.resolveActiveUserRoleIds !== "function" || typeof service.filterActiveRoleIds !== "function" || typeof service.resolveLabels !== "function" || typeof service.listSuperAdminUserIds !== "function") return null;
12
+ return service;
13
+ }
14
+ function resolveApiKeyPrincipalService(container) {
15
+ const service = tryResolve(container, "apiKeyPrincipalService");
16
+ return service && typeof service.resolveAssignedRoleIds === "function" ? service : null;
17
+ }
18
+ function resolveDocumentsRbacService(container) {
19
+ const service = tryResolve(container, "rbacService");
20
+ return service && typeof service.userHasAllFeatures === "function" ? service : null;
21
+ }
22
+ function resolveOrganizationScopeService(container) {
23
+ const service = tryResolve(container, "organizationScopeService");
24
+ if (!service || typeof service.resolve !== "function" || typeof service.resolveFresh !== "function" || typeof service.resolveForRequest !== "function") {
25
+ return null;
26
+ }
27
+ return service;
28
+ }
29
+ export {
30
+ resolveApiKeyPrincipalService,
31
+ resolveAuthPrincipalService,
32
+ resolveDocumentsRbacService,
33
+ resolveOrganizationScopeService
34
+ };
35
+ //# sourceMappingURL=platformServices.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/documents/lib/platformServices.ts"],
4
+ "sourcesContent": ["import type {\n ApiKeyPrincipalService,\n AuthPrincipalService,\n OrganizationScopeService,\n} from '@open-mercato/shared/lib/auth/principal-service'\n\nexport type DocumentsServiceContainer = {\n resolve(name: string): unknown\n}\n\nexport type DocumentsRbacService = {\n userHasAllFeatures(\n userId: string,\n required: string[],\n scope: { tenantId: string | null; organizationId: string | null },\n ): Promise<boolean>\n}\n\nfunction tryResolve(container: DocumentsServiceContainer | null | undefined, name: string): unknown {\n if (!container) return null\n try {\n return container.resolve(name)\n } catch {\n return null\n }\n}\n\nexport function resolveAuthPrincipalService(\n container: DocumentsServiceContainer | null | undefined,\n): AuthPrincipalService | null {\n const service = tryResolve(container, 'authPrincipalService') as Partial<AuthPrincipalService> | null\n if (!service\n || typeof service.principalExists !== 'function'\n || typeof service.resolveActiveUserRoleIds !== 'function'\n || typeof service.filterActiveRoleIds !== 'function'\n || typeof service.resolveLabels !== 'function'\n || typeof service.listSuperAdminUserIds !== 'function') return null\n return service as AuthPrincipalService\n}\n\nexport function resolveApiKeyPrincipalService(\n container: DocumentsServiceContainer | null | undefined,\n): ApiKeyPrincipalService | null {\n const service = tryResolve(container, 'apiKeyPrincipalService') as Partial<ApiKeyPrincipalService> | null\n return service && typeof service.resolveAssignedRoleIds === 'function'\n ? service as ApiKeyPrincipalService\n : null\n}\n\nexport function resolveDocumentsRbacService(\n container: DocumentsServiceContainer | null | undefined,\n): DocumentsRbacService | null {\n const service = tryResolve(container, 'rbacService') as Partial<DocumentsRbacService> | null\n return service && typeof service.userHasAllFeatures === 'function'\n ? service as DocumentsRbacService\n : null\n}\n\nexport function resolveOrganizationScopeService(\n container: DocumentsServiceContainer | null | undefined,\n): OrganizationScopeService | null {\n const service = tryResolve(container, 'organizationScopeService') as Partial<OrganizationScopeService> | null\n if (!service\n || typeof service.resolve !== 'function'\n || typeof service.resolveFresh !== 'function'\n || typeof service.resolveForRequest !== 'function') {\n return null\n }\n return service as OrganizationScopeService\n}\n"],
5
+ "mappings": "AAkBA,SAAS,WAAW,WAAyD,MAAuB;AAClG,MAAI,CAAC,UAAW,QAAO;AACvB,MAAI;AACF,WAAO,UAAU,QAAQ,IAAI;AAAA,EAC/B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,4BACd,WAC6B;AAC7B,QAAM,UAAU,WAAW,WAAW,sBAAsB;AAC5D,MAAI,CAAC,WACA,OAAO,QAAQ,oBAAoB,cACnC,OAAO,QAAQ,6BAA6B,cAC5C,OAAO,QAAQ,wBAAwB,cACvC,OAAO,QAAQ,kBAAkB,cACjC,OAAO,QAAQ,0BAA0B,WAAY,QAAO;AACjE,SAAO;AACT;AAEO,SAAS,8BACd,WAC+B;AAC/B,QAAM,UAAU,WAAW,WAAW,wBAAwB;AAC9D,SAAO,WAAW,OAAO,QAAQ,2BAA2B,aACxD,UACA;AACN;AAEO,SAAS,4BACd,WAC6B;AAC7B,QAAM,UAAU,WAAW,WAAW,aAAa;AACnD,SAAO,WAAW,OAAO,QAAQ,uBAAuB,aACpD,UACA;AACN;AAEO,SAAS,gCACd,WACiC;AACjC,QAAM,UAAU,WAAW,WAAW,0BAA0B;AAChE,MAAI,CAAC,WACA,OAAO,QAAQ,YAAY,cAC3B,OAAO,QAAQ,iBAAiB,cAChC,OAAO,QAAQ,sBAAsB,YAAY;AACpD,WAAO;AAAA,EACT;AACA,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -0,0 +1,58 @@
1
+ import { sanitizeDocumentsDisplayLabel } from "./displayLabels.js";
2
+ function normalizeRecordFieldSnapshots(fields) {
3
+ const normalized = /* @__PURE__ */ new Map();
4
+ for (const field of fields) {
5
+ const key = field.field.trim();
6
+ const label = sanitizeDocumentsDisplayLabel(field.label);
7
+ const value = sanitizeDocumentsDisplayLabel(field.value);
8
+ if (!key || !label || !value || normalized.has(key)) continue;
9
+ normalized.set(key, { field: key, label, value });
10
+ }
11
+ return Array.from(normalized.values());
12
+ }
13
+ function text(value, bold = false) {
14
+ return {
15
+ type: "text",
16
+ text: value,
17
+ ...bold ? { marks: [{ type: "bold" }] } : {}
18
+ };
19
+ }
20
+ function paragraph(field) {
21
+ return {
22
+ type: "paragraph",
23
+ content: [text(field.label, true), text(`: ${field.value}`)]
24
+ };
25
+ }
26
+ function tableCell(value, bold = false) {
27
+ return {
28
+ type: "tableCell",
29
+ content: [{ type: "paragraph", content: [text(value, bold)] }]
30
+ };
31
+ }
32
+ function buildRecordFieldSnapshotContent(input) {
33
+ const fields = normalizeRecordFieldSnapshots(input);
34
+ if (fields.length === 0) return [];
35
+ if (fields.length === 1) return [paragraph(fields[0])];
36
+ return [
37
+ {
38
+ type: "table",
39
+ content: fields.map((field) => ({
40
+ type: "tableRow",
41
+ content: [tableCell(field.label, true), tableCell(field.value)]
42
+ }))
43
+ },
44
+ { type: "paragraph" }
45
+ ];
46
+ }
47
+ function insertRecordFieldSnapshot(editor, fields) {
48
+ if (!editor || editor.isDestroyed || !editor.isEditable) return false;
49
+ const content = buildRecordFieldSnapshotContent(fields);
50
+ if (content.length === 0) return false;
51
+ return editor.chain().focus().insertContent(content).run();
52
+ }
53
+ export {
54
+ buildRecordFieldSnapshotContent,
55
+ insertRecordFieldSnapshot,
56
+ normalizeRecordFieldSnapshots
57
+ };
58
+ //# sourceMappingURL=recordFieldInsertion.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/documents/lib/recordFieldInsertion.ts"],
4
+ "sourcesContent": ["import type { Editor, JSONContent } from '@tiptap/core'\nimport { sanitizeDocumentsDisplayLabel } from './displayLabels'\n\nexport type RecordFieldSnapshot = {\n field: string\n label: string\n value: string\n}\n\nexport function normalizeRecordFieldSnapshots(\n fields: readonly RecordFieldSnapshot[],\n): RecordFieldSnapshot[] {\n const normalized = new Map<string, RecordFieldSnapshot>()\n for (const field of fields) {\n const key = field.field.trim()\n const label = sanitizeDocumentsDisplayLabel(field.label)\n const value = sanitizeDocumentsDisplayLabel(field.value)\n if (!key || !label || !value || normalized.has(key)) continue\n normalized.set(key, { field: key, label, value })\n }\n return Array.from(normalized.values())\n}\n\nfunction text(value: string, bold = false): JSONContent {\n return {\n type: 'text',\n text: value,\n ...(bold ? { marks: [{ type: 'bold' }] } : {}),\n }\n}\n\nfunction paragraph(field: RecordFieldSnapshot): JSONContent {\n return {\n type: 'paragraph',\n content: [text(field.label, true), text(`: ${field.value}`)],\n }\n}\n\nfunction tableCell(value: string, bold = false): JSONContent {\n return {\n type: 'tableCell',\n content: [{ type: 'paragraph', content: [text(value, bold)] }],\n }\n}\n\nexport function buildRecordFieldSnapshotContent(\n input: readonly RecordFieldSnapshot[],\n): JSONContent[] {\n const fields = normalizeRecordFieldSnapshots(input)\n if (fields.length === 0) return []\n if (fields.length === 1) return [paragraph(fields[0]!)]\n return [\n {\n type: 'table',\n content: fields.map((field) => ({\n type: 'tableRow',\n content: [tableCell(field.label, true), tableCell(field.value)],\n })),\n },\n { type: 'paragraph' },\n ]\n}\n\nexport function insertRecordFieldSnapshot(\n editor: Editor | null,\n fields: readonly RecordFieldSnapshot[],\n): boolean {\n if (!editor || editor.isDestroyed || !editor.isEditable) return false\n const content = buildRecordFieldSnapshotContent(fields)\n if (content.length === 0) return false\n return editor.chain().focus().insertContent(content).run()\n}\n"],
5
+ "mappings": "AACA,SAAS,qCAAqC;AAQvC,SAAS,8BACd,QACuB;AACvB,QAAM,aAAa,oBAAI,IAAiC;AACxD,aAAW,SAAS,QAAQ;AAC1B,UAAM,MAAM,MAAM,MAAM,KAAK;AAC7B,UAAM,QAAQ,8BAA8B,MAAM,KAAK;AACvD,UAAM,QAAQ,8BAA8B,MAAM,KAAK;AACvD,QAAI,CAAC,OAAO,CAAC,SAAS,CAAC,SAAS,WAAW,IAAI,GAAG,EAAG;AACrD,eAAW,IAAI,KAAK,EAAE,OAAO,KAAK,OAAO,MAAM,CAAC;AAAA,EAClD;AACA,SAAO,MAAM,KAAK,WAAW,OAAO,CAAC;AACvC;AAEA,SAAS,KAAK,OAAe,OAAO,OAAoB;AACtD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,MAAM;AAAA,IACN,GAAI,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC,EAAE,IAAI,CAAC;AAAA,EAC9C;AACF;AAEA,SAAS,UAAU,OAAyC;AAC1D,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS,CAAC,KAAK,MAAM,OAAO,IAAI,GAAG,KAAK,KAAK,MAAM,KAAK,EAAE,CAAC;AAAA,EAC7D;AACF;AAEA,SAAS,UAAU,OAAe,OAAO,OAAoB;AAC3D,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS,CAAC,EAAE,MAAM,aAAa,SAAS,CAAC,KAAK,OAAO,IAAI,CAAC,EAAE,CAAC;AAAA,EAC/D;AACF;AAEO,SAAS,gCACd,OACe;AACf,QAAM,SAAS,8BAA8B,KAAK;AAClD,MAAI,OAAO,WAAW,EAAG,QAAO,CAAC;AACjC,MAAI,OAAO,WAAW,EAAG,QAAO,CAAC,UAAU,OAAO,CAAC,CAAE,CAAC;AACtD,SAAO;AAAA,IACL;AAAA,MACE,MAAM;AAAA,MACN,SAAS,OAAO,IAAI,CAAC,WAAW;AAAA,QAC9B,MAAM;AAAA,QACN,SAAS,CAAC,UAAU,MAAM,OAAO,IAAI,GAAG,UAAU,MAAM,KAAK,CAAC;AAAA,MAChE,EAAE;AAAA,IACJ;AAAA,IACA,EAAE,MAAM,YAAY;AAAA,EACtB;AACF;AAEO,SAAS,0BACd,QACA,QACS;AACT,MAAI,CAAC,UAAU,OAAO,eAAe,CAAC,OAAO,WAAY,QAAO;AAChE,QAAM,UAAU,gCAAgC,MAAM;AACtD,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,SAAO,OAAO,MAAM,EAAE,MAAM,EAAE,cAAc,OAAO,EAAE,IAAI;AAC3D;",
6
+ "names": []
7
+ }
@@ -0,0 +1,80 @@
1
+ import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
2
+ import {
3
+ DOCUMENTS_MAX_CONTENT_HTML_BYTES,
4
+ DOCUMENTS_MAX_CONTENT_TEXT_BYTES
5
+ } from "./resourceLimits.js";
6
+ const KIBIBYTE = 1024;
7
+ const MEBIBYTE = 1024 * KIBIBYTE;
8
+ const JSON_ESCAPE_EXPANSION = 6;
9
+ const STREAM_INITIAL_CAPACITY = 64 * KIBIBYTE;
10
+ const DOCUMENTS_REQUEST_BODY_TOO_LARGE = "documents.errors.requestBodyTooLarge";
11
+ const DOCUMENTS_JSON_BODY_LIMITS = {
12
+ standard: 128 * KIBIBYTE,
13
+ template: 4 * MEBIBYTE,
14
+ templateRender: 4 * MEBIBYTE,
15
+ content: JSON_ESCAPE_EXPANSION * (DOCUMENTS_MAX_CONTENT_HTML_BYTES + DOCUMENTS_MAX_CONTENT_TEXT_BYTES) + 256 * KIBIBYTE,
16
+ exportSnapshot: JSON_ESCAPE_EXPANSION * DOCUMENTS_MAX_CONTENT_HTML_BYTES + 64 * KIBIBYTE
17
+ };
18
+ function payloadTooLarge() {
19
+ return new CrudHttpError(413, { error: DOCUMENTS_REQUEST_BODY_TOO_LARGE });
20
+ }
21
+ function declaredContentLength(request) {
22
+ const value = request.headers.get("content-length")?.trim();
23
+ if (!value || !/^\d+$/.test(value)) return null;
24
+ const declaredBytes = Number(value);
25
+ return Number.isSafeInteger(declaredBytes) ? declaredBytes : Number.POSITIVE_INFINITY;
26
+ }
27
+ function growBuffer(current, requiredBytes, maxBytes) {
28
+ let capacity = Math.max(1, current.byteLength);
29
+ while (capacity < requiredBytes) capacity = Math.min(maxBytes, capacity * 2);
30
+ const next = new Uint8Array(capacity);
31
+ next.set(current);
32
+ return next;
33
+ }
34
+ async function readBoundedJsonBody(request, maxBytes, fallback = null) {
35
+ if (!Number.isSafeInteger(maxBytes) || maxBytes <= 0) {
36
+ throw new TypeError("maxBytes must be a positive safe integer");
37
+ }
38
+ const contentLength = declaredContentLength(request);
39
+ if (contentLength !== null && contentLength > maxBytes) {
40
+ await request.body?.cancel().catch(() => void 0);
41
+ throw payloadTooLarge();
42
+ }
43
+ if (!request.body) return fallback;
44
+ const reader = request.body.getReader();
45
+ let buffer = new Uint8Array(
46
+ Math.min(maxBytes, STREAM_INITIAL_CAPACITY)
47
+ );
48
+ let byteLength = 0;
49
+ try {
50
+ while (true) {
51
+ const chunk = await reader.read();
52
+ if (chunk.done) break;
53
+ const nextByteLength = byteLength + chunk.value.byteLength;
54
+ if (nextByteLength > maxBytes) {
55
+ await reader.cancel().catch(() => void 0);
56
+ throw payloadTooLarge();
57
+ }
58
+ if (nextByteLength > buffer.byteLength) {
59
+ buffer = growBuffer(buffer, nextByteLength, maxBytes);
60
+ }
61
+ buffer.set(chunk.value, byteLength);
62
+ byteLength = nextByteLength;
63
+ }
64
+ } finally {
65
+ reader.releaseLock();
66
+ }
67
+ if (byteLength === 0) return fallback;
68
+ const raw = new TextDecoder().decode(buffer.subarray(0, byteLength));
69
+ try {
70
+ return JSON.parse(raw);
71
+ } catch {
72
+ return fallback;
73
+ }
74
+ }
75
+ export {
76
+ DOCUMENTS_JSON_BODY_LIMITS,
77
+ DOCUMENTS_REQUEST_BODY_TOO_LARGE,
78
+ readBoundedJsonBody
79
+ };
80
+ //# sourceMappingURL=requestBody.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/documents/lib/requestBody.ts"],
4
+ "sourcesContent": ["import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport {\n DOCUMENTS_MAX_CONTENT_HTML_BYTES,\n DOCUMENTS_MAX_CONTENT_TEXT_BYTES,\n} from './resourceLimits'\n\nconst KIBIBYTE = 1024\nconst MEBIBYTE = 1024 * KIBIBYTE\nconst JSON_ESCAPE_EXPANSION = 6\nconst STREAM_INITIAL_CAPACITY = 64 * KIBIBYTE\n\nexport const DOCUMENTS_REQUEST_BODY_TOO_LARGE = 'documents.errors.requestBodyTooLarge'\n\n/**\n * Request-level limits are intentionally larger than the decoded field limits.\n * JSON may encode one input byte as a six-byte `\\uXXXX` escape, so content\n * routes retain every payload accepted by their existing schemas while still\n * bounding buffering before JSON.parse runs.\n */\nexport const DOCUMENTS_JSON_BODY_LIMITS = {\n standard: 128 * KIBIBYTE,\n template: 4 * MEBIBYTE,\n templateRender: 4 * MEBIBYTE,\n content: (\n JSON_ESCAPE_EXPANSION\n * (DOCUMENTS_MAX_CONTENT_HTML_BYTES + DOCUMENTS_MAX_CONTENT_TEXT_BYTES)\n ) + (256 * KIBIBYTE),\n exportSnapshot: (JSON_ESCAPE_EXPANSION * DOCUMENTS_MAX_CONTENT_HTML_BYTES) + (64 * KIBIBYTE),\n} as const\n\nfunction payloadTooLarge(): CrudHttpError {\n return new CrudHttpError(413, { error: DOCUMENTS_REQUEST_BODY_TOO_LARGE })\n}\n\nfunction declaredContentLength(request: Request): number | null {\n const value = request.headers.get('content-length')?.trim()\n if (!value || !/^\\d+$/.test(value)) return null\n const declaredBytes = Number(value)\n return Number.isSafeInteger(declaredBytes) ? declaredBytes : Number.POSITIVE_INFINITY\n}\n\nfunction growBuffer(current: Uint8Array, requiredBytes: number, maxBytes: number): Uint8Array {\n let capacity = Math.max(1, current.byteLength)\n while (capacity < requiredBytes) capacity = Math.min(maxBytes, capacity * 2)\n const next = new Uint8Array(capacity)\n next.set(current)\n return next\n}\n\nexport async function readBoundedJsonBody<T>(\n request: Request,\n maxBytes: number,\n fallback: T | null = null,\n): Promise<T | null> {\n if (!Number.isSafeInteger(maxBytes) || maxBytes <= 0) {\n throw new TypeError('maxBytes must be a positive safe integer')\n }\n const contentLength = declaredContentLength(request)\n if (contentLength !== null && contentLength > maxBytes) {\n await request.body?.cancel().catch(() => undefined)\n throw payloadTooLarge()\n }\n if (!request.body) return fallback\n\n const reader = request.body.getReader()\n let buffer: Uint8Array<ArrayBufferLike> = new Uint8Array(\n Math.min(maxBytes, STREAM_INITIAL_CAPACITY),\n )\n let byteLength = 0\n\n try {\n while (true) {\n const chunk = await reader.read()\n if (chunk.done) break\n const nextByteLength = byteLength + chunk.value.byteLength\n if (nextByteLength > maxBytes) {\n await reader.cancel().catch(() => undefined)\n throw payloadTooLarge()\n }\n if (nextByteLength > buffer.byteLength) {\n buffer = growBuffer(buffer, nextByteLength, maxBytes)\n }\n buffer.set(chunk.value, byteLength)\n byteLength = nextByteLength\n }\n } finally {\n reader.releaseLock()\n }\n\n if (byteLength === 0) return fallback\n const raw = new TextDecoder().decode(buffer.subarray(0, byteLength))\n try {\n return JSON.parse(raw) as T\n } catch {\n return fallback\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,qBAAqB;AAC9B;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEP,MAAM,WAAW;AACjB,MAAM,WAAW,OAAO;AACxB,MAAM,wBAAwB;AAC9B,MAAM,0BAA0B,KAAK;AAE9B,MAAM,mCAAmC;AAQzC,MAAM,6BAA6B;AAAA,EACxC,UAAU,MAAM;AAAA,EAChB,UAAU,IAAI;AAAA,EACd,gBAAgB,IAAI;AAAA,EACpB,SACE,yBACG,mCAAmC,oCACnC,MAAM;AAAA,EACX,gBAAiB,wBAAwB,mCAAqC,KAAK;AACrF;AAEA,SAAS,kBAAiC;AACxC,SAAO,IAAI,cAAc,KAAK,EAAE,OAAO,iCAAiC,CAAC;AAC3E;AAEA,SAAS,sBAAsB,SAAiC;AAC9D,QAAM,QAAQ,QAAQ,QAAQ,IAAI,gBAAgB,GAAG,KAAK;AAC1D,MAAI,CAAC,SAAS,CAAC,QAAQ,KAAK,KAAK,EAAG,QAAO;AAC3C,QAAM,gBAAgB,OAAO,KAAK;AAClC,SAAO,OAAO,cAAc,aAAa,IAAI,gBAAgB,OAAO;AACtE;AAEA,SAAS,WAAW,SAAqB,eAAuB,UAA8B;AAC5F,MAAI,WAAW,KAAK,IAAI,GAAG,QAAQ,UAAU;AAC7C,SAAO,WAAW,cAAe,YAAW,KAAK,IAAI,UAAU,WAAW,CAAC;AAC3E,QAAM,OAAO,IAAI,WAAW,QAAQ;AACpC,OAAK,IAAI,OAAO;AAChB,SAAO;AACT;AAEA,eAAsB,oBACpB,SACA,UACA,WAAqB,MACF;AACnB,MAAI,CAAC,OAAO,cAAc,QAAQ,KAAK,YAAY,GAAG;AACpD,UAAM,IAAI,UAAU,0CAA0C;AAAA,EAChE;AACA,QAAM,gBAAgB,sBAAsB,OAAO;AACnD,MAAI,kBAAkB,QAAQ,gBAAgB,UAAU;AACtD,UAAM,QAAQ,MAAM,OAAO,EAAE,MAAM,MAAM,MAAS;AAClD,UAAM,gBAAgB;AAAA,EACxB;AACA,MAAI,CAAC,QAAQ,KAAM,QAAO;AAE1B,QAAM,SAAS,QAAQ,KAAK,UAAU;AACtC,MAAI,SAAsC,IAAI;AAAA,IAC5C,KAAK,IAAI,UAAU,uBAAuB;AAAA,EAC5C;AACA,MAAI,aAAa;AAEjB,MAAI;AACF,WAAO,MAAM;AACX,YAAM,QAAQ,MAAM,OAAO,KAAK;AAChC,UAAI,MAAM,KAAM;AAChB,YAAM,iBAAiB,aAAa,MAAM,MAAM;AAChD,UAAI,iBAAiB,UAAU;AAC7B,cAAM,OAAO,OAAO,EAAE,MAAM,MAAM,MAAS;AAC3C,cAAM,gBAAgB;AAAA,MACxB;AACA,UAAI,iBAAiB,OAAO,YAAY;AACtC,iBAAS,WAAW,QAAQ,gBAAgB,QAAQ;AAAA,MACtD;AACA,aAAO,IAAI,MAAM,OAAO,UAAU;AAClC,mBAAa;AAAA,IACf;AAAA,EACF,UAAE;AACA,WAAO,YAAY;AAAA,EACrB;AAEA,MAAI,eAAe,EAAG,QAAO;AAC7B,QAAM,MAAM,IAAI,YAAY,EAAE,OAAO,OAAO,SAAS,GAAG,UAAU,CAAC;AACnE,MAAI;AACF,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,62 @@
1
+ import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
2
+ const MEBIBYTE = 1024 * 1024;
3
+ const DOCUMENTS_MAX_CONTENT_HTML_BYTES = 2 * MEBIBYTE;
4
+ const DOCUMENTS_MAX_CONTENT_TEXT_BYTES = 2 * MEBIBYTE;
5
+ const DOCUMENTS_MAX_YJS_STATE_BYTES = 8 * MEBIBYTE;
6
+ const DOCUMENTS_MAX_ENTITY_LINKS_PER_DOCUMENT = 100;
7
+ const DOCUMENTS_COLLAB_MAX_PAYLOAD_BYTES = DOCUMENTS_MAX_YJS_STATE_BYTES + 64 * 1024;
8
+ function utf8ByteLength(value) {
9
+ return Buffer.byteLength(value, "utf8");
10
+ }
11
+ function isUtf8WithinLimit(value, maxBytes) {
12
+ return utf8ByteLength(value) <= maxBytes;
13
+ }
14
+ function maxBase64EncodedLength(maxDecodedBytes) {
15
+ return Math.ceil(maxDecodedBytes / 3) * 4;
16
+ }
17
+ function decodeBoundedCanonicalBase64(value, maxDecodedBytes, options = {}) {
18
+ const fail = () => {
19
+ throw new CrudHttpError(options.status ?? 413, {
20
+ error: options.error ?? "documents.content.tooLarge"
21
+ });
22
+ };
23
+ if (value.length > maxBase64EncodedLength(maxDecodedBytes) || value.length % 4 !== 0 || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value)) {
24
+ return fail();
25
+ }
26
+ const bytes = Buffer.from(value, "base64");
27
+ if (bytes.byteLength > maxDecodedBytes || bytes.toString("base64") !== value) return fail();
28
+ return bytes;
29
+ }
30
+ function assertDocumentYjsStateByteLength(byteLength, options = {}) {
31
+ if (byteLength <= DOCUMENTS_MAX_YJS_STATE_BYTES) return;
32
+ throw new CrudHttpError(options.status ?? 413, {
33
+ error: options.error ?? "documents.content.tooLarge"
34
+ });
35
+ }
36
+ function assertDocumentContentResourceLimits(input, options = {}) {
37
+ if (input.yjsState) assertDocumentYjsStateByteLength(input.yjsState.byteLength, options);
38
+ if (typeof input.contentHtml === "string" && !isUtf8WithinLimit(input.contentHtml, DOCUMENTS_MAX_CONTENT_HTML_BYTES)) {
39
+ throw new CrudHttpError(options.status ?? 413, {
40
+ error: options.error ?? "documents.content.tooLarge"
41
+ });
42
+ }
43
+ if (typeof input.contentText === "string" && !isUtf8WithinLimit(input.contentText, DOCUMENTS_MAX_CONTENT_TEXT_BYTES)) {
44
+ throw new CrudHttpError(options.status ?? 413, {
45
+ error: options.error ?? "documents.content.tooLarge"
46
+ });
47
+ }
48
+ }
49
+ export {
50
+ DOCUMENTS_COLLAB_MAX_PAYLOAD_BYTES,
51
+ DOCUMENTS_MAX_CONTENT_HTML_BYTES,
52
+ DOCUMENTS_MAX_CONTENT_TEXT_BYTES,
53
+ DOCUMENTS_MAX_ENTITY_LINKS_PER_DOCUMENT,
54
+ DOCUMENTS_MAX_YJS_STATE_BYTES,
55
+ assertDocumentContentResourceLimits,
56
+ assertDocumentYjsStateByteLength,
57
+ decodeBoundedCanonicalBase64,
58
+ isUtf8WithinLimit,
59
+ maxBase64EncodedLength,
60
+ utf8ByteLength
61
+ };
62
+ //# sourceMappingURL=resourceLimits.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/documents/lib/resourceLimits.ts"],
4
+ "sourcesContent": ["import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\n\nconst MEBIBYTE = 1024 * 1024\n\n/** Authoritative UTF-8 bounds for every stored representation of a document body. */\nexport const DOCUMENTS_MAX_CONTENT_HTML_BYTES = 2 * MEBIBYTE\nexport const DOCUMENTS_MAX_CONTENT_TEXT_BYTES = 2 * MEBIBYTE\nexport const DOCUMENTS_MAX_YJS_STATE_BYTES = 8 * MEBIBYTE\n\n/** Bound cross-module verification and reverse-widget work per document. */\nexport const DOCUMENTS_MAX_ENTITY_LINKS_PER_DOCUMENT = 100\n\n/** WebSocket messages include a small Hocuspocus/Yjs protocol envelope. */\nexport const DOCUMENTS_COLLAB_MAX_PAYLOAD_BYTES = DOCUMENTS_MAX_YJS_STATE_BYTES + 64 * 1024\n\nexport function utf8ByteLength(value: string): number {\n return Buffer.byteLength(value, 'utf8')\n}\n\nexport function isUtf8WithinLimit(value: string, maxBytes: number): boolean {\n return utf8ByteLength(value) <= maxBytes\n}\n\nexport function maxBase64EncodedLength(maxDecodedBytes: number): number {\n return Math.ceil(maxDecodedBytes / 3) * 4\n}\n\n/** Validate encoded size before Buffer allocates the decoded payload. */\nexport function decodeBoundedCanonicalBase64(\n value: string,\n maxDecodedBytes: number,\n options: { status?: number; error?: string } = {},\n): Buffer {\n const fail = () => {\n throw new CrudHttpError(options.status ?? 413, {\n error: options.error ?? 'documents.content.tooLarge',\n })\n }\n if (\n value.length > maxBase64EncodedLength(maxDecodedBytes)\n || value.length % 4 !== 0\n || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value)\n ) {\n return fail()\n }\n const bytes = Buffer.from(value, 'base64')\n if (bytes.byteLength > maxDecodedBytes || bytes.toString('base64') !== value) return fail()\n return bytes\n}\n\nexport type DocumentContentResourceInput = {\n yjsState?: Buffer | Uint8Array | null\n contentHtml?: string | null\n contentText?: string | null\n}\n\nexport function assertDocumentYjsStateByteLength(\n byteLength: number,\n options: { status?: number; error?: string } = {},\n): void {\n if (byteLength <= DOCUMENTS_MAX_YJS_STATE_BYTES) return\n throw new CrudHttpError(options.status ?? 413, {\n error: options.error ?? 'documents.content.tooLarge',\n })\n}\n\n/**\n * Enforce the same bound at API, command, collaboration, and version\n * boundaries. Keeping this check beside persistence prevents an internal or\n * legacy call site from bypassing request validation and creating a snapshot\n * that later exhausts the editor or export worker.\n */\nexport function assertDocumentContentResourceLimits(\n input: DocumentContentResourceInput,\n options: { status?: number; error?: string } = {},\n): void {\n if (input.yjsState) assertDocumentYjsStateByteLength(input.yjsState.byteLength, options)\n if (\n typeof input.contentHtml === 'string'\n && !isUtf8WithinLimit(input.contentHtml, DOCUMENTS_MAX_CONTENT_HTML_BYTES)\n ) {\n throw new CrudHttpError(options.status ?? 413, {\n error: options.error ?? 'documents.content.tooLarge',\n })\n }\n if (\n typeof input.contentText === 'string'\n && !isUtf8WithinLimit(input.contentText, DOCUMENTS_MAX_CONTENT_TEXT_BYTES)\n ) {\n throw new CrudHttpError(options.status ?? 413, {\n error: options.error ?? 'documents.content.tooLarge',\n })\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,qBAAqB;AAE9B,MAAM,WAAW,OAAO;AAGjB,MAAM,mCAAmC,IAAI;AAC7C,MAAM,mCAAmC,IAAI;AAC7C,MAAM,gCAAgC,IAAI;AAG1C,MAAM,0CAA0C;AAGhD,MAAM,qCAAqC,gCAAgC,KAAK;AAEhF,SAAS,eAAe,OAAuB;AACpD,SAAO,OAAO,WAAW,OAAO,MAAM;AACxC;AAEO,SAAS,kBAAkB,OAAe,UAA2B;AAC1E,SAAO,eAAe,KAAK,KAAK;AAClC;AAEO,SAAS,uBAAuB,iBAAiC;AACtE,SAAO,KAAK,KAAK,kBAAkB,CAAC,IAAI;AAC1C;AAGO,SAAS,6BACd,OACA,iBACA,UAA+C,CAAC,GACxC;AACR,QAAM,OAAO,MAAM;AACjB,UAAM,IAAI,cAAc,QAAQ,UAAU,KAAK;AAAA,MAC7C,OAAO,QAAQ,SAAS;AAAA,IAC1B,CAAC;AAAA,EACH;AACA,MACE,MAAM,SAAS,uBAAuB,eAAe,KAClD,MAAM,SAAS,MAAM,KACrB,CAAC,mEAAmE,KAAK,KAAK,GACjF;AACA,WAAO,KAAK;AAAA,EACd;AACA,QAAM,QAAQ,OAAO,KAAK,OAAO,QAAQ;AACzC,MAAI,MAAM,aAAa,mBAAmB,MAAM,SAAS,QAAQ,MAAM,MAAO,QAAO,KAAK;AAC1F,SAAO;AACT;AAQO,SAAS,iCACd,YACA,UAA+C,CAAC,GAC1C;AACN,MAAI,cAAc,8BAA+B;AACjD,QAAM,IAAI,cAAc,QAAQ,UAAU,KAAK;AAAA,IAC7C,OAAO,QAAQ,SAAS;AAAA,EAC1B,CAAC;AACH;AAQO,SAAS,oCACd,OACA,UAA+C,CAAC,GAC1C;AACN,MAAI,MAAM,SAAU,kCAAiC,MAAM,SAAS,YAAY,OAAO;AACvF,MACE,OAAO,MAAM,gBAAgB,YAC1B,CAAC,kBAAkB,MAAM,aAAa,gCAAgC,GACzE;AACA,UAAM,IAAI,cAAc,QAAQ,UAAU,KAAK;AAAA,MAC7C,OAAO,QAAQ,SAAS;AAAA,IAC1B,CAAC;AAAA,EACH;AACA,MACE,OAAO,MAAM,gBAAgB,YAC1B,CAAC,kBAAkB,MAAM,aAAa,gCAAgC,GACzE;AACA,UAAM,IAAI,cAAc,QAAQ,UAAU,KAAK;AAAA,MAC7C,OAAO,QAAQ,SAAS;AAAA,IAC1B,CAAC;AAAA,EACH;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,25 @@
1
+ import { raw } from "@mikro-orm/core";
2
+ import { DocumentShare } from "../data/entities.js";
3
+ async function loadDocumentShareCounts(em, scope, documentIds) {
4
+ const shareCounts = /* @__PURE__ */ new Map();
5
+ if (documentIds.length === 0) return shareCounts;
6
+ const groupedShares = await em.createQueryBuilder(DocumentShare, "document_share").select([
7
+ "document_share.documentId",
8
+ raw('count(*) as "shareCount"')
9
+ ]).where({
10
+ documentId: { $in: [...documentIds] },
11
+ tenantId: scope.tenantId,
12
+ organizationId: scope.organizationId,
13
+ deletedAt: null
14
+ }).groupBy("document_share.documentId").execute("all", false);
15
+ for (const row of groupedShares) {
16
+ const documentId = row.documentId ?? row.document_id;
17
+ const count = Number(row.shareCount ?? row.share_count ?? 0);
18
+ if (typeof documentId === "string" && Number.isFinite(count)) shareCounts.set(documentId, count);
19
+ }
20
+ return shareCounts;
21
+ }
22
+ export {
23
+ loadDocumentShareCounts
24
+ };
25
+ //# sourceMappingURL=shareCounts.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/documents/lib/shareCounts.ts"],
4
+ "sourcesContent": ["import { raw } from '@mikro-orm/core'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { DocumentShare } from '../data/entities'\n\nexport type DocumentShareCountScope = {\n tenantId: string\n organizationId: string\n}\n\ntype GroupedShareCountRow = {\n documentId?: unknown\n document_id?: unknown\n shareCount?: unknown\n share_count?: unknown\n}\n\nexport async function loadDocumentShareCounts(\n em: EntityManager,\n scope: DocumentShareCountScope,\n documentIds: readonly string[],\n): Promise<Map<string, number>> {\n const shareCounts = new Map<string, number>()\n if (documentIds.length === 0) return shareCounts\n const groupedShares = await em\n .createQueryBuilder(DocumentShare, 'document_share')\n .select([\n 'document_share.documentId',\n raw('count(*) as \"shareCount\"'),\n ])\n .where({\n documentId: { $in: [...documentIds] },\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n })\n .groupBy('document_share.documentId')\n .execute<GroupedShareCountRow[]>('all', false)\n for (const row of groupedShares) {\n const documentId = row.documentId ?? row.document_id\n const count = Number(row.shareCount ?? row.share_count ?? 0)\n if (typeof documentId === 'string' && Number.isFinite(count)) shareCounts.set(documentId, count)\n }\n return shareCounts\n}\n"],
5
+ "mappings": "AAAA,SAAS,WAAW;AAEpB,SAAS,qBAAqB;AAc9B,eAAsB,wBACpB,IACA,OACA,aAC8B;AAC9B,QAAM,cAAc,oBAAI,IAAoB;AAC5C,MAAI,YAAY,WAAW,EAAG,QAAO;AACrC,QAAM,gBAAgB,MAAM,GACzB,mBAAmB,eAAe,gBAAgB,EAClD,OAAO;AAAA,IACN;AAAA,IACA,IAAI,0BAA0B;AAAA,EAChC,CAAC,EACA,MAAM;AAAA,IACL,YAAY,EAAE,KAAK,CAAC,GAAG,WAAW,EAAE;AAAA,IACpC,UAAU,MAAM;AAAA,IAChB,gBAAgB,MAAM;AAAA,IACtB,WAAW;AAAA,EACb,CAAC,EACA,QAAQ,2BAA2B,EACnC,QAAgC,OAAO,KAAK;AAC/C,aAAW,OAAO,eAAe;AAC/B,UAAM,aAAa,IAAI,cAAc,IAAI;AACzC,UAAM,QAAQ,OAAO,IAAI,cAAc,IAAI,eAAe,CAAC;AAC3D,QAAI,OAAO,eAAe,YAAY,OAAO,SAAS,KAAK,EAAG,aAAY,IAAI,YAAY,KAAK;AAAA,EACjG;AACA,SAAO;AACT;",
6
+ "names": []
7
+ }