@ecsplico/editor 2.1.2 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (692) hide show
  1. package/CHANGELOG.md +68 -1
  2. package/README.md +39 -156
  3. package/dist/adapters/comments/drizzle.d.ts +1 -1
  4. package/dist/adapters/comments/{client.js → rest-model.js} +3 -3
  5. package/dist/adapters/comments/rest-model.js.map +1 -0
  6. package/dist/adapters/comments/types.d.ts +2 -2
  7. package/dist/adapters/document-state.d.ts +23 -0
  8. package/dist/adapters/document-state.js +66 -0
  9. package/dist/adapters/document-state.js.map +1 -0
  10. package/dist/adapters/documents.js +14 -11
  11. package/dist/adapters/documents.js.map +1 -1
  12. package/dist/adapters/drizzle.d.ts +2 -2
  13. package/dist/adapters/drizzle.js +97 -88
  14. package/dist/adapters/drizzle.js.map +1 -1
  15. package/dist/adapters/forge.d.ts +12 -7
  16. package/dist/adapters/forge.js +38 -45
  17. package/dist/adapters/forge.js.map +1 -1
  18. package/dist/adapters/http.d.ts +20 -0
  19. package/dist/adapters/http.js +68 -0
  20. package/dist/adapters/http.js.map +1 -0
  21. package/dist/adapters/index.d.ts +5 -5
  22. package/dist/adapters/json.d.ts +3 -3
  23. package/dist/adapters/json.js +58 -49
  24. package/dist/adapters/json.js.map +1 -1
  25. package/dist/adapters/memory.d.ts +21 -0
  26. package/dist/adapters/memory.js +53 -0
  27. package/dist/adapters/memory.js.map +1 -0
  28. package/dist/adapters/nitro.d.ts +23 -20
  29. package/dist/adapters/nitro.js +58 -163
  30. package/dist/adapters/nitro.js.map +1 -1
  31. package/dist/adapters/react.d.ts +55 -0
  32. package/dist/adapters/react.js +209 -0
  33. package/dist/adapters/react.js.map +1 -0
  34. package/dist/adapters/shared.d.ts +15 -21
  35. package/dist/adapters/shared.js +10 -8
  36. package/dist/adapters/shared.js.map +1 -1
  37. package/dist/adapters/vue.d.ts +15 -30
  38. package/dist/adapters/vue.js +36 -87
  39. package/dist/adapters/vue.js.map +1 -1
  40. package/dist/config/index.d.ts +105 -0
  41. package/dist/config/index.js +95 -0
  42. package/dist/config/index.js.map +1 -0
  43. package/dist/core/abilities.js +1 -0
  44. package/dist/core/abilities.js.map +1 -1
  45. package/dist/{adapters/routes.js → core/api-routes.js} +5 -3
  46. package/dist/core/api-routes.js.map +1 -0
  47. package/dist/core/backend-capability.d.ts +7 -0
  48. package/dist/core/backend-capability.js +17 -0
  49. package/dist/core/backend-capability.js.map +1 -0
  50. package/dist/core/backend-types.d.ts +245 -0
  51. package/dist/core/backend-types.js +17 -0
  52. package/dist/core/backend-types.js.map +1 -0
  53. package/dist/core/backend.d.ts +108 -0
  54. package/dist/core/backend.js +14 -0
  55. package/dist/core/backend.js.map +1 -0
  56. package/dist/core/block-convert.d.ts +7 -5
  57. package/dist/core/block-convert.js +28 -5
  58. package/dist/core/block-convert.js.map +1 -1
  59. package/dist/core/command-types.d.ts +89 -0
  60. package/dist/core/commands.d.ts +40 -0
  61. package/dist/core/commands.js +151 -0
  62. package/dist/core/commands.js.map +1 -0
  63. package/dist/core/context.d.ts +17 -20
  64. package/dist/core/context.js +24 -21
  65. package/dist/core/context.js.map +1 -1
  66. package/dist/core/core-feature.d.ts +22 -7
  67. package/dist/core/core-feature.js +51 -18
  68. package/dist/core/core-feature.js.map +1 -1
  69. package/dist/core/define-editor.d.ts +4 -3
  70. package/dist/core/define-editor.js +14 -7
  71. package/dist/core/define-editor.js.map +1 -1
  72. package/dist/core/document-markdown.js +12 -3
  73. package/dist/core/document-markdown.js.map +1 -1
  74. package/dist/core/editor-ui.d.ts +8 -1
  75. package/dist/core/editor-ui.js +9 -8
  76. package/dist/core/editor-ui.js.map +1 -1
  77. package/dist/core/extension-points.d.ts +3 -15
  78. package/dist/core/feature-context.d.ts +8 -7
  79. package/dist/core/feature-context.js +12 -11
  80. package/dist/core/feature-context.js.map +1 -1
  81. package/dist/core/feature.d.ts +35 -19
  82. package/dist/core/feature.js.map +1 -1
  83. package/dist/core/fences.d.ts +2 -1
  84. package/dist/core/fences.js +3 -2
  85. package/dist/core/fences.js.map +1 -1
  86. package/dist/core/fetch-error.d.ts +1 -1
  87. package/dist/core/fetch-error.js +3 -1
  88. package/dist/core/fetch-error.js.map +1 -1
  89. package/dist/core/hash.d.ts +5 -0
  90. package/dist/core/hash.js +11 -0
  91. package/dist/core/hash.js.map +1 -0
  92. package/dist/core/http-backend.d.ts +11 -0
  93. package/dist/core/http-backend.js +195 -0
  94. package/dist/core/http-backend.js.map +1 -0
  95. package/dist/core/index.d.ts +14 -9
  96. package/dist/core/index.js +12 -9
  97. package/dist/core/palette.d.ts +48 -0
  98. package/dist/core/palette.js +95 -0
  99. package/dist/core/palette.js.map +1 -0
  100. package/dist/core/schema/abilities.d.ts +2 -1
  101. package/dist/core/schema/abilities.js +5 -1
  102. package/dist/core/schema/abilities.js.map +1 -1
  103. package/dist/core/schema/index.d.ts +11 -5
  104. package/dist/core/schema/index.js +13 -8
  105. package/dist/core/schema/index.js.map +1 -1
  106. package/dist/core/schema/mark-flags.d.ts +8 -1
  107. package/dist/core/schema/of-features.d.ts +13 -0
  108. package/dist/core/schema/of-features.js +14 -0
  109. package/dist/core/schema/of-features.js.map +1 -0
  110. package/dist/core/slash.d.ts +8 -6
  111. package/dist/core/slash.js +24 -16
  112. package/dist/core/slash.js.map +1 -1
  113. package/dist/core/surface-session.js +13 -5
  114. package/dist/core/surface-session.js.map +1 -1
  115. package/dist/core/toolbar-items.d.ts +10 -4
  116. package/dist/core/toolbar-items.js +269 -195
  117. package/dist/core/toolbar-items.js.map +1 -1
  118. package/dist/core/util.js +11 -0
  119. package/dist/core/util.js.map +1 -0
  120. package/dist/document/DocumentSurface.d.ts +1 -1
  121. package/dist/features/ai/agent-op-review.d.ts +1 -1
  122. package/dist/features/ai/agent-op-review.js +1 -1
  123. package/dist/features/ai/agent-ops.js +1 -1
  124. package/dist/features/ai/ai-apply.d.ts +2 -2
  125. package/dist/features/ai/ai-apply.js +1 -1
  126. package/dist/features/ai/ai-review.js +1 -1
  127. package/dist/features/ai/ai-sse.d.ts +6 -6
  128. package/dist/features/ai/ai-sse.js +20 -63
  129. package/dist/features/ai/ai-sse.js.map +1 -1
  130. package/dist/features/ai/assistant.d.ts +8 -7
  131. package/dist/features/ai/assistant.js +3 -3
  132. package/dist/features/ai/assistant.js.map +1 -1
  133. package/dist/features/ai/block-actions.d.ts +3 -2
  134. package/dist/features/ai/block-actions.js +3 -2
  135. package/dist/features/ai/block-actions.js.map +1 -1
  136. package/dist/features/ai/components/AiSelectionMenu.js +1 -1
  137. package/dist/features/ai/components/DocumentChatAnswer.js +1 -1
  138. package/dist/features/ai/components/DocumentChatOps.vue.d.ts +1 -1
  139. package/dist/features/ai/components/SettingsModelProfiles.js +8 -8
  140. package/dist/features/ai/components/SettingsModelProfiles.js.map +1 -1
  141. package/dist/features/ai/document-chat-model.d.ts +8 -7
  142. package/dist/features/ai/document-chat-model.js +5 -5
  143. package/dist/features/ai/document-chat-model.js.map +1 -1
  144. package/dist/features/ai/document-chat.js +1 -1
  145. package/dist/features/ai/feature.js +27 -14
  146. package/dist/features/ai/feature.js.map +1 -1
  147. package/dist/features/ai/ghost-autocomplete.d.ts +7 -6
  148. package/dist/features/ai/ghost-autocomplete.js +6 -9
  149. package/dist/features/ai/ghost-autocomplete.js.map +1 -1
  150. package/dist/features/ai/index.d.ts +4 -3
  151. package/dist/features/ai/index.js +10 -10
  152. package/dist/features/ai/model-profiles.d.ts +5 -4
  153. package/dist/features/ai/model-profiles.js +2 -2
  154. package/dist/features/ai/model-profiles.js.map +1 -1
  155. package/dist/features/ai/protocol/block-lines.js +1 -1
  156. package/dist/features/ai/protocol/block-lines.js.map +1 -1
  157. package/dist/features/ai/protocol/chat.js +4 -1
  158. package/dist/features/ai/protocol/chat.js.map +1 -1
  159. package/dist/features/ai/protocol/index.d.ts +3 -2
  160. package/dist/features/ai/protocol/models.d.ts +2 -16
  161. package/dist/features/ai/protocol/models.js.map +1 -1
  162. package/dist/features/ai/rate-limit.d.ts +1 -1
  163. package/dist/features/ai/rate-limit.js +3 -1
  164. package/dist/features/ai/rate-limit.js.map +1 -1
  165. package/dist/features/ai/react/AiSelectionMenu.js +2 -2
  166. package/dist/features/ai/react/AiSidebar.js +4 -4
  167. package/dist/features/ai/react/DocumentChatAnswer.js +1 -1
  168. package/dist/features/ai/react/DocumentChatOps.d.ts +1 -1
  169. package/dist/features/ai/react/DocumentChatOps.js +1 -1
  170. package/dist/features/ai/react/SettingsModelProfiles.js +8 -10
  171. package/dist/features/ai/react/SettingsModelProfiles.js.map +1 -1
  172. package/dist/features/ai/react/use-ai-assistant.d.ts +1 -1
  173. package/dist/features/ai/react/use-ai-assistant.js +3 -3
  174. package/dist/features/ai/react/use-ai-assistant.js.map +1 -1
  175. package/dist/features/ai/react/use-document-chat.js +2 -2
  176. package/dist/features/ai/react/use-document-chat.js.map +1 -1
  177. package/dist/features/ai/react/use-model-profiles.d.ts +2 -1
  178. package/dist/features/ai/selection-flow.d.ts +1 -1
  179. package/dist/features/ai/selection-flow.js +1 -1
  180. package/dist/features/ai/sidebar-flow.d.ts +1 -1
  181. package/dist/features/ai/sidebar-flow.js +1 -1
  182. package/dist/features/ai/use-ai-assistant.d.ts +1 -1
  183. package/dist/features/ai/use-ai-assistant.js +3 -3
  184. package/dist/features/ai/use-ai-assistant.js.map +1 -1
  185. package/dist/features/ai/use-document-chat.js +2 -2
  186. package/dist/features/ai/use-document-chat.js.map +1 -1
  187. package/dist/features/ai/use-model-profiles.d.ts +2 -1
  188. package/dist/features/authors/attribution.d.ts +1 -1
  189. package/dist/features/authors/feature.js +12 -11
  190. package/dist/features/authors/feature.js.map +1 -1
  191. package/dist/features/block-ui/block-actions.d.ts +1 -1
  192. package/dist/features/block-ui/block-actions.js +1 -1
  193. package/dist/features/block-ui/block-actions.js.map +1 -1
  194. package/dist/features/block-ui/components/BlockUi.js +1 -1
  195. package/dist/features/block-ui/feature.js +1 -0
  196. package/dist/features/block-ui/feature.js.map +1 -1
  197. package/dist/features/block-ui/react/BlockUi.js +1 -1
  198. package/dist/features/collab/anchor-resolver.js +1 -1
  199. package/dist/features/collab/feature.js +2 -0
  200. package/dist/features/collab/feature.js.map +1 -1
  201. package/dist/features/comments/components/CommentThreadView.vue.d.ts +2 -1
  202. package/dist/features/comments/feature.js +12 -11
  203. package/dist/features/comments/feature.js.map +1 -1
  204. package/dist/features/comments/index.d.ts +3 -2
  205. package/dist/features/comments/index.js +1 -1
  206. package/dist/features/comments/margin-layout.d.ts +3 -2
  207. package/dist/features/comments/model.d.ts +3 -67
  208. package/dist/features/comments/model.js +2 -2
  209. package/dist/features/comments/model.js.map +1 -1
  210. package/dist/features/comments/react/CommentThreadView.d.ts +3 -2
  211. package/dist/features/comments/react/use-comments.d.ts +2 -1
  212. package/dist/features/comments/react.d.ts +1 -1
  213. package/dist/features/comments/schema.d.ts +2 -52
  214. package/dist/features/comments/schema.js.map +1 -1
  215. package/dist/features/comments/snapshot-equality.js.map +1 -1
  216. package/dist/features/comments/use-comments.d.ts +3 -2
  217. package/dist/features/comments/vue.d.ts +1 -1
  218. package/dist/features/composites/accepted-children.d.ts +1 -0
  219. package/dist/features/composites/accepted-children.js +16 -1
  220. package/dist/features/composites/accepted-children.js.map +1 -1
  221. package/dist/features/composites/ai-brief.js +31 -0
  222. package/dist/features/composites/ai-brief.js.map +1 -1
  223. package/dist/features/composites/bibliography-command.d.ts +11 -0
  224. package/dist/features/composites/bibliography-form.js +90 -0
  225. package/dist/features/composites/bibliography-form.js.map +1 -0
  226. package/dist/features/composites/bibliography.d.ts +33 -0
  227. package/dist/features/composites/bibliography.js +120 -0
  228. package/dist/features/composites/bibliography.js.map +1 -0
  229. package/dist/features/composites/components/BibliographyEditor.js +261 -0
  230. package/dist/features/composites/components/BibliographyEditor.js.map +1 -0
  231. package/dist/features/composites/components/CompositeChrome.js +75 -26
  232. package/dist/features/composites/components/CompositeChrome.js.map +1 -1
  233. package/dist/features/composites/components/CompositeDetails.js +46 -11
  234. package/dist/features/composites/components/CompositeDetails.js.map +1 -1
  235. package/dist/features/composites/components/CompositeDetails.vue.d.ts +2 -1
  236. package/dist/features/composites/components/CompositeFieldEditor.js +37 -4
  237. package/dist/features/composites/components/CompositeFieldEditor.js.map +1 -1
  238. package/dist/features/composites/components/CompositeInspector.js +17 -5
  239. package/dist/features/composites/components/CompositeInspector.js.map +1 -1
  240. package/dist/features/composites/components/CompositeTable.js +13 -4
  241. package/dist/features/composites/components/CompositeTable.js.map +1 -1
  242. package/dist/features/composites/components/CompositeTagsEditor.js +1 -1
  243. package/dist/features/composites/components/CompositeView.js +11 -1
  244. package/dist/features/composites/components/CompositeView.js.map +1 -1
  245. package/dist/features/composites/components/DerivationBadge.js +103 -0
  246. package/dist/features/composites/components/DerivationBadge.js.map +1 -0
  247. package/dist/features/composites/components/ReferenceFieldSelect.js +60 -0
  248. package/dist/features/composites/components/ReferenceFieldSelect.js.map +1 -0
  249. package/dist/features/composites/components/ReferenceView.js +122 -0
  250. package/dist/features/composites/components/ReferenceView.js.map +1 -0
  251. package/dist/features/composites/components/ReferenceView.vue.d.ts +96 -0
  252. package/dist/features/composites/components/SettingsEditingTab.js +1 -1
  253. package/dist/features/composites/convert.js +2 -2
  254. package/dist/features/composites/convert.js.map +1 -1
  255. package/dist/features/composites/derivation.d.ts +61 -0
  256. package/dist/features/composites/derivation.js +98 -0
  257. package/dist/features/composites/derivation.js.map +1 -0
  258. package/dist/features/composites/derived-chrome.js +69 -0
  259. package/dist/features/composites/derived-chrome.js.map +1 -0
  260. package/dist/features/composites/feature.js +159 -109
  261. package/dist/features/composites/feature.js.map +1 -1
  262. package/dist/features/composites/fences.d.ts +4 -3
  263. package/dist/features/composites/fences.js +51 -7
  264. package/dist/features/composites/fences.js.map +1 -1
  265. package/dist/features/composites/index.d.ts +12 -3
  266. package/dist/features/composites/index.js +15 -6
  267. package/dist/features/composites/inspector-model.d.ts +4 -1
  268. package/dist/features/composites/inspector-model.js +8 -4
  269. package/dist/features/composites/inspector-model.js.map +1 -1
  270. package/dist/features/composites/kinds.d.ts +8 -1
  271. package/dist/features/composites/kinds.js +145 -6
  272. package/dist/features/composites/kinds.js.map +1 -1
  273. package/dist/features/composites/legacy.d.ts +4 -1
  274. package/dist/features/composites/legacy.js +56 -1
  275. package/dist/features/composites/legacy.js.map +1 -1
  276. package/dist/features/composites/meta-display.js +40 -1
  277. package/dist/features/composites/meta-display.js.map +1 -1
  278. package/dist/features/composites/meta-keys.d.ts +7 -0
  279. package/dist/features/composites/meta-keys.js +21 -0
  280. package/dist/features/composites/meta-keys.js.map +1 -0
  281. package/dist/features/composites/node-views.d.ts +6 -3
  282. package/dist/features/composites/node-views.js +7 -3
  283. package/dist/features/composites/node-views.js.map +1 -1
  284. package/dist/features/composites/nodes.d.ts +6 -1
  285. package/dist/features/composites/nodes.js +33 -7
  286. package/dist/features/composites/nodes.js.map +1 -1
  287. package/dist/features/composites/react/BibliographyEditor.js +229 -0
  288. package/dist/features/composites/react/BibliographyEditor.js.map +1 -0
  289. package/dist/features/composites/react/CompositeChrome.js +65 -17
  290. package/dist/features/composites/react/CompositeChrome.js.map +1 -1
  291. package/dist/features/composites/react/CompositeDetails.d.ts +2 -1
  292. package/dist/features/composites/react/CompositeDetails.js +45 -6
  293. package/dist/features/composites/react/CompositeDetails.js.map +1 -1
  294. package/dist/features/composites/react/CompositeFieldEditor.d.ts +1 -1
  295. package/dist/features/composites/react/CompositeFieldEditor.js +24 -1
  296. package/dist/features/composites/react/CompositeFieldEditor.js.map +1 -1
  297. package/dist/features/composites/react/CompositeInspector.js +11 -5
  298. package/dist/features/composites/react/CompositeInspector.js.map +1 -1
  299. package/dist/features/composites/react/CompositeStatusMenu.js +1 -1
  300. package/dist/features/composites/react/CompositeSummaryEditor.js +1 -1
  301. package/dist/features/composites/react/CompositeTable.js +16 -2
  302. package/dist/features/composites/react/CompositeTable.js.map +1 -1
  303. package/dist/features/composites/react/CompositeTagsEditor.js +2 -2
  304. package/dist/features/composites/react/CompositeView.js +6 -1
  305. package/dist/features/composites/react/CompositeView.js.map +1 -1
  306. package/dist/features/composites/react/DerivationBadge.js +116 -0
  307. package/dist/features/composites/react/DerivationBadge.js.map +1 -0
  308. package/dist/features/composites/react/ReferenceFieldSelect.js +52 -0
  309. package/dist/features/composites/react/ReferenceFieldSelect.js.map +1 -0
  310. package/dist/features/composites/react/ReferenceView.d.ts +11 -0
  311. package/dist/features/composites/react/ReferenceView.js +115 -0
  312. package/dist/features/composites/react/ReferenceView.js.map +1 -0
  313. package/dist/features/composites/react/SettingsEditingTab.js +2 -2
  314. package/dist/features/composites/react.d.ts +2 -1
  315. package/dist/features/composites/react.js +7 -3
  316. package/dist/features/composites/react.js.map +1 -1
  317. package/dist/features/composites/reference-ai.js +16 -0
  318. package/dist/features/composites/reference-ai.js.map +1 -0
  319. package/dist/features/composites/reference-label.d.ts +19 -0
  320. package/dist/features/composites/reference-label.js +58 -0
  321. package/dist/features/composites/reference-label.js.map +1 -0
  322. package/dist/features/composites/reference-suggestion.d.ts +10 -0
  323. package/dist/features/composites/reference-suggestion.js +41 -0
  324. package/dist/features/composites/reference-suggestion.js.map +1 -0
  325. package/dist/features/composites/reference-text.d.ts +18 -0
  326. package/dist/features/composites/reference-text.js +89 -0
  327. package/dist/features/composites/reference-text.js.map +1 -0
  328. package/dist/features/composites/reference.d.ts +44 -0
  329. package/dist/features/composites/reference.js +148 -0
  330. package/dist/features/composites/reference.js.map +1 -0
  331. package/dist/features/composites/reveal-source.d.ts +32 -0
  332. package/dist/features/composites/reveal-source.js +89 -0
  333. package/dist/features/composites/reveal-source.js.map +1 -0
  334. package/dist/features/composites/rows.d.ts +21 -2
  335. package/dist/features/composites/rows.js +63 -9
  336. package/dist/features/composites/rows.js.map +1 -1
  337. package/dist/features/composites/schema.d.ts +9 -3
  338. package/dist/features/composites/schema.js +11 -4
  339. package/dist/features/composites/schema.js.map +1 -1
  340. package/dist/features/composites/table-model.js +12 -21
  341. package/dist/features/composites/table-model.js.map +1 -1
  342. package/dist/features/composites/vue.d.ts +3 -2
  343. package/dist/features/composites/vue.js +8 -4
  344. package/dist/features/composites/vue.js.map +1 -1
  345. package/dist/features/media/assets.d.ts +2 -0
  346. package/dist/features/media/assets.js +3 -2
  347. package/dist/features/media/assets.js.map +1 -1
  348. package/dist/features/media/components/FileView.js +13 -4
  349. package/dist/features/media/components/FileView.js.map +1 -1
  350. package/dist/features/media/feature.d.ts +3 -3
  351. package/dist/features/media/feature.js +21 -28
  352. package/dist/features/media/feature.js.map +1 -1
  353. package/dist/features/media/file-import.js +2 -2
  354. package/dist/features/media/file-import.js.map +1 -1
  355. package/dist/features/media/index.d.ts +2 -2
  356. package/dist/features/media/index.js +2 -2
  357. package/dist/features/media/react/FileView.js +19 -2
  358. package/dist/features/media/react/FileView.js.map +1 -1
  359. package/dist/features/media/schema.d.ts +8 -0
  360. package/dist/features/media/schema.js +126 -11
  361. package/dist/features/media/schema.js.map +1 -1
  362. package/dist/features/media/upload.d.ts +16 -14
  363. package/dist/features/media/upload.js +13 -19
  364. package/dist/features/media/upload.js.map +1 -1
  365. package/dist/features/mentions/index.d.ts +4 -3
  366. package/dist/features/mentions/index.js +4 -1
  367. package/dist/features/mentions/index.js.map +1 -1
  368. package/dist/features/mentions/mention-text.d.ts +1 -1
  369. package/dist/features/mentions/mention-text.js.map +1 -1
  370. package/dist/features/mentions/mention.d.ts +0 -6
  371. package/dist/features/mentions/mention.js.map +1 -1
  372. package/dist/features/mentions/metadata-defaults.d.ts +2 -2
  373. package/dist/features/mentions/metadata-defaults.js +3 -9
  374. package/dist/features/mentions/metadata-defaults.js.map +1 -1
  375. package/dist/features/mentions/schema.d.ts +2 -1
  376. package/dist/features/mentions/schema.js.map +1 -1
  377. package/dist/features/tables/feature.d.ts +2 -1
  378. package/dist/features/tables/feature.js +90 -70
  379. package/dist/features/tables/feature.js.map +1 -1
  380. package/dist/features/themes/components/SettingsAppearanceTab.js +6 -9
  381. package/dist/features/themes/components/SettingsAppearanceTab.js.map +1 -1
  382. package/dist/features/themes/feature.js +2 -0
  383. package/dist/features/themes/feature.js.map +1 -1
  384. package/dist/features/themes/index.d.ts +2 -1
  385. package/dist/features/themes/react/SettingsAppearanceTab.js +7 -10
  386. package/dist/features/themes/react/SettingsAppearanceTab.js.map +1 -1
  387. package/dist/features/themes/schema.d.ts +2 -6
  388. package/dist/features/themes/schema.js.map +1 -1
  389. package/dist/features/themes/theme-runtime.d.ts +1 -1
  390. package/dist/features/themes/theme-runtime.js +4 -2
  391. package/dist/features/themes/theme-runtime.js.map +1 -1
  392. package/dist/features/transcript/TranscriptSegmentView.js +48 -28
  393. package/dist/features/transcript/TranscriptSegmentView.js.map +1 -1
  394. package/dist/features/transcript/feature.js +21 -19
  395. package/dist/features/transcript/feature.js.map +1 -1
  396. package/dist/features/transcript/react/TranscriptSegmentView.js +55 -36
  397. package/dist/features/transcript/react/TranscriptSegmentView.js.map +1 -1
  398. package/dist/features/transcript/segment-node.d.ts +2 -0
  399. package/dist/features/transcript/segment-node.js +35 -7
  400. package/dist/features/transcript/segment-node.js.map +1 -1
  401. package/dist/i18n/de/composites.js +91 -1
  402. package/dist/i18n/de/composites.js.map +1 -1
  403. package/dist/i18n/de/integrations.js +49 -8
  404. package/dist/i18n/de/integrations.js.map +1 -1
  405. package/dist/i18n/de/media.js +0 -1
  406. package/dist/i18n/de/media.js.map +1 -1
  407. package/dist/i18n/de/palette.js +22 -0
  408. package/dist/i18n/de/palette.js.map +1 -0
  409. package/dist/i18n/de/tables.js +0 -1
  410. package/dist/i18n/de/tables.js.map +1 -1
  411. package/dist/i18n/de/transcript.js +2 -1
  412. package/dist/i18n/de/transcript.js.map +1 -1
  413. package/dist/i18n/de/tree.js +3 -0
  414. package/dist/i18n/de/tree.js.map +1 -1
  415. package/dist/i18n/de.d.ts +157 -11
  416. package/dist/i18n/de.js +3 -1
  417. package/dist/i18n/de.js.map +1 -1
  418. package/dist/i18n/en/composites.js +89 -1
  419. package/dist/i18n/en/composites.js.map +1 -1
  420. package/dist/i18n/en/integrations.js +49 -8
  421. package/dist/i18n/en/integrations.js.map +1 -1
  422. package/dist/i18n/en/media.js +0 -1
  423. package/dist/i18n/en/media.js.map +1 -1
  424. package/dist/i18n/en/palette.js +22 -0
  425. package/dist/i18n/en/palette.js.map +1 -0
  426. package/dist/i18n/en/tables.js +0 -1
  427. package/dist/i18n/en/tables.js.map +1 -1
  428. package/dist/i18n/en/transcript.js +2 -1
  429. package/dist/i18n/en/transcript.js.map +1 -1
  430. package/dist/i18n/en/tree.js +3 -0
  431. package/dist/i18n/en/tree.js.map +1 -1
  432. package/dist/i18n/en.js +3 -1
  433. package/dist/i18n/en.js.map +1 -1
  434. package/dist/index.d.ts +4 -3
  435. package/dist/integrations/composite-ai/bibliography-fetch.d.ts +31 -0
  436. package/dist/integrations/composite-ai/bibliography-fetch.js +159 -0
  437. package/dist/integrations/composite-ai/bibliography-fetch.js.map +1 -0
  438. package/dist/integrations/composite-ai/composite-fill.d.ts +30 -8
  439. package/dist/integrations/composite-ai/composite-fill.js +97 -56
  440. package/dist/integrations/composite-ai/composite-fill.js.map +1 -1
  441. package/dist/integrations/composite-ai/composite-summary.d.ts +4 -12
  442. package/dist/integrations/composite-ai/composite-summary.js +7 -26
  443. package/dist/integrations/composite-ai/composite-summary.js.map +1 -1
  444. package/dist/integrations/composite-ai/derive.d.ts +29 -0
  445. package/dist/integrations/composite-ai/derive.js +173 -0
  446. package/dist/integrations/composite-ai/derive.js.map +1 -0
  447. package/dist/integrations/composite-ai/index.d.ts +11 -7
  448. package/dist/integrations/composite-ai/index.js +72 -14
  449. package/dist/integrations/composite-ai/index.js.map +1 -1
  450. package/dist/integrations/composite-ai/rendition.d.ts +45 -0
  451. package/dist/integrations/composite-ai/rendition.js +314 -0
  452. package/dist/integrations/composite-ai/rendition.js.map +1 -0
  453. package/dist/integrations/composite-media/index.d.ts +7 -5
  454. package/dist/integrations/composite-media/index.js +86 -13
  455. package/dist/integrations/composite-media/index.js.map +1 -1
  456. package/dist/integrations/transcript-ai/index.d.ts +1 -1
  457. package/dist/integrations/transcript-ai/index.js +5 -2
  458. package/dist/integrations/transcript-ai/index.js.map +1 -1
  459. package/dist/integrations/transcript-ai/protocol.d.ts +2 -12
  460. package/dist/integrations/transcript-ai/protocol.js +0 -2
  461. package/dist/integrations/transcript-ai/protocol.js.map +1 -1
  462. package/dist/integrations/transcript-ai/transcript-cleanup.d.ts +5 -5
  463. package/dist/integrations/transcript-ai/transcript-cleanup.js +7 -10
  464. package/dist/integrations/transcript-ai/transcript-cleanup.js.map +1 -1
  465. package/dist/markdown/MarkdownSurface.vue.d.ts +6 -5
  466. package/dist/markdown/MarkdownSurface2.js +3 -6
  467. package/dist/markdown/MarkdownSurface2.js.map +1 -1
  468. package/dist/markdown/document.js +1 -1
  469. package/dist/markdown/source-mode.d.ts +1 -1
  470. package/dist/nuxt/module.d.ts +1402 -54
  471. package/dist/nuxt/module.js +6 -49
  472. package/dist/nuxt/module.js.map +1 -1
  473. package/dist/nuxt/runtime/install-context.d.ts +7 -3
  474. package/dist/nuxt/runtime/install-context.js +14 -8
  475. package/dist/nuxt/runtime/install-context.js.map +1 -1
  476. package/dist/nuxt/runtime/plugins/editor-context-nuxt-ui.client.ts +2 -2
  477. package/dist/nuxt/runtime/server/plugins/editor-server.js +20 -12
  478. package/dist/nuxt/runtime/server/plugins/editor-server.js.map +1 -1
  479. package/dist/presets/compose.d.ts +94 -0
  480. package/dist/presets/compose.js +164 -0
  481. package/dist/presets/compose.js.map +1 -0
  482. package/dist/react/AdvancedEditor.d.ts +15 -15
  483. package/dist/react/AdvancedEditor.js +15 -13
  484. package/dist/react/AdvancedEditor.js.map +1 -1
  485. package/dist/react/EditorCommandPalette.d.ts +13 -0
  486. package/dist/react/EditorCommandPalette.js +118 -0
  487. package/dist/react/EditorCommandPalette.js.map +1 -0
  488. package/dist/react/EditorSettingsModal.d.ts +5 -5
  489. package/dist/react/EditorSettingsModal.js +7 -4
  490. package/dist/react/EditorSettingsModal.js.map +1 -1
  491. package/dist/react/EditorSettingsPanel.d.ts +7 -7
  492. package/dist/react/EditorSettingsPanel.js +8 -5
  493. package/dist/react/EditorSettingsPanel.js.map +1 -1
  494. package/dist/react/EditorSurface.d.ts +7 -7
  495. package/dist/react/EditorSurface.js +10 -6
  496. package/dist/react/EditorSurface.js.map +1 -1
  497. package/dist/react/EditorToolbar.d.ts +4 -6
  498. package/dist/react/EditorToolbar.js +29 -37
  499. package/dist/react/EditorToolbar.js.map +1 -1
  500. package/dist/react/SimpleEditor.d.ts +22 -23
  501. package/dist/react/SimpleEditor.js +21 -7
  502. package/dist/react/SimpleEditor.js.map +1 -1
  503. package/dist/react/advanced.d.ts +4 -3
  504. package/dist/react/advanced.js +2 -2
  505. package/dist/react/compose.d.ts +20 -0
  506. package/dist/react/compose.js +49 -0
  507. package/dist/react/compose.js.map +1 -0
  508. package/dist/react/composed-surface.d.ts +39 -0
  509. package/dist/react/composed-surface.js +71 -0
  510. package/dist/react/composed-surface.js.map +1 -0
  511. package/dist/react/context.js +6 -2
  512. package/dist/react/context.js.map +1 -1
  513. package/dist/react/index.d.ts +2 -1
  514. package/dist/react/index.js +2 -1
  515. package/dist/react/key-labels.js +15 -0
  516. package/dist/react/key-labels.js.map +1 -0
  517. package/dist/react/settings.d.ts +1 -1
  518. package/dist/react/simple.d.ts +2 -2
  519. package/dist/react/ui/command.js +36 -1
  520. package/dist/react/ui/command.js.map +1 -1
  521. package/dist/schema/advanced.d.ts +6 -6
  522. package/dist/schema/advanced.js +26 -29
  523. package/dist/schema/advanced.js.map +1 -1
  524. package/dist/server/ai.d.ts +5 -16
  525. package/dist/server/ai.js.map +1 -1
  526. package/dist/server/auth.d.ts +9 -0
  527. package/dist/server/collab-state.d.ts +1 -1
  528. package/dist/server/collab-state.js.map +1 -1
  529. package/dist/server/collab.d.ts +13 -13
  530. package/dist/server/collab.js +34 -18
  531. package/dist/server/collab.js.map +1 -1
  532. package/dist/server/crossref.js +78 -0
  533. package/dist/server/crossref.js.map +1 -0
  534. package/dist/server/errors.d.ts +1 -1
  535. package/dist/server/errors.js +1 -1
  536. package/dist/server/errors.js.map +1 -1
  537. package/dist/server/extract.js +308 -0
  538. package/dist/server/extract.js.map +1 -0
  539. package/dist/{nuxt/runtime/server/utils/editor-route.js → server/h3.js} +8 -16
  540. package/dist/server/h3.js.map +1 -0
  541. package/dist/server/handlers/ai.d.ts +30 -9
  542. package/dist/server/handlers/ai.js +170 -36
  543. package/dist/server/handlers/ai.js.map +1 -1
  544. package/dist/server/handlers/context.d.ts +12 -5
  545. package/dist/server/handlers/context.js +10 -4
  546. package/dist/server/handlers/context.js.map +1 -1
  547. package/dist/server/handlers/editor.d.ts +19 -13
  548. package/dist/server/handlers/editor.js +49 -38
  549. package/dist/server/handlers/editor.js.map +1 -1
  550. package/dist/server/handlers/index.d.ts +4 -4
  551. package/dist/server/handlers/index.js +4 -4
  552. package/dist/server/host.d.ts +16 -25
  553. package/dist/server/host.js +58 -41
  554. package/dist/server/host.js.map +1 -1
  555. package/dist/server/index.d.ts +6 -4
  556. package/dist/server/index.js +3 -2
  557. package/dist/server/pdf.js +97 -0
  558. package/dist/server/pdf.js.map +1 -0
  559. package/dist/server/{adapter.d.ts → ports.d.ts} +81 -30
  560. package/dist/server/ports.js +29 -0
  561. package/dist/server/ports.js.map +1 -0
  562. package/dist/server/routes.js +144 -0
  563. package/dist/server/routes.js.map +1 -0
  564. package/dist/settings/index.d.ts +1 -1
  565. package/dist/settings/index.js.map +1 -1
  566. package/dist/styles/composites.css +25 -0
  567. package/dist/tree/DocumentTree.js +56 -29
  568. package/dist/tree/DocumentTree.js.map +1 -1
  569. package/dist/tree/react/DocumentTree.js +23 -0
  570. package/dist/tree/react/DocumentTree.js.map +1 -1
  571. package/dist/tree/tree-host.js +2 -2
  572. package/dist/tree/tree-items.js +1 -1
  573. package/dist/vue/AdvancedEditor.js +30 -9
  574. package/dist/vue/AdvancedEditor.js.map +1 -1
  575. package/dist/vue/AdvancedEditor.vue.d.ts +28 -8
  576. package/dist/vue/SimpleEditor.js +62 -16
  577. package/dist/vue/SimpleEditor.js.map +1 -1
  578. package/dist/vue/SimpleEditor.vue.d.ts +37 -7
  579. package/dist/vue/advanced.d.ts +3 -2
  580. package/dist/vue/advanced.js +2 -2
  581. package/dist/vue/components/EditorCommandPalette.js +91 -0
  582. package/dist/vue/components/EditorCommandPalette.js.map +1 -0
  583. package/dist/vue/components/EditorCommandPalette.vue.d.ts +15 -0
  584. package/dist/vue/components/EditorSettingsModal.js +10 -6
  585. package/dist/vue/components/EditorSettingsModal.js.map +1 -1
  586. package/dist/vue/components/EditorSettingsModal.vue.d.ts +4 -4
  587. package/dist/vue/components/EditorSettingsPanel.js +10 -6
  588. package/dist/vue/components/EditorSettingsPanel.js.map +1 -1
  589. package/dist/vue/components/EditorSettingsPanel.vue.d.ts +6 -6
  590. package/dist/vue/components/EditorSurface.js +11 -6
  591. package/dist/vue/components/EditorSurface.js.map +1 -1
  592. package/dist/vue/components/EditorSurface.vue.d.ts +7 -7
  593. package/dist/vue/components/EditorToolbar.js +41 -49
  594. package/dist/vue/components/EditorToolbar.js.map +1 -1
  595. package/dist/vue/components/EditorToolbar.vue.d.ts +2 -4
  596. package/dist/vue/compose.d.ts +17 -0
  597. package/dist/vue/compose.js +51 -0
  598. package/dist/vue/compose.js.map +1 -0
  599. package/dist/vue/context-key.d.ts +6 -0
  600. package/dist/vue/context-key.js +6 -0
  601. package/dist/vue/context-key.js.map +1 -0
  602. package/dist/vue/context.d.ts +8 -6
  603. package/dist/vue/context.js +6 -5
  604. package/dist/vue/context.js.map +1 -1
  605. package/dist/vue/index.d.ts +10 -8
  606. package/dist/vue/index.js +10 -8
  607. package/dist/vue/kit/index.d.ts +2 -2
  608. package/dist/vue/kit/nuxt-ui.js +23 -0
  609. package/dist/vue/kit/nuxt-ui.js.map +1 -1
  610. package/dist/vue/kit/types.d.ts +40 -1
  611. package/package.json +30 -16
  612. package/dist/adapters/comments/client.d.ts +0 -64
  613. package/dist/adapters/comments/client.js.map +0 -1
  614. package/dist/adapters/routes.js.map +0 -1
  615. package/dist/core/toolbar.d.ts +0 -74
  616. package/dist/core/toolbar.js +0 -45
  617. package/dist/core/toolbar.js.map +0 -1
  618. package/dist/integrations/composite-ai/document-transcribe.d.ts +0 -14
  619. package/dist/integrations/composite-ai/document-transcribe.js +0 -89
  620. package/dist/integrations/composite-ai/document-transcribe.js.map +0 -1
  621. package/dist/nuxt/runtime/server/api/ai/chat.post.d.ts +0 -5
  622. package/dist/nuxt/runtime/server/api/ai/chat.post.js +0 -8
  623. package/dist/nuxt/runtime/server/api/ai/chat.post.js.map +0 -1
  624. package/dist/nuxt/runtime/server/api/ai/complete.post.d.ts +0 -5
  625. package/dist/nuxt/runtime/server/api/ai/complete.post.js +0 -8
  626. package/dist/nuxt/runtime/server/api/ai/complete.post.js.map +0 -1
  627. package/dist/nuxt/runtime/server/api/ai/generate.post.d.ts +0 -5
  628. package/dist/nuxt/runtime/server/api/ai/generate.post.js +0 -8
  629. package/dist/nuxt/runtime/server/api/ai/generate.post.js.map +0 -1
  630. package/dist/nuxt/runtime/server/api/ai/models.get.d.ts +0 -5
  631. package/dist/nuxt/runtime/server/api/ai/models.get.js +0 -8
  632. package/dist/nuxt/runtime/server/api/ai/models.get.js.map +0 -1
  633. package/dist/nuxt/runtime/server/api/ai/transcribe.post.d.ts +0 -5
  634. package/dist/nuxt/runtime/server/api/ai/transcribe.post.js +0 -8
  635. package/dist/nuxt/runtime/server/api/ai/transcribe.post.js.map +0 -1
  636. package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.d.ts +0 -5
  637. package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.js +0 -8
  638. package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.js.map +0 -1
  639. package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.d.ts +0 -5
  640. package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.js +0 -8
  641. package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.js.map +0 -1
  642. package/dist/nuxt/runtime/server/api/editor/assets/[id].post.d.ts +0 -5
  643. package/dist/nuxt/runtime/server/api/editor/assets/[id].post.js +0 -8
  644. package/dist/nuxt/runtime/server/api/editor/assets/[id].post.js.map +0 -1
  645. package/dist/nuxt/runtime/server/api/editor/settings.get.d.ts +0 -5
  646. package/dist/nuxt/runtime/server/api/editor/settings.get.js +0 -8
  647. package/dist/nuxt/runtime/server/api/editor/settings.get.js.map +0 -1
  648. package/dist/nuxt/runtime/server/api/editor/settings.put.d.ts +0 -5
  649. package/dist/nuxt/runtime/server/api/editor/settings.put.js +0 -8
  650. package/dist/nuxt/runtime/server/api/editor/settings.put.js.map +0 -1
  651. package/dist/nuxt/runtime/server/api/editor/tags.get.d.ts +0 -5
  652. package/dist/nuxt/runtime/server/api/editor/tags.get.js +0 -8
  653. package/dist/nuxt/runtime/server/api/editor/tags.get.js.map +0 -1
  654. package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.d.ts +0 -5
  655. package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.js +0 -8
  656. package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.js.map +0 -1
  657. package/dist/nuxt/runtime/server/api/editor/themes/[id].get.d.ts +0 -5
  658. package/dist/nuxt/runtime/server/api/editor/themes/[id].get.js +0 -8
  659. package/dist/nuxt/runtime/server/api/editor/themes/[id].get.js.map +0 -1
  660. package/dist/nuxt/runtime/server/api/editor/themes/index.get.d.ts +0 -5
  661. package/dist/nuxt/runtime/server/api/editor/themes/index.get.js +0 -8
  662. package/dist/nuxt/runtime/server/api/editor/themes/index.get.js.map +0 -1
  663. package/dist/nuxt/runtime/server/api/editor/themes/index.post.d.ts +0 -5
  664. package/dist/nuxt/runtime/server/api/editor/themes/index.post.js +0 -8
  665. package/dist/nuxt/runtime/server/api/editor/themes/index.post.js.map +0 -1
  666. package/dist/nuxt/runtime/server/api/editor/users.get.d.ts +0 -5
  667. package/dist/nuxt/runtime/server/api/editor/users.get.js +0 -8
  668. package/dist/nuxt/runtime/server/api/editor/users.get.js.map +0 -1
  669. package/dist/nuxt/runtime/server/plugins/collab.d.ts +0 -5
  670. package/dist/nuxt/runtime/server/plugins/collab.js +0 -10
  671. package/dist/nuxt/runtime/server/plugins/collab.js.map +0 -1
  672. package/dist/nuxt/runtime/server/routes/collab/[id].d.ts +0 -5
  673. package/dist/nuxt/runtime/server/routes/collab/[id].js +0 -35
  674. package/dist/nuxt/runtime/server/routes/collab/[id].js.map +0 -1
  675. package/dist/nuxt/runtime/server/utils/editor-route.d.ts +0 -11
  676. package/dist/nuxt/runtime/server/utils/editor-route.js.map +0 -1
  677. package/dist/presets/advanced.d.ts +0 -33
  678. package/dist/presets/advanced.js +0 -50
  679. package/dist/presets/advanced.js.map +0 -1
  680. package/dist/presets/simple.d.ts +0 -9
  681. package/dist/presets/simple.js +0 -12
  682. package/dist/presets/simple.js.map +0 -1
  683. package/dist/react/advanced-preset.d.ts +0 -11
  684. package/dist/react/advanced-preset.js +0 -31
  685. package/dist/react/advanced-preset.js.map +0 -1
  686. package/dist/vue/advanced-preset.d.ts +0 -13
  687. package/dist/vue/advanced-preset.js +0 -33
  688. package/dist/vue/advanced-preset.js.map +0 -1
  689. package/docs/extending.md +0 -538
  690. package/docs/integration.md +0 -632
  691. package/docs/llms.md +0 -383
  692. package/docs/usage.md +0 -1076
@@ -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 adapter's `authorize` refuses. */
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 access = ctx.adapter.authorize ? await ctx.adapter.authorize(user, id) : { readOnly: false };
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 adapter stores; `404` when there is none.
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.adapter.loadContent(id);
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 on adapters that manage documents (`putDocument`); on the others only existing documents can be saved.
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
- if (ctx.adapter.putDocument) await ctx.adapter.putDocument(id, body);
49
- else if (await ctx.adapter.loadContent(id)) await ctx.adapter.storeContent(id, body);
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.store, {
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
- if ((await authorsOf(ctx.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");
98
- const { status, body: answer } = await handleCommentsRequest(ctx.store, {
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, type DocumentAdmin } 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 the adapter's `authenticate`, per-document access its optional `authorize`.\n\n/** What the document handlers need: the editor's handler context, whose adapter may also manage documents (`json`, `drizzle`). */\nexport interface DocumentsContext extends HandlerContext {\n adapter: HandlerContext['adapter'] & Partial<DocumentAdmin>\n}\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 adapter's `authorize` refuses. */\nasync function accessOf(ctx: DocumentsContext, request: HandlerRequest, id: string): Promise<{ user: object, readOnly: boolean }> {\n const user = await authenticated(ctx, request)\n const access = ctx.adapter.authorize ? await ctx.adapter.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 adapter stores; `404` when there is none.\n */\nexport async function documentGet(ctx: DocumentsContext, request: HandlerRequest): Promise<HandlerResponse> {\n const id = documentId(request)\n await accessOf(ctx, request, id)\n const content = ctx.collab?.liveContent(id) ?? await ctx.adapter.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 on adapters that manage documents (`putDocument`); on the others only existing documents can be saved.\n */\nexport async function documentPut(ctx: DocumentsContext, 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 if (ctx.adapter.putDocument) await ctx.adapter.putDocument(id, body)\n else if (await ctx.adapter.loadContent(id)) await ctx.adapter.storeContent(id, body)\n else throw new HttpError(404, 'Dokument nicht gefunden')\n return empty()\n}\n\nexport interface CommentsRouteContext extends DocumentsContext {\n store: CommentStore\n}\n\n/** `GET`: the document's `Discussion`. */\nexport async function commentsGet(ctx: CommentsRouteContext, request: HandlerRequest): Promise<HandlerResponse> {\n const id = documentId(request)\n await accessOf(ctx, request, id)\n const { status, body } = await handleCommentsRequest(ctx.store, { 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: CommentsRouteContext, 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 if ((await authorsOf(ctx.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(ctx.store, { method: 'POST', documentId: id, body })\n return json(answer, status)\n}\n"],"mappings":"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"}
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, StoredEditorAdapter } from "./shared.js";
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): StoredEditorAdapter;
290
+ export declare function createDrizzleAdapter({ db, tables, ...hooks }: DrizzleAdapterOptions): StoredPorts;
291
291
  //#endregion
292
292
  export {};
293
293
  //# sourceMappingURL=drizzle.d.ts.map
@@ -1,4 +1,4 @@
1
- import { bytesOf, identityPart, settingsKey } from "./shared.js";
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
- ...identityPart(hooks),
60
- loadContent: documentContent,
61
- async storeContent(id, content) {
62
- await db.update(documents).set({
63
- content,
64
- version: randomUUID(),
65
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
66
- }).where(eq(documents.id, id)).run();
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
- async contentVersion(id) {
69
- return (await db.select({ version: documents.version }).from(documents).where(eq(documents.id, id)).get())?.version ?? null;
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
- async listContents() {
72
- return db.select({
73
- id: documents.id,
74
- content: documents.content
75
- }).from(documents).orderBy(documents.id).all();
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
- }).run();
107
- },
108
- async loadAsset(id, name) {
109
- const row = await db.select({
110
- record: assets.record,
111
- data: assets.data
112
- }).from(assets).where(and(eq(assets.id, id), eq(assets.name, name))).get();
113
- return row ? {
114
- record: row.record,
115
- data: Buffer.from(row.data)
116
- } : null;
117
- },
118
- async loadSettings(user) {
119
- return (await db.select({ json: settings.json }).from(settings).where(eq(settings.userId, settingsKey(user))).get())?.json ?? null;
120
- },
121
- async storeSettings(user, value) {
122
- await db.insert(settings).values({
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
- async listDocumentIds() {
150
- return (await db.select({ id: documents.id }).from(documents).orderBy(documents.id).all()).map((row) => row.id);
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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"}
@@ -1,4 +1,4 @@
1
- import { EditorAdapter } from "../server/adapter.js";
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: Yjs state, assets, settings, themes (`createJsonAdapter` or `createDrizzleAdapter` fit). */
26
- export type ForgeState = Pick<EditorAdapter, 'loadState' | 'storeState' | 'removeState' | 'storeAsset' | 'loadAsset'> & Partial<Pick<EditorAdapter, 'loadSettings' | 'storeSettings' | 'listThemes' | 'loadTheme' | 'storeTheme' | 'removeTheme'>>;
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 `schemaExtensions`
33
- * (which lists only the additions to `advancedSchemaExtensions()`).
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: EditorAdapter['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): EditorAdapter;
61
+ export declare function createForgeAdapter(options: ForgeAdapterOptions): EditorPorts;
57
62
  //#endregion
58
63
  //# sourceMappingURL=forge.d.ts.map
@@ -1,17 +1,10 @@
1
+ import { directoryOf, identityOf } from "./shared.js";
1
2
  import { DocumentMarkdown } from "../core/document-markdown.js";
2
3
  import { hasFatalDiagnostics } from "../markdown/policy.js";
3
4
  import { contentToMarkdown, markdownToContent } from "../markdown/document.js";
4
5
  import { joinFrontmatter, splitFrontmatter } from "../markdown/frontmatter.js";
5
6
  import { Editor } from "@tiptap/core";
6
7
  //#region src/adapters/forge.ts
7
- const OPTIONAL_STATE = [
8
- "loadSettings",
9
- "storeSettings",
10
- "listThemes",
11
- "loadTheme",
12
- "storeTheme",
13
- "removeTheme"
14
- ];
15
8
  /**
16
9
  * The editor's adapter over the host's Markdown files (concept documents, vault pages). The host keeps owning the
17
10
  * files, their locations, change notifications and access rules and passes them in as functions; this adapter does the
@@ -26,7 +19,7 @@ const OPTIONAL_STATE = [
26
19
  * Yjs state, assets and settings are `state`'s. The converter is one headless editor per adapter, built on first use.
27
20
  */
28
21
  function createForgeAdapter(options) {
29
- const { content, state, schema, policy, onDiagnostics, authenticate, authorize, schemaExtensions, users } = options;
22
+ const { content, state, schema, policy, onDiagnostics, authenticate, authorize } = options;
30
23
  let converter;
31
24
  const editor = () => converter ??= new Editor({
32
25
  content: {
@@ -35,7 +28,6 @@ function createForgeAdapter(options) {
35
28
  },
36
29
  extensions: [DocumentMarkdown, ...schema]
37
30
  });
38
- const optionalState = Object.fromEntries(OPTIONAL_STATE.filter((method) => state[method]).map((method) => [method, (...args) => state[method].apply(state, args)]));
39
31
  function parse(id, file) {
40
32
  const { content: json, diagnostics } = markdownToContent(editor(), splitFrontmatter(file.markdown).body, policy ? { policy } : {});
41
33
  if (diagnostics.length) onDiagnostics?.(id, diagnostics);
@@ -46,43 +38,44 @@ function createForgeAdapter(options) {
46
38
  return json;
47
39
  }
48
40
  return {
49
- authenticate: (request) => authenticate(request),
50
- listUsers: async () => await users?.() ?? [],
51
- ...authorize && { authorize: (user, id) => authorize(user, id) },
52
- ...schemaExtensions && { schemaExtensions: () => schemaExtensions() },
53
- async loadContent(id) {
54
- const file = await content.read(id);
55
- return file && parse(id, file);
56
- },
57
- async storeContent(id, json) {
58
- const current = await content.read(id);
59
- if (!current) return;
60
- const instance = editor();
61
- instance.commands.setContent(json, { emitUpdate: false });
62
- const body = contentToMarkdown(instance);
63
- const markdown = joinFrontmatter(splitFrontmatter(current.markdown).frontmatter, body && `${body}\n`);
64
- if (markdown === current.markdown) return;
65
- await content.write(id, markdown);
66
- await content.onWritten?.(id);
67
- },
68
- async contentVersion(id) {
69
- return (await content.read(id))?.version ?? null;
70
- },
71
- async listContents() {
72
- return (await Promise.all((await content.list?.() ?? []).map(async (id) => {
41
+ identity: identityOf({
42
+ authenticate,
43
+ authorize
44
+ }),
45
+ directory: directoryOf(options),
46
+ documents: {
47
+ async loadContent(id) {
73
48
  const file = await content.read(id);
74
- return file && {
75
- id,
76
- content: parse(id, file)
77
- };
78
- }))).filter((entry) => entry !== null);
49
+ return file && parse(id, file);
50
+ },
51
+ async storeContent(id, json) {
52
+ const current = await content.read(id);
53
+ if (!current) return;
54
+ const instance = editor();
55
+ instance.commands.setContent(json, { emitUpdate: false });
56
+ const body = contentToMarkdown(instance);
57
+ const markdown = joinFrontmatter(splitFrontmatter(current.markdown).frontmatter, body && `${body}\n`);
58
+ if (markdown === current.markdown) return;
59
+ await content.write(id, markdown);
60
+ await content.onWritten?.(id);
61
+ },
62
+ async contentVersion(id) {
63
+ return (await content.read(id))?.version ?? null;
64
+ },
65
+ async listContents() {
66
+ return (await Promise.all((await content.list?.() ?? []).map(async (id) => {
67
+ const file = await content.read(id);
68
+ return file && {
69
+ id,
70
+ content: parse(id, file)
71
+ };
72
+ }))).filter((entry) => entry !== null);
73
+ }
79
74
  },
80
- loadState: (id) => state.loadState(id),
81
- storeState: (id, bytes) => state.storeState(id, bytes),
82
- removeState: (id) => state.removeState(id),
83
- storeAsset: (id, asset) => state.storeAsset(id, asset),
84
- loadAsset: (id, name) => state.loadAsset(id, name),
85
- ...optionalState
75
+ collabState: state.collabState,
76
+ assets: state.assets,
77
+ ...state.settings && { settings: state.settings },
78
+ ...state.themes && { themes: state.themes }
86
79
  };
87
80
  }
88
81
  //#endregion