@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.
- package/README.md +88 -0
- package/build.mjs +23 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +7 -0
- package/dist/modules/documents/acl.js +15 -0
- package/dist/modules/documents/acl.js.map +7 -0
- package/dist/modules/documents/api/[id]/archive/route.js +116 -0
- package/dist/modules/documents/api/[id]/archive/route.js.map +7 -0
- package/dist/modules/documents/api/[id]/attachments/[attachmentId]/route.js +155 -0
- package/dist/modules/documents/api/[id]/attachments/[attachmentId]/route.js.map +7 -0
- package/dist/modules/documents/api/[id]/attachments/route.js +153 -0
- package/dist/modules/documents/api/[id]/attachments/route.js.map +7 -0
- package/dist/modules/documents/api/[id]/collab-token/route.js +180 -0
- package/dist/modules/documents/api/[id]/collab-token/route.js.map +7 -0
- package/dist/modules/documents/api/[id]/comments/access-check/route.js +86 -0
- package/dist/modules/documents/api/[id]/comments/access-check/route.js.map +7 -0
- package/dist/modules/documents/api/[id]/comments/route.js +387 -0
- package/dist/modules/documents/api/[id]/comments/route.js.map +7 -0
- package/dist/modules/documents/api/[id]/content/route.js +143 -0
- package/dist/modules/documents/api/[id]/content/route.js.map +7 -0
- package/dist/modules/documents/api/[id]/duplicate/route.js +200 -0
- package/dist/modules/documents/api/[id]/duplicate/route.js.map +7 -0
- package/dist/modules/documents/api/[id]/export/route.js +421 -0
- package/dist/modules/documents/api/[id]/export/route.js.map +7 -0
- package/dist/modules/documents/api/[id]/favorite/route.js +96 -0
- package/dist/modules/documents/api/[id]/favorite/route.js.map +7 -0
- package/dist/modules/documents/api/[id]/links/[linkId]/route.js +85 -0
- package/dist/modules/documents/api/[id]/links/[linkId]/route.js.map +7 -0
- package/dist/modules/documents/api/[id]/links/route.js +257 -0
- package/dist/modules/documents/api/[id]/links/route.js.map +7 -0
- package/dist/modules/documents/api/[id]/principals/route.js +299 -0
- package/dist/modules/documents/api/[id]/principals/route.js.map +7 -0
- package/dist/modules/documents/api/[id]/route.js +246 -0
- package/dist/modules/documents/api/[id]/route.js.map +7 -0
- package/dist/modules/documents/api/[id]/shares/route.js +335 -0
- package/dist/modules/documents/api/[id]/shares/route.js.map +7 -0
- package/dist/modules/documents/api/[id]/unarchive/route.js +39 -0
- package/dist/modules/documents/api/[id]/unarchive/route.js.map +7 -0
- package/dist/modules/documents/api/[id]/versions/[versionId]/restore/route.js +133 -0
- package/dist/modules/documents/api/[id]/versions/[versionId]/restore/route.js.map +7 -0
- package/dist/modules/documents/api/[id]/versions/[versionId]/route.js +93 -0
- package/dist/modules/documents/api/[id]/versions/[versionId]/route.js.map +7 -0
- package/dist/modules/documents/api/[id]/versions/route.js +193 -0
- package/dist/modules/documents/api/[id]/versions/route.js.map +7 -0
- package/dist/modules/documents/api/[id]/watch/route.js +97 -0
- package/dist/modules/documents/api/[id]/watch/route.js.map +7 -0
- package/dist/modules/documents/api/_commands.js +34 -0
- package/dist/modules/documents/api/_commands.js.map +7 -0
- package/dist/modules/documents/api/_shared.js +490 -0
- package/dist/modules/documents/api/_shared.js.map +7 -0
- package/dist/modules/documents/api/folders/route.js +256 -0
- package/dist/modules/documents/api/folders/route.js.map +7 -0
- package/dist/modules/documents/api/instantiate/route.js +108 -0
- package/dist/modules/documents/api/instantiate/route.js.map +7 -0
- package/dist/modules/documents/api/route.js +322 -0
- package/dist/modules/documents/api/route.js.map +7 -0
- package/dist/modules/documents/api/templates/[templateId]/preview/route.js +94 -0
- package/dist/modules/documents/api/templates/[templateId]/preview/route.js.map +7 -0
- package/dist/modules/documents/api/templates/[templateId]/route.js +82 -0
- package/dist/modules/documents/api/templates/[templateId]/route.js.map +7 -0
- package/dist/modules/documents/api/templates/route.js +344 -0
- package/dist/modules/documents/api/templates/route.js.map +7 -0
- package/dist/modules/documents/backend/documents/DocumentsPageClient.js +160 -0
- package/dist/modules/documents/backend/documents/DocumentsPageClient.js.map +7 -0
- package/dist/modules/documents/backend/documents/DocumentsTable.js +143 -0
- package/dist/modules/documents/backend/documents/DocumentsTable.js.map +7 -0
- package/dist/modules/documents/backend/documents/FolderDialog.js +55 -0
- package/dist/modules/documents/backend/documents/FolderDialog.js.map +7 -0
- package/dist/modules/documents/backend/documents/FolderTree.js +40 -0
- package/dist/modules/documents/backend/documents/FolderTree.js.map +7 -0
- package/dist/modules/documents/backend/documents/MoveDocumentDialog.js +64 -0
- package/dist/modules/documents/backend/documents/MoveDocumentDialog.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/CommentAnchorNavigation.js +77 -0
- package/dist/modules/documents/backend/documents/[id]/CommentAnchorNavigation.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/CommentComposer.js +126 -0
- package/dist/modules/documents/backend/documents/[id]/CommentComposer.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/CommentThreadList.js +88 -0
- package/dist/modules/documents/backend/documents/[id]/CommentThreadList.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/CommentsRail.js +123 -0
- package/dist/modules/documents/backend/documents/[id]/CommentsRail.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/DocumentCanvas.js +91 -0
- package/dist/modules/documents/backend/documents/[id]/DocumentCanvas.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/DocumentEditorErrorBoundary.js +27 -0
- package/dist/modules/documents/backend/documents/[id]/DocumentEditorErrorBoundary.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/DocumentEditorIsland.js +50 -0
- package/dist/modules/documents/backend/documents/[id]/DocumentEditorIsland.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/DocumentEditorSurface.js +168 -0
- package/dist/modules/documents/backend/documents/[id]/DocumentEditorSurface.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/DocumentNavigator.js +129 -0
- package/dist/modules/documents/backend/documents/[id]/DocumentNavigator.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/DocumentPageClient.js +429 -0
- package/dist/modules/documents/backend/documents/[id]/DocumentPageClient.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/EditorColorTools.js +68 -0
- package/dist/modules/documents/backend/documents/[id]/EditorColorTools.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/EditorStatusPresence.js +69 -0
- package/dist/modules/documents/backend/documents/[id]/EditorStatusPresence.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/EditorToolbar.js +151 -0
- package/dist/modules/documents/backend/documents/[id]/EditorToolbar.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/ExportMenu.js +121 -0
- package/dist/modules/documents/backend/documents/[id]/ExportMenu.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/MentionPicker.js +114 -0
- package/dist/modules/documents/backend/documents/[id]/MentionPicker.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/OutlinePane.js +70 -0
- package/dist/modules/documents/backend/documents/[id]/OutlinePane.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/RecordFieldsDialog.js +157 -0
- package/dist/modules/documents/backend/documents/[id]/RecordFieldsDialog.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/RelatedRecordsPanel.js +266 -0
- package/dist/modules/documents/backend/documents/[id]/RelatedRecordsPanel.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/VersionHistoryPanel.js +227 -0
- package/dist/modules/documents/backend/documents/[id]/VersionHistoryPanel.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/VersionPreviewDialog.js +157 -0
- package/dist/modules/documents/backend/documents/[id]/VersionPreviewDialog.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/commentTypes.js +150 -0
- package/dist/modules/documents/backend/documents/[id]/commentTypes.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/componentCapabilities.js +13 -0
- package/dist/modules/documents/backend/documents/[id]/componentCapabilities.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/documentPagination.js +295 -0
- package/dist/modules/documents/backend/documents/[id]/documentPagination.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/editorPresenceStyles.js +81 -0
- package/dist/modules/documents/backend/documents/[id]/editorPresenceStyles.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/editorTypes.js +52 -0
- package/dist/modules/documents/backend/documents/[id]/editorTypes.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/page.js +10 -0
- package/dist/modules/documents/backend/documents/[id]/page.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/page.meta.js +16 -0
- package/dist/modules/documents/backend/documents/[id]/page.meta.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/relatedRecordModel.js +38 -0
- package/dist/modules/documents/backend/documents/[id]/relatedRecordModel.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/restoreVersion.js +33 -0
- package/dist/modules/documents/backend/documents/[id]/restoreVersion.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/useDocumentCollaboration.js +392 -0
- package/dist/modules/documents/backend/documents/[id]/useDocumentCollaboration.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/useDocumentComments.js +253 -0
- package/dist/modules/documents/backend/documents/[id]/useDocumentComments.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/useDocumentEditor.js +149 -0
- package/dist/modules/documents/backend/documents/[id]/useDocumentEditor.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/useDocumentTitle.js +82 -0
- package/dist/modules/documents/backend/documents/[id]/useDocumentTitle.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/useEditorInsertions.js +144 -0
- package/dist/modules/documents/backend/documents/[id]/useEditorInsertions.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/useFallbackContentPersistence.js +237 -0
- package/dist/modules/documents/backend/documents/[id]/useFallbackContentPersistence.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/useMentionPicker.js +216 -0
- package/dist/modules/documents/backend/documents/[id]/useMentionPicker.js.map +7 -0
- package/dist/modules/documents/backend/documents/[id]/versionHistoryModel.js +25 -0
- package/dist/modules/documents/backend/documents/[id]/versionHistoryModel.js.map +7 -0
- package/dist/modules/documents/backend/documents/components/EntityPicker.js +156 -0
- package/dist/modules/documents/backend/documents/components/EntityPicker.js.map +7 -0
- package/dist/modules/documents/backend/documents/components/EntityPickerResults.js +46 -0
- package/dist/modules/documents/backend/documents/components/EntityPickerResults.js.map +7 -0
- package/dist/modules/documents/backend/documents/components/NewFromTemplateDialog.js +127 -0
- package/dist/modules/documents/backend/documents/components/NewFromTemplateDialog.js.map +7 -0
- package/dist/modules/documents/backend/documents/components/PrincipalPicker.js +84 -0
- package/dist/modules/documents/backend/documents/components/PrincipalPicker.js.map +7 -0
- package/dist/modules/documents/backend/documents/components/ShareDialog.js +77 -0
- package/dist/modules/documents/backend/documents/components/ShareDialog.js.map +7 -0
- package/dist/modules/documents/backend/documents/components/ShareDialogAddForm.js +93 -0
- package/dist/modules/documents/backend/documents/components/ShareDialogAddForm.js.map +7 -0
- package/dist/modules/documents/backend/documents/components/ShareDialogList.js +82 -0
- package/dist/modules/documents/backend/documents/components/ShareDialogList.js.map +7 -0
- package/dist/modules/documents/backend/documents/components/TemplateBodyEditor.js +96 -0
- package/dist/modules/documents/backend/documents/components/TemplateBodyEditor.js.map +7 -0
- package/dist/modules/documents/backend/documents/components/TemplateEditorDialog.js +169 -0
- package/dist/modules/documents/backend/documents/components/TemplateEditorDialog.js.map +7 -0
- package/dist/modules/documents/backend/documents/components/TemplatePreview.js +38 -0
- package/dist/modules/documents/backend/documents/components/TemplatePreview.js.map +7 -0
- package/dist/modules/documents/backend/documents/components/TemplateSlotFields.js +69 -0
- package/dist/modules/documents/backend/documents/components/TemplateSlotFields.js.map +7 -0
- package/dist/modules/documents/backend/documents/components/TemplateSlotsEditor.js +71 -0
- package/dist/modules/documents/backend/documents/components/TemplateSlotsEditor.js.map +7 -0
- package/dist/modules/documents/backend/documents/components/principalPickerModel.js +63 -0
- package/dist/modules/documents/backend/documents/components/principalPickerModel.js.map +7 -0
- package/dist/modules/documents/backend/documents/components/shareDialogModel.js +68 -0
- package/dist/modules/documents/backend/documents/components/shareDialogModel.js.map +7 -0
- package/dist/modules/documents/backend/documents/components/templateUi.js +89 -0
- package/dist/modules/documents/backend/documents/components/templateUi.js.map +7 -0
- package/dist/modules/documents/backend/documents/components/useAvailableEntityRegistry.js +30 -0
- package/dist/modules/documents/backend/documents/components/useAvailableEntityRegistry.js.map +7 -0
- package/dist/modules/documents/backend/documents/components/useEntitySearch.js +191 -0
- package/dist/modules/documents/backend/documents/components/useEntitySearch.js.map +7 -0
- package/dist/modules/documents/backend/documents/components/usePrincipalPicker.js +221 -0
- package/dist/modules/documents/backend/documents/components/usePrincipalPicker.js.map +7 -0
- package/dist/modules/documents/backend/documents/components/useShareDialog.js +236 -0
- package/dist/modules/documents/backend/documents/components/useShareDialog.js.map +7 -0
- package/dist/modules/documents/backend/documents/components/useTemplateDetail.js +49 -0
- package/dist/modules/documents/backend/documents/components/useTemplateDetail.js.map +7 -0
- package/dist/modules/documents/backend/documents/components/useTemplateInstantiation.js +234 -0
- package/dist/modules/documents/backend/documents/components/useTemplateInstantiation.js.map +7 -0
- package/dist/modules/documents/backend/documents/documentUi.js +106 -0
- package/dist/modules/documents/backend/documents/documentUi.js.map +7 -0
- package/dist/modules/documents/backend/documents/documentsListTypes.js +132 -0
- package/dist/modules/documents/backend/documents/documentsListTypes.js.map +7 -0
- package/dist/modules/documents/backend/documents/page.js +9 -0
- package/dist/modules/documents/backend/documents/page.js.map +7 -0
- package/dist/modules/documents/backend/documents/page.meta.js +26 -0
- package/dist/modules/documents/backend/documents/page.meta.js.map +7 -0
- package/dist/modules/documents/backend/documents/templates/TemplatesPageClient.js +91 -0
- package/dist/modules/documents/backend/documents/templates/TemplatesPageClient.js.map +7 -0
- package/dist/modules/documents/backend/documents/templates/TemplatesTable.js +58 -0
- package/dist/modules/documents/backend/documents/templates/TemplatesTable.js.map +7 -0
- package/dist/modules/documents/backend/documents/templates/page.js +9 -0
- package/dist/modules/documents/backend/documents/templates/page.js.map +7 -0
- package/dist/modules/documents/backend/documents/templates/page.meta.js +34 -0
- package/dist/modules/documents/backend/documents/templates/page.meta.js.map +7 -0
- package/dist/modules/documents/backend/documents/templates/useTemplatesPage.js +139 -0
- package/dist/modules/documents/backend/documents/templates/useTemplatesPage.js.map +7 -0
- package/dist/modules/documents/backend/documents/useDocumentsList.js +314 -0
- package/dist/modules/documents/backend/documents/useDocumentsList.js.map +7 -0
- package/dist/modules/documents/ce.js +8 -0
- package/dist/modules/documents/ce.js.map +7 -0
- package/dist/modules/documents/commands/aggregate.js +99 -0
- package/dist/modules/documents/commands/aggregate.js.map +7 -0
- package/dist/modules/documents/commands/attachments.js +252 -0
- package/dist/modules/documents/commands/attachments.js.map +7 -0
- package/dist/modules/documents/commands/comments.js +782 -0
- package/dist/modules/documents/commands/comments.js.map +7 -0
- package/dist/modules/documents/commands/content.js +238 -0
- package/dist/modules/documents/commands/content.js.map +7 -0
- package/dist/modules/documents/commands/document-crud.js +505 -0
- package/dist/modules/documents/commands/document-crud.js.map +7 -0
- package/dist/modules/documents/commands/documents.js +473 -0
- package/dist/modules/documents/commands/documents.js.map +7 -0
- package/dist/modules/documents/commands/duplicate.js +640 -0
- package/dist/modules/documents/commands/duplicate.js.map +7 -0
- package/dist/modules/documents/commands/favorites.js +150 -0
- package/dist/modules/documents/commands/favorites.js.map +7 -0
- package/dist/modules/documents/commands/folders.js +420 -0
- package/dist/modules/documents/commands/folders.js.map +7 -0
- package/dist/modules/documents/commands/index.js +64 -0
- package/dist/modules/documents/commands/index.js.map +7 -0
- package/dist/modules/documents/commands/interceptors.js +300 -0
- package/dist/modules/documents/commands/interceptors.js.map +7 -0
- package/dist/modules/documents/commands/lifecycle.js +249 -0
- package/dist/modules/documents/commands/lifecycle.js.map +7 -0
- package/dist/modules/documents/commands/links.js +410 -0
- package/dist/modules/documents/commands/links.js.map +7 -0
- package/dist/modules/documents/commands/mutation-helpers.js +42 -0
- package/dist/modules/documents/commands/mutation-helpers.js.map +7 -0
- package/dist/modules/documents/commands/projection-types.js +1 -0
- package/dist/modules/documents/commands/projection-types.js.map +7 -0
- package/dist/modules/documents/commands/shared.js +143 -0
- package/dist/modules/documents/commands/shared.js.map +7 -0
- package/dist/modules/documents/commands/shares.js +527 -0
- package/dist/modules/documents/commands/shares.js.map +7 -0
- package/dist/modules/documents/commands/side-effects.js +103 -0
- package/dist/modules/documents/commands/side-effects.js.map +7 -0
- package/dist/modules/documents/commands/templates.js +461 -0
- package/dist/modules/documents/commands/templates.js.map +7 -0
- package/dist/modules/documents/commands/versions.js +432 -0
- package/dist/modules/documents/commands/versions.js.map +7 -0
- package/dist/modules/documents/commands/watchers.js +164 -0
- package/dist/modules/documents/commands/watchers.js.map +7 -0
- package/dist/modules/documents/data/entities.js +653 -0
- package/dist/modules/documents/data/entities.js.map +7 -0
- package/dist/modules/documents/data/extensions.js +44 -0
- package/dist/modules/documents/data/extensions.js.map +7 -0
- package/dist/modules/documents/data/validators.js +219 -0
- package/dist/modules/documents/data/validators.js.map +7 -0
- package/dist/modules/documents/di.js +35 -0
- package/dist/modules/documents/di.js.map +7 -0
- package/dist/modules/documents/encryption.js +12 -0
- package/dist/modules/documents/encryption.js.map +7 -0
- package/dist/modules/documents/events.js +27 -0
- package/dist/modules/documents/events.js.map +7 -0
- package/dist/modules/documents/index.js +19 -0
- package/dist/modules/documents/index.js.map +7 -0
- package/dist/modules/documents/lib/attachmentServicePort.js +30 -0
- package/dist/modules/documents/lib/attachmentServicePort.js.map +7 -0
- package/dist/modules/documents/lib/capabilities.js +32 -0
- package/dist/modules/documents/lib/capabilities.js.map +7 -0
- package/dist/modules/documents/lib/collabCloseEvents.js +17 -0
- package/dist/modules/documents/lib/collabCloseEvents.js.map +7 -0
- package/dist/modules/documents/lib/collabEndpoint.js +22 -0
- package/dist/modules/documents/lib/collabEndpoint.js.map +7 -0
- package/dist/modules/documents/lib/collabMaterializer.js +107 -0
- package/dist/modules/documents/lib/collabMaterializer.js.map +7 -0
- package/dist/modules/documents/lib/collabToken.js +120 -0
- package/dist/modules/documents/lib/collabToken.js.map +7 -0
- package/dist/modules/documents/lib/collaborationAwareness.js +68 -0
- package/dist/modules/documents/lib/collaborationAwareness.js.map +7 -0
- package/dist/modules/documents/lib/commentAnchors.js +15 -0
- package/dist/modules/documents/lib/commentAnchors.js.map +7 -0
- package/dist/modules/documents/lib/constants.js +25 -0
- package/dist/modules/documents/lib/constants.js.map +7 -0
- package/dist/modules/documents/lib/contentService.js +248 -0
- package/dist/modules/documents/lib/contentService.js.map +7 -0
- package/dist/modules/documents/lib/displayLabels.js +23 -0
- package/dist/modules/documents/lib/displayLabels.js.map +7 -0
- package/dist/modules/documents/lib/docxRenderer.js +601 -0
- package/dist/modules/documents/lib/docxRenderer.js.map +7 -0
- package/dist/modules/documents/lib/duplicateContent.js +22 -0
- package/dist/modules/documents/lib/duplicateContent.js.map +7 -0
- package/dist/modules/documents/lib/editorConfig.js +287 -0
- package/dist/modules/documents/lib/editorConfig.js.map +7 -0
- package/dist/modules/documents/lib/entityLinks.js +148 -0
- package/dist/modules/documents/lib/entityLinks.js.map +7 -0
- package/dist/modules/documents/lib/entityRegistry.js +258 -0
- package/dist/modules/documents/lib/entityRegistry.js.map +7 -0
- package/dist/modules/documents/lib/entityRegistry.server.js +189 -0
- package/dist/modules/documents/lib/entityRegistry.server.js.map +7 -0
- package/dist/modules/documents/lib/entityRegistryAvailability.server.js +9 -0
- package/dist/modules/documents/lib/entityRegistryAvailability.server.js.map +7 -0
- package/dist/modules/documents/lib/entitySuggestion.js +50 -0
- package/dist/modules/documents/lib/entitySuggestion.js.map +7 -0
- package/dist/modules/documents/lib/favorites.js +22 -0
- package/dist/modules/documents/lib/favorites.js.map +7 -0
- package/dist/modules/documents/lib/folderHierarchySerialization.js +19 -0
- package/dist/modules/documents/lib/folderHierarchySerialization.js.map +7 -0
- package/dist/modules/documents/lib/historyLimits.js +100 -0
- package/dist/modules/documents/lib/historyLimits.js.map +7 -0
- package/dist/modules/documents/lib/historyPolicy.js +14 -0
- package/dist/modules/documents/lib/historyPolicy.js.map +7 -0
- package/dist/modules/documents/lib/organizationAccess.js +17 -0
- package/dist/modules/documents/lib/organizationAccess.js.map +7 -0
- package/dist/modules/documents/lib/pdfHtml.js +62 -0
- package/dist/modules/documents/lib/pdfHtml.js.map +7 -0
- package/dist/modules/documents/lib/pdfRenderer.js +284 -0
- package/dist/modules/documents/lib/pdfRenderer.js.map +7 -0
- package/dist/modules/documents/lib/permissions.js +222 -0
- package/dist/modules/documents/lib/permissions.js.map +7 -0
- package/dist/modules/documents/lib/platformServices.js +35 -0
- package/dist/modules/documents/lib/platformServices.js.map +7 -0
- package/dist/modules/documents/lib/recordFieldInsertion.js +58 -0
- package/dist/modules/documents/lib/recordFieldInsertion.js.map +7 -0
- package/dist/modules/documents/lib/requestBody.js +80 -0
- package/dist/modules/documents/lib/requestBody.js.map +7 -0
- package/dist/modules/documents/lib/resourceLimits.js +62 -0
- package/dist/modules/documents/lib/resourceLimits.js.map +7 -0
- package/dist/modules/documents/lib/shareCounts.js +25 -0
- package/dist/modules/documents/lib/shareCounts.js.map +7 -0
- package/dist/modules/documents/lib/templateFill.js +84 -0
- package/dist/modules/documents/lib/templateFill.js.map +7 -0
- package/dist/modules/documents/lib/templateInstantiation.js +147 -0
- package/dist/modules/documents/lib/templateInstantiation.js.map +7 -0
- package/dist/modules/documents/lib/templatePreviewDigest.js +82 -0
- package/dist/modules/documents/lib/templatePreviewDigest.js.map +7 -0
- package/dist/modules/documents/lib/templateSeeds.js +201 -0
- package/dist/modules/documents/lib/templateSeeds.js.map +7 -0
- package/dist/modules/documents/lib/userLabels.js +37 -0
- package/dist/modules/documents/lib/userLabels.js.map +7 -0
- package/dist/modules/documents/lib/versionContent.js +131 -0
- package/dist/modules/documents/lib/versionContent.js.map +7 -0
- package/dist/modules/documents/lib/versionLabels.js +12 -0
- package/dist/modules/documents/lib/versionLabels.js.map +7 -0
- package/dist/modules/documents/lib/versioning.js +14 -0
- package/dist/modules/documents/lib/versioning.js.map +7 -0
- package/dist/modules/documents/lib/visibility.js +422 -0
- package/dist/modules/documents/lib/visibility.js.map +7 -0
- package/dist/modules/documents/lib/watchers.js +106 -0
- package/dist/modules/documents/lib/watchers.js.map +7 -0
- package/dist/modules/documents/migrations/Migration20260708163836_documents.js +44 -0
- package/dist/modules/documents/migrations/Migration20260708163836_documents.js.map +7 -0
- package/dist/modules/documents/migrations/Migration20260709164720_documents.js +17 -0
- package/dist/modules/documents/migrations/Migration20260709164720_documents.js.map +7 -0
- package/dist/modules/documents/migrations/Migration20260710002318_documents.js +32 -0
- package/dist/modules/documents/migrations/Migration20260710002318_documents.js.map +7 -0
- package/dist/modules/documents/migrations/Migration20260710071003_documents.js +13 -0
- package/dist/modules/documents/migrations/Migration20260710071003_documents.js.map +7 -0
- package/dist/modules/documents/migrations/Migration20260712120000_documents.js +13 -0
- package/dist/modules/documents/migrations/Migration20260712120000_documents.js.map +7 -0
- package/dist/modules/documents/migrations/Migration20260712183000_documents.js +14 -0
- package/dist/modules/documents/migrations/Migration20260712183000_documents.js.map +7 -0
- package/dist/modules/documents/migrations/Migration20260713092156_documents.js +29 -0
- package/dist/modules/documents/migrations/Migration20260713092156_documents.js.map +7 -0
- package/dist/modules/documents/migrations/Migration20260717000000_documents.js +37 -0
- package/dist/modules/documents/migrations/Migration20260717000000_documents.js.map +7 -0
- package/dist/modules/documents/notifications.js +47 -0
- package/dist/modules/documents/notifications.js.map +7 -0
- package/dist/modules/documents/search.js +101 -0
- package/dist/modules/documents/search.js.map +7 -0
- package/dist/modules/documents/setup.js +19 -0
- package/dist/modules/documents/setup.js.map +7 -0
- package/dist/modules/documents/widgets/injection/related-documents/LinkDocumentDialog.js +214 -0
- package/dist/modules/documents/widgets/injection/related-documents/LinkDocumentDialog.js.map +7 -0
- package/dist/modules/documents/widgets/injection/related-documents/context.js +84 -0
- package/dist/modules/documents/widgets/injection/related-documents/context.js.map +7 -0
- package/dist/modules/documents/widgets/injection/related-documents/useRelatedDocuments.js +98 -0
- package/dist/modules/documents/widgets/injection/related-documents/useRelatedDocuments.js.map +7 -0
- package/dist/modules/documents/widgets/injection/related-documents/widget.client.js +83 -0
- package/dist/modules/documents/widgets/injection/related-documents/widget.client.js.map +7 -0
- package/dist/modules/documents/widgets/injection/related-documents/widget.js +19 -0
- package/dist/modules/documents/widgets/injection/related-documents/widget.js.map +7 -0
- package/dist/modules/documents/widgets/injection-table.js +21 -0
- package/dist/modules/documents/widgets/injection-table.js.map +7 -0
- package/dist/server/documents-collab-server.js +2260 -0
- package/dist/server/documents-collab-server.js.map +7 -0
- package/generated/entities/document/index.ts +12 -0
- package/generated/entities/document_attachment/index.ts +9 -0
- package/generated/entities/document_comment/index.ts +14 -0
- package/generated/entities/document_content/index.ts +11 -0
- package/generated/entities/document_entity_link/index.ts +19 -0
- package/generated/entities/document_favorite/index.ts +7 -0
- package/generated/entities/document_folder/index.ts +9 -0
- package/generated/entities/document_share/index.ts +11 -0
- package/generated/entities/document_template/index.ts +13 -0
- package/generated/entities/document_version/index.ts +9 -0
- package/generated/entities/document_watcher/index.ts +7 -0
- package/generated/entities.ids.generated.ts +21 -0
- package/generated/entity-fields-registry.ts +151 -0
- package/jest.config.cjs +34 -0
- package/package.json +149 -0
- package/server/documents-collab-server.ts +3416 -0
- package/src/index.ts +1 -0
- package/src/modules/documents/acl.ts +11 -0
- package/src/modules/documents/api/[id]/archive/route.ts +132 -0
- package/src/modules/documents/api/[id]/attachments/[attachmentId]/route.ts +166 -0
- package/src/modules/documents/api/[id]/attachments/route.ts +173 -0
- package/src/modules/documents/api/[id]/collab-token/route.ts +203 -0
- package/src/modules/documents/api/[id]/comments/access-check/route.ts +96 -0
- package/src/modules/documents/api/[id]/comments/route.ts +442 -0
- package/src/modules/documents/api/[id]/content/route.ts +156 -0
- package/src/modules/documents/api/[id]/duplicate/route.ts +231 -0
- package/src/modules/documents/api/[id]/export/route.ts +535 -0
- package/src/modules/documents/api/[id]/favorite/route.ts +110 -0
- package/src/modules/documents/api/[id]/links/[linkId]/route.ts +92 -0
- package/src/modules/documents/api/[id]/links/route.ts +301 -0
- package/src/modules/documents/api/[id]/principals/route.ts +375 -0
- package/src/modules/documents/api/[id]/route.ts +267 -0
- package/src/modules/documents/api/[id]/shares/route.ts +372 -0
- package/src/modules/documents/api/[id]/unarchive/route.ts +42 -0
- package/src/modules/documents/api/[id]/versions/[versionId]/restore/route.ts +152 -0
- package/src/modules/documents/api/[id]/versions/[versionId]/route.ts +100 -0
- package/src/modules/documents/api/[id]/versions/route.ts +207 -0
- package/src/modules/documents/api/[id]/watch/route.ts +111 -0
- package/src/modules/documents/api/_commands.ts +53 -0
- package/src/modules/documents/api/_shared.ts +676 -0
- package/src/modules/documents/api/folders/route.ts +287 -0
- package/src/modules/documents/api/instantiate/route.ts +114 -0
- package/src/modules/documents/api/route.ts +362 -0
- package/src/modules/documents/api/templates/[templateId]/preview/route.ts +97 -0
- package/src/modules/documents/api/templates/[templateId]/route.ts +92 -0
- package/src/modules/documents/api/templates/route.ts +370 -0
- package/src/modules/documents/backend/documents/DocumentsPageClient.tsx +150 -0
- package/src/modules/documents/backend/documents/DocumentsTable.tsx +168 -0
- package/src/modules/documents/backend/documents/FolderDialog.tsx +62 -0
- package/src/modules/documents/backend/documents/FolderTree.tsx +72 -0
- package/src/modules/documents/backend/documents/MoveDocumentDialog.tsx +70 -0
- package/src/modules/documents/backend/documents/[id]/CommentAnchorNavigation.ts +88 -0
- package/src/modules/documents/backend/documents/[id]/CommentComposer.tsx +136 -0
- package/src/modules/documents/backend/documents/[id]/CommentThreadList.tsx +131 -0
- package/src/modules/documents/backend/documents/[id]/CommentsRail.tsx +138 -0
- package/src/modules/documents/backend/documents/[id]/DocumentCanvas.tsx +111 -0
- package/src/modules/documents/backend/documents/[id]/DocumentEditorErrorBoundary.tsx +39 -0
- package/src/modules/documents/backend/documents/[id]/DocumentEditorIsland.tsx +50 -0
- package/src/modules/documents/backend/documents/[id]/DocumentEditorSurface.tsx +162 -0
- package/src/modules/documents/backend/documents/[id]/DocumentNavigator.tsx +129 -0
- package/src/modules/documents/backend/documents/[id]/DocumentPageClient.tsx +463 -0
- package/src/modules/documents/backend/documents/[id]/EditorColorTools.tsx +71 -0
- package/src/modules/documents/backend/documents/[id]/EditorStatusPresence.tsx +78 -0
- package/src/modules/documents/backend/documents/[id]/EditorToolbar.tsx +108 -0
- package/src/modules/documents/backend/documents/[id]/ExportMenu.tsx +144 -0
- package/src/modules/documents/backend/documents/[id]/MentionPicker.tsx +137 -0
- package/src/modules/documents/backend/documents/[id]/OutlinePane.tsx +98 -0
- package/src/modules/documents/backend/documents/[id]/RecordFieldsDialog.tsx +185 -0
- package/src/modules/documents/backend/documents/[id]/RelatedRecordsPanel.tsx +320 -0
- package/src/modules/documents/backend/documents/[id]/VersionHistoryPanel.tsx +244 -0
- package/src/modules/documents/backend/documents/[id]/VersionPreviewDialog.tsx +197 -0
- package/src/modules/documents/backend/documents/[id]/commentTypes.ts +192 -0
- package/src/modules/documents/backend/documents/[id]/componentCapabilities.ts +17 -0
- package/src/modules/documents/backend/documents/[id]/documentPagination.ts +352 -0
- package/src/modules/documents/backend/documents/[id]/editorPresenceStyles.ts +77 -0
- package/src/modules/documents/backend/documents/[id]/editorTypes.ts +84 -0
- package/src/modules/documents/backend/documents/[id]/page.meta.ts +12 -0
- package/src/modules/documents/backend/documents/[id]/page.tsx +6 -0
- package/src/modules/documents/backend/documents/[id]/relatedRecordModel.ts +49 -0
- package/src/modules/documents/backend/documents/[id]/restoreVersion.ts +49 -0
- package/src/modules/documents/backend/documents/[id]/useDocumentCollaboration.ts +458 -0
- package/src/modules/documents/backend/documents/[id]/useDocumentComments.ts +286 -0
- package/src/modules/documents/backend/documents/[id]/useDocumentEditor.ts +157 -0
- package/src/modules/documents/backend/documents/[id]/useDocumentTitle.ts +77 -0
- package/src/modules/documents/backend/documents/[id]/useEditorInsertions.ts +147 -0
- package/src/modules/documents/backend/documents/[id]/useFallbackContentPersistence.ts +277 -0
- package/src/modules/documents/backend/documents/[id]/useMentionPicker.ts +259 -0
- package/src/modules/documents/backend/documents/[id]/versionHistoryModel.ts +29 -0
- package/src/modules/documents/backend/documents/components/EntityPicker.tsx +171 -0
- package/src/modules/documents/backend/documents/components/EntityPickerResults.tsx +64 -0
- package/src/modules/documents/backend/documents/components/NewFromTemplateDialog.tsx +136 -0
- package/src/modules/documents/backend/documents/components/PrincipalPicker.tsx +90 -0
- package/src/modules/documents/backend/documents/components/ShareDialog.tsx +88 -0
- package/src/modules/documents/backend/documents/components/ShareDialogAddForm.tsx +113 -0
- package/src/modules/documents/backend/documents/components/ShareDialogList.tsx +117 -0
- package/src/modules/documents/backend/documents/components/TemplateBodyEditor.tsx +96 -0
- package/src/modules/documents/backend/documents/components/TemplateEditorDialog.tsx +171 -0
- package/src/modules/documents/backend/documents/components/TemplatePreview.tsx +43 -0
- package/src/modules/documents/backend/documents/components/TemplateSlotFields.tsx +72 -0
- package/src/modules/documents/backend/documents/components/TemplateSlotsEditor.tsx +72 -0
- package/src/modules/documents/backend/documents/components/principalPickerModel.ts +86 -0
- package/src/modules/documents/backend/documents/components/shareDialogModel.ts +94 -0
- package/src/modules/documents/backend/documents/components/templateUi.ts +128 -0
- package/src/modules/documents/backend/documents/components/useAvailableEntityRegistry.ts +43 -0
- package/src/modules/documents/backend/documents/components/useEntitySearch.ts +197 -0
- package/src/modules/documents/backend/documents/components/usePrincipalPicker.ts +165 -0
- package/src/modules/documents/backend/documents/components/useShareDialog.ts +267 -0
- package/src/modules/documents/backend/documents/components/useTemplateDetail.ts +54 -0
- package/src/modules/documents/backend/documents/components/useTemplateInstantiation.ts +252 -0
- package/src/modules/documents/backend/documents/documentUi.ts +134 -0
- package/src/modules/documents/backend/documents/documentsListTypes.ts +163 -0
- package/src/modules/documents/backend/documents/page.meta.ts +24 -0
- package/src/modules/documents/backend/documents/page.tsx +5 -0
- package/src/modules/documents/backend/documents/templates/TemplatesPageClient.tsx +93 -0
- package/src/modules/documents/backend/documents/templates/TemplatesTable.tsx +71 -0
- package/src/modules/documents/backend/documents/templates/page.meta.ts +32 -0
- package/src/modules/documents/backend/documents/templates/page.tsx +5 -0
- package/src/modules/documents/backend/documents/templates/useTemplatesPage.tsx +148 -0
- package/src/modules/documents/backend/documents/useDocumentsList.tsx +305 -0
- package/src/modules/documents/ce.ts +6 -0
- package/src/modules/documents/commands/aggregate.ts +128 -0
- package/src/modules/documents/commands/attachments.ts +309 -0
- package/src/modules/documents/commands/comments.ts +966 -0
- package/src/modules/documents/commands/content.ts +294 -0
- package/src/modules/documents/commands/document-crud.ts +609 -0
- package/src/modules/documents/commands/documents.ts +581 -0
- package/src/modules/documents/commands/duplicate.ts +772 -0
- package/src/modules/documents/commands/favorites.ts +171 -0
- package/src/modules/documents/commands/folders.ts +506 -0
- package/src/modules/documents/commands/index.ts +33 -0
- package/src/modules/documents/commands/interceptors.ts +402 -0
- package/src/modules/documents/commands/lifecycle.ts +333 -0
- package/src/modules/documents/commands/links.ts +486 -0
- package/src/modules/documents/commands/mutation-helpers.ts +48 -0
- package/src/modules/documents/commands/projection-types.ts +77 -0
- package/src/modules/documents/commands/shared.ts +219 -0
- package/src/modules/documents/commands/shares.ts +612 -0
- package/src/modules/documents/commands/side-effects.ts +130 -0
- package/src/modules/documents/commands/templates.ts +522 -0
- package/src/modules/documents/commands/versions.ts +537 -0
- package/src/modules/documents/commands/watchers.ts +181 -0
- package/src/modules/documents/data/entities.ts +631 -0
- package/src/modules/documents/data/extensions.ts +42 -0
- package/src/modules/documents/data/validators.ts +248 -0
- package/src/modules/documents/di.ts +33 -0
- package/src/modules/documents/encryption.ts +14 -0
- package/src/modules/documents/events.ts +23 -0
- package/src/modules/documents/i18n/.hardcoded-allowlist.json +9 -0
- package/src/modules/documents/i18n/de.json +522 -0
- package/src/modules/documents/i18n/en.json +522 -0
- package/src/modules/documents/i18n/es.json +522 -0
- package/src/modules/documents/i18n/ko.json +522 -0
- package/src/modules/documents/i18n/pl.json +522 -0
- package/src/modules/documents/index.ts +17 -0
- package/src/modules/documents/lib/attachmentServicePort.ts +94 -0
- package/src/modules/documents/lib/capabilities.ts +57 -0
- package/src/modules/documents/lib/collabCloseEvents.ts +26 -0
- package/src/modules/documents/lib/collabEndpoint.ts +43 -0
- package/src/modules/documents/lib/collabMaterializer.ts +140 -0
- package/src/modules/documents/lib/collabToken.ts +170 -0
- package/src/modules/documents/lib/collaborationAwareness.ts +102 -0
- package/src/modules/documents/lib/commentAnchors.ts +19 -0
- package/src/modules/documents/lib/constants.ts +39 -0
- package/src/modules/documents/lib/contentService.ts +340 -0
- package/src/modules/documents/lib/displayLabels.ts +31 -0
- package/src/modules/documents/lib/docxRenderer.ts +685 -0
- package/src/modules/documents/lib/duplicateContent.ts +32 -0
- package/src/modules/documents/lib/editorConfig.ts +371 -0
- package/src/modules/documents/lib/entityLinks.ts +250 -0
- package/src/modules/documents/lib/entityRegistry.server.ts +267 -0
- package/src/modules/documents/lib/entityRegistry.ts +345 -0
- package/src/modules/documents/lib/entityRegistryAvailability.server.ts +19 -0
- package/src/modules/documents/lib/entitySuggestion.ts +61 -0
- package/src/modules/documents/lib/favorites.ts +35 -0
- package/src/modules/documents/lib/folderHierarchySerialization.ts +34 -0
- package/src/modules/documents/lib/historyLimits.ts +154 -0
- package/src/modules/documents/lib/historyPolicy.ts +7 -0
- package/src/modules/documents/lib/organizationAccess.ts +44 -0
- package/src/modules/documents/lib/pdfHtml.ts +65 -0
- package/src/modules/documents/lib/pdfRenderer.ts +367 -0
- package/src/modules/documents/lib/permissions.ts +299 -0
- package/src/modules/documents/lib/platformServices.ts +70 -0
- package/src/modules/documents/lib/recordFieldInsertion.ts +72 -0
- package/src/modules/documents/lib/requestBody.ts +97 -0
- package/src/modules/documents/lib/resourceLimits.ts +94 -0
- package/src/modules/documents/lib/shareCounts.ts +44 -0
- package/src/modules/documents/lib/templateFill.ts +138 -0
- package/src/modules/documents/lib/templateInstantiation.ts +206 -0
- package/src/modules/documents/lib/templatePreviewDigest.ts +122 -0
- package/src/modules/documents/lib/templateSeeds.ts +248 -0
- package/src/modules/documents/lib/userLabels.ts +58 -0
- package/src/modules/documents/lib/versionContent.ts +163 -0
- package/src/modules/documents/lib/versionLabels.ts +9 -0
- package/src/modules/documents/lib/versioning.ts +29 -0
- package/src/modules/documents/lib/visibility.ts +610 -0
- package/src/modules/documents/lib/watchers.ts +129 -0
- package/src/modules/documents/migrations/.snapshot-open-mercato.json +2892 -0
- package/src/modules/documents/migrations/Migration20260708163836_documents.ts +50 -0
- package/src/modules/documents/migrations/Migration20260709164720_documents.ts +19 -0
- package/src/modules/documents/migrations/Migration20260710002318_documents.ts +35 -0
- package/src/modules/documents/migrations/Migration20260710071003_documents.ts +13 -0
- package/src/modules/documents/migrations/Migration20260712120000_documents.ts +13 -0
- package/src/modules/documents/migrations/Migration20260712183000_documents.ts +12 -0
- package/src/modules/documents/migrations/Migration20260713092156_documents.ts +43 -0
- package/src/modules/documents/migrations/Migration20260717000000_documents.ts +40 -0
- package/src/modules/documents/notifications.ts +45 -0
- package/src/modules/documents/search.ts +118 -0
- package/src/modules/documents/setup.ts +19 -0
- package/src/modules/documents/widgets/injection/related-documents/LinkDocumentDialog.tsx +224 -0
- package/src/modules/documents/widgets/injection/related-documents/context.ts +101 -0
- package/src/modules/documents/widgets/injection/related-documents/useRelatedDocuments.ts +112 -0
- package/src/modules/documents/widgets/injection/related-documents/widget.client.tsx +76 -0
- package/src/modules/documents/widgets/injection/related-documents/widget.ts +19 -0
- package/src/modules/documents/widgets/injection-table.ts +20 -0
- package/tsconfig.json +9 -0
- package/watch.mjs +6 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../server/documents-collab-server.ts"],
|
|
4
|
+
"sourcesContent": ["import { resolve } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport { randomUUID } from 'node:crypto'\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { LockMode } from '@mikro-orm/core'\nimport {\n Connection as HocuspocusConnection,\n IncomingMessage as HocuspocusIncomingMessage,\n MessageReceiver as HocuspocusMessageReceiver,\n MessageType,\n OutgoingMessage as HocuspocusOutgoingMessage,\n} from '@hocuspocus/server'\nimport { Redis as HocuspocusRedis } from '@hocuspocus/extension-redis'\nimport type {\n afterLoadDocumentPayload,\n afterStoreDocumentPayload,\n afterUnloadDocumentPayload,\n beforeHandleAwarenessPayload,\n beforeHandleMessagePayload,\n beforeSyncPayload,\n connectedPayload,\n onAuthenticatePayload,\n onDisconnectPayload,\n onLoadDocumentPayload,\n onChangePayload,\n onRequestPayload,\n onStoreDocumentPayload,\n Server as HocuspocusServer,\n} from '@hocuspocus/server'\nimport * as Y from 'yjs'\nimport { hasAllFeatures } from '@open-mercato/shared/lib/auth/featureMatch'\nimport { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { OPTIMISTIC_LOCK_CONFLICT_CODE } from '@open-mercato/shared/lib/crud/optimistic-lock-headers'\nimport {\n COLLAB_TOKEN_CLOCK_SKEW_SECONDS,\n COLLAB_TOKEN_TTL_SECONDS,\n isCollabTokenV2Ready,\n resolveLegacyCollabTokenVerifier,\n verifyCollabTokenV2,\n type CollabTokenClaims,\n type VerifiedCollabTokenV2Claims,\n} from '@open-mercato/documents/modules/documents/lib/collabToken'\nimport {\n advanceDocumentCollaborationGeneration,\n loadDocumentCollaborationGeneration,\n loadDocumentContentForCollaboration,\n normalizeDocumentCollaborationGeneration,\n persistDocumentContent,\n type PersistDocumentContentDeps,\n} from '@open-mercato/documents/modules/documents/lib/contentService'\nimport {\n htmlToYDoc,\n yDocToContent,\n} from '@open-mercato/documents/modules/documents/lib/collabMaterializer'\nimport { Document, DocumentContent } from '@open-mercato/documents/modules/documents/data/entities'\nimport { deriveDocumentCapabilities } from '@open-mercato/documents/modules/documents/lib/capabilities'\nimport { resolveUserAccess } from '@open-mercato/documents/modules/documents/lib/permissions'\nimport { resolveUserLabels } from '@open-mercato/documents/modules/documents/lib/userLabels'\nimport { resolveOrganizationScopeService } from '@open-mercato/documents/modules/documents/lib/platformServices'\nimport {\n hasResolvedDocumentsOrganizationAccess,\n type ResolvedDocumentsOrganizationScope,\n} from '@open-mercato/documents/modules/documents/lib/organizationAccess'\nimport {\n createCanonicalCollaborationAwarenessUser,\n type CanonicalCollaborationAwarenessUser,\n} from '@open-mercato/documents/modules/documents/lib/collaborationAwareness'\nimport {\n assertDocumentContentResourceLimits,\n assertDocumentYjsStateByteLength,\n DOCUMENTS_COLLAB_MAX_PAYLOAD_BYTES,\n} from '@open-mercato/documents/modules/documents/lib/resourceLimits'\nimport { COLLAB_CONTENT_RESET_CLOSE_EVENT } from '@open-mercato/documents/modules/documents/lib/collabCloseEvents'\n\nexport { htmlToYDoc, yDocToContent }\n\nconst logger = createLogger('documents-collab')\n\nconst MAX_COLLAB_STORE_ATTEMPTS = 4\nexport const DOCUMENTS_COLLAB_MAX_AWARENESS_STATE_BYTES = 8 * 1024\nexport const DOCUMENTS_COLLAB_MAX_AWARENESS_CLIENT_IDS_PER_CONNECTION = 1\nexport const DOCUMENTS_COLLAB_MAX_AWARENESS_CLIENT_IDS_PER_ROOM = 256\nconst MAX_YJS_CLIENT_ID = 0xffff_ffff\nexport const COLLAB_SERVER_TRANSPORT_OPTIONS = {\n maxPayload: DOCUMENTS_COLLAB_MAX_PAYLOAD_BYTES,\n} as const\nexport const DOCUMENTS_COLLAB_MAX_PENDING_MESSAGES = 128\nexport const DOCUMENTS_COLLAB_MAX_PENDING_BYTES = DOCUMENTS_COLLAB_MAX_PAYLOAD_BYTES * 2\nexport const DOCUMENTS_COLLAB_MAX_PENDING_DOCUMENTS = 32\nexport const DOCUMENTS_COLLAB_MAX_AWARENESS_FRAME_BYTES = 32 * 1024\nexport const DOCUMENTS_COLLAB_MAX_CONTROL_FRAME_BYTES = 64 * 1024\nexport const DOCUMENTS_COLLAB_MAX_READ_ONLY_SYNC_FRAME_BYTES = 256 * 1024\nexport const DOCUMENTS_COLLAB_AUTHORIZATION_TICKET_TIMEOUT_MS = 60_000\nexport const DOCUMENTS_COLLAB_ACTIVE_REAUTHORIZATION_INTERVAL_MS = 15_000\nexport const COLLAB_SERVER_WEBSOCKET_CONFIGURATION = {\n websocketOptions: COLLAB_SERVER_TRANSPORT_OPTIONS,\n} as const\nexport const COLLAB_SERVER_UNAUTHENTICATED_CONFIGURATION = {\n maxUnauthenticatedQueueSize: DOCUMENTS_COLLAB_MAX_PENDING_BYTES,\n maxUnauthenticatedQueueMessages: DOCUMENTS_COLLAB_MAX_PENDING_MESSAGES,\n maxPendingDocuments: DOCUMENTS_COLLAB_MAX_PENDING_DOCUMENTS,\n} as const\nexport const COLLAB_SERVER_RUNTIME_CONFIGURATION = {\n ...COLLAB_SERVER_WEBSOCKET_CONFIGURATION,\n ...COLLAB_SERVER_UNAUTHENTICATED_CONFIGURATION,\n} as const\n\nexport type DocumentsCollabRedisConfiguration = {\n host: string\n port: number\n prefix: string\n options: {\n username?: string\n password?: string\n db?: number\n tls?: Record<string, never>\n maxRetriesPerRequest: null\n }\n}\n\nconst DEFAULT_DOCUMENTS_COLLAB_REDIS_PREFIX = 'open-mercato:documents:collab:development'\nconst LEGACY_DOCUMENTS_COLLAB_REDIS_PREFIX = 'open-mercato:documents:collab'\nconst DOCUMENTS_COLLAB_REDIS_PREFIX_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9._:-]{0,158}[A-Za-z0-9])?$/\n\nfunction resolveDocumentsCollabRedisPrefix(environment: NodeJS.ProcessEnv): string {\n const configured = environment.DOCUMENTS_COLLAB_REDIS_PREFIX?.trim() ?? ''\n if (!configured && environment.NODE_ENV === 'production') {\n throw new Error(\n '[internal] DOCUMENTS_COLLAB_REDIS_PREFIX is required in production when collaboration Redis is configured',\n )\n }\n const prefix = configured || DEFAULT_DOCUMENTS_COLLAB_REDIS_PREFIX\n if (\n prefix === LEGACY_DOCUMENTS_COLLAB_REDIS_PREFIX\n || !DOCUMENTS_COLLAB_REDIS_PREFIX_PATTERN.test(prefix)\n ) {\n throw new Error(\n '[internal] DOCUMENTS_COLLAB_REDIS_PREFIX must be a deployment-scoped Redis key prefix using letters, numbers, dot, underscore, colon, or dash',\n )\n }\n return prefix\n}\n\nexport function resolveDocumentsCollabRedisConfiguration(\n environment: NodeJS.ProcessEnv = process.env,\n): DocumentsCollabRedisConfiguration | null {\n const configured = environment.DOCUMENTS_COLLAB_REDIS_URL?.trim()\n || environment.REDIS_URL?.trim()\n || ''\n if (!configured) return null\n\n let parsed: URL\n try {\n parsed = new URL(configured)\n } catch {\n throw new Error('[internal] Documents collaboration Redis URL is invalid')\n }\n if ((parsed.protocol !== 'redis:' && parsed.protocol !== 'rediss:') || !parsed.hostname) {\n throw new Error('[internal] Documents collaboration Redis URL must use redis:// or rediss://')\n }\n const port = parsed.port ? Number(parsed.port) : 6379\n const databasePath = parsed.pathname.replace(/^\\//, '')\n const db = databasePath ? Number(databasePath) : undefined\n if (!Number.isInteger(port) || port < 1 || port > 65_535) {\n throw new Error('[internal] Documents collaboration Redis port is invalid')\n }\n if (db !== undefined && (!Number.isInteger(db) || db < 0)) {\n throw new Error('[internal] Documents collaboration Redis database is invalid')\n }\n\n return {\n host: parsed.hostname,\n port,\n prefix: resolveDocumentsCollabRedisPrefix(environment),\n options: {\n ...(parsed.username ? { username: decodeURIComponent(parsed.username) } : {}),\n ...(parsed.password ? { password: decodeURIComponent(parsed.password) } : {}),\n ...(db !== undefined ? { db } : {}),\n ...(parsed.protocol === 'rediss:' ? { tls: {} } : {}),\n maxRetriesPerRequest: null,\n },\n }\n}\n\ntype DocumentsCollabRedisStorePayload = Pick<\n onStoreDocumentPayload,\n 'lastTransactionOrigin'\n>\n\ntype DocumentsCollabRedisStoreExtension = {\n onStoreDocument: (payload: onStoreDocumentPayload) => unknown\n}\n\ntype DocumentsCollabRedisReplicationOptions = {\n onRejectedAggregate?: (documentName: string, document: Y.Doc) => void\n onAcceptedAggregate?: (document: Y.Doc, byteLength: number) => void\n resolveCollaborationGeneration?: (document: Y.Doc) => number | undefined\n}\n\ntype DocumentsCollabRedisFanoutPublisher = {\n publish: (channel: string, message: Buffer) => Promise<unknown>\n disconnect: (reconnect?: boolean) => void\n}\n\ntype DocumentsCollabPendingFanout = {\n document: Y.Doc\n collaborationGeneration: number\n revision: number\n message: Uint8Array\n}\n\ntype DocumentsCollabBufferedFanout = {\n collaborationGeneration: number\n update: Uint8Array\n}\n\nconst DOCUMENTS_COLLAB_REDIS_FANOUT_COMMAND_TIMEOUT_MS = 1_000\nconst DOCUMENTS_COLLAB_REDIS_FANOUT_RETRY_MIN_MS = 250\nconst DOCUMENTS_COLLAB_REDIS_FANOUT_RETRY_MAX_MS = 5_000\nconst DOCUMENTS_COLLAB_REDIS_FANOUT_SHUTDOWN_TIMEOUT_MS = 1_500\nconst DOCUMENTS_COLLAB_REDIS_LOCK_RELEASE_TIMEOUT_MS = 1_250\n// The collaboration clients run with maxRetriesPerRequest: null, which disables\n// ioredis' offline-queue flush. Every awaited Redis command therefore needs its\n// own deadline or a prolonged outage parks the caller forever.\nexport const DOCUMENTS_COLLAB_REDIS_SUBSCRIBE_TIMEOUT_MS = 5_000\nexport const DOCUMENTS_COLLAB_REDIS_FANOUT_MAX_PENDING_ROOMS = 256\nexport const DOCUMENTS_COLLAB_REDIS_FANOUT_MAX_PENDING_BYTES = DOCUMENTS_COLLAB_MAX_PAYLOAD_BYTES * 8\nconst DOCUMENTS_COLLAB_REDIS_FANOUT_MAGIC = Buffer.from('OMDF1', 'ascii')\nconst DOCUMENTS_COLLAB_REDIS_FANOUT_GENERATION_BYTES = 8\n\nfunction isHocuspocusStoreLockContention(error: unknown): error is Error {\n // @hocuspocus/extension-redis throws this cross-package error when another\n // replica owns the room's store lock. Match by the stable error name rather\n // than instanceof so duplicated Hocuspocus packages do not hide contention.\n return error instanceof Error && error.name === 'SkipFurtherHooksError'\n}\n\n/**\n * Redis replicas receive an authenticated source replica's Yjs update, but\n * they do not receive its browser authorization context. Only a store caused\n * by a local authenticated connection may compete for the distributed store\n * lock. A receiving replica therefore mirrors the update in memory while the\n * source replica remains responsible for making it durable. When two source\n * replicas race, Hocuspocus otherwise swallows the Redis lock loser's\n * SkipFurtherHooksError and may unload it even though a later Redis merge does\n * not schedule store hooks. Queue a complete Hocuspocus store retry before\n * propagating the sentinel so the room stays mapped and the retry still runs\n * the Redis lock, live authorization, and optimistic merge hooks in order.\n */\nexport function isDocumentsCollabSourceStore(\n payload: DocumentsCollabRedisStorePayload,\n): boolean {\n const origin = payload.lastTransactionOrigin\n return !(\n origin\n && typeof origin === 'object'\n && 'source' in origin\n && origin.source === 'redis'\n )\n}\n\nexport function enforceDocumentsCollabSourceStoreOwnership<\n RedisExtension extends DocumentsCollabRedisStoreExtension,\n>(extension: RedisExtension): RedisExtension {\n const onStoreDocument = extension.onStoreDocument.bind(extension)\n extension.onStoreDocument = async (payload: onStoreDocumentPayload) => {\n if (!isDocumentsCollabSourceStore(payload)) return\n try {\n return await onStoreDocument(payload)\n } catch (error) {\n if (isHocuspocusStoreLockContention(error)) {\n // Register the retry synchronously. shouldUnloadDocument() observes\n // the debounced work when Hocuspocus catches the sentinel on the\n // current attempt, including when the last client just disconnected.\n void payload.instance.storeDocumentHooks(payload.document, payload)\n }\n throw error\n }\n }\n return extension\n}\n\n/**\n * Verify a durable update against the receiver's complete in-memory state\n * before Redis is allowed to apply or broadcast it. Each source replica\n * enforces the same limits before persistence, but two individually valid\n * replicas can still exceed the aggregate limit when their states merge.\n */\nexport function assertDocumentsCollabRedisAggregateUpdate(\n document: Y.Doc,\n update: Uint8Array,\n): number {\n const candidate = new Y.Doc()\n try {\n Y.applyUpdate(candidate, Y.encodeStateAsUpdate(document))\n Y.applyUpdate(candidate, update)\n return assertDocumentsCollabRedisAggregateState(candidate)\n } finally {\n candidate.destroy()\n }\n}\n\nfunction assertDocumentsCollabRedisAggregateState(document: Y.Doc): number {\n const materialized = yDocToContent(document)\n const yjsState = Y.encodeStateAsUpdate(document)\n assertDocumentContentResourceLimits({\n yjsState,\n contentHtml: materialized?.html,\n contentText: materialized?.text,\n })\n return yjsState.byteLength\n}\n\n/**\n * Documents collaboration deliberately does not use Hocuspocus Redis' Yjs\n * state-vector handshake. That handshake reads the live room and can fan out\n * an edit before its source replica has re-authorized and durably persisted\n * it. Instead, this extension keeps Redis awareness behaviour and the\n * distributed store lock, but publishes one complete Yjs update only after\n * the Documents store hook confirms the exact state became durable.\n */\nexport class DocumentsCollabRedisExtension extends HocuspocusRedis {\n private readonly persistedStates = new WeakMap<Y.Doc, {\n collaborationGeneration: number\n state: Uint8Array\n }>()\n\n /**\n * Durable fanout uses a dedicated fail-fast Redis connection. The stock\n * publisher is also the Redlock client and intentionally waits for Redis to\n * recover before allowing another replica to persist; post-store delivery\n * must not keep that distributed lock or Hocuspocus' save mutex occupied.\n */\n private readonly fanoutPublisher: DocumentsCollabRedisFanoutPublisher\n\n private readonly pendingFanouts = new Map<string, DocumentsCollabPendingFanout>()\n\n private readonly activeFanouts = new Set<string>()\n\n private readonly fanoutRetryAttempts = new Map<string, number>()\n\n private readonly fanoutRetryTimers = new Map<string, ReturnType<typeof setTimeout>>()\n\n private readonly pendingLocalAfterStoreDelays = new Map<string, {\n timeout: ReturnType<typeof setTimeout>\n resolve: () => void\n }>()\n\n private readonly invalidatedFanoutDocuments = new WeakSet<Y.Doc>()\n\n private readonly bufferedFanouts = new WeakMap<\n Y.Doc,\n Map<string, DocumentsCollabBufferedFanout>\n >()\n\n private readonly bufferedFanoutBytes = new WeakMap<Y.Doc, number>()\n\n private nextFanoutRevision = 0\n\n private pendingFanoutBytes = 0\n\n private fanoutDestroyed = false\n\n /**\n * Hocuspocus does not publish a loading document in instance.documents until\n * after afterLoadDocument completes. Keep the hook payload reachable so a\n * Redis update received while the scoped database snapshot is loading can be\n * applied to that same Y.Doc instead of being silently dropped.\n */\n private readonly knownDocuments = new Map<string, onLoadDocumentPayload['document']>()\n\n private readonly onRejectedAggregate?: (\n documentName: string,\n document: Y.Doc,\n ) => void\n\n private readonly onAcceptedAggregate?: (\n document: Y.Doc,\n byteLength: number,\n ) => void\n\n private readonly resolveCollaborationGeneration?: (\n document: Y.Doc,\n ) => number | undefined\n\n constructor(\n configuration: DocumentsCollabRedisConfiguration,\n options: DocumentsCollabRedisReplicationOptions = {},\n ) {\n super(configuration)\n this.onRejectedAggregate = options.onRejectedAggregate\n this.onAcceptedAggregate = options.onAcceptedAggregate\n this.resolveCollaborationGeneration = options.resolveCollaborationGeneration\n this.fanoutPublisher = (\n this.pub as unknown as {\n duplicate: (options: {\n autoResendUnfulfilledCommands: boolean\n commandTimeout: number\n enableOfflineQueue: boolean\n maxRetriesPerRequest: number\n }) => DocumentsCollabRedisFanoutPublisher\n }\n ).duplicate({\n autoResendUnfulfilledCommands: false,\n commandTimeout: DOCUMENTS_COLLAB_REDIS_FANOUT_COMMAND_TIMEOUT_MS,\n enableOfflineQueue: false,\n maxRetriesPerRequest: 1,\n })\n\n // Redis registers its stock state-vector receiver in super(). Replace only\n // that freshly-created client's messageBuffer listener with the durable\n // update receiver below; connection/error listeners remain untouched.\n for (const listener of this.sub.listeners('messageBuffer')) {\n this.sub.off('messageBuffer', listener)\n }\n this.sub.on('messageBuffer', this.receiveDocumentsRedisMessage)\n }\n\n /**\n * EventEmitter never observes the promise an async listener returns, so a\n * truncated frame, a receiver failure, or a failed reply publish would become\n * an unhandled rejection and terminate the sidecar. Keep the registered\n * listener synchronous and terminate every rejection here instead: one\n * malformed frame must cost at most that frame.\n */\n private readonly receiveDocumentsRedisMessage = (channel: Buffer, data: Buffer): void => {\n void this.handleDocumentsRedisMessage(channel, data).catch((error: unknown) => {\n logger.warn('rejected malformed Redis collaboration frame', {\n error: error instanceof Error ? error.message : String(error),\n })\n })\n }\n\n markPersisted(\n document: Y.Doc,\n yjsState: Uint8Array,\n collaborationGeneration: number,\n ): void {\n if (this.invalidatedFanoutDocuments.has(document)) return\n this.persistedStates.set(document, {\n collaborationGeneration,\n state: Uint8Array.from(yjsState),\n })\n }\n\n async onLoadDocument(data: onLoadDocumentPayload): Promise<void> {\n const { documentName, document } = data\n this.knownDocuments.set(documentName, document)\n const loadingDocument = data.instance.loadingDocuments.get(documentName)\n if (loadingDocument) {\n void loadingDocument.catch(() => {\n if (\n this.knownDocuments.get(documentName) === document\n && !data.instance.documents.has(documentName)\n ) {\n this.knownDocuments.delete(documentName)\n void this.sub.unsubscribe(this.redisKey(documentName)).catch(() => undefined)\n }\n })\n }\n try {\n await this.subscribeDocument(documentName)\n } catch (error) {\n if (this.knownDocuments.get(documentName) === document) {\n this.knownDocuments.delete(documentName)\n }\n throw error\n }\n }\n\n override async afterLoadDocument({\n documentName,\n document,\n }: afterLoadDocumentPayload): Promise<void> {\n let aggregateByteLength: number\n try {\n this.applyBufferedFanouts(documentName, document)\n // A durable Redis update can arrive after the database snapshot was read\n // but before it is applied. Validate and re-budget their final union.\n aggregateByteLength = assertDocumentsCollabRedisAggregateState(document)\n } catch (error) {\n this.onRejectedAggregate?.(documentName, document)\n throw error\n }\n this.onAcceptedAggregate?.(document, aggregateByteLength)\n\n const awarenessQuery = new HocuspocusOutgoingMessage(documentName)\n .writeQueryAwareness()\n .toUint8Array()\n await this.publishRedisMessage(documentName, awarenessQuery)\n }\n\n override async afterUnloadDocument(data: afterUnloadDocumentPayload): Promise<void> {\n // The old room can finish unloading after a reconnect has already begun.\n // Keep the replacement's early subscription intact until its load either\n // succeeds or the failed-load cleanup above releases it.\n if (\n data.instance.documents.has(data.documentName)\n || data.instance.loadingDocuments.has(data.documentName)\n ) return\n\n this.knownDocuments.delete(data.documentName)\n await super.afterUnloadDocument(data)\n }\n\n /**\n * The subscriber client keeps ioredis' offline queue enabled, so a room load\n * started during a Redis outage would otherwise wait for recovery with no\n * deadline while its socket stays open. Fail the load instead: onLoadDocument\n * releases the early subscription and Hocuspocus rejects the connection, so\n * the client can retry rather than hanging against a green health check.\n */\n private async subscribeDocument(documentName: string): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const deadline = setTimeout(() => {\n reject(new Error('[internal] documents collab: Redis room subscription timed out'))\n }, DOCUMENTS_COLLAB_REDIS_SUBSCRIBE_TIMEOUT_MS)\n deadline.unref?.()\n this.sub.subscribe(this.redisKey(documentName), (error: Error | null | undefined) => {\n clearTimeout(deadline)\n if (error) reject(error)\n else resolve()\n })\n })\n }\n\n override async onChange(_data: onChangePayload): Promise<void> {\n // Document updates are published from afterStoreDocument only. Awareness\n // and stateless hooks continue to use the parent extension unchanged.\n }\n\n override async afterStoreDocument(data: afterStoreDocumentPayload): Promise<void> {\n const persistedState = this.persistedStates.get(data.document)\n this.persistedStates.delete(data.document)\n try {\n await this.releaseStoreLock(data)\n } finally {\n if (\n persistedState\n && isDocumentsCollabSourceStore(data)\n && !this.invalidatedFanoutDocuments.has(data.document)\n ) {\n this.queuePersistedFanout(\n data.documentName,\n data.document,\n persistedState.state,\n persistedState.collaborationGeneration,\n )\n }\n }\n }\n\n private async releaseStoreLock(data: afterStoreDocumentPayload): Promise<void> {\n const lockKey = `${this.redisKey(data.documentName)}:lock`\n const lockState = this.locks.get(lockKey)\n if (lockState) {\n let timeout: ReturnType<typeof setTimeout> | null = null\n try {\n const releasing = lockState.lock.release()\n lockState.release = releasing\n const deadline = new Promise<false>((resolve) => {\n timeout = setTimeout(\n () => resolve(false),\n DOCUMENTS_COLLAB_REDIS_LOCK_RELEASE_TIMEOUT_MS,\n )\n timeout.unref?.()\n })\n const released = await Promise.race([\n releasing.then(\n () => true as const,\n () => true as const,\n ),\n deadline,\n ])\n if (!released) {\n // The Redlock token expires after one second. Do not retain the\n // room's save mutex forever when ioredis keeps the release command\n // queued during an outage. The old promise is deliberately detached:\n // unlike the parent hook, its late settlement cannot delete a newer\n // lock entry installed for the same room.\n void releasing.catch(() => undefined)\n logger.warn('Redis collaboration store lock release timed out; continuing after TTL', {\n room: data.documentName,\n })\n }\n } catch {\n // Match the parent extension: Redlock expiry is the release fallback.\n } finally {\n if (timeout) clearTimeout(timeout)\n if (this.locks.get(lockKey) === lockState) this.locks.delete(lockKey)\n }\n }\n\n await this.delayLocalAfterStore(data)\n }\n\n private async delayLocalAfterStore(data: afterStoreDocumentPayload): Promise<void> {\n const origin = data.lastTransactionOrigin\n if (\n !origin\n || typeof origin !== 'object'\n || !('source' in origin)\n || origin.source !== 'local'\n ) return\n\n // Mirror the parent extension's direct-connection debounce without calling\n // its lock cleanup after our bounded release. Keeping this map subclass-\n // owned also makes every cleanup identity-safe.\n const previous = this.pendingLocalAfterStoreDelays.get(data.documentName)\n if (previous) {\n clearTimeout(previous.timeout)\n this.pendingLocalAfterStoreDelays.delete(data.documentName)\n previous.resolve()\n }\n\n await new Promise<void>((resolve) => {\n const finish = (): void => {\n const current = this.pendingLocalAfterStoreDelays.get(data.documentName)\n if (current?.resolve === finish) {\n this.pendingLocalAfterStoreDelays.delete(data.documentName)\n }\n resolve()\n }\n const timeout = setTimeout(finish, this.configuration.disconnectDelay)\n this.pendingLocalAfterStoreDelays.set(data.documentName, { timeout, resolve: finish })\n })\n }\n\n discardPendingFanout(documentName: string, document?: Y.Doc): void {\n if (document) this.invalidatedFanoutDocuments.add(document)\n const pending = this.pendingFanouts.get(documentName)\n if (document && pending && pending.document !== document) return\n this.forgetPendingFanout(documentName)\n }\n\n private removePendingFanoutEntry(documentName: string): void {\n const pending = this.pendingFanouts.get(documentName)\n if (!pending) return\n this.pendingFanoutBytes -= pending.message.byteLength\n this.pendingFanouts.delete(documentName)\n }\n\n private forgetPendingFanout(documentName: string): void {\n this.removePendingFanoutEntry(documentName)\n this.fanoutRetryAttempts.delete(documentName)\n const retryTimer = this.fanoutRetryTimers.get(documentName)\n if (retryTimer) clearTimeout(retryTimer)\n this.fanoutRetryTimers.delete(documentName)\n }\n\n /**\n * A pending fanout carries the room's complete post-store state and pins its\n * Y.Doc, so an unreachable Redis would otherwise let one entry per edited\n * document accumulate for the whole outage \u2014 long after those rooms unload.\n * Bound the backlog by dropping the least recently refreshed rooms: their\n * state is already durable, every later fanout for a room supersedes the one\n * dropped here, and a peer that never sees the frame reloads authoritative\n * content on its next load anyway.\n */\n private evictOverflowingFanouts(retainedDocumentName: string): void {\n for (const documentName of this.pendingFanouts.keys()) {\n if (\n this.pendingFanouts.size <= DOCUMENTS_COLLAB_REDIS_FANOUT_MAX_PENDING_ROOMS\n && this.pendingFanoutBytes <= DOCUMENTS_COLLAB_REDIS_FANOUT_MAX_PENDING_BYTES\n ) return\n if (documentName === retainedDocumentName) continue\n this.forgetPendingFanout(documentName)\n logger.warn('dropped a stale pending Redis collaboration fanout to bound the outage backlog', {\n room: documentName,\n pendingRooms: this.pendingFanouts.size,\n pendingBytes: this.pendingFanoutBytes,\n })\n }\n }\n\n private queuePersistedFanout(\n documentName: string,\n document: Y.Doc,\n persistedState: Uint8Array,\n collaborationGeneration: number,\n ): void {\n if (this.fanoutDestroyed) return\n const message = new HocuspocusOutgoingMessage(documentName)\n .createSyncMessage()\n .writeUpdate(persistedState)\n .toUint8Array()\n // Re-insert rather than overwrite so the map stays ordered by how recently\n // each room was refreshed; eviction below relies on that ordering. The\n // room's retry attempts and backoff timer deliberately survive: a coalesced\n // update must not reset an outage's exponential backoff.\n this.removePendingFanoutEntry(documentName)\n this.pendingFanouts.set(documentName, {\n document,\n collaborationGeneration,\n revision: ++this.nextFanoutRevision,\n message,\n })\n this.pendingFanoutBytes += message.byteLength\n this.evictOverflowingFanouts(documentName)\n this.startPersistedFanout(documentName)\n }\n\n private startPersistedFanout(documentName: string): void {\n if (\n this.fanoutDestroyed\n || this.activeFanouts.has(documentName)\n || this.fanoutRetryTimers.has(documentName)\n || !this.pendingFanouts.has(documentName)\n ) return\n\n this.activeFanouts.add(documentName)\n void this.drainPersistedFanout(documentName).finally(() => {\n this.activeFanouts.delete(documentName)\n if (\n !this.fanoutDestroyed\n && this.pendingFanouts.has(documentName)\n && !this.fanoutRetryTimers.has(documentName)\n ) {\n this.startPersistedFanout(documentName)\n }\n })\n }\n\n private async drainPersistedFanout(documentName: string): Promise<void> {\n const pending = this.pendingFanouts.get(documentName)\n if (!pending || this.fanoutDestroyed) return\n\n try {\n await this.publishRedisMessage(\n documentName,\n pending.message,\n this.fanoutPublisher,\n pending.collaborationGeneration,\n )\n } catch (error) {\n if (this.fanoutDestroyed || !this.pendingFanouts.has(documentName)) return\n const attempt = (this.fanoutRetryAttempts.get(documentName) ?? 0) + 1\n this.fanoutRetryAttempts.set(documentName, attempt)\n const delay = Math.min(\n DOCUMENTS_COLLAB_REDIS_FANOUT_RETRY_MAX_MS,\n DOCUMENTS_COLLAB_REDIS_FANOUT_RETRY_MIN_MS * 2 ** Math.min(attempt - 1, 8),\n )\n logger.warn('durable Redis collaboration fanout failed; retrying latest state', {\n room: documentName,\n attempt,\n error: error instanceof Error ? error.message : String(error),\n })\n const retryTimer = setTimeout(() => {\n this.fanoutRetryTimers.delete(documentName)\n this.startPersistedFanout(documentName)\n }, delay)\n retryTimer.unref?.()\n this.fanoutRetryTimers.set(documentName, retryTimer)\n return\n }\n\n this.fanoutRetryAttempts.delete(documentName)\n if (this.pendingFanouts.get(documentName)?.revision === pending.revision) {\n this.removePendingFanoutEntry(documentName)\n }\n }\n\n private redisKey(documentName: string): string {\n return `${this.configuration.prefix}:${documentName}`\n }\n\n private encodeRedisMessage(message: Uint8Array): Buffer {\n return Buffer.concat([this.messagePrefix, Buffer.from(message)])\n }\n\n private encodeDurableFanoutMessage(\n message: Uint8Array,\n collaborationGeneration: number,\n ): Buffer {\n if (\n !Number.isSafeInteger(collaborationGeneration)\n || collaborationGeneration < 1\n ) {\n throw new Error('[internal] documents collab: invalid Redis fanout generation')\n }\n const generation = Buffer.allocUnsafe(DOCUMENTS_COLLAB_REDIS_FANOUT_GENERATION_BYTES)\n generation.writeBigUInt64BE(BigInt(collaborationGeneration))\n return Buffer.concat([\n this.messagePrefix,\n DOCUMENTS_COLLAB_REDIS_FANOUT_MAGIC,\n generation,\n Buffer.from(message),\n ])\n }\n\n private async publishRedisMessage(\n documentName: string,\n message: Uint8Array,\n publisher: DocumentsCollabRedisFanoutPublisher | Pick<DocumentsCollabRedisFanoutPublisher, 'publish'> = this.pub,\n collaborationGeneration?: number,\n ): Promise<unknown> {\n return await publisher.publish(\n this.redisKey(documentName),\n collaborationGeneration === undefined\n ? this.encodeRedisMessage(message)\n : this.encodeDurableFanoutMessage(message, collaborationGeneration),\n )\n }\n\n private bufferDurableFanout(\n sender: string,\n documentName: string,\n document: Y.Doc,\n collaborationGeneration: number,\n update: Uint8Array,\n ): void {\n const buffered = this.bufferedFanouts.get(document) ?? new Map()\n const previous = buffered.get(sender)\n const bufferedBytes = (this.bufferedFanoutBytes.get(document) ?? 0)\n - (previous?.update.byteLength ?? 0)\n + update.byteLength\n if (\n (!previous && buffered.size >= DOCUMENTS_COLLAB_MAX_PENDING_MESSAGES)\n || bufferedBytes > DOCUMENTS_COLLAB_MAX_PENDING_BYTES\n ) {\n this.bufferedFanouts.delete(document)\n this.bufferedFanoutBytes.delete(document)\n this.onRejectedAggregate?.(documentName, document)\n logger.warn('rejected buffered Redis collaboration updates that exceeded load limits', {\n room: documentName,\n })\n return\n }\n buffered.set(sender, {\n collaborationGeneration,\n update: Uint8Array.from(update),\n })\n this.bufferedFanouts.set(document, buffered)\n this.bufferedFanoutBytes.set(document, bufferedBytes)\n }\n\n private applyDurableFanout(\n sender: string,\n documentName: string,\n document: Y.Doc,\n collaborationGeneration: number,\n update: Uint8Array,\n ): void {\n const loadedGeneration = this.resolveCollaborationGeneration?.(document)\n if (loadedGeneration === undefined) {\n this.bufferDurableFanout(\n sender,\n documentName,\n document,\n collaborationGeneration,\n update,\n )\n return\n }\n if (loadedGeneration !== collaborationGeneration) {\n logger.warn('rejected stale Redis collaboration update from another generation', {\n room: documentName,\n expectedGeneration: loadedGeneration,\n receivedGeneration: collaborationGeneration,\n })\n return\n }\n\n let aggregateByteLength: number\n try {\n aggregateByteLength = assertDocumentsCollabRedisAggregateUpdate(document, update)\n } catch (error) {\n if (isCrudHttpError(error) && error.status === 413) {\n this.onRejectedAggregate?.(documentName, document)\n logger.warn('rejected Redis collaboration update that exceeded room limits', {\n room: documentName,\n })\n return\n }\n logger.warn('rejected malformed Redis collaboration update', {\n room: documentName,\n error: error instanceof Error ? error.message : String(error),\n })\n return\n }\n // Refresh the receiver's aggregate budget before Yjs forwards the accepted\n // update to local connections.\n this.onAcceptedAggregate?.(document, aggregateByteLength)\n Y.applyUpdate(document, update, this.redisTransactionOrigin)\n }\n\n private applyBufferedFanouts(documentName: string, document: Y.Doc): void {\n const buffered = this.bufferedFanouts.get(document)\n this.bufferedFanouts.delete(document)\n this.bufferedFanoutBytes.delete(document)\n if (!buffered) return\n const loadedGeneration = this.resolveCollaborationGeneration?.(document)\n if (loadedGeneration === undefined) {\n this.onRejectedAggregate?.(documentName, document)\n throw new Error('[internal] documents collab: loaded room has no collaboration generation')\n }\n for (const [sender, fanout] of buffered) {\n this.applyDurableFanout(\n sender,\n documentName,\n document,\n fanout.collaborationGeneration,\n fanout.update,\n )\n }\n }\n\n private readonly handleDocumentsRedisMessage = async (\n _channel: Buffer,\n data: Buffer,\n ): Promise<void> => {\n const identifierLength = data[0]\n if (identifierLength === undefined || data.byteLength <= identifierLength + 1) return\n const identifier = data.toString('utf8', 1, identifierLength + 1)\n if (identifier === this.configuration.identifier) return\n\n const redisPayload = data.subarray(identifierLength + 1)\n let collaborationGeneration: number | undefined\n let messageBuffer = redisPayload\n if (\n redisPayload.byteLength\n >= DOCUMENTS_COLLAB_REDIS_FANOUT_MAGIC.byteLength\n + DOCUMENTS_COLLAB_REDIS_FANOUT_GENERATION_BYTES\n && redisPayload.subarray(0, DOCUMENTS_COLLAB_REDIS_FANOUT_MAGIC.byteLength)\n .equals(DOCUMENTS_COLLAB_REDIS_FANOUT_MAGIC)\n ) {\n const generationOffset = DOCUMENTS_COLLAB_REDIS_FANOUT_MAGIC.byteLength\n const encodedGeneration = redisPayload.readBigUInt64BE(generationOffset)\n if (encodedGeneration > BigInt(Number.MAX_SAFE_INTEGER) || encodedGeneration < 1n) return\n collaborationGeneration = Number(encodedGeneration)\n messageBuffer = redisPayload.subarray(\n generationOffset + DOCUMENTS_COLLAB_REDIS_FANOUT_GENERATION_BYTES,\n )\n }\n const header = new HocuspocusIncomingMessage(messageBuffer)\n const documentName = header.readVarString()\n const document = this.instance.documents.get(documentName)\n ?? this.knownDocuments.get(documentName)\n if (!document) return\n\n const messageType = header.readVarUint()\n if (messageType === MessageType.Sync || messageType === MessageType.SyncReply) {\n // The Documents protocol accepts only complete, post-persistence update\n // frames. Ignore state vectors/step-two replies so no peer can ask this\n // replica to reveal transient browser-authored room state.\n const syncType = header.readVarUint()\n if (messageType !== MessageType.Sync || syncType !== 2) return\n if (collaborationGeneration === undefined) {\n logger.warn('rejected unversioned Redis collaboration update', { room: documentName })\n return\n }\n const update = header.readVarUint8Array()\n this.applyDurableFanout(\n identifier,\n documentName,\n document,\n collaborationGeneration,\n update,\n )\n return\n }\n\n const receiverMessage = new HocuspocusIncomingMessage(messageBuffer)\n receiverMessage.readVarString()\n receiverMessage.writeVarString(documentName)\n const receiver = new HocuspocusMessageReceiver(\n receiverMessage,\n this.redisTransactionOrigin,\n )\n await receiver.apply(document, undefined, (reply) => {\n void this.publishRedisMessage(documentName, reply).catch((error: unknown) => {\n logger.warn('failed to publish Redis collaboration reply', {\n room: documentName,\n error: error instanceof Error ? error.message : String(error),\n })\n })\n })\n }\n\n private async flushPendingFanoutsBeforeDestroy(): Promise<void> {\n const pending = [...this.pendingFanouts.entries()]\n if (pending.length === 0) return\n\n const attempts = Promise.allSettled(pending.map(async ([documentName, fanout]) => {\n await this.publishRedisMessage(\n documentName,\n fanout.message,\n this.fanoutPublisher,\n fanout.collaborationGeneration,\n )\n }))\n let timeout: ReturnType<typeof setTimeout> | null = null\n const deadline = new Promise<null>((resolve) => {\n timeout = setTimeout(() => resolve(null), DOCUMENTS_COLLAB_REDIS_FANOUT_SHUTDOWN_TIMEOUT_MS)\n timeout.unref?.()\n })\n const results = await Promise.race([attempts, deadline])\n if (timeout) clearTimeout(timeout)\n\n if (results === null) {\n logger.warn('durable Redis collaboration fanout did not drain before shutdown', {\n pending: pending.length,\n })\n return\n }\n const failed = results.filter((result) => result.status === 'rejected').length\n if (failed > 0) {\n logger.warn('durable Redis collaboration fanout failed during shutdown', {\n pending: pending.length,\n failed,\n })\n }\n }\n\n override async onDestroy(): Promise<void> {\n this.fanoutDestroyed = true\n for (const retryTimer of this.fanoutRetryTimers.values()) clearTimeout(retryTimer)\n this.fanoutRetryTimers.clear()\n const pendingLocalDelays = [...this.pendingLocalAfterStoreDelays.values()]\n this.pendingLocalAfterStoreDelays.clear()\n for (const pending of pendingLocalDelays) {\n clearTimeout(pending.timeout)\n pending.resolve()\n }\n try {\n await this.flushPendingFanoutsBeforeDestroy()\n } finally {\n this.pendingFanouts.clear()\n this.pendingFanoutBytes = 0\n this.fanoutRetryAttempts.clear()\n this.fanoutPublisher.disconnect(false)\n // The stock extension calls Redlock.quit(), which can wait forever when\n // its shared client uses maxRetriesPerRequest=null during a Redis outage.\n // Shutdown has already made every room inert and attempted a bounded\n // durable drain, so force-close the transport clients instead.\n this.pub.disconnect(false)\n this.sub.disconnect(false)\n }\n }\n}\n\n/**\n * The Redis extension replicates updates and awareness across sidecar\n * replicas. It is activated only when a Redis URL is explicitly configured:\n * defaulting to a localhost instance could silently attach the sidecar to an\n * unrelated Redis, and failing hard would block valid single-node\n * deployments. Without Redis the sidecar runs in single-node mode and logs a\n * prominent startup warning, because multi-instance deployments require\n * Redis for cross-instance document sync.\n */\nexport function resolveDocumentsCollabRedisExtensions<RedisExtension>(\n environment: NodeJS.ProcessEnv,\n createRedisExtension: (configuration: DocumentsCollabRedisConfiguration) => RedisExtension,\n): RedisExtension[] {\n const configuration = resolveDocumentsCollabRedisConfiguration(environment)\n if (!configuration) {\n logger.warn(\n 'DOCUMENTS_COLLAB_REDIS_URL and REDIS_URL are unset; '\n + 'running in single-node mode. Multi-instance deployments require Redis '\n + 'for cross-instance document sync.',\n )\n return []\n }\n return [createRedisExtension(configuration)]\n}\n\nexport type CollabFinalDrainConsumeResult =\n | 'unmarked'\n | 'busy'\n | 'connected'\n | 'failed'\n | 'consumed'\n\nexport type CollabAuthorizationTicket = {\n readonly documentName: string\n readonly epoch: number\n readonly state: object\n}\n\nexport type CollabFinalDrainRegistry = {\n /**\n * Mark the exact trusted in-memory room before its sockets are closed, but\n * only when it currently has at least one live logical connection.\n */\n mark: (document: Y.Doc, readiness: Promise<void>) => boolean\n /** Read-only identity check used to keep reconnects out until the drain starts. */\n isMarked: (document: Y.Doc) => boolean\n /**\n * Consume the mark once, but only after every captured connection queue has\n * drained and the room has no live connections.\n */\n consume: (document: Y.Doc) => Promise<CollabFinalDrainConsumeResult>\n /** Seal the old room identity after durable success; unmapping releases it via WeakMap. */\n complete: (document: Y.Doc) => void\n /** Permanently withdraw a pending exception when another guard wins. */\n discard: (document: Y.Doc) => void\n /** Begin a bounded auth ticket for one currently authenticating scoped document. */\n beginAuthorization: (\n documentName: string,\n scope?: { tenantId: string; organizationId: string },\n ) => CollabAuthorizationTicket\n /** Advance only in-flight tickets for this exact document and scope. */\n bumpAuthorization: (\n documentName: string,\n scope?: { tenantId: string; organizationId: string },\n ) => void\n /** Verify no trusted access event crossed any authentication await. */\n isAuthorizationCurrent: (ticket: CollabAuthorizationTicket) => boolean\n /** Release the ticket and delete its document state when the last auth ends. */\n endAuthorization: (ticket: CollabAuthorizationTicket) => void\n}\n\ntype ConnectionCountedYDoc = Y.Doc & {\n getConnectionsCount?: () => unknown\n getConnections?: () => unknown\n}\n\ntype PendingMessagesConnection = {\n waitForPendingMessages?: () => unknown\n}\n\ntype CollabFinalDrainState = {\n /** Resolves false when any captured queue cannot be proven drained. */\n readiness: Promise<boolean>\n /** Only the caller that installs this promise may receive the drain grant. */\n consuming?: Promise<CollabFinalDrainConsumeResult>\n /** The one store invocation allowed to finish the final durable write. */\n granted?: boolean\n /** Durable success seals this old room identity until Hocuspocus unmaps it. */\n completed?: boolean\n}\n\ntype CollabAuthorizationState = {\n active: number\n epoch: number\n}\n\ntype InternalCollabAuthorizationTicket = CollabAuthorizationTicket & {\n expiry?: ReturnType<typeof setTimeout>\n released: boolean\n stateKey: string\n}\n\nfunction collabAuthorizationStateKey(\n documentName: string,\n scope?: { tenantId: string; organizationId: string },\n): string {\n return scope\n ? `${documentName}\\u0000${scope.tenantId}\\u0000${scope.organizationId}`\n : documentName\n}\n\nfunction liveCollabConnectionCount(document: Y.Doc): number | null {\n const getConnectionsCount = (document as ConnectionCountedYDoc).getConnectionsCount\n if (typeof getConnectionsCount !== 'function') return null\n try {\n const count = getConnectionsCount.call(document)\n return typeof count === 'number' && Number.isSafeInteger(count) && count >= 0\n ? count\n : null\n } catch {\n return null\n }\n}\n\nfunction captureCollabPendingMessageReadiness(document: Y.Doc): Promise<void> | null {\n const getConnections = (document as ConnectionCountedYDoc).getConnections\n if (typeof getConnections !== 'function') return null\n\n let connections: unknown\n try {\n connections = getConnections.call(document)\n } catch {\n return null\n }\n if (!Array.isArray(connections) || connections.length === 0) return null\n\n // Capture the public queue promises synchronously, while the exact\n // connections are still registered and before closeConnections removes\n // them from the room. A missing/throwing/non-Promise readiness contract is\n // represented by a rejection so the registry can fail the drain closed.\n const pending = connections.map((candidate) => {\n const connection = candidate as PendingMessagesConnection | null\n if (typeof connection?.waitForPendingMessages !== 'function') {\n return Promise.reject(new Error(\n '[internal] documents collab: connection queue readiness is unavailable',\n ))\n }\n try {\n const readiness = connection.waitForPendingMessages()\n if (\n !readiness\n || (typeof readiness !== 'object' && typeof readiness !== 'function')\n || typeof (readiness as PromiseLike<unknown>).then !== 'function'\n ) {\n return Promise.reject(new Error(\n '[internal] documents collab: connection queue readiness is invalid',\n ))\n }\n return Promise.resolve(readiness)\n } catch (error) {\n return Promise.reject(error)\n }\n })\n\n return Promise.all(pending).then(() => undefined)\n}\n\n/**\n * One-shot, room-identity-bound exception for draining edits accepted before\n * a trusted share-change event closed the room. Unknown connection state is\n * deliberately treated as connected. No timer can make a marked room become\n * eligible: every exact pre-close connection queue must drain and the live\n * connection count must then be zero.\n */\nexport function createCollabFinalDrainRegistry(\n resolveLiveConnections: (document: Y.Doc) => number | null = liveCollabConnectionCount,\n): CollabFinalDrainRegistry {\n const markedDocuments = new WeakMap<Y.Doc, CollabFinalDrainState>()\n const authorizationStates = new Map<string, CollabAuthorizationState>()\n const releaseAuthorization = (ticket: InternalCollabAuthorizationTicket): void => {\n if (ticket.released) return\n ticket.released = true\n if (ticket.expiry) clearTimeout(ticket.expiry)\n const state = authorizationStates.get(ticket.stateKey)\n if (state !== ticket.state) return\n state.active = Math.max(0, state.active - 1)\n if (state.active === 0) authorizationStates.delete(ticket.stateKey)\n }\n return {\n mark(document, readiness) {\n const guardedReadiness = readiness.then(\n () => true,\n () => false,\n )\n if ((resolveLiveConnections(document) ?? 0) <= 0) return false\n if (markedDocuments.has(document)) return true\n markedDocuments.set(document, {\n readiness: guardedReadiness,\n })\n return true\n },\n isMarked(document) {\n return markedDocuments.has(document)\n },\n async consume(document) {\n const state = markedDocuments.get(document)\n if (!state) return 'unmarked'\n // A non-owner store must not invalidate or release the room while the\n // owner is waiting for captured queues or completing its durable write.\n if (state.consuming || state.granted) return 'busy'\n\n const consuming = (async (): Promise<CollabFinalDrainConsumeResult> => {\n const ready = await state.readiness\n if (markedDocuments.get(document) !== state) return 'unmarked'\n\n // Every terminal outcome stays marked until its caller synchronously\n // installs invalidation or Hocuspocus unmaps the successfully drained\n // old Y.Doc. Deleting here would create a microtask gap in which an\n // in-flight authentication could enter before invalidation is visible.\n if (!ready) return 'failed'\n if (resolveLiveConnections(document) !== 0) return 'connected'\n state.granted = true\n return 'consumed'\n })()\n state.consuming = consuming\n return consuming\n },\n complete(document) {\n const state = markedDocuments.get(document)\n if (state?.granted) state.completed = true\n },\n discard(document) {\n markedDocuments.delete(document)\n },\n beginAuthorization(documentName, scope) {\n const stateKey = collabAuthorizationStateKey(documentName, scope)\n let state = authorizationStates.get(stateKey)\n if (!state) {\n state = { active: 0, epoch: 0 }\n authorizationStates.set(stateKey, state)\n }\n state.active += 1\n const ticket: InternalCollabAuthorizationTicket = {\n documentName,\n epoch: state.epoch,\n released: false,\n stateKey,\n state,\n }\n ticket.expiry = setTimeout(() => {\n releaseAuthorization(ticket)\n }, DOCUMENTS_COLLAB_AUTHORIZATION_TICKET_TIMEOUT_MS)\n ticket.expiry.unref?.()\n return ticket\n },\n bumpAuthorization(documentName, scope) {\n const state = authorizationStates.get(collabAuthorizationStateKey(documentName, scope))\n if (state) state.epoch += 1\n },\n isAuthorizationCurrent(ticket) {\n const internalTicket = ticket as InternalCollabAuthorizationTicket\n const state = authorizationStates.get(internalTicket.stateKey)\n return !internalTicket.released\n && state === ticket.state\n && state.epoch === ticket.epoch\n },\n endAuthorization(ticket) {\n releaseAuthorization(ticket as InternalCollabAuthorizationTicket)\n },\n }\n}\n\n/**\n * Prepare the current room for a trusted share-event close. Direct-only or\n * already-disconnected rooms are deliberately left unmarked because\n * closeConnections cannot produce a last-websocket store for them.\n */\nexport function markCollabFinalDrainForReauth(\n document: Y.Doc,\n registry: CollabFinalDrainRegistry,\n): boolean {\n const readiness = captureCollabPendingMessageReadiness(document)\n if (!readiness) return false\n return registry.mark(document, readiness)\n}\n\ntype CollabIngressLimits = {\n maxPendingBytes: number\n maxPendingMessages: number\n}\n\ntype CollabIngressState = {\n blocked: boolean\n pendingBytes: number\n pendingMessages: number\n}\n\ntype BoundedCollabConnection = Pick<\n HocuspocusConnection<CollabContext>,\n 'close' | 'handleMessage' | 'waitForPendingMessages' | 'webSocket'\n>\n\nconst collabIngressStates = new WeakMap<object, CollabIngressState>()\nconst boundedCollabConnections = new WeakSet<object>()\nconst guardedCollabConnectionPrototypes = new WeakSet<object>()\n\nfunction closeCollabIngress(\n connection: BoundedCollabConnection,\n state: CollabIngressState,\n): void {\n if (state.blocked) return\n state.blocked = true\n try {\n connection.close()\n } catch {\n // The transport close below is authoritative even if the logical room was\n // concurrently removed.\n }\n try {\n connection.webSocket.close(1009, 'Collaboration ingress limit exceeded')\n } catch {\n // A concurrently closed socket is already in the required terminal state.\n }\n}\n\nfunction handleBoundedCollabIngress(\n connection: BoundedCollabConnection,\n data: Uint8Array,\n handleMessage: (input: Uint8Array) => void,\n limits: CollabIngressLimits,\n): void {\n const socketKey = connection.webSocket as object\n let state = collabIngressStates.get(socketKey)\n if (!state) {\n state = { blocked: false, pendingBytes: 0, pendingMessages: 0 }\n collabIngressStates.set(socketKey, state)\n }\n if (state.blocked) return\n\n const pendingBytes = state.pendingBytes + data.byteLength\n const pendingMessages = state.pendingMessages + 1\n if (\n pendingBytes > limits.maxPendingBytes\n || pendingMessages > limits.maxPendingMessages\n ) {\n closeCollabIngress(connection, state)\n return\n }\n\n state.pendingBytes = pendingBytes\n state.pendingMessages = pendingMessages\n let released = false\n const release = () => {\n if (released) return\n released = true\n state.pendingBytes = Math.max(0, state.pendingBytes - data.byteLength)\n state.pendingMessages = Math.max(0, state.pendingMessages - 1)\n }\n\n try {\n handleMessage(data)\n void connection.waitForPendingMessages().then(release, release)\n } catch (error) {\n release()\n throw error\n }\n}\n\n/**\n * Bound Hocuspocus' otherwise-unbounded authenticated message queue.\n *\n * The state is keyed by the physical socket, not the logical document\n * connection, because one provider can multiplex several documents. Every\n * accepted frame stays charged until Hocuspocus reports that its serial queue\n * drained, including frames rejected by a later hook.\n */\nexport function installBoundedCollabIngress(\n connection: BoundedCollabConnection,\n limits: CollabIngressLimits = {\n maxPendingBytes: DOCUMENTS_COLLAB_MAX_PENDING_BYTES,\n maxPendingMessages: DOCUMENTS_COLLAB_MAX_PENDING_MESSAGES,\n },\n): void {\n const connectionKey = connection as object\n if (boundedCollabConnections.has(connectionKey)) return\n boundedCollabConnections.add(connectionKey)\n\n const handleMessage = connection.handleMessage.bind(connection)\n connection.handleMessage = (data: Uint8Array): void => {\n handleBoundedCollabIngress(connection, data, handleMessage, limits)\n }\n}\n\ntype HocuspocusConnectionClass = {\n prototype: BoundedCollabConnection\n}\n\n/**\n * Guard every logical Hocuspocus connection before the server can drain its\n * pre-authentication queue. Hocuspocus invokes `connected` only after that\n * synchronous drain, so installing the guard from the hook would miss the\n * oldest (and usually largest) retained batch.\n */\nexport function installHocuspocusCollabIngressGuard(\n ConnectionClass: HocuspocusConnectionClass = HocuspocusConnection,\n): void {\n const prototype = ConnectionClass.prototype\n if (guardedCollabConnectionPrototypes.has(prototype as object)) return\n guardedCollabConnectionPrototypes.add(prototype as object)\n\n const handleMessage = prototype.handleMessage\n prototype.handleMessage = function guardedHandleMessage(\n this: BoundedCollabConnection,\n data: Uint8Array,\n ): void {\n if (boundedCollabConnections.has(this as object)) {\n handleMessage.call(this, data)\n return\n }\n handleBoundedCollabIngress(\n this,\n data,\n (input) => handleMessage.call(this, input),\n {\n maxPendingBytes: DOCUMENTS_COLLAB_MAX_PENDING_BYTES,\n maxPendingMessages: DOCUMENTS_COLLAB_MAX_PENDING_MESSAGES,\n },\n )\n }\n}\n\n/** Reject expensive control/read-only frames before Hocuspocus decodes Yjs/JSON payloads. */\nexport function assertCollabInboundFramePolicy(\n update: Uint8Array,\n options: { readOnly: boolean },\n): void {\n if (update.byteLength > DOCUMENTS_COLLAB_MAX_PAYLOAD_BYTES) {\n throw new Error('[internal] documents collab: frame exceeds transport limit')\n }\n\n let messageType: number\n try {\n const message = new HocuspocusIncomingMessage(update)\n message.readVarString()\n messageType = message.readVarUint()\n } catch {\n throw new Error('[internal] documents collab: malformed frame envelope')\n }\n\n const isSync = messageType === MessageType.Sync || messageType === MessageType.SyncReply\n if (\n messageType === MessageType.Awareness\n && update.byteLength > DOCUMENTS_COLLAB_MAX_AWARENESS_FRAME_BYTES\n ) {\n throw new Error('[internal] documents collab: awareness frame exceeds limit')\n }\n if (\n !isSync\n && update.byteLength > DOCUMENTS_COLLAB_MAX_CONTROL_FRAME_BYTES\n ) {\n throw new Error('[internal] documents collab: control frame exceeds limit')\n }\n if (\n options.readOnly\n && isSync\n && update.byteLength > DOCUMENTS_COLLAB_MAX_READ_ONLY_SYNC_FRAME_BYTES\n ) {\n throw new Error('[internal] documents collab: read-only sync frame exceeds limit')\n }\n}\n\nexport type CollabConnection = { readOnly: boolean }\ntype CollabTier = CollabTokenClaims['tier']\nexport type CollabContext = {\n userId: string\n tenantId: string\n organizationId: string\n documentId: string\n tier: CollabTier\n readOnly: boolean\n exp: number | null\n awarenessUser?: CanonicalCollaborationAwarenessUser\n}\nconst COLLAB_AUTHORIZATION_LIFECYCLE = Symbol('documents.collab.authorization-lifecycle')\ntype CollabAuthorizationLifecycle = {\n established: boolean\n ticket?: CollabAuthorizationTicket\n}\ntype TicketedCollabContext = CollabContext & {\n [COLLAB_AUTHORIZATION_LIFECYCLE]?: CollabAuthorizationLifecycle\n}\ntype CollabScope = { tenantId: string; organizationId: string }\ntype CollabContainer = { resolve: (name: string) => unknown }\ntype CollabAcl = {\n isSuperAdmin: boolean\n features: string[]\n organizations: string[] | null\n}\ntype CollabRbacService = {\n invalidateUserCache: (userId: string) => Promise<void>\n loadAcl: (\n userId: string,\n scope: { tenantId: string | null; organizationId: string | null },\n ) => Promise<CollabAcl>\n}\n\nasync function loadFreshCollabAcl(\n rbacService: CollabRbacService,\n userId: string,\n scope: { tenantId: string | null; organizationId: string | null },\n): Promise<CollabAcl | null> {\n if (\n typeof rbacService?.invalidateUserCache !== 'function'\n || typeof rbacService.loadAcl !== 'function'\n ) {\n return null\n }\n await rbacService.invalidateUserCache(userId)\n return rbacService.loadAcl(userId, scope)\n}\nexport type CollabHooksDeps = {\n verifyToken?: (token: string) => CollabTokenClaims | null\n verifyTokenV2?: (token: string) => VerifiedCollabTokenV2Claims | null\n authorizeContext: (context: CollabContext) => Promise<boolean>\n resolveAwarenessName?: (context: CollabContext) => Promise<unknown>\n resolveContainer: () => Promise<CollabContainer>\n loadContent: (\n em: unknown,\n documentId: string,\n scope: CollabScope,\n ) => Promise<{\n yjsState: Buffer | null\n contentHtml: string | null\n updatedAt: string | Date\n collaborationGeneration: number\n } | null>\n loadCollaborationGeneration?: (\n em: unknown,\n documentId: string,\n scope: CollabScope,\n ) => Promise<number | null>\n initializeYjsState: (\n em: unknown,\n documentId: string,\n scope: CollabScope,\n ) => Promise<Buffer | null>\n persistContent: (\n em: unknown,\n documentId: string,\n scope: CollabScope,\n input: { yjsState: Buffer; contentHtml?: string | null; contentText?: string | null },\n deps: {\n searchIndexer: unknown\n expectedUpdatedAt: string\n expectedCollaborationGeneration: number\n requireExpectedVersion: true\n },\n ) => Promise<{ updatedAt: string | Date; collaborationGeneration: number }>\n /** Notify the replication layer only after this exact generation/state is durable. */\n onPersisted?: (\n document: Y.Doc,\n yjsState: Uint8Array,\n collaborationGeneration: number,\n ) => void\n allowedOrigins?: string[] | null\n /** Require both an Origin header and a configured exact-match trusted origin. */\n requireOrigin?: boolean\n isRoomInvalidated?: (documentName: string, document?: Y.Doc) => boolean\n invalidateRoom?: (documentName: string, document: Y.Doc) => void\n finalDrainRegistry?: CollabFinalDrainRegistry\n /** Resolve only the room identity currently mapped by Hocuspocus. */\n resolveRoomDocument?: (documentName: string) => Y.Doc | undefined\n /** @deprecated Use isRoomInvalidated. Kept for extension compatibility. */\n isRoomClosing?: (documentName: string, document: Y.Doc) => boolean\n}\n\nexport type CollabExpiryConnection = {\n close: () => void\n onClose: (callback: () => void) => unknown\n}\n\nexport type CollabHealthRequest = Pick<IncomingMessage, 'method' | 'url'>\nexport type CollabHealthResponse = Pick<ServerResponse, 'statusCode' | 'setHeader' | 'end'>\n\ntype CollabAwarenessOwnership = {\n ownedClientIds: ReadonlySet<number>\n occupiedClientIds: ReadonlySet<number>\n /** All client ids admitted during this websocket connection's lifetime. */\n claimedClientIds?: Set<number>\n /** Stable room-lifetime binding that prevents a different actor recycling an id. */\n roomClientOwners?: Map<number, string>\n}\n\ntype RequestHeaders = Record<string, string | string[] | undefined>\n\nfunction readHeader(headers: RequestHeaders | undefined, name: string): string | undefined {\n const value = headers?.[name] ?? headers?.[name.toLowerCase()]\n if (Array.isArray(value)) return value[0]\n return value\n}\n\nfunction normalizeTrustedOrigin(value: string): string | null {\n try {\n const url = new URL(value)\n return (url.protocol === 'http:' || url.protocol === 'https:') ? url.origin : null\n } catch {\n return null\n }\n}\n\nexport function resolveCollabAllowedOrigins(\n env: { DOCUMENTS_COLLAB_ALLOWED_ORIGINS?: string; APP_URL?: string; NEXT_PUBLIC_APP_URL?: string },\n): string[] {\n const candidates = [\n ...(env.DOCUMENTS_COLLAB_ALLOWED_ORIGINS ?? '').split(','),\n env.APP_URL ?? '',\n env.NEXT_PUBLIC_APP_URL ?? '',\n ]\n return Array.from(new Set(candidates\n .map((candidate) => candidate.trim())\n .filter(Boolean)\n .map(normalizeTrustedOrigin)\n .filter((origin): origin is string => Boolean(origin))))\n}\n\nexport function isCollabRequestOriginAllowed(input: {\n origin?: string\n allowedOrigins?: string[] | null\n requireOrigin: boolean\n}): boolean {\n if (!input.origin) return !input.requireOrigin\n const origin = normalizeTrustedOrigin(input.origin)\n if (!origin) return false\n const allowedOrigins = input.allowedOrigins ?? []\n if (allowedOrigins.length === 0) return !input.requireOrigin\n return allowedOrigins.includes(origin)\n}\n\nfunction assertScopedContext(context: CollabContext | null | undefined): asserts context is CollabContext {\n if (!context?.tenantId || !context.organizationId) {\n throw new Error('[internal] documents collab: missing tenant scope')\n }\n}\n\nfunction normalizeContentVersion(value: string | Date | null | undefined): string | null {\n if (value instanceof Date) {\n return Number.isFinite(value.getTime()) ? value.toISOString() : null\n }\n if (typeof value !== 'string' || !value.trim()) return null\n const parsed = new Date(value)\n return Number.isFinite(parsed.getTime()) ? parsed.toISOString() : null\n}\n\nfunction isReadOnlyTier(tier: CollabTier): boolean {\n return tier === 'viewer' || tier === 'commenter'\n}\n\nfunction toContext(\n claims: CollabTokenClaims,\n readOnly: boolean,\n exp: number | null,\n): CollabContext {\n return {\n userId: claims.userId,\n tenantId: claims.tenantId,\n organizationId: claims.organizationId,\n documentId: claims.documentId,\n tier: claims.tier,\n readOnly,\n exp,\n }\n}\n\nfunction resolveCollabClaims(deps: CollabHooksDeps, token: string): CollabContext | null {\n const v2Claims = deps.verifyTokenV2?.(token) ?? null\n if (v2Claims) return toContext(v2Claims, v2Claims.readOnly, v2Claims.exp)\n\n const legacyClaims = deps.verifyToken?.(token) ?? null\n if (!legacyClaims) return null\n const exp = readLegacyTokenExpiration(token)\n return exp === null\n ? null\n : toContext(legacyClaims, isReadOnlyTier(legacyClaims.tier), exp)\n}\n\nexport function bindCollabAwarenessStates(\n context: CollabContext | null | undefined,\n states: Map<number, Record<string, unknown>>,\n ownership?: CollabAwarenessOwnership,\n): void {\n if (!context?.awarenessUser) {\n throw new Error('[internal] documents collab: awareness identity is not authenticated')\n }\n\n const admittedThisUpdate = new Set<number>()\n for (const [clientId, state] of states) {\n // Hocuspocus decodes the inbound update through a scratch Awareness whose\n // constructor creates one empty local state. Never turn that decoder-only\n // entry into a broadcast collaborator or retain it in the room.\n if (\n !state\n || typeof state !== 'object'\n || Array.isArray(state)\n || Object.keys(state).length === 0\n ) {\n states.delete(clientId)\n continue\n }\n if (\n !Number.isSafeInteger(clientId)\n || clientId < 0\n || clientId > MAX_YJS_CLIENT_ID\n || !isBoundedAwarenessState(state)\n ) {\n states.delete(clientId)\n continue\n }\n if (\n ownership?.occupiedClientIds.has(clientId)\n && !ownership.ownedClientIds.has(clientId)\n ) {\n // Hocuspocus providers echo awareness updates received from peers. An\n // occupied id owned by another socket is therefore either a harmless\n // echo or an attempted overwrite; in both cases it must be discarded,\n // never rebound to the sender or treated as a reason to disconnect it.\n states.delete(clientId)\n continue\n }\n\n const roomOwner = ownership?.roomClientOwners?.get(clientId)\n if (roomOwner !== undefined && roomOwner !== context.awarenessUser.id) {\n states.delete(clientId)\n continue\n }\n\n const knownToConnection = Boolean(\n ownership?.ownedClientIds.has(clientId)\n || ownership?.claimedClientIds?.has(clientId),\n )\n const connectionClientCount = new Set([\n ...(ownership?.ownedClientIds ?? []),\n ...(ownership?.claimedClientIds ?? []),\n ...admittedThisUpdate,\n ]).size\n if (\n !knownToConnection\n && connectionClientCount >= DOCUMENTS_COLLAB_MAX_AWARENESS_CLIENT_IDS_PER_CONNECTION\n ) {\n states.delete(clientId)\n continue\n }\n if (\n roomOwner === undefined\n && (ownership?.roomClientOwners?.size ?? 0)\n >= DOCUMENTS_COLLAB_MAX_AWARENESS_CLIENT_IDS_PER_ROOM\n ) {\n states.delete(clientId)\n continue\n }\n\n const cursor = sanitizeAwarenessCursor(state.cursor)\n const canonicalState: Record<string, unknown> = {\n user: { ...context.awarenessUser },\n }\n if (cursor) canonicalState.cursor = cursor\n states.set(clientId, canonicalState)\n admittedThisUpdate.add(clientId)\n ownership?.claimedClientIds?.add(clientId)\n if (roomOwner === undefined) {\n ownership?.roomClientOwners?.set(clientId, context.awarenessUser.id)\n }\n }\n}\n\nfunction isBoundedAwarenessState(state: Record<string, unknown>): boolean {\n try {\n const serialized = JSON.stringify(state)\n return typeof serialized === 'string'\n && Buffer.byteLength(serialized, 'utf8') <= DOCUMENTS_COLLAB_MAX_AWARENESS_STATE_BYTES\n } catch {\n return false\n }\n}\n\ntype SanitizedAwarenessPosition = {\n assoc: number\n item?: { client: number; clock: number }\n tname?: string\n type?: { client: number; clock: number }\n}\n\nfunction sanitizeAwarenessId(value: unknown): { client: number; clock: number } | null {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return null\n const record = value as Record<string, unknown>\n if (\n !Number.isSafeInteger(record.client)\n || (record.client as number) < 0\n || (record.client as number) > MAX_YJS_CLIENT_ID\n || !Number.isSafeInteger(record.clock)\n || (record.clock as number) < 0\n ) {\n return null\n }\n return { client: record.client as number, clock: record.clock as number }\n}\n\nfunction sanitizeAwarenessPosition(value: unknown): SanitizedAwarenessPosition | null {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return null\n const record = value as Record<string, unknown>\n const assoc = record.assoc === undefined ? 0 : record.assoc\n if (!Number.isSafeInteger(assoc) || (assoc as number) < -1 || (assoc as number) > 1) {\n return null\n }\n\n const item = sanitizeAwarenessId(record.item)\n if (item) return { item, assoc: assoc as number }\n if (typeof record.tname === 'string' && record.tname.length > 0 && record.tname.length <= 120) {\n return { tname: record.tname, assoc: assoc as number }\n }\n const type = sanitizeAwarenessId(record.type)\n return type ? { type, assoc: assoc as number } : null\n}\n\nfunction sanitizeAwarenessCursor(value: unknown): {\n anchor: SanitizedAwarenessPosition\n head: SanitizedAwarenessPosition\n} | null {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return null\n const record = value as Record<string, unknown>\n const anchor = sanitizeAwarenessPosition(record.anchor)\n const head = sanitizeAwarenessPosition(record.head)\n return anchor && head ? { anchor, head } : null\n}\n\nfunction readLegacyTokenExpiration(token: string): number | null {\n const encodedPayload = token.split('.')[1]\n if (!encodedPayload) return null\n try {\n const payload: unknown = JSON.parse(Buffer.from(encodedPayload, 'base64url').toString('utf8'))\n if (!payload || typeof payload !== 'object') return null\n const { exp, iat } = payload as Record<string, unknown>\n const now = Math.floor(Date.now() / 1000)\n if (\n typeof exp !== 'number'\n || !Number.isInteger(exp)\n || typeof iat !== 'number'\n || !Number.isInteger(iat)\n || exp <= now\n || exp <= iat\n || exp - iat > COLLAB_TOKEN_TTL_SECONDS\n || iat > now + COLLAB_TOKEN_CLOCK_SKEW_SECONDS\n || exp > now + COLLAB_TOKEN_TTL_SECONDS + COLLAB_TOKEN_CLOCK_SKEW_SECONDS\n ) {\n return null\n }\n return exp\n } catch {\n return null\n }\n}\n\nexport function scheduleCollabConnectionExpiry(\n connection: CollabExpiryConnection,\n exp: number,\n): () => void {\n let timer: ReturnType<typeof setTimeout> | null = null\n const clearExpiry = () => {\n if (timer === null) return\n clearTimeout(timer)\n timer = null\n }\n timer = setTimeout(() => {\n timer = null\n connection.close()\n }, Math.max(0, exp * 1000 - Date.now()))\n connection.onClose(clearExpiry)\n return clearExpiry\n}\n\n/**\n * Revalidate one active logical connection without retaining or scanning a\n * process-wide connection registry. Refreshes never overlap, and the timer is\n * released with the connection even when authorization infrastructure fails.\n */\nexport function scheduleCollabConnectionReauthorization(\n connection: CollabExpiryConnection,\n authorize: () => Promise<boolean>,\n intervalMs = DOCUMENTS_COLLAB_ACTIVE_REAUTHORIZATION_INTERVAL_MS,\n): () => void {\n let stopped = false\n let timer: ReturnType<typeof setTimeout> | null = null\n\n const stop = (): void => {\n stopped = true\n if (timer === null) return\n clearTimeout(timer)\n timer = null\n }\n const schedule = (): void => {\n if (stopped) return\n timer = setTimeout(() => {\n timer = null\n void (async () => {\n let authorized = false\n try {\n authorized = await authorize()\n } catch {\n authorized = false\n }\n if (stopped) return\n if (!authorized) {\n stop()\n connection.close()\n return\n }\n schedule()\n })()\n }, Math.max(1, intervalMs))\n timer.unref?.()\n }\n\n connection.onClose(stop)\n schedule()\n return stop\n}\n\nexport function handleCollabHealthRequest(\n request: CollabHealthRequest,\n response: CollabHealthResponse,\n): boolean {\n const pathname = new URL(request.url ?? '/', 'http://documents-collab.local').pathname\n if (pathname !== '/healthz') return false\n\n response.setHeader('Cache-Control', 'no-store')\n if (request.method !== 'GET') {\n response.statusCode = 405\n response.setHeader('Allow', 'GET')\n response.end()\n return true\n }\n\n const ready = isCollabTokenV2Ready()\n response.statusCode = ready ? 200 : 503\n response.setHeader('Content-Type', 'application/json')\n response.end(JSON.stringify({\n status: ready ? 'ok' : 'unavailable',\n service: 'documents-collab',\n capabilityTokenVersion: 2,\n }))\n return true\n}\n\nexport async function handleCollabServerRequest(input: {\n request: CollabHealthRequest\n response: CollabHealthResponse\n}): Promise<void> {\n if (handleCollabHealthRequest(input.request, input.response)) {\n return Promise.reject()\n }\n}\n\n/**\n * HTML-only legacy rows need one durable Yjs identity before they are sent to\n * a client. Hocuspocus installs its change listener after onLoadDocument, so a\n * conversion performed only in that hook is never scheduled for persistence.\n * A reconnect would then convert the same HTML with a new Yjs client ID and\n * merge duplicate content into the provider's retained local Y.Doc.\n *\n * The bootstrap is internal representation work, not a user edit: nativeUpdate\n * intentionally leaves updated_at unchanged and avoids a redundant reindex.\n */\nexport async function initializeDocumentYjsState(\n em: EntityManager,\n documentId: string,\n scope: CollabScope,\n): Promise<Buffer | null> {\n return em.transactional(async (transactionalEm) => {\n // Serialize legacy-content bootstrap behind the aggregate root. This also\n // lets two first-time sockets safely repair a pre-M6 document that has no\n // DocumentContent row without racing the document_id unique constraint.\n const document = await findOneWithDecryption(\n transactionalEm,\n Document,\n {\n id: documentId,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n },\n { lockMode: LockMode.PESSIMISTIC_WRITE },\n scope,\n )\n if (!document) return null\n\n let content = await findOneWithDecryption(\n transactionalEm,\n DocumentContent,\n {\n documentId,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n },\n { filters: false, lockMode: LockMode.PESSIMISTIC_WRITE },\n scope,\n )\n if (!content) {\n content = transactionalEm.create(DocumentContent, {\n id: randomUUID(),\n documentId,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n contentHtml: '',\n contentText: '',\n yjsState: null,\n collaborationGeneration: 1,\n deletedAt: null,\n updatedAt: new Date(),\n })\n transactionalEm.persist(content)\n await transactionalEm.flush()\n return null\n }\n if (content.deletedAt) {\n // A live document with a tombstoned content row represents a logically\n // blank body (for example an undo of its first legacy content write).\n // Repair the one-row invariant without reviving the tombstoned body.\n const now = new Date()\n content.yjsState = null\n content.contentHtml = ''\n content.contentText = ''\n content.deletedAt = null\n advanceDocumentCollaborationGeneration(content)\n content.updatedAt = now.getTime() > content.updatedAt.getTime()\n ? now\n : new Date(content.updatedAt.getTime() + 1)\n await transactionalEm.flush()\n }\n if (content.yjsState && content.yjsState.length > 0) {\n assertDocumentContentResourceLimits({\n yjsState: content.yjsState,\n contentHtml: content.contentHtml,\n contentText: content.contentText,\n })\n return Buffer.from(content.yjsState)\n }\n if (!content.contentHtml) return null\n\n assertDocumentContentResourceLimits({\n contentHtml: content.contentHtml,\n contentText: content.contentText,\n })\n const materialized = htmlToYDoc(content.contentHtml)\n const yjsState = Buffer.from(Y.encodeStateAsUpdate(materialized))\n assertDocumentContentResourceLimits({ yjsState, contentHtml: content.contentHtml })\n await transactionalEm.nativeUpdate(\n DocumentContent,\n {\n id: content.id,\n documentId,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n },\n { yjsState },\n )\n return yjsState\n })\n}\n\nexport type CollabAuthorizationSnapshot = {\n relationshipTier: CollabTier | null\n isSuperAdmin: boolean\n features: string[]\n organizations: string[] | null\n organizationScope?: ResolvedDocumentsOrganizationScope | null\n archived?: boolean\n}\n\nexport function isCollabAuthorizationCurrent(\n context: CollabContext,\n snapshot: CollabAuthorizationSnapshot,\n): boolean {\n if (!hasResolvedDocumentsOrganizationAccess(\n snapshot,\n context.organizationId,\n snapshot.organizationScope,\n )) {\n return false\n }\n\n const features = snapshot.isSuperAdmin ? ['*'] : snapshot.features\n const managerOverride = hasAllFeatures(['documents.manage'], features)\n const relationshipTier = managerOverride ? 'owner' : snapshot.relationshipTier\n const capabilities = deriveDocumentCapabilities({\n relationshipTier,\n managerOverride,\n archived: snapshot.archived === true,\n userFeatures: features,\n })\n\n return capabilities.canView\n && relationshipTier === context.tier\n && context.readOnly === !capabilities.canEdit\n}\n\n/**\n * Re-resolve the actor's current ACL and document relationship whenever a\n * socket authenticates. Share events close existing sockets; this check makes\n * replaying their still-signed short-lived token fail even after a sidecar\n * restart, and also rejects capability downgrades or role-share removal.\n */\nexport async function authorizeCollabContext(\n container: CollabContainer,\n context: CollabContext,\n): Promise<boolean> {\n try {\n const scope = {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n }\n const em = container.resolve('em') as EntityManager\n const rbacService = container.resolve('rbacService') as CollabRbacService\n let acl = await loadFreshCollabAcl(rbacService, context.userId, scope)\n if (!acl) return false\n\n let organizationScope: ResolvedDocumentsOrganizationScope | null = null\n if (!hasResolvedDocumentsOrganizationAccess(acl, context.organizationId)) {\n // Organization hierarchy resolution performs its own ACL read. Route it\n // through the same fail-closed fresh-load primitive so no secondary\n // lookup can revive a warm sidecar cache after a role/ACL revocation.\n const organizationScopeService = resolveOrganizationScopeService(container)\n if (!organizationScopeService) return false\n const freshAuthorization = await organizationScopeService.resolveFresh({\n auth: {\n sub: context.userId,\n userId: context.userId,\n tenantId: context.tenantId,\n orgId: context.organizationId,\n isSuperAdmin: false,\n },\n selectedId: context.organizationId,\n tenantId: context.tenantId,\n })\n organizationScope = freshAuthorization.scope\n acl = freshAuthorization.acl\n if (!hasResolvedDocumentsOrganizationAccess(\n acl,\n context.organizationId,\n organizationScope,\n )) {\n return false\n }\n }\n const features = acl.isSuperAdmin ? ['*'] : acl.features\n const managerOverride = hasAllFeatures(['documents.manage'], features)\n const relationshipTier = managerOverride\n ? 'owner'\n : await resolveUserAccess(\n em,\n context.documentId,\n scope,\n context.userId,\n container,\n )\n const documentRow = await findOneWithDecryption(\n em,\n Document,\n { id: context.documentId, tenantId: scope.tenantId, organizationId: scope.organizationId },\n { fields: ['id', 'archivedAt', 'deletedAt'], filters: false },\n scope,\n )\n if (!documentRow || documentRow.deletedAt) return false\n return isCollabAuthorizationCurrent(context, {\n relationshipTier,\n isSuperAdmin: acl.isSuperAdmin,\n features: acl.features,\n organizations: acl.organizations,\n organizationScope,\n archived: documentRow?.archivedAt != null,\n })\n } catch (error) {\n // Fail closed, but leave a trace: an RBAC/DB outage otherwise surfaces\n // only as an unexplained mass disconnect.\n logger.warn('collab authorization check failed; failing closed', {\n error: error instanceof Error ? error.message : String(error),\n })\n return false\n }\n}\n\nexport function createCollabHooks(deps: CollabHooksDeps) {\n const materializationWarningRooms = new Set<string>()\n const loadedContentVersions = new WeakMap<Y.Doc, string>()\n const loadedCollaborationGenerations = new WeakMap<Y.Doc, number>()\n const roomResourceBudgets = new WeakMap<Y.Doc, { bytes: number; revision: number }>()\n const roomScopes = new WeakMap<Y.Doc, CollabScope>()\n const awarenessConnectionClientIds = new WeakMap<object, Set<number>>()\n const awarenessRoomClientOwners = new WeakMap<Y.Doc, Map<number, string>>()\n // Hocuspocus hands onStoreDocument only the room's lastContext, which can be\n // a read-only viewer even when the retained edits were authored by an\n // editor. Remember the most recent writable authorization per room identity\n // so a debounced store can still persist under a freshly re-validated\n // writable context. The WeakMap releases the entry with the Y.Doc when\n // Hocuspocus unloads or destroys the room.\n const roomWritableContexts = new WeakMap<Y.Doc, CollabContext>()\n\n const rememberWritableContext = (\n document: Y.Doc | undefined,\n context: CollabContext | undefined,\n ): void => {\n if (!document || !context || context.readOnly) return\n roomWritableContexts.set(document, context)\n }\n\n const isInvalidated = (documentName: string, document?: Y.Doc): boolean => (\n deps.isRoomInvalidated?.(documentName, document)\n ?? (document ? deps.isRoomClosing?.(documentName, document) : false)\n ?? false\n )\n\n const isFinalDrainPending = (documentName: string): boolean => {\n const room = deps.resolveRoomDocument?.(documentName)\n return Boolean(room && deps.finalDrainRegistry?.isMarked(room))\n }\n\n const assertRoomAcceptsAuthentication = (documentName: string): void => {\n if (isInvalidated(documentName)) {\n throw new Error('[internal] documents collab: room is reloading authoritative content')\n }\n if (isFinalDrainPending(documentName)) {\n throw new Error('[internal] documents collab: room is draining accepted edits')\n }\n }\n\n type StoreAuthorization = 'normal' | 'final-drain' | 'denied'\n\n const invalidateStoreRoom = (data: {\n documentName: string\n document: Y.Doc\n }): void => {\n deps.finalDrainRegistry?.discard(data.document)\n deps.invalidateRoom?.(data.documentName, data.document)\n }\n\n const retireFailedStore = (data: {\n documentName: string\n document: Y.Doc\n }, ownsGrant = false): boolean => {\n const invalidated = isInvalidated(data.documentName, data.document)\n if (\n !ownsGrant\n && !invalidated\n && !deps.finalDrainRegistry?.isMarked(data.document)\n ) return false\n if (invalidated) {\n deps.finalDrainRegistry?.discard(data.document)\n } else {\n invalidateStoreRoom(data)\n }\n // Keep this diagnostic bounded: persistence errors can contain driver\n // details or document data. Returning normally is intentional so\n // Hocuspocus reaches its zero-connection unload path instead of retaining\n // an invalidated, permanently authentication-blocked room.\n logger.error(invalidated\n ? 'invalidated store failed; retiring in-memory room'\n : 'final drain failed; retiring in-memory room')\n return true\n }\n\n const assertConnectionAuthorization = (context: CollabContext): void => {\n if (!deps.finalDrainRegistry) return\n const lifecycle = (context as TicketedCollabContext)[COLLAB_AUTHORIZATION_LIFECYCLE]\n if (lifecycle?.established) return\n if (\n !lifecycle?.ticket\n || !deps.finalDrainRegistry.isAuthorizationCurrent(lifecycle.ticket)\n ) {\n throw new Error('[internal] documents collab: access changed during connection setup')\n }\n assertRoomAcceptsAuthentication(context.documentId)\n }\n\n const establishConnectionAuthorization = (context: CollabContext): void => {\n if (!deps.finalDrainRegistry) return\n assertConnectionAuthorization(context)\n const lifecycle = (context as TicketedCollabContext)[COLLAB_AUTHORIZATION_LIFECYCLE]\n if (!lifecycle) return\n if (lifecycle.ticket) deps.finalDrainRegistry.endAuthorization(lifecycle.ticket)\n lifecycle.ticket = undefined\n lifecycle.established = true\n }\n\n const releaseConnectionAuthorization = (context: CollabContext): void => {\n if (!deps.finalDrainRegistry) return\n const ticketedContext = context as TicketedCollabContext\n const lifecycle = ticketedContext[COLLAB_AUTHORIZATION_LIFECYCLE]\n if (lifecycle?.ticket) deps.finalDrainRegistry.endAuthorization(lifecycle.ticket)\n delete ticketedContext[COLLAB_AUTHORIZATION_LIFECYCLE]\n }\n\n const authorizeStoreAttempt = async (data: {\n documentName: string\n context: CollabContext\n document: Y.Doc\n }): Promise<StoreAuthorization> => {\n let finalDrain: CollabFinalDrainConsumeResult = 'unmarked'\n if (deps.finalDrainRegistry?.isMarked(data.document)) {\n finalDrain = await deps.finalDrainRegistry.consume(data.document)\n if (finalDrain === 'busy') {\n // Another store owns the one-shot grant. It alone keeps the room\n // marked through persistence; this duplicate must be inert.\n return 'denied'\n }\n if (finalDrain === 'connected' || finalDrain === 'failed') {\n invalidateStoreRoom(data)\n return 'denied'\n }\n }\n\n let authorized: boolean\n try {\n authorized = await deps.authorizeContext(data.context)\n } catch {\n // Authorization infrastructure failures are security failures here. A\n // room containing edits accepted under an unverified capability must\n // never remain eligible for a later debounced/retried store. Complete\n // normally after invalidation so Hocuspocus can retire the mapped room.\n invalidateStoreRoom(data)\n logger.error('store authorization failed; retiring in-memory room')\n return 'denied'\n }\n\n // Content replacement/deletion invalidation always wins over a share\n // final drain. It is never safe to merge the old collaboration epoch.\n if (isInvalidated(data.documentName, data.document)) {\n deps.finalDrainRegistry?.discard(data.document)\n return 'denied'\n }\n\n // A store can start its live ACL check immediately before the trusted\n // event marks the room. Re-read and claim the drain after that async\n // boundary so the store still waits for every pre-close queue.\n if (finalDrain === 'unmarked') {\n finalDrain = await (deps.finalDrainRegistry?.consume(data.document)\n ?? Promise.resolve('unmarked' as const))\n }\n if (finalDrain === 'busy') return 'denied'\n if (finalDrain === 'connected' || finalDrain === 'failed') {\n // Seeing a live connection after every captured queue drained means the\n // room was recycled, reconnected, or has a direct connection. Missing,\n // malformed, or rejected queue readiness is equally untrusted.\n invalidateStoreRoom(data)\n return 'denied'\n }\n if (finalDrain === 'consumed') return 'final-drain'\n\n if (authorized) return 'normal'\n\n // Revoke the whole room identity, not only this store invocation. This\n // closes its sockets in production and prevents its unauthorized Y.Doc\n // from being persisted by a later debounce after access is restored.\n invalidateStoreRoom(data)\n return 'denied'\n }\n\n const reauthorizeActiveConnection = async (context: CollabContext): Promise<boolean> => {\n if (isInvalidated(context.documentId) || isFinalDrainPending(context.documentId)) {\n return false\n }\n\n let authorized = false\n try {\n authorized = await deps.authorizeContext(context)\n } catch {\n authorized = false\n }\n\n // A trusted document event that crossed the ACL refresh already owns the\n // room shutdown/final-drain decision. Do not replace it with invalidation.\n if (isInvalidated(context.documentId) || isFinalDrainPending(context.documentId)) {\n return false\n }\n if (authorized) {\n const resolveRoomDocument = deps.resolveRoomDocument\n // Production active connections always belong to an exact mapped room.\n // Keep the hook backwards-compatible for embedders that do not expose\n // room resolution, but close a connection whose production mapping has\n // already disappeared instead of authorizing a stale logical socket.\n if (!resolveRoomDocument) return true\n const room = resolveRoomDocument(context.documentId)\n if (!room) return false\n\n const loadedGeneration = loadedCollaborationGenerations.get(room)\n let durableGeneration: number | null = null\n try {\n const container = await deps.resolveContainer()\n const em = container.resolve('em')\n const scope = {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n }\n if (deps.loadCollaborationGeneration) {\n durableGeneration = await deps.loadCollaborationGeneration(\n em,\n context.documentId,\n scope,\n )\n } else {\n const content = await deps.loadContent(em, context.documentId, scope)\n durableGeneration = normalizeDocumentCollaborationGeneration(\n content?.collaborationGeneration,\n )\n }\n } catch {\n durableGeneration = null\n }\n\n // A trusted event may have reached this process while the durable read\n // was in flight. Its invalidation/final-drain path owns room retirement.\n if (isInvalidated(context.documentId) || isFinalDrainPending(context.documentId)) {\n return false\n }\n // Never apply a stale connection's refresh result to a replacement room\n // that loaded after an unload/reconnect race.\n if (resolveRoomDocument(context.documentId) !== room) return false\n\n if (\n loadedGeneration !== undefined\n && durableGeneration !== null\n && durableGeneration === loadedGeneration\n ) {\n rememberWritableContext(room, context)\n return true\n }\n\n // Reconcile a missed cross-process content-reset/deletion event from the\n // durable, scoped DocumentContent generation. Retiring the old Y.Doc is\n // required so no pre-reset update can later pass the store CAS and merge\n // back into the authoritative epoch.\n invalidateStoreRoom({\n documentName: context.documentId,\n document: room,\n })\n return false\n }\n\n // A failed active refresh can follow an RBAC/role change that did not name\n // a document. Retire only the exact mapped room; the per-connection timer\n // avoids any global room or connection scan.\n const room = deps.resolveRoomDocument?.(context.documentId)\n if (room) {\n invalidateStoreRoom({\n documentName: context.documentId,\n document: room,\n })\n }\n return false\n }\n\n return {\n assertConnectionAuthorization,\n establishConnectionAuthorization,\n releaseConnectionAuthorization,\n reauthorizeActiveConnection,\n recordRedisAggregate(document: Y.Doc, byteLength: number): void {\n const previous = roomResourceBudgets.get(document)\n roomResourceBudgets.set(document, {\n bytes: byteLength,\n revision: (previous?.revision ?? 0) + 1,\n })\n },\n resolveCollaborationGeneration(document: Y.Doc): number | undefined {\n return loadedCollaborationGenerations.get(document)\n },\n resolveRoomScope(document: Y.Doc): CollabScope | null {\n return roomScopes.get(document) ?? null\n },\n async onAuthenticate(data: {\n token?: string\n documentName: string\n connection: CollabConnection\n requestHeaders?: RequestHeaders\n }): Promise<CollabContext> {\n if (!isCollabRequestOriginAllowed({\n origin: readHeader(data.requestHeaders, 'origin'),\n allowedOrigins: deps.allowedOrigins,\n requireOrigin: deps.requireOrigin ?? process.env.NODE_ENV === 'production',\n })) {\n throw new Error('[internal] documents collab: origin not allowed')\n }\n\n const context = resolveCollabClaims(deps, data.token ?? '')\n if (!context) {\n throw new Error('[internal] documents collab: invalid token')\n }\n if (context.documentId !== data.documentName) {\n throw new Error('[internal] documents collab: room mismatch')\n }\n const authorizationTicket = deps.finalDrainRegistry\n ?.beginAuthorization(data.documentName, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n })\n let retainAuthorizationTicket = false\n try {\n assertRoomAcceptsAuthentication(data.documentName)\n if (!(await deps.authorizeContext(context))) {\n throw new Error('[internal] documents collab: stale or revoked token')\n }\n\n const awarenessName = await deps.resolveAwarenessName?.(context)\n // Label resolution is another async boundary and the old room can even\n // unmap while it is in flight. Re-resolve live authorization after it,\n // then synchronously verify the bounded event ticket and exact mapped\n // room tombstone before admitting the connection.\n if (!(await deps.authorizeContext(context))) {\n throw new Error('[internal] documents collab: stale or revoked token')\n }\n if (\n authorizationTicket\n && !deps.finalDrainRegistry?.isAuthorizationCurrent(authorizationTicket)\n ) {\n throw new Error('[internal] documents collab: access changed during authentication')\n }\n assertRoomAcceptsAuthentication(data.documentName)\n context.awarenessUser = createCanonicalCollaborationAwarenessUser(\n context.userId,\n awarenessName,\n )\n data.connection.readOnly = context.readOnly\n if (authorizationTicket) {\n (context as TicketedCollabContext)[COLLAB_AUTHORIZATION_LIFECYCLE] = {\n established: false,\n ticket: authorizationTicket,\n }\n retainAuthorizationTicket = true\n }\n return context\n } finally {\n if (authorizationTicket && !retainAuthorizationTicket) {\n deps.finalDrainRegistry?.endAuthorization(authorizationTicket)\n }\n }\n },\n\n async beforeHandleAwareness(data: {\n context?: CollabContext\n states: Map<number, Record<string, unknown>>\n ownedClientIds?: ReadonlySet<number>\n occupiedClientIds?: ReadonlySet<number>\n connection?: object\n document?: Y.Doc\n }): Promise<void> {\n let claimedClientIds: Set<number> | undefined\n if (data.connection) {\n claimedClientIds = awarenessConnectionClientIds.get(data.connection)\n if (!claimedClientIds) {\n claimedClientIds = new Set()\n awarenessConnectionClientIds.set(data.connection, claimedClientIds)\n }\n }\n let roomClientOwners: Map<number, string> | undefined\n if (data.document) {\n roomClientOwners = awarenessRoomClientOwners.get(data.document)\n if (!roomClientOwners) {\n roomClientOwners = new Map()\n awarenessRoomClientOwners.set(data.document, roomClientOwners)\n }\n }\n bindCollabAwarenessStates(\n data.context,\n data.states,\n data.ownedClientIds && data.occupiedClientIds\n ? {\n ownedClientIds: data.ownedClientIds,\n occupiedClientIds: data.occupiedClientIds,\n claimedClientIds,\n roomClientOwners,\n }\n : undefined,\n )\n },\n\n async onLoadDocument(data: {\n documentName: string\n context: CollabContext\n document: Y.Doc\n }): Promise<Y.Doc> {\n assertScopedContext(data.context)\n roomScopes.set(data.document, {\n tenantId: data.context.tenantId,\n organizationId: data.context.organizationId,\n })\n rememberWritableContext(data.document, data.context)\n\n const container = await deps.resolveContainer()\n const em = container.resolve('em')\n const scope = {\n tenantId: data.context.tenantId,\n organizationId: data.context.organizationId,\n }\n let content = await deps.loadContent(em, data.documentName, scope)\n if (!content) {\n // Pre-M6 document creation was a two-request flow, so durable legacy\n // rows can legitimately exist without DocumentContent. Repair that\n // representation under the aggregate lock, then load its CAS version.\n await deps.initializeYjsState(em, data.documentName, scope)\n content = await deps.loadContent(em, data.documentName, scope)\n }\n const loadedVersion = normalizeContentVersion(content?.updatedAt)\n if (!loadedVersion) {\n throw new Error('[internal] documents collab: content row has no durable version')\n }\n if (content?.yjsState && content.yjsState.length > 0) {\n assertDocumentContentResourceLimits({\n yjsState: content.yjsState,\n contentHtml: content.contentHtml,\n })\n Y.applyUpdate(data.document, new Uint8Array(content.yjsState))\n roomResourceBudgets.set(data.document, {\n bytes: content.yjsState.byteLength,\n revision: 0,\n })\n } else if (content?.contentHtml) {\n assertDocumentContentResourceLimits({ contentHtml: content.contentHtml })\n const initializedState = await deps.initializeYjsState(\n em,\n data.documentName,\n scope,\n )\n if (initializedState && initializedState.length > 0) {\n Y.applyUpdate(data.document, new Uint8Array(initializedState))\n roomResourceBudgets.set(data.document, {\n bytes: initializedState.byteLength,\n revision: 0,\n })\n }\n }\n if (!roomResourceBudgets.has(data.document)) {\n roomResourceBudgets.set(data.document, { bytes: 0, revision: 0 })\n }\n\n const collaborationGeneration = normalizeDocumentCollaborationGeneration(\n content?.collaborationGeneration,\n )\n if (collaborationGeneration === null) {\n throw new Error('[internal] documents collab: content row has no durable collaboration generation')\n }\n loadedContentVersions.set(data.document, loadedVersion)\n loadedCollaborationGenerations.set(data.document, collaborationGeneration)\n return data.document\n },\n\n async beforeSync(data: {\n type: number\n payload: Uint8Array\n document: Y.Doc\n connection: { readOnly: boolean; close?: () => void }\n context?: CollabContext\n }): Promise<void> {\n // SyncStep1 contains only a state vector. Read-only SyncStep2/updates are\n // dropped by Hocuspocus and must not consume the writable room budget.\n if (data.connection.readOnly || (data.type !== 1 && data.type !== 2)) return\n // Revalidate the exact writer before Hocuspocus applies the frame. The\n // periodic connection refresh protects idle sockets, but it leaves a\n // bounded revocation window in which an update could otherwise enter the\n // shared Y.Doc and later be persisted under another writer's context.\n // MessageReceiver awaits beforeSync before readSyncStep2/readUpdate, so a\n // rejected frame never reaches local peers or Redis.\n let authorized = false\n if (data.context && !isInvalidated(data.context.documentId, data.document)) {\n try {\n authorized = await deps.authorizeContext(data.context)\n } catch {\n authorized = false\n }\n }\n if (\n !authorized\n || !data.context\n || isInvalidated(data.context.documentId, data.document)\n || isFinalDrainPending(data.context.documentId)\n ) {\n data.connection.close?.()\n if (data.context) {\n invalidateStoreRoom({\n documentName: data.context.documentId,\n document: data.document,\n })\n }\n throw new Error('[internal] documents collab: write authorization is no longer current')\n }\n rememberWritableContext(data.document, data.context)\n const budget = roomResourceBudgets.get(data.document) ?? { bytes: 0, revision: 0 }\n const nextBytes = budget.bytes + data.payload.byteLength\n assertDocumentYjsStateByteLength(nextBytes)\n budget.bytes = nextBytes\n budget.revision += 1\n roomResourceBudgets.set(data.document, budget)\n },\n\n async onStoreDocument(data: {\n documentName: string\n context: CollabContext\n document: Y.Doc\n }): Promise<void> {\n try {\n assertScopedContext(data.context)\n } catch (error) {\n if (retireFailedStore(data)) return\n throw error\n }\n if (isInvalidated(data.documentName, data.document)) return\n if (data.context.readOnly) {\n // Edits retained by this room were necessarily authored by a writable\n // connection: read-only sync frames are dropped before they reach the\n // Y.Doc. Fall back to the room's last writable authorization so a\n // viewer being the last-seen context cannot silently drop the\n // debounced store; authorizeStoreAttempt below re-validates that\n // context's live access before anything is persisted. A room with no\n // recorded writable context has nothing a viewer could have authored.\n const writableContext = roomWritableContexts.get(data.document)\n if (!writableContext) {\n retireFailedStore(data)\n return\n }\n data = { ...data, context: writableContext }\n } else {\n rememberWritableContext(data.document, data.context)\n }\n\n let expectedUpdatedAt = loadedContentVersions.get(data.document)\n let collaborationGeneration = loadedCollaborationGenerations.get(data.document)\n if (!expectedUpdatedAt || collaborationGeneration === undefined) {\n const error = new Error('[internal] documents collab: room store has no loaded content version')\n if (retireFailedStore(data)) return\n throw error\n }\n let em: unknown\n let searchIndexer: unknown = null\n try {\n const container = await deps.resolveContainer()\n em = container.resolve('em')\n try {\n searchIndexer = container.resolve('searchIndexer')\n } catch {\n searchIndexer = null\n }\n } catch (error) {\n if (retireFailedStore(data)) return\n throw error\n }\n const scope = {\n tenantId: data.context.tenantId,\n organizationId: data.context.organizationId,\n }\n let finalDrainAuthorized = false\n\n try {\n for (let attempt = 0; attempt < MAX_COLLAB_STORE_ATTEMPTS; attempt += 1) {\n if (isInvalidated(data.documentName, data.document)) {\n if (finalDrainAuthorized) deps.finalDrainRegistry?.discard(data.document)\n return\n }\n if (!finalDrainAuthorized) {\n // Re-resolve ACL, organization scope, relationship tier/share, and\n // edit capability immediately before every normal persistence\n // attempt, including every multi-replica CAS retry. The sole\n // exception is a one-shot trusted share-event drain whose sockets\n // are already gone; that consumed grant remains local to this one\n // store invocation so its bounded CAS merge can finish.\n const authorization = await authorizeStoreAttempt(data)\n if (authorization === 'denied') return\n finalDrainAuthorized = authorization === 'final-drain'\n }\n\n // Authorization and a final-drain queue wait both cross async\n // boundaries. Snapshot only afterwards so every frame accepted before\n // closeConnections (and every update merged for a CAS retry) is part\n // of the exact state that becomes durable.\n if (isInvalidated(data.documentName, data.document)) {\n if (finalDrainAuthorized) deps.finalDrainRegistry?.discard(data.document)\n return\n }\n const materialized = yDocToContent(data.document)\n if (materialized) materializationWarningRooms.delete(data.documentName)\n if (!materialized && !materializationWarningRooms.has(data.documentName)) {\n materializationWarningRooms.add(data.documentName)\n logger.warn('materialization failed; preserving previous html/text', { room: data.documentName })\n }\n const yjsState = Buffer.from(Y.encodeStateAsUpdate(data.document))\n const resourceBudgetRevision = roomResourceBudgets.get(data.document)?.revision ?? 0\n assertDocumentContentResourceLimits({\n yjsState,\n contentHtml: materialized?.html,\n contentText: materialized?.text,\n })\n\n let persisted: { updatedAt: string | Date; collaborationGeneration: number }\n try {\n persisted = await deps.persistContent(\n em,\n data.documentName,\n scope,\n materialized\n ? {\n yjsState,\n contentHtml: materialized.html,\n contentText: materialized.text,\n }\n : { yjsState },\n {\n searchIndexer,\n expectedUpdatedAt,\n expectedCollaborationGeneration: collaborationGeneration,\n requireExpectedVersion: true,\n },\n )\n } catch (error) {\n // An explicit replace/restore event can arrive while the CAS waits.\n // Its room marker always wins and stale content is discarded.\n if (isInvalidated(data.documentName, data.document)) {\n if (finalDrainAuthorized) deps.finalDrainRegistry?.discard(data.document)\n return\n }\n const isVersionConflict = isCrudHttpError(error)\n && error.status === 409\n && error.body.code === OPTIMISTIC_LOCK_CONFLICT_CODE\n if (!isVersionConflict) throw error\n\n // A normal competing store from another sidecar replica shares the\n // same server-owned collaboration generation. Merge its\n // authoritative Yjs update into this room and retry against its new\n // version so neither replica's edit depends on a client reconnect to\n // become durable.\n const authoritative = await deps.loadContent(em, data.documentName, scope)\n const authoritativeVersion = normalizeContentVersion(authoritative?.updatedAt)\n if (!authoritativeVersion) {\n invalidateStoreRoom(data)\n return\n }\n const authoritativeGeneration = normalizeDocumentCollaborationGeneration(\n authoritative?.collaborationGeneration,\n )\n if (authoritativeGeneration === null) {\n invalidateStoreRoom(data)\n return\n }\n const authoritativeDocument = authoritative?.yjsState?.length\n ? new Y.Doc()\n : htmlToYDoc(authoritative?.contentHtml ?? '')\n if (authoritative?.yjsState?.length) {\n assertDocumentContentResourceLimits({\n yjsState: authoritative.yjsState,\n contentHtml: authoritative.contentHtml,\n })\n Y.applyUpdate(authoritativeDocument, new Uint8Array(authoritative.yjsState))\n }\n if (\n authoritativeGeneration !== collaborationGeneration\n || isInvalidated(data.documentName, data.document)\n ) {\n // A changed generation is an intentional content replacement/restore,\n // even if its event has not reached this replica yet. Never merge\n // pre-reset edits back into the new authoritative document.\n invalidateStoreRoom(data)\n return\n }\n\n Y.applyUpdate(\n data.document,\n Y.encodeStateAsUpdate(authoritativeDocument),\n {\n source: 'local',\n skipStoreHooks: true,\n context: data.context,\n },\n )\n expectedUpdatedAt = authoritativeVersion\n collaborationGeneration = authoritativeGeneration\n loadedContentVersions.set(data.document, authoritativeVersion)\n loadedCollaborationGenerations.set(data.document, authoritativeGeneration)\n if (attempt + 1 >= MAX_COLLAB_STORE_ATTEMPTS) throw error\n continue\n }\n\n if (isInvalidated(data.documentName, data.document)) {\n if (finalDrainAuthorized) deps.finalDrainRegistry?.discard(data.document)\n return\n }\n const persistedVersion = normalizeContentVersion(persisted.updatedAt)\n if (!persistedVersion) {\n throw new Error('[internal] documents collab: content store returned no durable version')\n }\n if (persisted.collaborationGeneration !== collaborationGeneration) {\n invalidateStoreRoom(data)\n return\n }\n loadedContentVersions.set(data.document, persistedVersion)\n loadedCollaborationGenerations.set(data.document, collaborationGeneration)\n const currentBudget = roomResourceBudgets.get(data.document)\n if (!currentBudget || currentBudget.revision === resourceBudgetRevision) {\n roomResourceBudgets.set(data.document, {\n bytes: yjsState.byteLength,\n revision: resourceBudgetRevision,\n })\n }\n deps.onPersisted?.(data.document, yjsState, collaborationGeneration)\n if (finalDrainAuthorized) deps.finalDrainRegistry?.complete(data.document)\n return\n }\n } catch (error) {\n if (isCrudHttpError(error) && error.status === 413) {\n // A Redis/CAS merge can make the complete room exceed the aggregate\n // resource limit even though each individual inbound frame passed.\n // Retire the room so the oversized state cannot be retried, fanned\n // out, or later persisted under another connection's context.\n invalidateStoreRoom(data)\n logger.warn('retiring collaboration room that exceeded content limits', {\n room: data.documentName,\n })\n return\n }\n // Ordinary store failures retain Hocuspocus' normal retry/data-loss\n // protection. A final drain cannot: with every socket already closed,\n // rethrowing makes Hocuspocus retain an invalidated mapped Y.Doc and\n // block every future authentication forever. Retire it fail-closed\n // and complete the hook normally so Hocuspocus schedules unload.\n if (retireFailedStore(data, finalDrainAuthorized)) return\n throw error\n }\n },\n }\n}\n\nfunction headersToRecord(headers: Headers): RequestHeaders {\n return { origin: headers.get('origin') ?? undefined }\n}\n\nfunction eventDocumentId(payload: unknown): string | null {\n if (!payload || typeof payload !== 'object') return null\n const record = payload as Record<string, unknown>\n const id = record.documentId ?? record.id\n return typeof id === 'string' && id.length > 0 ? id : null\n}\n\ntype CollabRoomConnectionRegistry = {\n documents: Map<string, { connections: Map<{ close: (event?: { code: number; reason: string }) => void }, unknown> }>\n}\n\n/**\n * A content-replaced room must not be rejoined by the same browser Y.Doc:\n * Hocuspocus' plain `closeConnections` reset makes the provider reconnect with\n * its local document, whose pre-replacement state would sync straight back\n * into the freshly loaded room and undo the restore. Close these connections\n * with the dedicated reason the browser resets its document on instead.\n */\nexport function closeCollabRoomConnectionsForContentReset(\n registry: CollabRoomConnectionRegistry,\n documentName: string,\n): number {\n const room = registry.documents.get(documentName)\n if (!room) return 0\n let closed = 0\n room.connections.forEach((_clients, connection) => {\n connection.close(COLLAB_CONTENT_RESET_CLOSE_EVENT)\n closed += 1\n })\n return closed\n}\n\nexport type CollabRoomEventAction = 'ignore' | 'reauth' | 'invalidate'\n\n/**\n * Access changes only require sockets to re-authenticate. Suppressing a store\n * for those events can drop edits made shortly after a share is created or\n * changed. Content-invalidating events must additionally prevent the closing\n * room's stale Y.Doc from overwriting the authoritative database state.\n */\nexport function resolveCollabRoomEventAction(event: string, payload?: unknown): CollabRoomEventAction {\n if (\n event === 'documents.document.shared'\n || event === 'documents.document.unshared'\n || event === 'documents.document.archived'\n || event === 'documents.document.unarchived'\n ) {\n return 'reauth'\n }\n const record = payload && typeof payload === 'object' ? payload as Record<string, unknown> : null\n if (\n event === 'documents.document.deleted'\n || event === 'documents.version.restored'\n || (event === 'documents.document.updated' && record?.contentEpochReset === true)\n ) {\n return 'invalidate'\n }\n return 'ignore'\n}\n\ntype DocumentsCrossProcessEventEnvelope = {\n event: string\n payload: unknown\n options?: {\n tenantId?: unknown\n organizationId?: unknown\n emitterModuleId?: unknown\n }\n originPid?: unknown\n originInstanceId?: unknown\n}\n\nexport type TrustedDocumentsCrossProcessEvent = {\n action: Exclude<CollabRoomEventAction, 'ignore'>\n documentId: string\n scope: CollabScope\n}\n\nfunction nonEmptyString(value: unknown): string | null {\n return typeof value === 'string' && value.trim().length > 0 ? value : null\n}\n\n/**\n * Resolve only private Documents events stamped by the Documents module and\n * carrying trusted envelope scope. Payload scope is intentionally ignored: it\n * is application data and can be authored by a workflow.\n */\nexport function resolveTrustedDocumentsCrossProcessEvent(\n envelope: DocumentsCrossProcessEventEnvelope,\n): TrustedDocumentsCrossProcessEvent | null {\n if (envelope.options?.emitterModuleId !== 'documents') return null\n const tenantId = nonEmptyString(envelope.options?.tenantId)\n const organizationId = nonEmptyString(envelope.options?.organizationId)\n if (!tenantId || !organizationId) return null\n const action = resolveCollabRoomEventAction(envelope.event, envelope.payload)\n if (action === 'ignore') return null\n const documentId = eventDocumentId(envelope.payload)\n if (!documentId) return null\n return {\n action,\n documentId,\n scope: { tenantId, organizationId },\n }\n}\n\nexport function isTrustedDocumentsCollabRoomScope(\n event: TrustedDocumentsCrossProcessEvent,\n roomScope: CollabScope | null,\n): boolean {\n return roomScope?.tenantId === event.scope.tenantId\n && roomScope.organizationId === event.scope.organizationId\n}\n\n/**\n * The Events bridge stamps every envelope this sidecar publishes with its\n * random per-process instance id, so an envelope without one is provably\n * foreign. Falling back to originPid would discard it whenever another\n * container happens to share our pid \u2014 commonly pid 1 \u2014 silently dropping\n * reauth and invalidation across a rolling deploy.\n */\nexport function isOwnDocumentsCrossProcessEvent(\n envelope: DocumentsCrossProcessEventEnvelope,\n ownInstanceId: string,\n): boolean {\n return envelope.originInstanceId === ownInstanceId\n}\n\nfunction isMainModule(): boolean {\n const entry = process.argv[1]\n return Boolean(entry && import.meta.url === pathToFileURL(resolve(entry)).href)\n}\n\nexport async function main(): Promise<void> {\n const [\n { Connection, Server },\n { bootstrapFromAppRoot },\n { createRequestContainer },\n { registerCrossProcessEventListener, CROSS_PROCESS_EVENT_INSTANCE_ID },\n ] = await Promise.all([\n import('@hocuspocus/server'),\n import('@open-mercato/shared/lib/bootstrap/dynamicLoader'),\n import('@open-mercato/shared/lib/di/container'),\n import('@open-mercato/events'),\n ])\n\n installHocuspocusCollabIngressGuard(Connection)\n\n const appRoot = process.env.DOCUMENTS_COLLAB_APP_ROOT || undefined\n await bootstrapFromAppRoot(appRoot)\n\n const port = Number(process.env.DOCUMENTS_COLLAB_PORT || 4101)\n const allowedOrigins = resolveCollabAllowedOrigins(process.env)\n // Invalidated room identities stay marked for their whole lifetime. The\n // WeakSet releases them only after Hocuspocus unloads/destroys the Y.Doc;\n // unlike a timer, a slow debounced/in-flight store can never become writable\n // again merely because cleanup took longer than expected.\n const invalidatedRoomDocuments = new WeakSet<Y.Doc>()\n const finalDrainRegistry = createCollabFinalDrainRegistry()\n let server: HocuspocusServer<CollabContext> | null = null\n let redisStoreExtension: DocumentsCollabRedisExtension | null = null\n const closeRoomConnectionsForContentReset = (documentName: string): void => {\n if (server) closeCollabRoomConnectionsForContentReset(server.hocuspocus, documentName)\n }\n const invalidateRoom = (documentName: string, document: Y.Doc): void => {\n redisStoreExtension?.discardPendingFanout(documentName, document)\n finalDrainRegistry.discard(document)\n invalidatedRoomDocuments.add(document)\n closeRoomConnectionsForContentReset(documentName)\n }\n const legacyTokenVerifier = resolveLegacyCollabTokenVerifier(process.env)\n const hooks = createCollabHooks({\n ...(legacyTokenVerifier ? { verifyToken: legacyTokenVerifier } : {}),\n verifyTokenV2: verifyCollabTokenV2,\n authorizeContext: async (context) => {\n const container = await createRequestContainer()\n return authorizeCollabContext(container, context)\n },\n resolveAwarenessName: async (context) => {\n const container = await createRequestContainer()\n const labels = await resolveUserLabels(\n container,\n {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n },\n [context.userId],\n )\n return labels.get(context.userId)?.label ?? null\n },\n resolveContainer: () => createRequestContainer(),\n loadContent: async (em, id, scope) => {\n const content = await loadDocumentContentForCollaboration(\n em as EntityManager,\n id,\n scope,\n )\n if (!content) return null\n return {\n yjsState: content.yjsState ?? null,\n contentHtml: content.contentHtml ?? null,\n updatedAt: content.updatedAt,\n collaborationGeneration: content.collaborationGeneration,\n }\n },\n loadCollaborationGeneration: (em, id, scope) => loadDocumentCollaborationGeneration(\n em as EntityManager,\n id,\n scope,\n ),\n initializeYjsState: (em, id, scope) => initializeDocumentYjsState(\n em as EntityManager,\n id,\n scope,\n ),\n persistContent: (em, id, scope, input, serviceDeps) => persistDocumentContent(\n em as EntityManager,\n id,\n scope,\n input,\n serviceDeps as PersistDocumentContentDeps,\n ),\n allowedOrigins,\n requireOrigin: process.env.NODE_ENV === 'production',\n onPersisted: (document, yjsState, collaborationGeneration) => {\n redisStoreExtension?.markPersisted(document, yjsState, collaborationGeneration)\n },\n finalDrainRegistry,\n resolveRoomDocument: (documentName) => server?.hocuspocus.documents.get(documentName),\n isRoomInvalidated: (documentName, document) => {\n const room = document ?? server?.hocuspocus.documents.get(documentName)\n return Boolean(room && invalidatedRoomDocuments.has(room))\n },\n invalidateRoom,\n })\n\n const runningServer: HocuspocusServer<CollabContext> = new Server<CollabContext>({\n port,\n ...COLLAB_SERVER_RUNTIME_CONFIGURATION,\n extensions: resolveDocumentsCollabRedisExtensions(\n process.env,\n (configuration) => {\n redisStoreExtension = new DocumentsCollabRedisExtension(configuration, {\n resolveCollaborationGeneration: hooks.resolveCollaborationGeneration,\n onRejectedAggregate: invalidateRoom,\n onAcceptedAggregate: (document, byteLength) => {\n hooks.recordRedisAggregate(document, byteLength)\n },\n })\n return enforceDocumentsCollabSourceStoreOwnership(redisStoreExtension)\n },\n ),\n async onAuthenticate(data: onAuthenticatePayload<CollabContext>) {\n return await hooks.onAuthenticate({\n token: data.token,\n documentName: data.documentName,\n connection: data.connectionConfig,\n requestHeaders: headersToRecord(data.requestHeaders),\n })\n },\n async connected(data: connectedPayload<CollabContext>) {\n try {\n hooks.establishConnectionAuthorization(data.context)\n } catch (error) {\n data.connection.close()\n hooks.releaseConnectionAuthorization(data.context)\n throw error\n }\n if (data.context.exp !== null) {\n scheduleCollabConnectionExpiry(data.connection, data.context.exp)\n }\n scheduleCollabConnectionReauthorization(\n data.connection,\n () => hooks.reauthorizeActiveConnection(data.context),\n )\n },\n async beforeHandleMessage(data: beforeHandleMessagePayload<CollabContext>) {\n try {\n hooks.assertConnectionAuthorization(data.context)\n } catch (error) {\n data.connection.close()\n hooks.releaseConnectionAuthorization(data.context)\n throw error\n }\n assertCollabInboundFramePolicy(data.update, {\n readOnly: data.connection.readOnly,\n })\n },\n async beforeHandleAwareness(data: beforeHandleAwarenessPayload<CollabContext>) {\n // Redis replication has no browser connection/context. Its awareness\n // payload was already authenticated, size-bounded, and canonicalized at\n // the source replica, so only client-originated frames enter the\n // per-connection identity guard below.\n if (!data.connection) return\n return hooks.beforeHandleAwareness({\n context: data.context,\n states: data.states,\n ownedClientIds: new Set(data.document.getClients(data.connection)),\n occupiedClientIds: new Set(data.awareness.getStates().keys()),\n connection: data.connection,\n document: data.document,\n })\n },\n async beforeSync(data: beforeSyncPayload<CollabContext>) {\n return hooks.beforeSync({\n type: data.type,\n payload: data.payload,\n document: data.document,\n connection: data.connection,\n context: data.context,\n })\n },\n async onLoadDocument(data: onLoadDocumentPayload<CollabContext>) {\n try {\n hooks.assertConnectionAuthorization(data.context)\n const document = await hooks.onLoadDocument({\n documentName: data.documentName,\n context: data.context,\n document: data.document,\n })\n hooks.assertConnectionAuthorization(data.context)\n return document\n } catch (error) {\n hooks.releaseConnectionAuthorization(data.context)\n // Hocuspocus has not mapped this freshly created Document yet, so its\n // unload helper cannot destroy the Y.Doc on a load-hook failure.\n data.document.destroy()\n throw error\n }\n },\n async afterLoadDocument(data: afterLoadDocumentPayload<CollabContext>) {\n try {\n hooks.assertConnectionAuthorization(data.context)\n } catch (error) {\n hooks.releaseConnectionAuthorization(data.context)\n data.document.destroy()\n throw error\n }\n },\n async onStoreDocument(data: onStoreDocumentPayload<CollabContext>) {\n if (!isDocumentsCollabSourceStore(data)) return\n return await hooks.onStoreDocument({\n documentName: data.documentName,\n context: data.lastContext,\n document: data.document,\n })\n },\n async onRequest(data: onRequestPayload) {\n return handleCollabServerRequest(data)\n },\n async onDisconnect(data: onDisconnectPayload<CollabContext>) {\n hooks.releaseConnectionAuthorization(data.context)\n },\n })\n server = runningServer\n\n await runningServer.listen()\n\n registerCrossProcessEventListener((envelope: DocumentsCrossProcessEventEnvelope) => {\n if (isOwnDocumentsCrossProcessEvent(envelope, CROSS_PROCESS_EVENT_INSTANCE_ID)) return\n const trustedEvent = resolveTrustedDocumentsCrossProcessEvent(envelope)\n if (!trustedEvent) return\n const { action, documentId, scope } = trustedEvent\n\n // Bump the exact scoped handshake epoch before consulting live room\n // metadata. A room can be mapped during the narrow load transition before\n // its scope WeakMap is observable here; scoped tickets still let the event\n // reject that in-flight authentication without touching another tenant.\n finalDrainRegistry.bumpAuthorization(documentId, scope)\n const roomDocument = runningServer.hocuspocus.documents.get(documentId)\n if (\n roomDocument\n && !isTrustedDocumentsCollabRoomScope(trustedEvent, hooks.resolveRoomScope(roomDocument))\n ) {\n return\n }\n if (roomDocument && action === 'reauth') {\n // Capture every exact connection queue before closeConnections removes\n // those logical connections synchronously. The one-shot store cannot\n // snapshot or unblock reconnects until all captured queues have drained.\n markCollabFinalDrainForReauth(roomDocument, finalDrainRegistry)\n }\n if (action === 'invalidate') {\n // A durable fanout intentionally survives ordinary unload, so discard by\n // room name even when no Y.Doc is currently mapped. When one is mapped,\n // also tombstone that exact identity so a store already releasing its\n // lock cannot enqueue the pre-invalidation generation afterwards.\n redisStoreExtension?.discardPendingFanout(documentId)\n if (roomDocument) {\n redisStoreExtension?.discardPendingFanout(documentId, roomDocument)\n finalDrainRegistry.discard(roomDocument)\n invalidatedRoomDocuments.add(roomDocument)\n }\n closeRoomConnectionsForContentReset(documentId)\n return\n }\n runningServer.hocuspocus.closeConnections(documentId)\n })\n\n // Hocuspocus debounces onStoreDocument, so an unhandled SIGTERM/SIGINT\n // (docker stop, redeploy) would drop the last seconds of edits. destroy()\n // unloads every document, which drains pending stores before the process\n // exits.\n let shuttingDown = false\n const shutdown = (signal: NodeJS.Signals): void => {\n if (shuttingDown) return\n shuttingDown = true\n logger.info('shutting down; draining pending document stores', { signal })\n void runningServer\n .destroy()\n .catch((error: unknown) => {\n logger.error('graceful shutdown failed', {\n error: error instanceof Error ? error.message : String(error),\n })\n process.exitCode = 1\n })\n .finally(() => {\n process.exit(process.exitCode ?? 0)\n })\n }\n process.once('SIGTERM', shutdown)\n process.once('SIGINT', shutdown)\n\n logger.info(`listening on :${port}`)\n}\n\nif (process.env.DOCUMENTS_COLLAB_START !== 'off' && isMainModule()) {\n void main().catch((error: unknown) => {\n logger.error('failed to start', {\n error: error instanceof Error ? (error.stack ?? error.message) : String(error),\n })\n process.exitCode = 1\n })\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["resolve", "room"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const id = "id";
|
|
2
|
+
export const organization_id = "organization_id";
|
|
3
|
+
export const tenant_id = "tenant_id";
|
|
4
|
+
export const title = "title";
|
|
5
|
+
export const folder_id = "folder_id";
|
|
6
|
+
export const owner_user_id = "owner_user_id";
|
|
7
|
+
export const created_by_user_id = "created_by_user_id";
|
|
8
|
+
export const is_active = "is_active";
|
|
9
|
+
export const archived_at = "archived_at";
|
|
10
|
+
export const created_at = "created_at";
|
|
11
|
+
export const updated_at = "updated_at";
|
|
12
|
+
export const deleted_at = "deleted_at";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const id = "id";
|
|
2
|
+
export const organization_id = "organization_id";
|
|
3
|
+
export const tenant_id = "tenant_id";
|
|
4
|
+
export const document_id = "document_id";
|
|
5
|
+
export const attachment_id = "attachment_id";
|
|
6
|
+
export const created_by_user_id = "created_by_user_id";
|
|
7
|
+
export const created_at = "created_at";
|
|
8
|
+
export const updated_at = "updated_at";
|
|
9
|
+
export const deleted_at = "deleted_at";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const id = "id";
|
|
2
|
+
export const organization_id = "organization_id";
|
|
3
|
+
export const tenant_id = "tenant_id";
|
|
4
|
+
export const document_id = "document_id";
|
|
5
|
+
export const parent_comment_id = "parent_comment_id";
|
|
6
|
+
export const author_user_id = "author_user_id";
|
|
7
|
+
export const body = "body";
|
|
8
|
+
export const anchor = "anchor";
|
|
9
|
+
export const mentions = "mentions";
|
|
10
|
+
export const resolved_at = "resolved_at";
|
|
11
|
+
export const resolved_by_user_id = "resolved_by_user_id";
|
|
12
|
+
export const created_at = "created_at";
|
|
13
|
+
export const updated_at = "updated_at";
|
|
14
|
+
export const deleted_at = "deleted_at";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const id = "id";
|
|
2
|
+
export const document_id = "document_id";
|
|
3
|
+
export const organization_id = "organization_id";
|
|
4
|
+
export const tenant_id = "tenant_id";
|
|
5
|
+
export const yjs_state = "yjs_state";
|
|
6
|
+
export const content_html = "content_html";
|
|
7
|
+
export const content_text = "content_text";
|
|
8
|
+
export const collaboration_generation = "collaboration_generation";
|
|
9
|
+
export const created_at = "created_at";
|
|
10
|
+
export const updated_at = "updated_at";
|
|
11
|
+
export const deleted_at = "deleted_at";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const id = "id";
|
|
2
|
+
export const organization_id = "organization_id";
|
|
3
|
+
export const tenant_id = "tenant_id";
|
|
4
|
+
export const document_id = "document_id";
|
|
5
|
+
export const customer_entity_id = "customer_entity_id";
|
|
6
|
+
export const customer_kind = "customer_kind";
|
|
7
|
+
export const deal_id = "deal_id";
|
|
8
|
+
export const product_id = "product_id";
|
|
9
|
+
export const catalog_offer_id = "catalog_offer_id";
|
|
10
|
+
export const quote_id = "quote_id";
|
|
11
|
+
export const sales_order_id = "sales_order_id";
|
|
12
|
+
export const linked_document_id = "linked_document_id";
|
|
13
|
+
export const label_snapshot = "label_snapshot";
|
|
14
|
+
export const href_snapshot = "href_snapshot";
|
|
15
|
+
export const source = "source";
|
|
16
|
+
export const created_by_user_id = "created_by_user_id";
|
|
17
|
+
export const created_at = "created_at";
|
|
18
|
+
export const updated_at = "updated_at";
|
|
19
|
+
export const deleted_at = "deleted_at";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export const id = "id";
|
|
2
|
+
export const document_id = "document_id";
|
|
3
|
+
export const user_id = "user_id";
|
|
4
|
+
export const organization_id = "organization_id";
|
|
5
|
+
export const tenant_id = "tenant_id";
|
|
6
|
+
export const created_at = "created_at";
|
|
7
|
+
export const deleted_at = "deleted_at";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const id = "id";
|
|
2
|
+
export const organization_id = "organization_id";
|
|
3
|
+
export const tenant_id = "tenant_id";
|
|
4
|
+
export const name = "name";
|
|
5
|
+
export const parent_folder_id = "parent_folder_id";
|
|
6
|
+
export const owner_user_id = "owner_user_id";
|
|
7
|
+
export const created_at = "created_at";
|
|
8
|
+
export const updated_at = "updated_at";
|
|
9
|
+
export const deleted_at = "deleted_at";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const id = "id";
|
|
2
|
+
export const organization_id = "organization_id";
|
|
3
|
+
export const tenant_id = "tenant_id";
|
|
4
|
+
export const document_id = "document_id";
|
|
5
|
+
export const principal_type = "principal_type";
|
|
6
|
+
export const principal_id = "principal_id";
|
|
7
|
+
export const permission = "permission";
|
|
8
|
+
export const created_by_user_id = "created_by_user_id";
|
|
9
|
+
export const created_at = "created_at";
|
|
10
|
+
export const updated_at = "updated_at";
|
|
11
|
+
export const deleted_at = "deleted_at";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const id = "id";
|
|
2
|
+
export const organization_id = "organization_id";
|
|
3
|
+
export const tenant_id = "tenant_id";
|
|
4
|
+
export const name = "name";
|
|
5
|
+
export const description = "description";
|
|
6
|
+
export const body_html = "body_html";
|
|
7
|
+
export const context_slots = "context_slots";
|
|
8
|
+
export const seed_key = "seed_key";
|
|
9
|
+
export const created_by_user_id = "created_by_user_id";
|
|
10
|
+
export const is_active = "is_active";
|
|
11
|
+
export const created_at = "created_at";
|
|
12
|
+
export const updated_at = "updated_at";
|
|
13
|
+
export const deleted_at = "deleted_at";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const id = "id";
|
|
2
|
+
export const organization_id = "organization_id";
|
|
3
|
+
export const tenant_id = "tenant_id";
|
|
4
|
+
export const document_id = "document_id";
|
|
5
|
+
export const label = "label";
|
|
6
|
+
export const yjs_snapshot = "yjs_snapshot";
|
|
7
|
+
export const content_html = "content_html";
|
|
8
|
+
export const created_by_user_id = "created_by_user_id";
|
|
9
|
+
export const created_at = "created_at";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export const id = "id";
|
|
2
|
+
export const document_id = "document_id";
|
|
3
|
+
export const user_id = "user_id";
|
|
4
|
+
export const organization_id = "organization_id";
|
|
5
|
+
export const tenant_id = "tenant_id";
|
|
6
|
+
export const created_at = "created_at";
|
|
7
|
+
export const deleted_at = "deleted_at";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// AUTO-GENERATED by mercato generate entity-ids
|
|
2
|
+
export const M = {
|
|
3
|
+
"documents": "documents"
|
|
4
|
+
} as const, E = {
|
|
5
|
+
"documents": {
|
|
6
|
+
"document": "documents:document",
|
|
7
|
+
"document_content": "documents:document_content",
|
|
8
|
+
"document_folder": "documents:document_folder",
|
|
9
|
+
"document_share": "documents:document_share",
|
|
10
|
+
"document_favorite": "documents:document_favorite",
|
|
11
|
+
"document_watcher": "documents:document_watcher",
|
|
12
|
+
"document_comment": "documents:document_comment",
|
|
13
|
+
"document_version": "documents:document_version",
|
|
14
|
+
"document_attachment": "documents:document_attachment",
|
|
15
|
+
"document_template": "documents:document_template",
|
|
16
|
+
"document_entity_link": "documents:document_entity_link"
|
|
17
|
+
}
|
|
18
|
+
} as const;
|
|
19
|
+
|
|
20
|
+
export type KnownModuleId = keyof typeof M;
|
|
21
|
+
export type KnownEntities = typeof E;
|