@ecsplico/editor 2.1.2 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +78 -1
- package/README.md +39 -156
- package/dist/adapters/comments/drizzle.d.ts +1 -1
- package/dist/adapters/comments/record.d.ts +15 -0
- package/dist/adapters/comments/{client.js → rest-model.js} +3 -3
- package/dist/adapters/comments/rest-model.js.map +1 -0
- package/dist/adapters/comments/server.d.ts +2 -1
- package/dist/adapters/comments/server.js +2 -1
- package/dist/adapters/comments/server.js.map +1 -1
- package/dist/adapters/comments/types.d.ts +2 -2
- package/dist/adapters/document-state.d.ts +23 -0
- package/dist/adapters/document-state.js +66 -0
- package/dist/adapters/document-state.js.map +1 -0
- package/dist/adapters/documents.js +14 -11
- package/dist/adapters/documents.js.map +1 -1
- package/dist/adapters/drizzle.d.ts +2 -2
- package/dist/adapters/drizzle.js +97 -88
- package/dist/adapters/drizzle.js.map +1 -1
- package/dist/adapters/forge.d.ts +12 -7
- package/dist/adapters/forge.js +38 -45
- package/dist/adapters/forge.js.map +1 -1
- package/dist/adapters/http.d.ts +20 -0
- package/dist/adapters/http.js +68 -0
- package/dist/adapters/http.js.map +1 -0
- package/dist/adapters/index.d.ts +5 -5
- package/dist/adapters/json.d.ts +3 -3
- package/dist/adapters/json.js +58 -49
- package/dist/adapters/json.js.map +1 -1
- package/dist/adapters/memory.d.ts +21 -0
- package/dist/adapters/memory.js +53 -0
- package/dist/adapters/memory.js.map +1 -0
- package/dist/adapters/nitro.d.ts +27 -20
- package/dist/adapters/nitro.js +59 -163
- package/dist/adapters/nitro.js.map +1 -1
- package/dist/adapters/react.d.ts +55 -0
- package/dist/adapters/react.js +209 -0
- package/dist/adapters/react.js.map +1 -0
- package/dist/adapters/shared.d.ts +15 -21
- package/dist/adapters/shared.js +10 -8
- package/dist/adapters/shared.js.map +1 -1
- package/dist/adapters/vue.d.ts +15 -30
- package/dist/adapters/vue.js +36 -87
- package/dist/adapters/vue.js.map +1 -1
- package/dist/code/CodeSurface.vue.d.ts +2 -2
- package/dist/config/index.d.ts +105 -0
- package/dist/config/index.js +95 -0
- package/dist/config/index.js.map +1 -0
- package/dist/core/abilities.js +1 -0
- package/dist/core/abilities.js.map +1 -1
- package/dist/{adapters/routes.js → core/api-routes.js} +5 -3
- package/dist/core/api-routes.js.map +1 -0
- package/dist/core/backend-capability.d.ts +7 -0
- package/dist/core/backend-capability.js +17 -0
- package/dist/core/backend-capability.js.map +1 -0
- package/dist/core/backend-types.d.ts +245 -0
- package/dist/core/backend-types.js +17 -0
- package/dist/core/backend-types.js.map +1 -0
- package/dist/core/backend.d.ts +108 -0
- package/dist/core/backend.js +14 -0
- package/dist/core/backend.js.map +1 -0
- package/dist/core/block-convert.d.ts +7 -5
- package/dist/core/block-convert.js +28 -5
- package/dist/core/block-convert.js.map +1 -1
- package/dist/core/command-types.d.ts +96 -0
- package/dist/core/commands.d.ts +40 -0
- package/dist/core/commands.js +151 -0
- package/dist/core/commands.js.map +1 -0
- package/dist/core/context.d.ts +17 -20
- package/dist/core/context.js +24 -21
- package/dist/core/context.js.map +1 -1
- package/dist/core/core-feature.d.ts +22 -7
- package/dist/core/core-feature.js +51 -18
- package/dist/core/core-feature.js.map +1 -1
- package/dist/core/define-editor.d.ts +4 -3
- package/dist/core/define-editor.js +14 -7
- package/dist/core/define-editor.js.map +1 -1
- package/dist/core/document-markdown.js +12 -3
- package/dist/core/document-markdown.js.map +1 -1
- package/dist/core/editor-ui.d.ts +8 -1
- package/dist/core/editor-ui.js +9 -8
- package/dist/core/editor-ui.js.map +1 -1
- package/dist/core/extension-points.d.ts +3 -15
- package/dist/core/feature-context.d.ts +8 -7
- package/dist/core/feature-context.js +12 -11
- package/dist/core/feature-context.js.map +1 -1
- package/dist/core/feature.d.ts +41 -19
- package/dist/core/feature.js.map +1 -1
- package/dist/core/fences.d.ts +2 -1
- package/dist/core/fences.js +3 -2
- package/dist/core/fences.js.map +1 -1
- package/dist/core/fetch-error.d.ts +1 -1
- package/dist/core/fetch-error.js +3 -1
- package/dist/core/fetch-error.js.map +1 -1
- package/dist/core/hash.d.ts +5 -0
- package/dist/core/hash.js +11 -0
- package/dist/core/hash.js.map +1 -0
- package/dist/core/http-backend.d.ts +11 -0
- package/dist/core/http-backend.js +195 -0
- package/dist/core/http-backend.js.map +1 -0
- package/dist/core/index.d.ts +15 -9
- package/dist/core/index.js +13 -9
- package/dist/core/palette.d.ts +48 -0
- package/dist/core/palette.js +95 -0
- package/dist/core/palette.js.map +1 -0
- package/dist/core/schema/abilities.d.ts +2 -1
- package/dist/core/schema/abilities.js +5 -1
- package/dist/core/schema/abilities.js.map +1 -1
- package/dist/core/schema/index.d.ts +11 -5
- package/dist/core/schema/index.js +13 -8
- package/dist/core/schema/index.js.map +1 -1
- package/dist/core/schema/mark-flags.d.ts +8 -1
- package/dist/core/schema/of-features.d.ts +13 -0
- package/dist/core/schema/of-features.js +14 -0
- package/dist/core/schema/of-features.js.map +1 -0
- package/dist/core/slash.d.ts +8 -6
- package/dist/core/slash.js +24 -16
- package/dist/core/slash.js.map +1 -1
- package/dist/core/surface-session.js +15 -5
- package/dist/core/surface-session.js.map +1 -1
- package/dist/core/toolbar-items.d.ts +10 -4
- package/dist/core/toolbar-items.js +269 -195
- package/dist/core/toolbar-items.js.map +1 -1
- package/dist/core/transaction-guards.d.ts +21 -0
- package/dist/core/transaction-guards.js +49 -0
- package/dist/core/transaction-guards.js.map +1 -0
- package/dist/core/util.js +11 -0
- package/dist/core/util.js.map +1 -0
- package/dist/document/DocumentSurface.d.ts +1 -1
- package/dist/document/DocumentSurface.vue.d.ts +2 -2
- package/dist/features/ai/agent-op-review.d.ts +1 -1
- package/dist/features/ai/agent-op-review.js +1 -1
- package/dist/features/ai/agent-ops.js +1 -1
- package/dist/features/ai/ai-apply.d.ts +2 -2
- package/dist/features/ai/ai-apply.js +1 -1
- package/dist/features/ai/ai-review.js +1 -1
- package/dist/features/ai/ai-sse.d.ts +6 -6
- package/dist/features/ai/ai-sse.js +20 -63
- package/dist/features/ai/ai-sse.js.map +1 -1
- package/dist/features/ai/assistant.d.ts +8 -7
- package/dist/features/ai/assistant.js +3 -3
- package/dist/features/ai/assistant.js.map +1 -1
- package/dist/features/ai/block-actions.d.ts +3 -2
- package/dist/features/ai/block-actions.js +3 -2
- package/dist/features/ai/block-actions.js.map +1 -1
- package/dist/features/ai/components/AiSelectionMenu.js +1 -1
- package/dist/features/ai/components/DocumentChatAnswer.js +1 -1
- package/dist/features/ai/components/DocumentChatOps.vue.d.ts +1 -1
- package/dist/features/ai/components/SettingsModelProfiles.js +8 -8
- package/dist/features/ai/components/SettingsModelProfiles.js.map +1 -1
- package/dist/features/ai/document-chat-model.d.ts +8 -7
- package/dist/features/ai/document-chat-model.js +5 -5
- package/dist/features/ai/document-chat-model.js.map +1 -1
- package/dist/features/ai/document-chat.js +1 -1
- package/dist/features/ai/feature.js +27 -14
- package/dist/features/ai/feature.js.map +1 -1
- package/dist/features/ai/ghost-autocomplete.d.ts +7 -6
- package/dist/features/ai/ghost-autocomplete.js +6 -9
- package/dist/features/ai/ghost-autocomplete.js.map +1 -1
- package/dist/features/ai/index.d.ts +4 -3
- package/dist/features/ai/index.js +10 -10
- package/dist/features/ai/model-profiles.d.ts +5 -4
- package/dist/features/ai/model-profiles.js +2 -2
- package/dist/features/ai/model-profiles.js.map +1 -1
- package/dist/features/ai/protocol/block-lines.js +1 -1
- package/dist/features/ai/protocol/block-lines.js.map +1 -1
- package/dist/features/ai/protocol/chat.js +6 -2
- package/dist/features/ai/protocol/chat.js.map +1 -1
- package/dist/features/ai/protocol/index.d.ts +3 -2
- package/dist/features/ai/protocol/models.d.ts +2 -16
- package/dist/features/ai/protocol/models.js.map +1 -1
- package/dist/features/ai/rate-limit.d.ts +1 -1
- package/dist/features/ai/rate-limit.js +3 -1
- package/dist/features/ai/rate-limit.js.map +1 -1
- package/dist/features/ai/react/AiSelectionMenu.js +2 -2
- package/dist/features/ai/react/AiSidebar.js +4 -4
- package/dist/features/ai/react/DocumentChatAnswer.js +1 -1
- package/dist/features/ai/react/DocumentChatOps.d.ts +1 -1
- package/dist/features/ai/react/DocumentChatOps.js +1 -1
- package/dist/features/ai/react/SettingsModelProfiles.js +8 -10
- package/dist/features/ai/react/SettingsModelProfiles.js.map +1 -1
- package/dist/features/ai/react/use-ai-assistant.d.ts +1 -1
- package/dist/features/ai/react/use-ai-assistant.js +3 -3
- package/dist/features/ai/react/use-ai-assistant.js.map +1 -1
- package/dist/features/ai/react/use-document-chat.js +2 -2
- package/dist/features/ai/react/use-document-chat.js.map +1 -1
- package/dist/features/ai/react/use-model-profiles.d.ts +2 -1
- package/dist/features/ai/selection-flow.d.ts +1 -1
- package/dist/features/ai/selection-flow.js +1 -1
- package/dist/features/ai/sidebar-flow.d.ts +1 -1
- package/dist/features/ai/sidebar-flow.js +1 -1
- package/dist/features/ai/use-ai-assistant.d.ts +1 -1
- package/dist/features/ai/use-ai-assistant.js +3 -3
- package/dist/features/ai/use-ai-assistant.js.map +1 -1
- package/dist/features/ai/use-document-chat.js +2 -2
- package/dist/features/ai/use-document-chat.js.map +1 -1
- package/dist/features/ai/use-model-profiles.d.ts +2 -1
- package/dist/features/authors/attribution.d.ts +1 -1
- package/dist/features/authors/feature.js +12 -11
- package/dist/features/authors/feature.js.map +1 -1
- package/dist/features/block-ui/block-actions.d.ts +1 -1
- package/dist/features/block-ui/block-actions.js +1 -1
- package/dist/features/block-ui/block-actions.js.map +1 -1
- package/dist/features/block-ui/components/BlockUi.js +1 -1
- package/dist/features/block-ui/feature.js +1 -0
- package/dist/features/block-ui/feature.js.map +1 -1
- package/dist/features/block-ui/react/BlockUi.js +1 -1
- package/dist/features/collab/anchor-resolver.js +1 -1
- package/dist/features/collab/feature.js +2 -0
- package/dist/features/collab/feature.js.map +1 -1
- package/dist/features/comments/components/CommentThreadView.vue.d.ts +2 -1
- package/dist/features/comments/feature.js +12 -11
- package/dist/features/comments/feature.js.map +1 -1
- package/dist/features/comments/index.d.ts +3 -2
- package/dist/features/comments/index.js +1 -1
- package/dist/features/comments/margin-layout.d.ts +3 -2
- package/dist/features/comments/model.d.ts +3 -67
- package/dist/features/comments/model.js +2 -2
- package/dist/features/comments/model.js.map +1 -1
- package/dist/features/comments/react/CommentThreadView.d.ts +3 -2
- package/dist/features/comments/react/use-comments.d.ts +2 -1
- package/dist/features/comments/react.d.ts +1 -1
- package/dist/features/comments/schema.d.ts +2 -52
- package/dist/features/comments/schema.js.map +1 -1
- package/dist/features/comments/snapshot-equality.js.map +1 -1
- package/dist/features/comments/use-comments.d.ts +3 -2
- package/dist/features/comments/vue.d.ts +1 -1
- package/dist/features/composites/accepted-children.d.ts +1 -0
- package/dist/features/composites/accepted-children.js +16 -1
- package/dist/features/composites/accepted-children.js.map +1 -1
- package/dist/features/composites/ai-brief.js +39 -0
- package/dist/features/composites/ai-brief.js.map +1 -1
- package/dist/features/composites/bibliography-command.d.ts +11 -0
- package/dist/features/composites/bibliography-form.js +90 -0
- package/dist/features/composites/bibliography-form.js.map +1 -0
- package/dist/features/composites/bibliography.d.ts +33 -0
- package/dist/features/composites/bibliography.js +120 -0
- package/dist/features/composites/bibliography.js.map +1 -0
- package/dist/features/composites/components/BibliographyEditor.js +261 -0
- package/dist/features/composites/components/BibliographyEditor.js.map +1 -0
- package/dist/features/composites/components/CompositeChrome.js +78 -29
- package/dist/features/composites/components/CompositeChrome.js.map +1 -1
- package/dist/features/composites/components/CompositeChrome.vue.d.ts +1 -1
- package/dist/features/composites/components/CompositeDetails.js +46 -11
- package/dist/features/composites/components/CompositeDetails.js.map +1 -1
- package/dist/features/composites/components/CompositeDetails.vue.d.ts +2 -1
- package/dist/features/composites/components/CompositeFieldEditor.js +64 -5
- package/dist/features/composites/components/CompositeFieldEditor.js.map +1 -1
- package/dist/features/composites/components/CompositeInspector.js +20 -7
- package/dist/features/composites/components/CompositeInspector.js.map +1 -1
- package/dist/features/composites/components/CompositeTable.js +13 -4
- package/dist/features/composites/components/CompositeTable.js.map +1 -1
- package/dist/features/composites/components/CompositeTagsEditor.js +1 -1
- package/dist/features/composites/components/CompositeView.js +11 -1
- package/dist/features/composites/components/CompositeView.js.map +1 -1
- package/dist/features/composites/components/DerivationBadge.js +103 -0
- package/dist/features/composites/components/DerivationBadge.js.map +1 -0
- package/dist/features/composites/components/ReferenceFieldSelect.js +60 -0
- package/dist/features/composites/components/ReferenceFieldSelect.js.map +1 -0
- package/dist/features/composites/components/ReferenceView.js +122 -0
- package/dist/features/composites/components/ReferenceView.js.map +1 -0
- package/dist/features/composites/components/ReferenceView.vue.d.ts +96 -0
- package/dist/features/composites/components/SettingsEditingTab.js +1 -1
- package/dist/features/composites/convert.js +2 -2
- package/dist/features/composites/convert.js.map +1 -1
- package/dist/features/composites/derivation.d.ts +61 -0
- package/dist/features/composites/derivation.js +98 -0
- package/dist/features/composites/derivation.js.map +1 -0
- package/dist/features/composites/derived-chrome.js +69 -0
- package/dist/features/composites/derived-chrome.js.map +1 -0
- package/dist/features/composites/feature.js +159 -109
- package/dist/features/composites/feature.js.map +1 -1
- package/dist/features/composites/fences.d.ts +4 -3
- package/dist/features/composites/fences.js +51 -7
- package/dist/features/composites/fences.js.map +1 -1
- package/dist/features/composites/index.d.ts +12 -3
- package/dist/features/composites/index.js +15 -6
- package/dist/features/composites/inspector-model.d.ts +4 -1
- package/dist/features/composites/inspector-model.js +8 -4
- package/dist/features/composites/inspector-model.js.map +1 -1
- package/dist/features/composites/kinds.d.ts +16 -2
- package/dist/features/composites/kinds.js +145 -6
- package/dist/features/composites/kinds.js.map +1 -1
- package/dist/features/composites/legacy.d.ts +4 -1
- package/dist/features/composites/legacy.js +56 -1
- package/dist/features/composites/legacy.js.map +1 -1
- package/dist/features/composites/meta-display.js +45 -2
- package/dist/features/composites/meta-display.js.map +1 -1
- package/dist/features/composites/meta-keys.d.ts +7 -0
- package/dist/features/composites/meta-keys.js +21 -0
- package/dist/features/composites/meta-keys.js.map +1 -0
- package/dist/features/composites/node-views.d.ts +6 -3
- package/dist/features/composites/node-views.js +7 -3
- package/dist/features/composites/node-views.js.map +1 -1
- package/dist/features/composites/nodes.d.ts +6 -1
- package/dist/features/composites/nodes.js +33 -7
- package/dist/features/composites/nodes.js.map +1 -1
- package/dist/features/composites/react/BibliographyEditor.js +229 -0
- package/dist/features/composites/react/BibliographyEditor.js.map +1 -0
- package/dist/features/composites/react/CompositeChrome.js +67 -19
- package/dist/features/composites/react/CompositeChrome.js.map +1 -1
- package/dist/features/composites/react/CompositeDetails.d.ts +2 -1
- package/dist/features/composites/react/CompositeDetails.js +45 -6
- package/dist/features/composites/react/CompositeDetails.js.map +1 -1
- package/dist/features/composites/react/CompositeFieldEditor.d.ts +1 -1
- package/dist/features/composites/react/CompositeFieldEditor.js +43 -1
- package/dist/features/composites/react/CompositeFieldEditor.js.map +1 -1
- package/dist/features/composites/react/CompositeInspector.js +12 -6
- package/dist/features/composites/react/CompositeInspector.js.map +1 -1
- package/dist/features/composites/react/CompositeStatusMenu.js +1 -1
- package/dist/features/composites/react/CompositeSummaryEditor.js +1 -1
- package/dist/features/composites/react/CompositeTable.js +16 -2
- package/dist/features/composites/react/CompositeTable.js.map +1 -1
- package/dist/features/composites/react/CompositeTagsEditor.js +2 -2
- package/dist/features/composites/react/CompositeView.js +6 -1
- package/dist/features/composites/react/CompositeView.js.map +1 -1
- package/dist/features/composites/react/DerivationBadge.js +116 -0
- package/dist/features/composites/react/DerivationBadge.js.map +1 -0
- package/dist/features/composites/react/ReferenceFieldSelect.js +52 -0
- package/dist/features/composites/react/ReferenceFieldSelect.js.map +1 -0
- package/dist/features/composites/react/ReferenceView.d.ts +11 -0
- package/dist/features/composites/react/ReferenceView.js +115 -0
- package/dist/features/composites/react/ReferenceView.js.map +1 -0
- package/dist/features/composites/react/SettingsEditingTab.js +2 -2
- package/dist/features/composites/react.d.ts +2 -1
- package/dist/features/composites/react.js +7 -3
- package/dist/features/composites/react.js.map +1 -1
- package/dist/features/composites/reference-ai.js +16 -0
- package/dist/features/composites/reference-ai.js.map +1 -0
- package/dist/features/composites/reference-label.d.ts +19 -0
- package/dist/features/composites/reference-label.js +58 -0
- package/dist/features/composites/reference-label.js.map +1 -0
- package/dist/features/composites/reference-suggestion.d.ts +10 -0
- package/dist/features/composites/reference-suggestion.js +41 -0
- package/dist/features/composites/reference-suggestion.js.map +1 -0
- package/dist/features/composites/reference-text.d.ts +18 -0
- package/dist/features/composites/reference-text.js +89 -0
- package/dist/features/composites/reference-text.js.map +1 -0
- package/dist/features/composites/reference.d.ts +44 -0
- package/dist/features/composites/reference.js +148 -0
- package/dist/features/composites/reference.js.map +1 -0
- package/dist/features/composites/reveal-source.d.ts +32 -0
- package/dist/features/composites/reveal-source.js +89 -0
- package/dist/features/composites/reveal-source.js.map +1 -0
- package/dist/features/composites/rows.d.ts +25 -3
- package/dist/features/composites/rows.js +72 -10
- package/dist/features/composites/rows.js.map +1 -1
- package/dist/features/composites/schema.d.ts +9 -3
- package/dist/features/composites/schema.js +11 -4
- package/dist/features/composites/schema.js.map +1 -1
- package/dist/features/composites/table-model.js +12 -21
- package/dist/features/composites/table-model.js.map +1 -1
- package/dist/features/composites/vue.d.ts +3 -2
- package/dist/features/composites/vue.js +8 -4
- package/dist/features/composites/vue.js.map +1 -1
- package/dist/features/media/assets.d.ts +2 -0
- package/dist/features/media/assets.js +3 -2
- package/dist/features/media/assets.js.map +1 -1
- package/dist/features/media/components/FileView.js +13 -4
- package/dist/features/media/components/FileView.js.map +1 -1
- package/dist/features/media/feature.d.ts +3 -3
- package/dist/features/media/feature.js +21 -28
- package/dist/features/media/feature.js.map +1 -1
- package/dist/features/media/file-import.js +2 -2
- package/dist/features/media/file-import.js.map +1 -1
- package/dist/features/media/index.d.ts +2 -2
- package/dist/features/media/index.js +3 -3
- package/dist/features/media/react/AudioRecorderModal.js +1 -1
- package/dist/features/media/react/FileView.js +19 -2
- package/dist/features/media/react/FileView.js.map +1 -1
- package/dist/features/media/react.js +1 -1
- package/dist/features/media/schema.d.ts +8 -0
- package/dist/features/media/schema.js +126 -11
- package/dist/features/media/schema.js.map +1 -1
- package/dist/features/media/upload.d.ts +16 -14
- package/dist/features/media/upload.js +13 -19
- package/dist/features/media/upload.js.map +1 -1
- package/dist/features/mentions/index.d.ts +4 -3
- package/dist/features/mentions/index.js +4 -1
- package/dist/features/mentions/index.js.map +1 -1
- package/dist/features/mentions/mention-text.d.ts +1 -1
- package/dist/features/mentions/mention-text.js.map +1 -1
- package/dist/features/mentions/mention.d.ts +0 -6
- package/dist/features/mentions/mention.js.map +1 -1
- package/dist/features/mentions/metadata-defaults.d.ts +2 -2
- package/dist/features/mentions/metadata-defaults.js +3 -9
- package/dist/features/mentions/metadata-defaults.js.map +1 -1
- package/dist/features/mentions/schema.d.ts +2 -1
- package/dist/features/mentions/schema.js.map +1 -1
- package/dist/features/tables/feature.d.ts +2 -1
- package/dist/features/tables/feature.js +90 -70
- package/dist/features/tables/feature.js.map +1 -1
- package/dist/features/themes/components/SettingsAppearanceTab.js +6 -9
- package/dist/features/themes/components/SettingsAppearanceTab.js.map +1 -1
- package/dist/features/themes/feature.js +2 -0
- package/dist/features/themes/feature.js.map +1 -1
- package/dist/features/themes/index.d.ts +2 -1
- package/dist/features/themes/react/SettingsAppearanceTab.js +7 -10
- package/dist/features/themes/react/SettingsAppearanceTab.js.map +1 -1
- package/dist/features/themes/schema.d.ts +2 -6
- package/dist/features/themes/schema.js.map +1 -1
- package/dist/features/themes/theme-runtime.d.ts +1 -1
- package/dist/features/themes/theme-runtime.js +4 -2
- package/dist/features/themes/theme-runtime.js.map +1 -1
- package/dist/features/transcript/TranscriptSegmentView.js +48 -28
- package/dist/features/transcript/TranscriptSegmentView.js.map +1 -1
- package/dist/features/transcript/feature.js +21 -19
- package/dist/features/transcript/feature.js.map +1 -1
- package/dist/features/transcript/react/TranscriptSegmentView.js +55 -36
- package/dist/features/transcript/react/TranscriptSegmentView.js.map +1 -1
- package/dist/features/transcript/segment-node.d.ts +2 -0
- package/dist/features/transcript/segment-node.js +35 -7
- package/dist/features/transcript/segment-node.js.map +1 -1
- package/dist/i18n/de/composites.js +91 -1
- package/dist/i18n/de/composites.js.map +1 -1
- package/dist/i18n/de/integrations.js +49 -8
- package/dist/i18n/de/integrations.js.map +1 -1
- package/dist/i18n/de/media.js +0 -1
- package/dist/i18n/de/media.js.map +1 -1
- package/dist/i18n/de/palette.js +22 -0
- package/dist/i18n/de/palette.js.map +1 -0
- package/dist/i18n/de/tables.js +0 -1
- package/dist/i18n/de/tables.js.map +1 -1
- package/dist/i18n/de/transcript.js +2 -1
- package/dist/i18n/de/transcript.js.map +1 -1
- package/dist/i18n/de/tree.js +3 -0
- package/dist/i18n/de/tree.js.map +1 -1
- package/dist/i18n/de.d.ts +157 -11
- package/dist/i18n/de.js +3 -1
- package/dist/i18n/de.js.map +1 -1
- package/dist/i18n/en/composites.js +89 -1
- package/dist/i18n/en/composites.js.map +1 -1
- package/dist/i18n/en/integrations.js +49 -8
- package/dist/i18n/en/integrations.js.map +1 -1
- package/dist/i18n/en/media.js +0 -1
- package/dist/i18n/en/media.js.map +1 -1
- package/dist/i18n/en/palette.js +22 -0
- package/dist/i18n/en/palette.js.map +1 -0
- package/dist/i18n/en/tables.js +0 -1
- package/dist/i18n/en/tables.js.map +1 -1
- package/dist/i18n/en/transcript.js +2 -1
- package/dist/i18n/en/transcript.js.map +1 -1
- package/dist/i18n/en/tree.js +3 -0
- package/dist/i18n/en/tree.js.map +1 -1
- package/dist/i18n/en.js +3 -1
- package/dist/i18n/en.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/integrations/composite-ai/bibliography-fetch.d.ts +31 -0
- package/dist/integrations/composite-ai/bibliography-fetch.js +159 -0
- package/dist/integrations/composite-ai/bibliography-fetch.js.map +1 -0
- package/dist/integrations/composite-ai/composite-fill.d.ts +30 -8
- package/dist/integrations/composite-ai/composite-fill.js +97 -56
- package/dist/integrations/composite-ai/composite-fill.js.map +1 -1
- package/dist/integrations/composite-ai/composite-summary.d.ts +4 -12
- package/dist/integrations/composite-ai/composite-summary.js +7 -26
- package/dist/integrations/composite-ai/composite-summary.js.map +1 -1
- package/dist/integrations/composite-ai/derive.d.ts +29 -0
- package/dist/integrations/composite-ai/derive.js +173 -0
- package/dist/integrations/composite-ai/derive.js.map +1 -0
- package/dist/integrations/composite-ai/index.d.ts +11 -7
- package/dist/integrations/composite-ai/index.js +72 -14
- package/dist/integrations/composite-ai/index.js.map +1 -1
- package/dist/integrations/composite-ai/rendition.d.ts +45 -0
- package/dist/integrations/composite-ai/rendition.js +314 -0
- package/dist/integrations/composite-ai/rendition.js.map +1 -0
- package/dist/integrations/composite-media/index.d.ts +7 -5
- package/dist/integrations/composite-media/index.js +86 -13
- package/dist/integrations/composite-media/index.js.map +1 -1
- package/dist/integrations/transcript-ai/index.d.ts +1 -1
- package/dist/integrations/transcript-ai/index.js +5 -2
- package/dist/integrations/transcript-ai/index.js.map +1 -1
- package/dist/integrations/transcript-ai/protocol.d.ts +2 -12
- package/dist/integrations/transcript-ai/protocol.js +0 -2
- package/dist/integrations/transcript-ai/protocol.js.map +1 -1
- package/dist/integrations/transcript-ai/transcript-cleanup.d.ts +5 -5
- package/dist/integrations/transcript-ai/transcript-cleanup.js +7 -10
- package/dist/integrations/transcript-ai/transcript-cleanup.js.map +1 -1
- package/dist/markdown/MarkdownSurface.vue.d.ts +12 -11
- package/dist/markdown/MarkdownSurface2.js +3 -6
- package/dist/markdown/MarkdownSurface2.js.map +1 -1
- package/dist/markdown/document.js +1 -1
- package/dist/markdown/source-mode.d.ts +1 -1
- package/dist/nuxt/module.d.ts +1402 -54
- package/dist/nuxt/module.js +6 -49
- package/dist/nuxt/module.js.map +1 -1
- package/dist/nuxt/runtime/install-context.d.ts +7 -3
- package/dist/nuxt/runtime/install-context.js +14 -8
- package/dist/nuxt/runtime/install-context.js.map +1 -1
- package/dist/nuxt/runtime/plugins/editor-context-nuxt-ui.client.ts +2 -2
- package/dist/nuxt/runtime/server/plugins/editor-server.js +20 -12
- package/dist/nuxt/runtime/server/plugins/editor-server.js.map +1 -1
- package/dist/presets/compose.d.ts +94 -0
- package/dist/presets/compose.js +164 -0
- package/dist/presets/compose.js.map +1 -0
- package/dist/react/AdvancedEditor.d.ts +15 -15
- package/dist/react/AdvancedEditor.js +15 -13
- package/dist/react/AdvancedEditor.js.map +1 -1
- package/dist/react/EditorCommandPalette.d.ts +13 -0
- package/dist/react/EditorCommandPalette.js +118 -0
- package/dist/react/EditorCommandPalette.js.map +1 -0
- package/dist/react/EditorSettingsModal.d.ts +5 -5
- package/dist/react/EditorSettingsModal.js +7 -4
- package/dist/react/EditorSettingsModal.js.map +1 -1
- package/dist/react/EditorSettingsPanel.d.ts +7 -7
- package/dist/react/EditorSettingsPanel.js +8 -5
- package/dist/react/EditorSettingsPanel.js.map +1 -1
- package/dist/react/EditorSurface.d.ts +7 -7
- package/dist/react/EditorSurface.js +10 -6
- package/dist/react/EditorSurface.js.map +1 -1
- package/dist/react/EditorToolbar.d.ts +4 -6
- package/dist/react/EditorToolbar.js +29 -37
- package/dist/react/EditorToolbar.js.map +1 -1
- package/dist/react/SimpleEditor.d.ts +22 -23
- package/dist/react/SimpleEditor.js +21 -7
- package/dist/react/SimpleEditor.js.map +1 -1
- package/dist/react/advanced.d.ts +4 -3
- package/dist/react/advanced.js +2 -2
- package/dist/react/compose.d.ts +20 -0
- package/dist/react/compose.js +49 -0
- package/dist/react/compose.js.map +1 -0
- package/dist/react/composed-surface.d.ts +39 -0
- package/dist/react/composed-surface.js +71 -0
- package/dist/react/composed-surface.js.map +1 -0
- package/dist/react/context.js +6 -2
- package/dist/react/context.js.map +1 -1
- package/dist/react/index.d.ts +2 -1
- package/dist/react/index.js +2 -1
- package/dist/react/key-labels.js +15 -0
- package/dist/react/key-labels.js.map +1 -0
- package/dist/react/settings.d.ts +1 -1
- package/dist/react/simple.d.ts +2 -2
- package/dist/react/ui/command.js +36 -1
- package/dist/react/ui/command.js.map +1 -1
- package/dist/schema/advanced.d.ts +6 -6
- package/dist/schema/advanced.js +26 -29
- package/dist/schema/advanced.js.map +1 -1
- package/dist/server/ai.d.ts +5 -16
- package/dist/server/ai.js.map +1 -1
- package/dist/server/auth.d.ts +9 -0
- package/dist/server/collab-state.d.ts +1 -1
- package/dist/server/collab-state.js.map +1 -1
- package/dist/server/collab.d.ts +13 -13
- package/dist/server/collab.js +34 -18
- package/dist/server/collab.js.map +1 -1
- package/dist/server/crossref.js +78 -0
- package/dist/server/crossref.js.map +1 -0
- package/dist/server/errors.d.ts +1 -1
- package/dist/server/errors.js +1 -1
- package/dist/server/errors.js.map +1 -1
- package/dist/server/extract.js +308 -0
- package/dist/server/extract.js.map +1 -0
- package/dist/server/h3.d.ts +8 -0
- package/dist/{nuxt/runtime/server/utils/editor-route.js → server/h3.js} +8 -16
- package/dist/server/h3.js.map +1 -0
- package/dist/server/handlers/ai.d.ts +32 -10
- package/dist/server/handlers/ai.js +172 -37
- package/dist/server/handlers/ai.js.map +1 -1
- package/dist/server/handlers/context.d.ts +12 -5
- package/dist/server/handlers/context.js +10 -4
- package/dist/server/handlers/context.js.map +1 -1
- package/dist/server/handlers/editor.d.ts +19 -13
- package/dist/server/handlers/editor.js +49 -38
- package/dist/server/handlers/editor.js.map +1 -1
- package/dist/server/handlers/index.d.ts +4 -4
- package/dist/server/handlers/index.js +4 -4
- package/dist/server/host.d.ts +16 -25
- package/dist/server/host.js +58 -41
- package/dist/server/host.js.map +1 -1
- package/dist/server/index.d.ts +6 -4
- package/dist/server/index.js +3 -2
- package/dist/server/pdf.js +97 -0
- package/dist/server/pdf.js.map +1 -0
- package/dist/server/{adapter.d.ts → ports.d.ts} +81 -30
- package/dist/server/ports.js +29 -0
- package/dist/server/ports.js.map +1 -0
- package/dist/server/routes.d.ts +22 -0
- package/dist/server/routes.js +144 -0
- package/dist/server/routes.js.map +1 -0
- package/dist/settings/index.d.ts +1 -1
- package/dist/settings/index.js.map +1 -1
- package/dist/styles/composites.css +25 -0
- package/dist/tree/DocumentTree.js +56 -29
- package/dist/tree/DocumentTree.js.map +1 -1
- package/dist/tree/react/DocumentTree.js +23 -0
- package/dist/tree/react/DocumentTree.js.map +1 -1
- package/dist/tree/tree-host.js +2 -2
- package/dist/tree/tree-items.js +1 -1
- package/dist/vue/AdvancedEditor.js +30 -9
- package/dist/vue/AdvancedEditor.js.map +1 -1
- package/dist/vue/AdvancedEditor.vue.d.ts +32 -12
- package/dist/vue/SimpleEditor.js +62 -16
- package/dist/vue/SimpleEditor.js.map +1 -1
- package/dist/vue/SimpleEditor.vue.d.ts +39 -9
- package/dist/vue/advanced.d.ts +3 -2
- package/dist/vue/advanced.js +2 -2
- package/dist/vue/components/EditorCommandPalette.js +91 -0
- package/dist/vue/components/EditorCommandPalette.js.map +1 -0
- package/dist/vue/components/EditorCommandPalette.vue.d.ts +15 -0
- package/dist/vue/components/EditorSettingsModal.js +10 -6
- package/dist/vue/components/EditorSettingsModal.js.map +1 -1
- package/dist/vue/components/EditorSettingsModal.vue.d.ts +4 -4
- package/dist/vue/components/EditorSettingsPanel.js +10 -6
- package/dist/vue/components/EditorSettingsPanel.js.map +1 -1
- package/dist/vue/components/EditorSettingsPanel.vue.d.ts +6 -6
- package/dist/vue/components/EditorSurface.js +11 -6
- package/dist/vue/components/EditorSurface.js.map +1 -1
- package/dist/vue/components/EditorSurface.vue.d.ts +11 -11
- package/dist/vue/components/EditorToolbar.js +41 -49
- package/dist/vue/components/EditorToolbar.js.map +1 -1
- package/dist/vue/components/EditorToolbar.vue.d.ts +2 -4
- package/dist/vue/compose.d.ts +17 -0
- package/dist/vue/compose.js +51 -0
- package/dist/vue/compose.js.map +1 -0
- package/dist/vue/context-key.d.ts +6 -0
- package/dist/vue/context-key.js +6 -0
- package/dist/vue/context-key.js.map +1 -0
- package/dist/vue/context.d.ts +8 -6
- package/dist/vue/context.js +6 -5
- package/dist/vue/context.js.map +1 -1
- package/dist/vue/index.d.ts +10 -8
- package/dist/vue/index.js +10 -8
- package/dist/vue/kit/index.d.ts +2 -2
- package/dist/vue/kit/nuxt-ui.js +23 -0
- package/dist/vue/kit/nuxt-ui.js.map +1 -1
- package/dist/vue/kit/types.d.ts +40 -1
- package/package.json +30 -16
- package/dist/adapters/comments/client.d.ts +0 -64
- package/dist/adapters/comments/client.js.map +0 -1
- package/dist/adapters/routes.js.map +0 -1
- package/dist/core/toolbar.d.ts +0 -74
- package/dist/core/toolbar.js +0 -45
- package/dist/core/toolbar.js.map +0 -1
- package/dist/integrations/composite-ai/document-transcribe.d.ts +0 -14
- package/dist/integrations/composite-ai/document-transcribe.js +0 -89
- package/dist/integrations/composite-ai/document-transcribe.js.map +0 -1
- package/dist/nuxt/runtime/server/api/ai/chat.post.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/ai/chat.post.js +0 -8
- package/dist/nuxt/runtime/server/api/ai/chat.post.js.map +0 -1
- package/dist/nuxt/runtime/server/api/ai/complete.post.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/ai/complete.post.js +0 -8
- package/dist/nuxt/runtime/server/api/ai/complete.post.js.map +0 -1
- package/dist/nuxt/runtime/server/api/ai/generate.post.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/ai/generate.post.js +0 -8
- package/dist/nuxt/runtime/server/api/ai/generate.post.js.map +0 -1
- package/dist/nuxt/runtime/server/api/ai/models.get.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/ai/models.get.js +0 -8
- package/dist/nuxt/runtime/server/api/ai/models.get.js.map +0 -1
- package/dist/nuxt/runtime/server/api/ai/transcribe.post.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/ai/transcribe.post.js +0 -8
- package/dist/nuxt/runtime/server/api/ai/transcribe.post.js.map +0 -1
- package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.js +0 -8
- package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/assets/[id].post.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/assets/[id].post.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/assets/[id].post.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/settings.get.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/settings.get.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/settings.get.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/settings.put.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/settings.put.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/settings.put.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/tags.get.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/tags.get.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/tags.get.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/themes/[id].get.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/themes/[id].get.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/themes/[id].get.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/themes/index.get.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/themes/index.get.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/themes/index.get.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/themes/index.post.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/themes/index.post.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/themes/index.post.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/users.get.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/users.get.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/users.get.js.map +0 -1
- package/dist/nuxt/runtime/server/plugins/collab.d.ts +0 -5
- package/dist/nuxt/runtime/server/plugins/collab.js +0 -10
- package/dist/nuxt/runtime/server/plugins/collab.js.map +0 -1
- package/dist/nuxt/runtime/server/routes/collab/[id].d.ts +0 -5
- package/dist/nuxt/runtime/server/routes/collab/[id].js +0 -35
- package/dist/nuxt/runtime/server/routes/collab/[id].js.map +0 -1
- package/dist/nuxt/runtime/server/utils/editor-route.d.ts +0 -11
- package/dist/nuxt/runtime/server/utils/editor-route.js.map +0 -1
- package/dist/presets/advanced.d.ts +0 -33
- package/dist/presets/advanced.js +0 -50
- package/dist/presets/advanced.js.map +0 -1
- package/dist/presets/simple.d.ts +0 -9
- package/dist/presets/simple.js +0 -12
- package/dist/presets/simple.js.map +0 -1
- package/dist/react/advanced-preset.d.ts +0 -11
- package/dist/react/advanced-preset.js +0 -31
- package/dist/react/advanced-preset.js.map +0 -1
- package/dist/vue/advanced-preset.d.ts +0 -13
- package/dist/vue/advanced-preset.js +0 -33
- package/dist/vue/advanced-preset.js.map +0 -1
- package/docs/extending.md +0 -538
- package/docs/integration.md +0 -632
- package/docs/llms.md +0 -383
- package/docs/usage.md +0 -1076
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","names":[],"sources":["../../../src/adapters/comments/server.ts"],"sourcesContent":["import { isPortableAnchor } from '../anchors'\nimport { CommentStoreError, invalid } from './errors'\nimport type { Annotation, Comment, CommentStore, CreateThreadInput, ThreadStatus } from './types'\n\n// Framework-free REST handler of a document's comments endpoint: the host routes `GET`/`POST` of\n// `<base>/<documentId>` here after its own authentication and authorisation (the handler trusts the caller, including\n// the `authorId` of new comments), and answers with `status` and `body`. Nitro, Express or a test's fetch stub all fit.\n\nexport type { Annotation, Comment, CommentAction, CommentStore, CommentThread, CreateThreadInput, Discussion, NewThread, ThreadStatus } from './types'\nexport { CommentStoreError } from './errors'\n\nexport interface CommentsRequest {\n method: string\n documentId: string\n /** The parsed JSON body of a `POST`. */\n body?: unknown\n}\n\nexport interface CommentsResponse {\n status: number\n /** JSON; `{ error }` for a refused request. */\n body: unknown\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null && !Array.isArray(value)\nconst isText = (value: unknown): value is string => typeof value === 'string'\nconst isId = (value: unknown): value is string => isText(value) && value !== ''\n\nfunction commentOf(value: unknown): Comment {\n if (!isRecord(value) || !isId(value.id) || !isText(value.body) || !isId(value.authorId) || !isId(value.createdAt)) throw invalid('Ein Kommentar braucht id, body, authorId und createdAt')\n for (const key of ['authorName', 'authorColor', 'editedAt']) {\n if (value[key] !== undefined && !isText(value[key])) throw invalid(`Kommentar-Feld „${key}“ muss Text sein`)\n }\n const { id, body, authorId, createdAt } = value\n const { authorName, authorColor, editedAt } = value as Record<string, string | undefined>\n return { id, body, authorId, createdAt, ...(authorName !== undefined && { authorName }), ...(authorColor !== undefined && { authorColor }), ...(editedAt !== undefined && { editedAt }) }\n}\n\nfunction annotationOf(value: unknown): Annotation {\n if (!isRecord(value) || !isId(value.id) || !isId(value.threadId) || !isPortableAnchor(value.anchor)) throw invalid('Eine Annotation braucht id, threadId und einen gültigen anchor')\n return { id: value.id, threadId: value.threadId, anchor: value.anchor }\n}\n\nfunction threadInputOf(value: unknown): CreateThreadInput {\n if (!isRecord(value) || !isId(value.id) || !isId(value.annotationId)) throw invalid('Ein Thread braucht id, annotationId und initialComment')\n return { id: value.id, annotationId: value.annotationId, initialComment: commentOf(value.initialComment) }\n}\n\nfunction idOf(value: Record<string, unknown>, key: string): string {\n const id = value[key]\n if (!isId(id)) throw invalid(`„${key}“ muss ein nicht leerer String sein`)\n return id\n}\n\nfunction statusOf(value: unknown): ThreadStatus {\n if (value !== 'open' && value !== 'resolved') throw invalid('„status“ muss „open“ oder „resolved“ sein')\n return value\n}\n\nasync function perform(store: CommentStore, documentId: string, body: unknown): Promise<unknown> {\n if (!isRecord(body)) throw invalid('Der Request-Body muss ein JSON-Objekt mit „action“ sein')\n switch (body.action) {\n case 'create':\n return store.createThread(documentId, { annotation: annotationOf(body.annotation), thread: threadInputOf(body.thread) })\n case 'reply':\n return { thread: await store.addComment(documentId, idOf(body, 'threadId'), commentOf(body.comment)) }\n case 'status':\n return { thread: await store.setThreadStatus(documentId, idOf(body, 'threadId'), statusOf(body.status)) }\n case 'update-annotation':\n return { annotation: await store.updateAnchor(documentId, annotationOf(body.annotation)) }\n case 'remove':\n await store.removeThread(documentId, idOf(body, 'threadId'))\n return { ok: true }\n case 'edit':\n if (!isText(body.body)) throw invalid('„body“ muss Text sein')\n return { thread: await store.editComment(documentId, idOf(body, 'threadId'), idOf(body, 'commentId'), body.body, isId(body.editedAt) ? body.editedAt : new Date().toISOString()) }\n case 'remove-comment':\n return { thread: await store.removeComment(documentId, idOf(body, 'threadId'), idOf(body, 'commentId')) }\n default:\n throw invalid('Unbekannte Kommentar-Aktion')\n }\n}\n\n/**\n * `GET` → `200` with the document's `Discussion`; `POST` with a `CommentAction` → `200` with the changed `thread`/`annotation`\n * (`create`: both; `remove`: `{ ok: true }`). Refused requests answer `404` (unknown ids), `409` (duplicate ids), `400`\n * (malformed input) or `405` with `{ error }`; anything else is a server fault and is thrown.\n */\nexport async function handleCommentsRequest(store: CommentStore, request: CommentsRequest): Promise<CommentsResponse> {\n const method = request.method.toUpperCase()\n try {\n if (method === 'GET') return { status: 200, body: await store.getDiscussion(request.documentId) }\n if (method === 'POST') return { status: 200, body: await perform(store, request.documentId, request.body) }\n return { status: 405, body: { error: `Methode ${request.method} ist nicht erlaubt` } } // i18n-ignore: HTTP error body of the comments route (server messages stay German, like src/server)\n }\n catch (error) {\n if (error instanceof CommentStoreError) return { status: error.status, body: { error: error.message } }\n throw error\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.js","names":[],"sources":["../../../src/adapters/comments/server.ts"],"sourcesContent":["import { isPortableAnchor } from '../anchors'\nimport { CommentStoreError, invalid } from './errors'\nimport type { Annotation, Comment, CommentStore, CreateThreadInput, ThreadStatus } from './types'\n\n// Framework-free REST handler of a document's comments endpoint: the host routes `GET`/`POST` of\n// `<base>/<documentId>` here after its own authentication and authorisation (the handler trusts the caller, including\n// the `authorId` of new comments), and answers with `status` and `body`. Nitro, Express or a test's fetch stub all fit.\n\nexport type { Annotation, Comment, CommentAction, CommentStore, CommentThread, CreateThreadInput, Discussion, NewThread, ThreadStatus } from './types'\nexport { CommentStoreError } from './errors'\n// The pure operations of a stored `Discussion` (the JSON store is built from them): a host `CommentStore` over its own database applies them to the record it loaded and writes the record back. They change the record in place and throw `CommentStoreError` for unknown ids, duplicates and malformed input.\nexport { addComment, createThread, editComment, emptyDiscussion, removeComment, removeThread, setThreadStatus, updateAnchor } from './record'\n\nexport interface CommentsRequest {\n method: string\n documentId: string\n /** The parsed JSON body of a `POST`. */\n body?: unknown\n}\n\nexport interface CommentsResponse {\n status: number\n /** JSON; `{ error }` for a refused request. */\n body: unknown\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null && !Array.isArray(value)\nconst isText = (value: unknown): value is string => typeof value === 'string'\nconst isId = (value: unknown): value is string => isText(value) && value !== ''\n\nfunction commentOf(value: unknown): Comment {\n if (!isRecord(value) || !isId(value.id) || !isText(value.body) || !isId(value.authorId) || !isId(value.createdAt)) throw invalid('Ein Kommentar braucht id, body, authorId und createdAt')\n for (const key of ['authorName', 'authorColor', 'editedAt']) {\n if (value[key] !== undefined && !isText(value[key])) throw invalid(`Kommentar-Feld „${key}“ muss Text sein`)\n }\n const { id, body, authorId, createdAt } = value\n const { authorName, authorColor, editedAt } = value as Record<string, string | undefined>\n return { id, body, authorId, createdAt, ...(authorName !== undefined && { authorName }), ...(authorColor !== undefined && { authorColor }), ...(editedAt !== undefined && { editedAt }) }\n}\n\nfunction annotationOf(value: unknown): Annotation {\n if (!isRecord(value) || !isId(value.id) || !isId(value.threadId) || !isPortableAnchor(value.anchor)) throw invalid('Eine Annotation braucht id, threadId und einen gültigen anchor')\n return { id: value.id, threadId: value.threadId, anchor: value.anchor }\n}\n\nfunction threadInputOf(value: unknown): CreateThreadInput {\n if (!isRecord(value) || !isId(value.id) || !isId(value.annotationId)) throw invalid('Ein Thread braucht id, annotationId und initialComment')\n return { id: value.id, annotationId: value.annotationId, initialComment: commentOf(value.initialComment) }\n}\n\nfunction idOf(value: Record<string, unknown>, key: string): string {\n const id = value[key]\n if (!isId(id)) throw invalid(`„${key}“ muss ein nicht leerer String sein`)\n return id\n}\n\nfunction statusOf(value: unknown): ThreadStatus {\n if (value !== 'open' && value !== 'resolved') throw invalid('„status“ muss „open“ oder „resolved“ sein')\n return value\n}\n\nasync function perform(store: CommentStore, documentId: string, body: unknown): Promise<unknown> {\n if (!isRecord(body)) throw invalid('Der Request-Body muss ein JSON-Objekt mit „action“ sein')\n switch (body.action) {\n case 'create':\n return store.createThread(documentId, { annotation: annotationOf(body.annotation), thread: threadInputOf(body.thread) })\n case 'reply':\n return { thread: await store.addComment(documentId, idOf(body, 'threadId'), commentOf(body.comment)) }\n case 'status':\n return { thread: await store.setThreadStatus(documentId, idOf(body, 'threadId'), statusOf(body.status)) }\n case 'update-annotation':\n return { annotation: await store.updateAnchor(documentId, annotationOf(body.annotation)) }\n case 'remove':\n await store.removeThread(documentId, idOf(body, 'threadId'))\n return { ok: true }\n case 'edit':\n if (!isText(body.body)) throw invalid('„body“ muss Text sein')\n return { thread: await store.editComment(documentId, idOf(body, 'threadId'), idOf(body, 'commentId'), body.body, isId(body.editedAt) ? body.editedAt : new Date().toISOString()) }\n case 'remove-comment':\n return { thread: await store.removeComment(documentId, idOf(body, 'threadId'), idOf(body, 'commentId')) }\n default:\n throw invalid('Unbekannte Kommentar-Aktion')\n }\n}\n\n/**\n * `GET` → `200` with the document's `Discussion`; `POST` with a `CommentAction` → `200` with the changed `thread`/`annotation`\n * (`create`: both; `remove`: `{ ok: true }`). Refused requests answer `404` (unknown ids), `409` (duplicate ids), `400`\n * (malformed input) or `405` with `{ error }`; anything else is a server fault and is thrown.\n */\nexport async function handleCommentsRequest(store: CommentStore, request: CommentsRequest): Promise<CommentsResponse> {\n const method = request.method.toUpperCase()\n try {\n if (method === 'GET') return { status: 200, body: await store.getDiscussion(request.documentId) }\n if (method === 'POST') return { status: 200, body: await perform(store, request.documentId, request.body) }\n return { status: 405, body: { error: `Methode ${request.method} ist nicht erlaubt` } } // i18n-ignore: HTTP error body of the comments route (server messages stay German, like src/server)\n }\n catch (error) {\n if (error instanceof CommentStoreError) return { status: error.status, body: { error: error.message } }\n throw error\n }\n}\n"],"mappings":"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"}
|
|
@@ -73,8 +73,8 @@ export interface CommentStore {
|
|
|
73
73
|
removeDocuments(prefix: string): Promise<number>;
|
|
74
74
|
}
|
|
75
75
|
/**
|
|
76
|
-
* The body of a `POST` to a document's comments endpoint (`handleCommentsRequest` answers it,
|
|
77
|
-
* sends it). `GET` answers the document's `Discussion`. The first five actions are skaile-forge's original wire format.
|
|
76
|
+
* The body of a `POST` to a document's comments endpoint (`handleCommentsRequest` answers it, the REST comments model of
|
|
77
|
+
* `createHttpBackend().comments` sends it). `GET` answers the document's `Discussion`. The first five actions are skaile-forge's original wire format.
|
|
78
78
|
*/
|
|
79
79
|
export type CommentAction = {
|
|
80
80
|
action: 'create';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { CollaborationToken, EditorBackend } from "../core/backend.js";
|
|
2
|
+
import { AuthorIdentity } from "../core/feature.js";
|
|
3
|
+
import "../core/index.js";
|
|
4
|
+
import { JSONContent } from "@tiptap/core";
|
|
5
|
+
//#region src/adapters/document-state.d.ts
|
|
6
|
+
export interface EditorDocumentOptions {
|
|
7
|
+
/** Where documents, the collaboration session and the comments live. Default: the backend of the editor context (`createEditor` / `EditorContextProvider`). Collaboration and comments need one that has them, e.g. `createHttpBackend()` (`@ecsplico/editor/adapters/http`). */
|
|
8
|
+
backend?: EditorBackend;
|
|
9
|
+
/**
|
|
10
|
+
* Live collaboration: `true`, or `{ token }` for the token the collaboration server's `Identity.authenticate` receives.
|
|
11
|
+
* With it the Y.Doc is the document: the server loads and stores the content, so the hook neither loads nor saves it
|
|
12
|
+
* (`modelValue` stays `undefined`, `dirty` stays `false`, `save()` resolves at once). Needs `backend.collaboration`.
|
|
13
|
+
*/
|
|
14
|
+
collaboration?: boolean | {
|
|
15
|
+
token?: CollaborationToken;
|
|
16
|
+
};
|
|
17
|
+
/** Who writes: labels remote carets and is the author of new comments; `null` or omitted = nobody (no comments). */
|
|
18
|
+
user?: AuthorIdentity | null | (() => AuthorIdentity | null);
|
|
19
|
+
/** Whether the user may comment now (a reader of a read-only document may be refused); default `true`. */
|
|
20
|
+
canComment?: boolean | (() => boolean);
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
//# sourceMappingURL=document-state.d.ts.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
//#region src/adapters/document-state.ts
|
|
2
|
+
const EMPTY_DOCUMENT = {
|
|
3
|
+
type: "doc",
|
|
4
|
+
content: [{ type: "paragraph" }]
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* The document as compared for "unsaved changes": without block ids. The editor gives every block of loaded content an
|
|
8
|
+
* id the moment it mounts (stored files have none yet); that is not an edit the user made, so it must not mark a
|
|
9
|
+
* freshly opened document dirty. Saving still writes the ids.
|
|
10
|
+
*/
|
|
11
|
+
const withoutBlockIds = (node) => {
|
|
12
|
+
const { attrs, content, ...rest } = node;
|
|
13
|
+
const kept = attrs && Object.fromEntries(Object.entries(attrs).filter(([name]) => name !== "id"));
|
|
14
|
+
return {
|
|
15
|
+
...rest,
|
|
16
|
+
...kept && Object.keys(kept).length > 0 && { attrs: kept },
|
|
17
|
+
...content && { content: content.map(withoutBlockIds) }
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
const fingerprint = (content) => JSON.stringify(withoutBlockIds(content));
|
|
21
|
+
/** The state after `documents.load` answered: `null` (no such document) starts empty. */
|
|
22
|
+
function loadedDocument(content) {
|
|
23
|
+
if (content === null) return {
|
|
24
|
+
modelValue: structuredClone(EMPTY_DOCUMENT),
|
|
25
|
+
saved: fingerprint(EMPTY_DOCUMENT),
|
|
26
|
+
missing: true
|
|
27
|
+
};
|
|
28
|
+
return {
|
|
29
|
+
modelValue: content,
|
|
30
|
+
saved: fingerprint(content),
|
|
31
|
+
missing: false
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** Edits since the content was loaded or last saved; never in collaboration mode (the Y.Doc is the document) or before the first load. */
|
|
35
|
+
function isDirty(collaborating, modelValue, saved) {
|
|
36
|
+
return !collaborating && modelValue !== void 0 && saved !== null && fingerprint(modelValue) !== saved;
|
|
37
|
+
}
|
|
38
|
+
/** The `fingerprint` a save of `content` would store, or `null` when there is nothing to save: no content yet, not ready, or unchanged and already stored. */
|
|
39
|
+
function fingerprintToSave(content, ready, saved, missing) {
|
|
40
|
+
if (content === void 0 || !ready) return null;
|
|
41
|
+
const next = fingerprint(content);
|
|
42
|
+
return next === saved && !missing ? null : next;
|
|
43
|
+
}
|
|
44
|
+
const errorOf = (cause) => cause instanceof Error ? cause : new Error(String(cause));
|
|
45
|
+
/** The `capability` of `backend`; throws the developer error when it lacks it (called at setup, not on the first request). */
|
|
46
|
+
function requireCapability(backend, capability) {
|
|
47
|
+
const port = backend[capability];
|
|
48
|
+
if (!port) throw new Error(`@ecsplico/editor: useEditorDocument needs backend capability "${capability}", which "${backend.name}" lacks`);
|
|
49
|
+
return port;
|
|
50
|
+
}
|
|
51
|
+
/** Opens the live session of document `id`; `close()` ends it (provider and Y.Doc). */
|
|
52
|
+
function openSession(collaboration, id, option, user) {
|
|
53
|
+
const { ydoc, provider, dispose } = collaboration.connect(id, typeof option === "object" ? option.token : void 0);
|
|
54
|
+
return {
|
|
55
|
+
session: {
|
|
56
|
+
ydoc,
|
|
57
|
+
provider,
|
|
58
|
+
...user && { user }
|
|
59
|
+
},
|
|
60
|
+
close: dispose
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
//#endregion
|
|
64
|
+
export { EMPTY_DOCUMENT, errorOf, fingerprint, fingerprintToSave, isDirty, loadedDocument, openSession, requireCapability };
|
|
65
|
+
|
|
66
|
+
//# sourceMappingURL=document-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document-state.js","names":[],"sources":["../../src/adapters/document-state.ts"],"sourcesContent":["import type { JSONContent } from '@tiptap/core'\nimport type { CollaborationSession } from '../core'\nimport type { CollaborationToken, EditorBackend } from '../core/backend'\nimport type { AuthorIdentity } from '../core/feature'\n\n// What the Vue and React `useEditorDocument` hooks share: their options, the empty document, the \"unsaved edits\"\n// comparison and the collaboration session built from the backend. Framework-free; nothing here talks HTTP, the hooks\n// reach documents, collaboration and comments only through `EditorBackend`.\n\nexport interface EditorDocumentOptions {\n /** Where documents, the collaboration session and the comments live. Default: the backend of the editor context (`createEditor` / `EditorContextProvider`). Collaboration and comments need one that has them, e.g. `createHttpBackend()` (`@ecsplico/editor/adapters/http`). */\n backend?: EditorBackend\n /**\n * Live collaboration: `true`, or `{ token }` for the token the collaboration server's `Identity.authenticate` receives.\n * With it the Y.Doc is the document: the server loads and stores the content, so the hook neither loads nor saves it\n * (`modelValue` stays `undefined`, `dirty` stays `false`, `save()` resolves at once). Needs `backend.collaboration`.\n */\n collaboration?: boolean | { token?: CollaborationToken }\n /** Who writes: labels remote carets and is the author of new comments; `null` or omitted = nobody (no comments). */\n user?: AuthorIdentity | null | (() => AuthorIdentity | null)\n /** Whether the user may comment now (a reader of a read-only document may be refused); default `true`. */\n canComment?: boolean | (() => boolean)\n}\n\nexport const EMPTY_DOCUMENT: JSONContent = { type: 'doc', content: [{ type: 'paragraph' }] }\n\n/**\n * The document as compared for \"unsaved changes\": without block ids. The editor gives every block of loaded content an\n * id the moment it mounts (stored files have none yet); that is not an edit the user made, so it must not mark a\n * freshly opened document dirty. Saving still writes the ids.\n */\nconst withoutBlockIds = (node: JSONContent): JSONContent => {\n const { attrs, content, ...rest } = node\n const kept = attrs && Object.fromEntries(Object.entries(attrs).filter(([name]) => name !== 'id'))\n return {\n ...rest,\n ...(kept && Object.keys(kept).length > 0 && { attrs: kept }),\n ...(content && { content: content.map(withoutBlockIds) }),\n }\n}\nexport const fingerprint = (content: JSONContent): string => JSON.stringify(withoutBlockIds(content))\n\n/** What a load leaves in the hook's state; `missing` = the document does not exist in the backend yet (the first save creates it even if nothing was typed). */\nexport interface LoadedDocument {\n modelValue: JSONContent\n /** `fingerprint` of the JSON the backend holds (the empty document while it does not exist). */\n saved: string\n missing: boolean\n}\n\n/** The state after `documents.load` answered: `null` (no such document) starts empty. */\nexport function loadedDocument(content: JSONContent | null): LoadedDocument {\n if (content === null) return { modelValue: structuredClone(EMPTY_DOCUMENT), saved: fingerprint(EMPTY_DOCUMENT), missing: true }\n return { modelValue: content, saved: fingerprint(content), missing: false }\n}\n\n/** Edits since the content was loaded or last saved; never in collaboration mode (the Y.Doc is the document) or before the first load. */\nexport function isDirty(collaborating: boolean, modelValue: JSONContent | undefined, saved: string | null): boolean {\n return !collaborating && modelValue !== undefined && saved !== null && fingerprint(modelValue) !== saved\n}\n\n/** The `fingerprint` a save of `content` would store, or `null` when there is nothing to save: no content yet, not ready, or unchanged and already stored. */\nexport function fingerprintToSave(content: JSONContent | undefined, ready: boolean, saved: string | null, missing: boolean): string | null {\n if (content === undefined || !ready) return null\n const next = fingerprint(content)\n return next === saved && !missing ? null : next\n}\n\nexport const errorOf = (cause: unknown) => cause instanceof Error ? cause : new Error(String(cause))\n\n/** The `capability` of `backend`; throws the developer error when it lacks it (called at setup, not on the first request). */\nexport function requireCapability<K extends 'documents' | 'collaboration'>(backend: EditorBackend, capability: K): NonNullable<EditorBackend[K]> {\n const port = backend[capability]\n if (!port) throw new Error(`@ecsplico/editor: useEditorDocument needs backend capability \"${capability}\", which \"${backend.name}\" lacks`)\n return port as NonNullable<EditorBackend[K]>\n}\n\n/** Opens the live session of document `id`; `close()` ends it (provider and Y.Doc). */\nexport function openSession(collaboration: NonNullable<EditorBackend['collaboration']>, id: string, option: EditorDocumentOptions['collaboration'], user: AuthorIdentity | null): { session: CollaborationSession, close: () => void } {\n const { ydoc, provider, dispose } = collaboration.connect(id, typeof option === 'object' ? option.token : undefined)\n return { session: { ydoc, provider, ...(user && { user }) }, close: dispose }\n}\n"],"mappings":";AAwBA,MAAa,iBAA8B;CAAE,MAAM;CAAO,SAAS,CAAC,EAAE,MAAM,YAAY,CAAC;AAAE;;;;;;AAO3F,MAAM,mBAAmB,SAAmC;CAC1D,MAAM,EAAE,OAAO,SAAS,GAAG,SAAS;CACpC,MAAM,OAAO,SAAS,OAAO,YAAY,OAAO,QAAQ,KAAK,CAAC,CAAC,QAAQ,CAAC,UAAU,SAAS,IAAI,CAAC;CAChG,OAAO;EACL,GAAG;EACH,GAAI,QAAQ,OAAO,KAAK,IAAI,CAAC,CAAC,SAAS,KAAK,EAAE,OAAO,KAAK;EAC1D,GAAI,WAAW,EAAE,SAAS,QAAQ,IAAI,eAAe,EAAE;CACzD;AACF;AACA,MAAa,eAAe,YAAiC,KAAK,UAAU,gBAAgB,OAAO,CAAC;;AAWpG,SAAgB,eAAe,SAA6C;CAC1E,IAAI,YAAY,MAAM,OAAO;EAAE,YAAY,gBAAgB,cAAc;EAAG,OAAO,YAAY,cAAc;EAAG,SAAS;CAAK;CAC9H,OAAO;EAAE,YAAY;EAAS,OAAO,YAAY,OAAO;EAAG,SAAS;CAAM;AAC5E;;AAGA,SAAgB,QAAQ,eAAwB,YAAqC,OAA+B;CAClH,OAAO,CAAC,iBAAiB,eAAe,KAAA,KAAa,UAAU,QAAQ,YAAY,UAAU,MAAM;AACrG;;AAGA,SAAgB,kBAAkB,SAAkC,OAAgB,OAAsB,SAAiC;CACzI,IAAI,YAAY,KAAA,KAAa,CAAC,OAAO,OAAO;CAC5C,MAAM,OAAO,YAAY,OAAO;CAChC,OAAO,SAAS,SAAS,CAAC,UAAU,OAAO;AAC7C;AAEA,MAAa,WAAW,UAAmB,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;;AAGnG,SAAgB,kBAA2D,SAAwB,YAA8C;CAC/I,MAAM,OAAO,QAAQ;CACrB,IAAI,CAAC,MAAM,MAAM,IAAI,MAAM,iEAAiE,WAAW,YAAY,QAAQ,KAAK,QAAQ;CACxI,OAAO;AACT;;AAGA,SAAgB,YAAY,eAA4D,IAAY,QAAgD,MAAmF;CACrO,MAAM,EAAE,MAAM,UAAU,YAAY,cAAc,QAAQ,IAAI,OAAO,WAAW,WAAW,OAAO,QAAQ,KAAA,CAAS;CACnH,OAAO;EAAE,SAAS;GAAE;GAAM;GAAU,GAAI,QAAQ,EAAE,KAAK;EAAG;EAAG,OAAO;CAAQ;AAC9E"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { settingsKey } from "./shared.js";
|
|
2
2
|
import { HttpError } from "../server/errors.js";
|
|
3
|
-
import { authenticated, empty, json } from "../server/handlers/context.js";
|
|
3
|
+
import { authenticated, empty, json, port } from "../server/handlers/context.js";
|
|
4
4
|
import { handleCommentsRequest } from "./comments/server.js";
|
|
5
5
|
//#region src/adapters/documents.ts
|
|
6
6
|
const isDocument = (value) => typeof value === "object" && value !== null && "type" in value && value.type === "doc";
|
|
@@ -9,10 +9,11 @@ function documentId(request) {
|
|
|
9
9
|
if (!id) throw new HttpError(400, "Dokument-ID fehlt");
|
|
10
10
|
return id;
|
|
11
11
|
}
|
|
12
|
-
/** The user and what they may do with document `id`: 403 when the
|
|
12
|
+
/** The user and what they may do with document `id`: 403 when the identity port's `authorize` refuses. */
|
|
13
13
|
async function accessOf(ctx, request, id) {
|
|
14
14
|
const user = await authenticated(ctx, request);
|
|
15
|
-
const
|
|
15
|
+
const { identity } = ctx.ports;
|
|
16
|
+
const access = identity.authorize ? await identity.authorize(user, id) : { readOnly: false };
|
|
16
17
|
if (!access) throw new HttpError(403, "Kein Zugriff auf dieses Dokument");
|
|
17
18
|
return {
|
|
18
19
|
user,
|
|
@@ -21,18 +22,18 @@ async function accessOf(ctx, request, id) {
|
|
|
21
22
|
}
|
|
22
23
|
/**
|
|
23
24
|
* The document's TipTap JSON: the live content when the collaboration server has the document open (unsaved edits
|
|
24
|
-
* included), else what the
|
|
25
|
+
* included), else what the documents port stores; `404` when there is none.
|
|
25
26
|
*/
|
|
26
27
|
async function documentGet(ctx, request) {
|
|
27
28
|
const id = documentId(request);
|
|
28
29
|
await accessOf(ctx, request, id);
|
|
29
|
-
const content = ctx.collab?.liveContent(id) ?? await ctx.
|
|
30
|
+
const content = ctx.collab?.liveContent(id) ?? await ctx.ports.documents.loadContent(id);
|
|
30
31
|
if (!content) throw new HttpError(404, "Dokument nicht gefunden");
|
|
31
32
|
return json(content);
|
|
32
33
|
}
|
|
33
34
|
/**
|
|
34
35
|
* Stores the request's TipTap JSON (`400` for anything but a `{ type: 'doc' }` object, `403` for a read-only user).
|
|
35
|
-
* Creates the document
|
|
36
|
+
* Creates the document when the documents port manages documents (`putDocument`); otherwise only existing documents can be saved.
|
|
36
37
|
*/
|
|
37
38
|
async function documentPut(ctx, request) {
|
|
38
39
|
const id = documentId(request);
|
|
@@ -45,8 +46,9 @@ async function documentPut(ctx, request) {
|
|
|
45
46
|
throw new HttpError(400, "Ungültiger JSON-Body");
|
|
46
47
|
}
|
|
47
48
|
if (!isDocument(body)) throw new HttpError(400, "Der Body muss ein TipTap-Dokument sein ({ \"type\": \"doc\", … })");
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
const { documents } = ctx.ports;
|
|
50
|
+
if (documents.putDocument) await documents.putDocument(id, body);
|
|
51
|
+
else if (await documents.loadContent(id)) await documents.storeContent(id, body);
|
|
50
52
|
else throw new HttpError(404, "Dokument nicht gefunden");
|
|
51
53
|
return empty();
|
|
52
54
|
}
|
|
@@ -54,7 +56,7 @@ async function documentPut(ctx, request) {
|
|
|
54
56
|
async function commentsGet(ctx, request) {
|
|
55
57
|
const id = documentId(request);
|
|
56
58
|
await accessOf(ctx, request, id);
|
|
57
|
-
const { status, body } = await handleCommentsRequest(ctx
|
|
59
|
+
const { status, body } = await handleCommentsRequest(port(ctx, "comments"), {
|
|
58
60
|
method: "GET",
|
|
59
61
|
documentId: id
|
|
60
62
|
});
|
|
@@ -94,8 +96,9 @@ async function commentsPost(ctx, request) {
|
|
|
94
96
|
throw new HttpError(400, "Ungültiger JSON-Body");
|
|
95
97
|
}
|
|
96
98
|
const self = settingsKey(user);
|
|
97
|
-
|
|
98
|
-
|
|
99
|
+
const store = port(ctx, "comments");
|
|
100
|
+
if ((await authorsOf(store, id, recordOf(body))).some((author) => author !== self)) throw new HttpError(403, "Kommentare dürfen nur unter der eigenen Kennung geschrieben und geändert werden");
|
|
101
|
+
const { status, body: answer } = await handleCommentsRequest(store, {
|
|
99
102
|
method: "POST",
|
|
100
103
|
documentId: id,
|
|
101
104
|
body
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"documents.js","names":[],"sources":["../../src/adapters/documents.ts"],"sourcesContent":["import type { JSONContent } from '@tiptap/core'\nimport { HttpError } from '../server'\nimport { authenticated, empty, type HandlerContext, type HandlerRequest, type HandlerResponse, json } from '../server/handlers'\nimport { settingsKey
|
|
1
|
+
{"version":3,"file":"documents.js","names":[],"sources":["../../src/adapters/documents.ts"],"sourcesContent":["import type { JSONContent } from '@tiptap/core'\nimport { HttpError } from '../server'\nimport { authenticated, empty, type HandlerContext, type HandlerRequest, type HandlerResponse, json, port } from '../server/handlers'\nimport { settingsKey } from './shared'\nimport { handleCommentsRequest } from './comments/server'\nimport type { CommentStore, Discussion } from './comments/types'\n\n// Framework-free handlers of the two routes this package adds to the editor's own (`/api/editor/*`): a document's\n// content (what `useEditorDocument` loads and saves) and its comments endpoint. They follow the editor's handler shape\n// (`(ctx, request) => HandlerResponse`, `HttpError` for refusals), so any HTTP server can mount them; `./server/nitro`\n// does so for Nitro. Authentication is `ports.identity.authenticate`, per-document access its optional `authorize`.\n\nconst isDocument = (value: unknown): value is JSONContent => typeof value === 'object' && value !== null && 'type' in value && value.type === 'doc'\n\nfunction documentId(request: HandlerRequest): string {\n const id = request.params?.id\n if (!id) throw new HttpError(400, 'Dokument-ID fehlt')\n return id\n}\n\n/** The user and what they may do with document `id`: 403 when the identity port's `authorize` refuses. */\nasync function accessOf(ctx: HandlerContext, request: HandlerRequest, id: string): Promise<{ user: object, readOnly: boolean }> {\n const user = await authenticated(ctx, request)\n const { identity } = ctx.ports\n const access = identity.authorize ? await identity.authorize(user, id) : { readOnly: false }\n if (!access) throw new HttpError(403, 'Kein Zugriff auf dieses Dokument')\n return { user, readOnly: access.readOnly }\n}\n\n/**\n * The document's TipTap JSON: the live content when the collaboration server has the document open (unsaved edits\n * included), else what the documents port stores; `404` when there is none.\n */\nexport async function documentGet(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n const id = documentId(request)\n await accessOf(ctx, request, id)\n const content = ctx.collab?.liveContent(id) ?? await ctx.ports.documents.loadContent(id)\n if (!content) throw new HttpError(404, 'Dokument nicht gefunden')\n return json(content)\n}\n\n/**\n * Stores the request's TipTap JSON (`400` for anything but a `{ type: 'doc' }` object, `403` for a read-only user).\n * Creates the document when the documents port manages documents (`putDocument`); otherwise only existing documents can be saved.\n */\nexport async function documentPut(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n const id = documentId(request)\n const { readOnly } = await accessOf(ctx, request, id)\n if (readOnly) throw new HttpError(403, 'Dokument ist schreibgeschützt')\n let body: unknown\n try {\n body = await request.readJson?.()\n }\n catch {\n throw new HttpError(400, 'Ungültiger JSON-Body')\n }\n if (!isDocument(body)) throw new HttpError(400, 'Der Body muss ein TipTap-Dokument sein ({ \"type\": \"doc\", … })')\n const { documents } = ctx.ports\n if (documents.putDocument) await documents.putDocument(id, body)\n else if (await documents.loadContent(id)) await documents.storeContent(id, body)\n else throw new HttpError(404, 'Dokument nicht gefunden')\n return empty()\n}\n\n/** `GET`: the document's `Discussion`. */\nexport async function commentsGet(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n const id = documentId(request)\n await accessOf(ctx, request, id)\n const { status, body } = await handleCommentsRequest(port(ctx, 'comments'), { method: 'GET', documentId: id })\n return json(body, status)\n}\n\nconst recordOf = (value: unknown): Record<string, unknown> => typeof value === 'object' && value !== null ? value as Record<string, unknown> : {}\n\n/** The comment authors a `CommentAction` writes (new comments) or touches (edits and deletions of existing ones). */\nasync function authorsOf(store: CommentStore, id: string, body: Record<string, unknown>): Promise<string[]> {\n // A malformed comment has no author to compare; the handler answers it with 400.\n const authorOf = (comment: unknown) => {\n const { authorId } = recordOf(comment)\n return typeof authorId === 'string' ? [authorId] : []\n }\n switch (body.action) {\n case 'create':\n return authorOf(recordOf(body.thread).initialComment)\n case 'reply':\n return authorOf(body.comment)\n case 'edit':\n case 'remove-comment': {\n const discussion: Discussion = await store.getDiscussion(id)\n const thread = discussion.threads.find(candidate => candidate.id === body.threadId)\n const comment = thread?.comments.find(candidate => candidate.id === body.commentId)\n // An unknown id is the handler's 404, not an authorisation question.\n return comment ? [comment.authorId] : []\n }\n default:\n return []\n }\n}\n\n/**\n * `POST` with a `CommentAction` (see `handleCommentsRequest`). The handler trusts its caller, so this route does the\n * authorising: read-only users write nothing, and a user only writes comments under their own id and only edits or\n * deletes their own comments (`403` otherwise).\n */\nexport async function commentsPost(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n const id = documentId(request)\n const { user, readOnly } = await accessOf(ctx, request, id)\n if (readOnly) throw new HttpError(403, 'Dokument ist schreibgeschützt')\n let body: unknown\n try {\n body = await request.readJson?.()\n }\n catch {\n throw new HttpError(400, 'Ungültiger JSON-Body')\n }\n const self = settingsKey(user)\n const store = port(ctx, 'comments')\n if ((await authorsOf(store, id, recordOf(body))).some(author => author !== self)) throw new HttpError(403, 'Kommentare dürfen nur unter der eigenen Kennung geschrieben und geändert werden')\n const { status, body: answer } = await handleCommentsRequest(store, { method: 'POST', documentId: id, body })\n return json(answer, status)\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AssetRecord } from "../features/media/assets.js";
|
|
2
2
|
import "../features/media/schema.js";
|
|
3
|
-
import { AdapterHooks,
|
|
3
|
+
import { AdapterHooks, StoredPorts } from "./shared.js";
|
|
4
4
|
import { BaseSQLiteDatabase } from "drizzle-orm/sqlite-core";
|
|
5
5
|
import { JSONContent } from "@tiptap/core";
|
|
6
6
|
//#region src/adapters/drizzle.d.ts
|
|
@@ -287,7 +287,7 @@ export interface DrizzleAdapterOptions extends AdapterHooks {
|
|
|
287
287
|
* `putDocument`. Single-statement operations only (no transaction), so the adapter runs on sync and async drivers;
|
|
288
288
|
* `deleteDocument` removes content first, so a concurrent `storeContent` finds nothing to update. No theme library.
|
|
289
289
|
*/
|
|
290
|
-
export declare function createDrizzleAdapter({ db, tables, ...hooks }: DrizzleAdapterOptions):
|
|
290
|
+
export declare function createDrizzleAdapter({ db, tables, ...hooks }: DrizzleAdapterOptions): StoredPorts;
|
|
291
291
|
//#endregion
|
|
292
292
|
export {};
|
|
293
293
|
//# sourceMappingURL=drizzle.d.ts.map
|
package/dist/adapters/drizzle.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { bytesOf,
|
|
1
|
+
import { bytesOf, directoryOf, identityOf, settingsKey } from "./shared.js";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { and, eq, sql } from "drizzle-orm";
|
|
4
4
|
import { blob, primaryKey, sqliteTable, text } from "drizzle-orm/sqlite-core";
|
|
@@ -56,98 +56,107 @@ function createDrizzleAdapter({ db, tables, ...hooks }) {
|
|
|
56
56
|
const { documents, states, settings, assets } = tables;
|
|
57
57
|
const documentContent = async (id) => (await db.select({ content: documents.content }).from(documents).where(eq(documents.id, id)).get())?.content ?? null;
|
|
58
58
|
return {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
59
|
+
identity: identityOf(hooks),
|
|
60
|
+
directory: directoryOf(hooks),
|
|
61
|
+
documents: {
|
|
62
|
+
loadContent: documentContent,
|
|
63
|
+
async storeContent(id, content) {
|
|
64
|
+
await db.update(documents).set({
|
|
65
|
+
content,
|
|
66
|
+
version: randomUUID(),
|
|
67
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
68
|
+
}).where(eq(documents.id, id)).run();
|
|
69
|
+
},
|
|
70
|
+
async contentVersion(id) {
|
|
71
|
+
return (await db.select({ version: documents.version }).from(documents).where(eq(documents.id, id)).get())?.version ?? null;
|
|
72
|
+
},
|
|
73
|
+
async listContents() {
|
|
74
|
+
return db.select({
|
|
75
|
+
id: documents.id,
|
|
76
|
+
content: documents.content
|
|
77
|
+
}).from(documents).orderBy(documents.id).all();
|
|
78
|
+
},
|
|
79
|
+
async putDocument(id, content) {
|
|
80
|
+
const row = {
|
|
81
|
+
content,
|
|
82
|
+
version: randomUUID(),
|
|
83
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
84
|
+
};
|
|
85
|
+
await db.insert(documents).values({
|
|
86
|
+
id,
|
|
87
|
+
...row
|
|
88
|
+
}).onConflictDoUpdate({
|
|
89
|
+
target: documents.id,
|
|
90
|
+
set: row
|
|
91
|
+
}).run();
|
|
92
|
+
},
|
|
93
|
+
async deleteDocument(id) {
|
|
94
|
+
await db.delete(documents).where(eq(documents.id, id)).run();
|
|
95
|
+
await db.delete(states).where(eq(states.id, id)).run();
|
|
96
|
+
await db.delete(assets).where(eq(assets.id, id)).run();
|
|
97
|
+
},
|
|
98
|
+
async listDocumentIds() {
|
|
99
|
+
return (await db.select({ id: documents.id }).from(documents).orderBy(documents.id).all()).map((row) => row.id);
|
|
100
|
+
}
|
|
67
101
|
},
|
|
68
|
-
|
|
69
|
-
|
|
102
|
+
collabState: {
|
|
103
|
+
async loadState(id) {
|
|
104
|
+
const row = await db.select({ state: states.state }).from(states).where(eq(states.id, id)).get();
|
|
105
|
+
return row ? bytesOf(row.state) : null;
|
|
106
|
+
},
|
|
107
|
+
async storeState(id, state) {
|
|
108
|
+
const data = Buffer.from(state);
|
|
109
|
+
await db.insert(states).values({
|
|
110
|
+
id,
|
|
111
|
+
state: data
|
|
112
|
+
}).onConflictDoUpdate({
|
|
113
|
+
target: states.id,
|
|
114
|
+
set: { state: data }
|
|
115
|
+
}).run();
|
|
116
|
+
},
|
|
117
|
+
async removeState(id) {
|
|
118
|
+
await db.delete(states).where(eq(states.id, id)).run();
|
|
119
|
+
}
|
|
70
120
|
},
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
},
|
|
77
|
-
async loadState(id) {
|
|
78
|
-
const row = await db.select({ state: states.state }).from(states).where(eq(states.id, id)).get();
|
|
79
|
-
return row ? bytesOf(row.state) : null;
|
|
80
|
-
},
|
|
81
|
-
async storeState(id, state) {
|
|
82
|
-
const data = Buffer.from(state);
|
|
83
|
-
await db.insert(states).values({
|
|
84
|
-
id,
|
|
85
|
-
state: data
|
|
86
|
-
}).onConflictDoUpdate({
|
|
87
|
-
target: states.id,
|
|
88
|
-
set: { state: data }
|
|
89
|
-
}).run();
|
|
90
|
-
},
|
|
91
|
-
async removeState(id) {
|
|
92
|
-
await db.delete(states).where(eq(states.id, id)).run();
|
|
93
|
-
},
|
|
94
|
-
async storeAsset(id, { data, ...record }) {
|
|
95
|
-
await db.insert(assets).values({
|
|
96
|
-
id,
|
|
97
|
-
name: record.name,
|
|
98
|
-
record,
|
|
99
|
-
data
|
|
100
|
-
}).onConflictDoUpdate({
|
|
101
|
-
target: [assets.id, assets.name],
|
|
102
|
-
set: {
|
|
121
|
+
assets: {
|
|
122
|
+
async storeAsset(id, { data, ...record }) {
|
|
123
|
+
await db.insert(assets).values({
|
|
124
|
+
id,
|
|
125
|
+
name: record.name,
|
|
103
126
|
record,
|
|
104
127
|
data
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
userId: settingsKey(user),
|
|
124
|
-
json: value
|
|
125
|
-
}).onConflictDoUpdate({
|
|
126
|
-
target: settings.userId,
|
|
127
|
-
set: { json: value }
|
|
128
|
-
}).run();
|
|
129
|
-
},
|
|
130
|
-
async putDocument(id, content) {
|
|
131
|
-
const row = {
|
|
132
|
-
content,
|
|
133
|
-
version: randomUUID(),
|
|
134
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
135
|
-
};
|
|
136
|
-
await db.insert(documents).values({
|
|
137
|
-
id,
|
|
138
|
-
...row
|
|
139
|
-
}).onConflictDoUpdate({
|
|
140
|
-
target: documents.id,
|
|
141
|
-
set: row
|
|
142
|
-
}).run();
|
|
143
|
-
},
|
|
144
|
-
async deleteDocument(id) {
|
|
145
|
-
await db.delete(documents).where(eq(documents.id, id)).run();
|
|
146
|
-
await db.delete(states).where(eq(states.id, id)).run();
|
|
147
|
-
await db.delete(assets).where(eq(assets.id, id)).run();
|
|
128
|
+
}).onConflictDoUpdate({
|
|
129
|
+
target: [assets.id, assets.name],
|
|
130
|
+
set: {
|
|
131
|
+
record,
|
|
132
|
+
data
|
|
133
|
+
}
|
|
134
|
+
}).run();
|
|
135
|
+
},
|
|
136
|
+
async loadAsset(id, name) {
|
|
137
|
+
const row = await db.select({
|
|
138
|
+
record: assets.record,
|
|
139
|
+
data: assets.data
|
|
140
|
+
}).from(assets).where(and(eq(assets.id, id), eq(assets.name, name))).get();
|
|
141
|
+
return row ? {
|
|
142
|
+
record: row.record,
|
|
143
|
+
data: Buffer.from(row.data)
|
|
144
|
+
} : null;
|
|
145
|
+
}
|
|
148
146
|
},
|
|
149
|
-
|
|
150
|
-
|
|
147
|
+
settings: {
|
|
148
|
+
async loadSettings(user) {
|
|
149
|
+
return (await db.select({ json: settings.json }).from(settings).where(eq(settings.userId, settingsKey(user))).get())?.json ?? null;
|
|
150
|
+
},
|
|
151
|
+
async storeSettings(user, value) {
|
|
152
|
+
await db.insert(settings).values({
|
|
153
|
+
userId: settingsKey(user),
|
|
154
|
+
json: value
|
|
155
|
+
}).onConflictDoUpdate({
|
|
156
|
+
target: settings.userId,
|
|
157
|
+
set: { json: value }
|
|
158
|
+
}).run();
|
|
159
|
+
}
|
|
151
160
|
}
|
|
152
161
|
};
|
|
153
162
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drizzle.js","names":[],"sources":["../../src/adapters/drizzle.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto'\nimport type { JSONContent } from '@tiptap/core'\nimport type { AssetRecord } from '../features/media/schema'\nimport { and, eq, sql } from 'drizzle-orm'\nimport { type BaseSQLiteDatabase, blob, primaryKey, sqliteTable, text } from 'drizzle-orm/sqlite-core'\nimport { type AdapterHooks, bytesOf, identityPart, settingsKey, type StoredEditorAdapter } from './shared'\n\nconst EDITOR_TABLES = {\n documents: sqliteTable('editor_documents', {\n id: text('id').primaryKey(),\n content: text('content', { mode: 'json' }).$type<JSONContent>().notNull(),\n version: text('version').notNull(),\n updatedAt: text('updated_at').notNull(),\n }),\n states: sqliteTable('editor_states', {\n id: text('id').primaryKey(),\n state: blob('state', { mode: 'buffer' }).notNull(),\n }),\n settings: sqliteTable('editor_settings', {\n userId: text('user_id').primaryKey(),\n json: text('json', { mode: 'json' }).$type<unknown>().notNull(),\n }),\n assets: sqliteTable('editor_assets', {\n id: text('id').notNull(),\n name: text('name').notNull(),\n record: text('record', { mode: 'json' }).$type<AssetRecord>().notNull(),\n data: blob('data', { mode: 'buffer' }).notNull(),\n }, table => [primaryKey({ columns: [table.id, table.name] })]),\n}\n\nexport type EditorTables = typeof EDITOR_TABLES\n\n/**\n * The tables of the editor's storage in a SQLite database (`bun:sqlite`, better-sqlite3, libsql, D1):\n *\n * editor_documents(id PK, content json, version text, updated_at text) TipTap JSON + opaque content version\n * editor_states(id PK, state blob) the collab server's state bytes\n * editor_settings(user_id PK, json json) per-user editor settings\n * editor_assets(id, name, record json, data blob; PK (id, name)) uploaded files\n *\n * Call it from the host's drizzle schema (so `drizzle-kit` migrates the tables) or use `bootstrapEditorTables` in a\n * host that creates its tables with `CREATE TABLE IF NOT EXISTS`.\n */\nexport const editorTables = (): EditorTables => EDITOR_TABLES\n\n/** Any drizzle SQLite database, sync (`bun:sqlite`, better-sqlite3) or async (libsql, D1). */\nexport type EditorDb = BaseSQLiteDatabase<'sync' | 'async', unknown, Record<string, unknown>>\n\n/** `CREATE TABLE IF NOT EXISTS` for the tables of `editorTables()`, for hosts without migrations. */\nexport async function bootstrapEditorTables(db: EditorDb): Promise<void> {\n await db.run(sql`CREATE TABLE IF NOT EXISTS editor_documents (id text PRIMARY KEY NOT NULL, content text NOT NULL, version text NOT NULL, updated_at text NOT NULL)`)\n await db.run(sql`CREATE TABLE IF NOT EXISTS editor_states (id text PRIMARY KEY NOT NULL, state blob NOT NULL)`)\n await db.run(sql`CREATE TABLE IF NOT EXISTS editor_settings (user_id text PRIMARY KEY NOT NULL, json text NOT NULL)`)\n await db.run(sql`CREATE TABLE IF NOT EXISTS editor_assets (id text NOT NULL, name text NOT NULL, record text NOT NULL, data blob NOT NULL, PRIMARY KEY (id, name))`)\n}\n\nexport interface DrizzleAdapterOptions extends AdapterHooks {\n db: EditorDb\n tables: EditorTables\n}\n\n/**\n * The editor's adapter on drizzle tables. Every write to a document's content (`storeContent`, `putDocument`) stores a\n * fresh random `version`, which `contentVersion` returns: a document changed by any other writer keeps its row's\n * version only if that writer left it alone, so hosts that edit `editor_documents` themselves must go through\n * `putDocument` (or set `version` to anything new). `storeContent` only updates existing rows: create documents with\n * `putDocument`. Single-statement operations only (no transaction), so the adapter runs on sync and async drivers;\n * `deleteDocument` removes content first, so a concurrent `storeContent` finds nothing to update. No theme library.\n */\nexport function createDrizzleAdapter({ db, tables, ...hooks }: DrizzleAdapterOptions): StoredEditorAdapter {\n const { documents, states, settings, assets } = tables\n\n const documentContent = async (id: string) => (await db.select({ content: documents.content }).from(documents).where(eq(documents.id, id)).get())?.content ?? null\n\n return {\n ...identityPart(hooks),\n\n loadContent: documentContent,\n async storeContent(id, content) {\n await db.update(documents).set({ content, version: randomUUID(), updatedAt: new Date().toISOString() }).where(eq(documents.id, id)).run()\n },\n async contentVersion(id) {\n return (await db.select({ version: documents.version }).from(documents).where(eq(documents.id, id)).get())?.version ?? null\n },\n async listContents() {\n return db.select({ id: documents.id, content: documents.content }).from(documents).orderBy(documents.id).all()\n },\n\n async loadState(id) {\n const row = await db.select({ state: states.state }).from(states).where(eq(states.id, id)).get()\n return row ? bytesOf(row.state) : null\n },\n async storeState(id, state) {\n const data = Buffer.from(state)\n await db.insert(states).values({ id, state: data }).onConflictDoUpdate({ target: states.id, set: { state: data } }).run()\n },\n async removeState(id) {\n await db.delete(states).where(eq(states.id, id)).run()\n },\n\n async storeAsset(id, { data, ...record }) {\n await db.insert(assets).values({ id, name: record.name, record, data })\n .onConflictDoUpdate({ target: [assets.id, assets.name], set: { record, data } }).run()\n },\n async loadAsset(id, name) {\n const row = await db.select({ record: assets.record, data: assets.data }).from(assets).where(and(eq(assets.id, id), eq(assets.name, name))).get()\n return row ? { record: row.record, data: Buffer.from(row.data) } : null\n },\n\n async loadSettings(user) {\n return (await db.select({ json: settings.json }).from(settings).where(eq(settings.userId, settingsKey(user))).get())?.json ?? null\n },\n async storeSettings(user, value) {\n await db.insert(settings).values({ userId: settingsKey(user), json: value })\n .onConflictDoUpdate({ target: settings.userId, set: { json: value } }).run()\n },\n\n async putDocument(id, content) {\n const row = { content, version: randomUUID(), updatedAt: new Date().toISOString() }\n await db.insert(documents).values({ id, ...row }).onConflictDoUpdate({ target: documents.id, set: row }).run()\n },\n async deleteDocument(id) {\n await db.delete(documents).where(eq(documents.id, id)).run()\n await db.delete(states).where(eq(states.id, id)).run()\n await db.delete(assets).where(eq(assets.id, id)).run()\n },\n async listDocumentIds() {\n return (await db.select({ id: documents.id }).from(documents).orderBy(documents.id).all()).map(row => row.id)\n },\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"drizzle.js","names":[],"sources":["../../src/adapters/drizzle.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto'\nimport type { JSONContent } from '@tiptap/core'\nimport type { AssetRecord } from '../features/media/schema'\nimport { and, eq, sql } from 'drizzle-orm'\nimport { type BaseSQLiteDatabase, blob, primaryKey, sqliteTable, text } from 'drizzle-orm/sqlite-core'\nimport { type AdapterHooks, bytesOf, directoryOf, identityOf, settingsKey, type StoredPorts } from './shared'\n\nconst EDITOR_TABLES = {\n documents: sqliteTable('editor_documents', {\n id: text('id').primaryKey(),\n content: text('content', { mode: 'json' }).$type<JSONContent>().notNull(),\n version: text('version').notNull(),\n updatedAt: text('updated_at').notNull(),\n }),\n states: sqliteTable('editor_states', {\n id: text('id').primaryKey(),\n state: blob('state', { mode: 'buffer' }).notNull(),\n }),\n settings: sqliteTable('editor_settings', {\n userId: text('user_id').primaryKey(),\n json: text('json', { mode: 'json' }).$type<unknown>().notNull(),\n }),\n assets: sqliteTable('editor_assets', {\n id: text('id').notNull(),\n name: text('name').notNull(),\n record: text('record', { mode: 'json' }).$type<AssetRecord>().notNull(),\n data: blob('data', { mode: 'buffer' }).notNull(),\n }, table => [primaryKey({ columns: [table.id, table.name] })]),\n}\n\nexport type EditorTables = typeof EDITOR_TABLES\n\n/**\n * The tables of the editor's storage in a SQLite database (`bun:sqlite`, better-sqlite3, libsql, D1):\n *\n * editor_documents(id PK, content json, version text, updated_at text) TipTap JSON + opaque content version\n * editor_states(id PK, state blob) the collab server's state bytes\n * editor_settings(user_id PK, json json) per-user editor settings\n * editor_assets(id, name, record json, data blob; PK (id, name)) uploaded files\n *\n * Call it from the host's drizzle schema (so `drizzle-kit` migrates the tables) or use `bootstrapEditorTables` in a\n * host that creates its tables with `CREATE TABLE IF NOT EXISTS`.\n */\nexport const editorTables = (): EditorTables => EDITOR_TABLES\n\n/** Any drizzle SQLite database, sync (`bun:sqlite`, better-sqlite3) or async (libsql, D1). */\nexport type EditorDb = BaseSQLiteDatabase<'sync' | 'async', unknown, Record<string, unknown>>\n\n/** `CREATE TABLE IF NOT EXISTS` for the tables of `editorTables()`, for hosts without migrations. */\nexport async function bootstrapEditorTables(db: EditorDb): Promise<void> {\n await db.run(sql`CREATE TABLE IF NOT EXISTS editor_documents (id text PRIMARY KEY NOT NULL, content text NOT NULL, version text NOT NULL, updated_at text NOT NULL)`)\n await db.run(sql`CREATE TABLE IF NOT EXISTS editor_states (id text PRIMARY KEY NOT NULL, state blob NOT NULL)`)\n await db.run(sql`CREATE TABLE IF NOT EXISTS editor_settings (user_id text PRIMARY KEY NOT NULL, json text NOT NULL)`)\n await db.run(sql`CREATE TABLE IF NOT EXISTS editor_assets (id text NOT NULL, name text NOT NULL, record text NOT NULL, data blob NOT NULL, PRIMARY KEY (id, name))`)\n}\n\nexport interface DrizzleAdapterOptions extends AdapterHooks {\n db: EditorDb\n tables: EditorTables\n}\n\n/**\n * The editor's adapter on drizzle tables. Every write to a document's content (`storeContent`, `putDocument`) stores a\n * fresh random `version`, which `contentVersion` returns: a document changed by any other writer keeps its row's\n * version only if that writer left it alone, so hosts that edit `editor_documents` themselves must go through\n * `putDocument` (or set `version` to anything new). `storeContent` only updates existing rows: create documents with\n * `putDocument`. Single-statement operations only (no transaction), so the adapter runs on sync and async drivers;\n * `deleteDocument` removes content first, so a concurrent `storeContent` finds nothing to update. No theme library.\n */\nexport function createDrizzleAdapter({ db, tables, ...hooks }: DrizzleAdapterOptions): StoredPorts {\n const { documents, states, settings, assets } = tables\n\n const documentContent = async (id: string) => (await db.select({ content: documents.content }).from(documents).where(eq(documents.id, id)).get())?.content ?? null\n\n return {\n identity: identityOf(hooks),\n directory: directoryOf(hooks),\n\n documents: {\n loadContent: documentContent,\n async storeContent(id, content) {\n await db.update(documents).set({ content, version: randomUUID(), updatedAt: new Date().toISOString() }).where(eq(documents.id, id)).run()\n },\n async contentVersion(id) {\n return (await db.select({ version: documents.version }).from(documents).where(eq(documents.id, id)).get())?.version ?? null\n },\n async listContents() {\n return db.select({ id: documents.id, content: documents.content }).from(documents).orderBy(documents.id).all()\n },\n\n async putDocument(id, content) {\n const row = { content, version: randomUUID(), updatedAt: new Date().toISOString() }\n await db.insert(documents).values({ id, ...row }).onConflictDoUpdate({ target: documents.id, set: row }).run()\n },\n async deleteDocument(id) {\n await db.delete(documents).where(eq(documents.id, id)).run()\n await db.delete(states).where(eq(states.id, id)).run()\n await db.delete(assets).where(eq(assets.id, id)).run()\n },\n async listDocumentIds() {\n return (await db.select({ id: documents.id }).from(documents).orderBy(documents.id).all()).map(row => row.id)\n },\n },\n\n collabState: {\n async loadState(id) {\n const row = await db.select({ state: states.state }).from(states).where(eq(states.id, id)).get()\n return row ? bytesOf(row.state) : null\n },\n async storeState(id, state) {\n const data = Buffer.from(state)\n await db.insert(states).values({ id, state: data }).onConflictDoUpdate({ target: states.id, set: { state: data } }).run()\n },\n async removeState(id) {\n await db.delete(states).where(eq(states.id, id)).run()\n },\n },\n\n assets: {\n async storeAsset(id, { data, ...record }) {\n await db.insert(assets).values({ id, name: record.name, record, data })\n .onConflictDoUpdate({ target: [assets.id, assets.name], set: { record, data } }).run()\n },\n async loadAsset(id, name) {\n const row = await db.select({ record: assets.record, data: assets.data }).from(assets).where(and(eq(assets.id, id), eq(assets.name, name))).get()\n return row ? { record: row.record, data: Buffer.from(row.data) } : null\n },\n },\n\n settings: {\n async loadSettings(user) {\n return (await db.select({ json: settings.json }).from(settings).where(eq(settings.userId, settingsKey(user))).get())?.json ?? null\n },\n async storeSettings(user, value) {\n await db.insert(settings).values({ userId: settingsKey(user), json: value })\n .onConflictDoUpdate({ target: settings.userId, set: { json: value } }).run()\n },\n },\n }\n}\n"],"mappings":"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"}
|
package/dist/adapters/forge.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AssetStore, CollabStateStore, EditorPorts, Identity, SettingsStore, ThemeStore } from "../server/ports.js";
|
|
2
2
|
import "../server/index.js";
|
|
3
3
|
import { AdapterHooks } from "./shared.js";
|
|
4
4
|
import { MarkdownDiagnostic, MarkdownPolicy } from "../markdown/policy.js";
|
|
@@ -22,19 +22,24 @@ export interface ForgeContent {
|
|
|
22
22
|
/** Ids of all documents, for `listContents` (the tag index). Omitted: `listContents` is empty. */
|
|
23
23
|
list?(): Promise<string[]>;
|
|
24
24
|
}
|
|
25
|
-
/** What the adapter delegates to a storage adapter that has no use for Markdown:
|
|
26
|
-
export
|
|
25
|
+
/** What the adapter delegates to a storage adapter that has no use for Markdown: the `collabState`, `assets`, `settings` and `themes` ports (the result of `createJsonAdapter` or `createDrizzleAdapter` fits). */
|
|
26
|
+
export interface ForgeState {
|
|
27
|
+
collabState: CollabStateStore;
|
|
28
|
+
assets: AssetStore;
|
|
29
|
+
settings?: SettingsStore;
|
|
30
|
+
themes?: ThemeStore;
|
|
31
|
+
}
|
|
27
32
|
export interface ForgeAdapterOptions extends Omit<AdapterHooks, 'authenticate'> {
|
|
28
33
|
content: ForgeContent;
|
|
29
34
|
state: ForgeState;
|
|
30
35
|
/**
|
|
31
36
|
* The complete TipTap extension list of the Markdown conversion, e.g. `[...advancedSchemaExtensions(), MermaidBlock]`:
|
|
32
|
-
* nodes and marks with their Markdown handlers. Host nodes also go to the collaboration schema through
|
|
33
|
-
*
|
|
37
|
+
* nodes and marks with their Markdown handlers. Host nodes also go to the collaboration schema through the server's
|
|
38
|
+
* `schema` option (`() => [...advancedSchemaExtensions(), MermaidBlock]`).
|
|
34
39
|
*/
|
|
35
40
|
schema: AnyExtension[];
|
|
36
41
|
/** The host's session check; there is no default for a host that owns its Markdown files. */
|
|
37
|
-
authenticate:
|
|
42
|
+
authenticate: Identity['authenticate'];
|
|
38
43
|
/** Markdown policy of the conversion (raw HTML, data URLs); the default drops both. */
|
|
39
44
|
policy?: MarkdownPolicy;
|
|
40
45
|
/** Called with the diagnostics of every load that produced some (dropped HTML or URLs, unknown fences kept verbatim). */
|
|
@@ -53,6 +58,6 @@ export interface ForgeAdapterOptions extends Omit<AdapterHooks, 'authenticate'>
|
|
|
53
58
|
*
|
|
54
59
|
* Yjs state, assets and settings are `state`'s. The converter is one headless editor per adapter, built on first use.
|
|
55
60
|
*/
|
|
56
|
-
export declare function createForgeAdapter(options: ForgeAdapterOptions):
|
|
61
|
+
export declare function createForgeAdapter(options: ForgeAdapterOptions): EditorPorts;
|
|
57
62
|
//#endregion
|
|
58
63
|
//# sourceMappingURL=forge.d.ts.map
|