@elytracms/studio 0.0.91 → 0.0.93

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 (353) hide show
  1. package/convex/asset-alt-migration.test.ts +127 -0
  2. package/convex/asset-replace.test.ts +168 -0
  3. package/convex/asset-revalidate.test.ts +6 -6
  4. package/convex/assets.ts +41 -5
  5. package/convex/cms.ts +537 -177
  6. package/convex/contract-parity.test.ts +173 -19
  7. package/convex/delivery.ts +26 -13
  8. package/convex/document-revalidate.test.ts +17 -16
  9. package/convex/orphan-hydration.test.ts +116 -0
  10. package/convex/presence-cost.test.ts +89 -0
  11. package/convex/rail-write-parity.test.ts +17 -14
  12. package/convex/referenceMaintenance.ts +15 -15
  13. package/convex/schema.ts +70 -5
  14. package/convex/triggers.ts +21 -8
  15. package/convex/write-limits.ts +1 -1
  16. package/dist/components/ai/ai-chat-panel.js +33 -22
  17. package/dist/components/ai/ai-chat-panel.js.map +1 -1
  18. package/dist/components/ai/proposal-card.js +2 -1
  19. package/dist/components/ai/proposal-card.js.map +1 -1
  20. package/dist/components/ai/translate-document-action.d.ts +8 -2
  21. package/dist/components/ai/translate-document-action.js.map +1 -1
  22. package/dist/components/ai/translate-document-dialog.d.ts +1 -1
  23. package/dist/components/ai/translate-document-dialog.js +14 -8
  24. package/dist/components/ai/translate-document-dialog.js.map +1 -1
  25. package/dist/components/ai/translate-skipped.d.ts +19 -0
  26. package/dist/components/ai/translate-skipped.js +46 -0
  27. package/dist/components/ai/translate-skipped.js.map +1 -0
  28. package/dist/components/app-views/default-modules.d.ts +23 -6
  29. package/dist/components/app-views/default-modules.js +33 -14
  30. package/dist/components/app-views/default-modules.js.map +1 -1
  31. package/dist/components/assets/asset-detail.js +18 -8
  32. package/dist/components/assets/asset-detail.js.map +1 -1
  33. package/dist/components/assets/asset-metadata.d.ts +15 -4
  34. package/dist/components/assets/asset-metadata.js +15 -2
  35. package/dist/components/assets/asset-metadata.js.map +1 -1
  36. package/dist/components/assets/asset-search.js +4 -1
  37. package/dist/components/assets/asset-search.js.map +1 -1
  38. package/dist/components/assets/asset-selection.d.ts +0 -8
  39. package/dist/components/assets/asset-selection.js +4 -23
  40. package/dist/components/assets/asset-selection.js.map +1 -1
  41. package/dist/components/assets/asset-thumb.js +3 -2
  42. package/dist/components/assets/asset-thumb.js.map +1 -1
  43. package/dist/components/assets/assets-collection-view-source.d.ts +3 -3
  44. package/dist/components/assets/assets-collection-view-source.js +9 -5
  45. package/dist/components/assets/assets-collection-view-source.js.map +1 -1
  46. package/dist/components/assets/assets-store.d.ts +14 -0
  47. package/dist/components/assets/assets-store.js +23 -1
  48. package/dist/components/assets/assets-store.js.map +1 -1
  49. package/dist/components/assets/replace-file.d.ts +22 -0
  50. package/dist/components/assets/replace-file.js +59 -0
  51. package/dist/components/assets/replace-file.js.map +1 -0
  52. package/dist/components/builder/blocks-model.d.ts +14 -26
  53. package/dist/components/builder/blocks-model.js +35 -41
  54. package/dist/components/builder/blocks-model.js.map +1 -1
  55. package/dist/components/builder/blocks-panel.d.ts +7 -3
  56. package/dist/components/builder/blocks-panel.js +6 -5
  57. package/dist/components/builder/blocks-panel.js.map +1 -1
  58. package/dist/components/builder/builder-store.d.ts +126 -30
  59. package/dist/components/builder/builder-store.js +169 -67
  60. package/dist/components/builder/builder-store.js.map +1 -1
  61. package/dist/components/builder/canvas-body.d.ts +30 -0
  62. package/dist/components/builder/canvas-body.js +38 -0
  63. package/dist/components/builder/canvas-body.js.map +1 -0
  64. package/dist/components/builder/canvas-chrome-css.js +7 -0
  65. package/dist/components/builder/canvas-chrome-css.js.map +1 -1
  66. package/dist/components/builder/canvas-dnd.d.ts +7 -7
  67. package/dist/components/builder/canvas-dnd.js +5 -5
  68. package/dist/components/builder/canvas-dnd.js.map +1 -1
  69. package/dist/components/builder/canvas-render-context.d.ts +30 -22
  70. package/dist/components/builder/canvas-render-context.js +44 -44
  71. package/dist/components/builder/canvas-render-context.js.map +1 -1
  72. package/dist/components/builder/canvas-runs.d.ts +45 -0
  73. package/dist/components/builder/canvas-runs.js +51 -0
  74. package/dist/components/builder/canvas-runs.js.map +1 -0
  75. package/dist/components/builder/canvas.d.ts +5 -4
  76. package/dist/components/builder/canvas.js +34 -6
  77. package/dist/components/builder/canvas.js.map +1 -1
  78. package/dist/components/builder/clipboard-actions.d.ts +15 -0
  79. package/dist/components/builder/clipboard-actions.js +50 -13
  80. package/dist/components/builder/clipboard-actions.js.map +1 -1
  81. package/dist/components/builder/composition-field-editor.d.ts +10 -16
  82. package/dist/components/builder/composition-field-editor.js +30 -40
  83. package/dist/components/builder/composition-field-editor.js.map +1 -1
  84. package/dist/components/builder/composition-patches.d.ts +7 -0
  85. package/dist/components/builder/composition-patches.js +256 -0
  86. package/dist/components/builder/composition-patches.js.map +1 -0
  87. package/dist/components/builder/delete-key.d.ts +5 -0
  88. package/dist/components/builder/delete-key.js +4 -0
  89. package/dist/components/builder/delete-key.js.map +1 -1
  90. package/dist/components/builder/insert-context.d.ts +17 -25
  91. package/dist/components/builder/insert-context.js +45 -92
  92. package/dist/components/builder/insert-context.js.map +1 -1
  93. package/dist/components/builder/page-tree.d.ts +3 -3
  94. package/dist/components/builder/page-tree.js +3 -3
  95. package/dist/components/builder/page-tree.js.map +1 -1
  96. package/dist/components/builder/placement-refusal.d.ts +18 -0
  97. package/dist/components/builder/placement-refusal.js +46 -0
  98. package/dist/components/builder/placement-refusal.js.map +1 -0
  99. package/dist/components/builder/property-inspector.js +4 -0
  100. package/dist/components/builder/property-inspector.js.map +1 -1
  101. package/dist/components/builder/reuse-context.js +65 -26
  102. package/dist/components/builder/reuse-context.js.map +1 -1
  103. package/dist/components/builder/reuse.d.ts +79 -8
  104. package/dist/components/builder/reuse.js +71 -23
  105. package/dist/components/builder/reuse.js.map +1 -1
  106. package/dist/components/builder/run-inspector.d.ts +11 -0
  107. package/dist/components/builder/run-inspector.js +33 -0
  108. package/dist/components/builder/run-inspector.js.map +1 -0
  109. package/dist/components/cms/asset-field.d.ts +38 -1
  110. package/dist/components/cms/asset-field.js +182 -58
  111. package/dist/components/cms/asset-field.js.map +1 -1
  112. package/dist/components/cms/asset-picker-core.d.ts +16 -0
  113. package/dist/components/cms/asset-picker-core.js +20 -0
  114. package/dist/components/cms/asset-picker-core.js.map +1 -1
  115. package/dist/components/cms/asset-picker.js +12 -18
  116. package/dist/components/cms/asset-picker.js.map +1 -1
  117. package/dist/components/cms/bulk-actions.d.ts +4 -20
  118. package/dist/components/cms/bulk-actions.js +3 -24
  119. package/dist/components/cms/bulk-actions.js.map +1 -1
  120. package/dist/components/cms/cell-edit.d.ts +5 -5
  121. package/dist/components/cms/cell-edit.js.map +1 -1
  122. package/dist/components/cms/cell.js.map +1 -1
  123. package/dist/components/cms/cms-store.d.ts +87 -45
  124. package/dist/components/cms/cms-store.js +347 -203
  125. package/dist/components/cms/cms-store.js.map +1 -1
  126. package/dist/components/cms/collection-table.js +7 -2
  127. package/dist/components/cms/collection-table.js.map +1 -1
  128. package/dist/components/cms/commit-policy.d.ts +24 -0
  129. package/dist/components/cms/commit-policy.js +37 -0
  130. package/dist/components/cms/commit-policy.js.map +1 -0
  131. package/dist/components/cms/detail-editor.js +83 -137
  132. package/dist/components/cms/detail-editor.js.map +1 -1
  133. package/dist/components/cms/document-diff-view.d.ts +3 -1
  134. package/dist/components/cms/document-diff-view.js +12 -7
  135. package/dist/components/cms/document-diff-view.js.map +1 -1
  136. package/dist/components/cms/draft-commit.d.ts +70 -0
  137. package/dist/components/cms/draft-commit.js +82 -0
  138. package/dist/components/cms/draft-commit.js.map +1 -0
  139. package/dist/components/cms/duplicate-document.d.ts +11 -13
  140. package/dist/components/cms/duplicate-document.js +26 -34
  141. package/dist/components/cms/duplicate-document.js.map +1 -1
  142. package/dist/components/cms/field-anchors.d.ts +7 -4
  143. package/dist/components/cms/field-anchors.js +9 -7
  144. package/dist/components/cms/field-anchors.js.map +1 -1
  145. package/dist/components/cms/field-collaboration.d.ts +47 -0
  146. package/dist/components/cms/field-collaboration.js +87 -0
  147. package/dist/components/cms/field-collaboration.js.map +1 -0
  148. package/dist/components/cms/field-control-lib.d.ts +22 -1
  149. package/dist/components/cms/field-control-lib.js +33 -4
  150. package/dist/components/cms/field-control-lib.js.map +1 -1
  151. package/dist/components/cms/field-control.js +17 -14
  152. package/dist/components/cms/field-control.js.map +1 -1
  153. package/dist/components/cms/gate-validation.d.ts +21 -30
  154. package/dist/components/cms/gate-validation.js +11 -22
  155. package/dist/components/cms/gate-validation.js.map +1 -1
  156. package/dist/components/cms/object-array-field.js +4 -4
  157. package/dist/components/cms/object-array-field.js.map +1 -1
  158. package/dist/components/cms/prop-fields.d.ts +7 -2
  159. package/dist/components/cms/prop-fields.js +13 -6
  160. package/dist/components/cms/prop-fields.js.map +1 -1
  161. package/dist/components/cms/reference-confirm.js +10 -4
  162. package/dist/components/cms/reference-confirm.js.map +1 -1
  163. package/dist/components/cms/rich-text/embed-picker.d.ts +1 -1
  164. package/dist/components/cms/rich-text/embed-picker.js.map +1 -1
  165. package/dist/components/cms/rich-text/insert.d.ts +20 -9
  166. package/dist/components/cms/rich-text/insert.js +25 -17
  167. package/dist/components/cms/rich-text/insert.js.map +1 -1
  168. package/dist/components/cms/use-document-draft.d.ts +96 -0
  169. package/dist/components/cms/use-document-draft.js +283 -0
  170. package/dist/components/cms/use-document-draft.js.map +1 -0
  171. package/dist/components/cms/use-document-versions.js +2 -2
  172. package/dist/components/cms/use-document-versions.js.map +1 -1
  173. package/dist/components/cms/use-live-url.d.ts +6 -12
  174. package/dist/components/cms/use-live-url.js +16 -15
  175. package/dist/components/cms/use-live-url.js.map +1 -1
  176. package/dist/components/cms/use-publish-controller.d.ts +13 -18
  177. package/dist/components/cms/use-publish-controller.js +24 -41
  178. package/dist/components/cms/use-publish-controller.js.map +1 -1
  179. package/dist/components/cms/use-published-perspective.d.ts +2 -3
  180. package/dist/components/cms/use-published-perspective.js +4 -3
  181. package/dist/components/cms/use-published-perspective.js.map +1 -1
  182. package/dist/components/cms/versions-pane.js +8 -1
  183. package/dist/components/cms/versions-pane.js.map +1 -1
  184. package/dist/components/collection-view/cms-collection-view-source.js +24 -7
  185. package/dist/components/collection-view/cms-collection-view-source.js.map +1 -1
  186. package/dist/components/components/transfer-dialog.js +28 -23
  187. package/dist/components/components/transfer-dialog.js.map +1 -1
  188. package/dist/components/components/transfer.d.ts +31 -21
  189. package/dist/components/components/transfer.js +49 -35
  190. package/dist/components/components/transfer.js.map +1 -1
  191. package/dist/components/project-list.js.map +1 -1
  192. package/dist/components/project-session.d.ts +6 -3
  193. package/dist/components/project-session.js +14 -8
  194. package/dist/components/project-session.js.map +1 -1
  195. package/dist/components/project-switcher.js.map +1 -1
  196. package/dist/components/rail-view-source.js +2 -1
  197. package/dist/components/rail-view-source.js.map +1 -1
  198. package/dist/components/section-nav.js +1 -1
  199. package/dist/components/section-nav.js.map +1 -1
  200. package/dist/lib/ai/apply-content.js +56 -116
  201. package/dist/lib/ai/apply-content.js.map +1 -1
  202. package/dist/lib/ai/chat-context.d.ts +8 -0
  203. package/dist/lib/ai/chat-context.js +37 -6
  204. package/dist/lib/ai/chat-context.js.map +1 -1
  205. package/dist/lib/ai/chat-state.d.ts +14 -1
  206. package/dist/lib/ai/chat-state.js.map +1 -1
  207. package/dist/lib/ai/constraints.d.ts +20 -5
  208. package/dist/lib/ai/constraints.js +125 -33
  209. package/dist/lib/ai/constraints.js.map +1 -1
  210. package/dist/lib/ai/fake-provider.d.ts +16 -3
  211. package/dist/lib/ai/fake-provider.js +22 -6
  212. package/dist/lib/ai/fake-provider.js.map +1 -1
  213. package/dist/lib/ai/index.d.ts +2 -0
  214. package/dist/lib/ai/index.js +2 -0
  215. package/dist/lib/ai/index.js.map +1 -1
  216. package/dist/lib/ai/leash.d.ts +34 -0
  217. package/dist/lib/ai/leash.js +28 -0
  218. package/dist/lib/ai/leash.js.map +1 -0
  219. package/dist/lib/ai/provenance.d.ts +1 -1
  220. package/dist/lib/ai/provenance.js.map +1 -1
  221. package/dist/lib/ai/rich-text-input.d.ts +8 -8
  222. package/dist/lib/ai/rich-text-input.js +2 -1
  223. package/dist/lib/ai/rich-text-input.js.map +1 -1
  224. package/dist/lib/ai/rich-text-translate.d.ts +27 -4
  225. package/dist/lib/ai/rich-text-translate.js +28 -10
  226. package/dist/lib/ai/rich-text-translate.js.map +1 -1
  227. package/dist/lib/ai/shape.d.ts +13 -13
  228. package/dist/lib/ai/shape.js +69 -93
  229. package/dist/lib/ai/shape.js.map +1 -1
  230. package/dist/lib/ai/tools.d.ts +36 -41
  231. package/dist/lib/ai/tools.js +85 -87
  232. package/dist/lib/ai/tools.js.map +1 -1
  233. package/dist/lib/ai/translate-document.d.ts +39 -5
  234. package/dist/lib/ai/translate-document.js +160 -51
  235. package/dist/lib/ai/translate-document.js.map +1 -1
  236. package/dist/lib/ai/types.d.ts +34 -23
  237. package/dist/lib/ai/types.js.map +1 -1
  238. package/dist/lib/ai/update-fields.d.ts +21 -12
  239. package/dist/lib/ai/update-fields.js +118 -177
  240. package/dist/lib/ai/update-fields.js.map +1 -1
  241. package/dist/lib/ai/writer-form.d.ts +116 -0
  242. package/dist/lib/ai/writer-form.js +429 -0
  243. package/dist/lib/ai/writer-form.js.map +1 -0
  244. package/dist/lib/ai-current-document.d.ts +11 -11
  245. package/dist/lib/ai-current-document.js +4 -4
  246. package/dist/lib/ai-current-document.js.map +1 -1
  247. package/dist/lib/assets/upload.d.ts +31 -2
  248. package/dist/lib/assets/upload.js +61 -6
  249. package/dist/lib/assets/upload.js.map +1 -1
  250. package/dist/lib/clipboard/os-clipboard.d.ts +10 -2
  251. package/dist/lib/clipboard/os-clipboard.js +8 -4
  252. package/dist/lib/clipboard/os-clipboard.js.map +1 -1
  253. package/dist/lib/clipboard/paste-target.d.ts +18 -9
  254. package/dist/lib/clipboard/paste-target.js +8 -7
  255. package/dist/lib/clipboard/paste-target.js.map +1 -1
  256. package/dist/lib/cms/field-patch.d.ts +15 -0
  257. package/dist/lib/cms/field-patch.js +25 -0
  258. package/dist/lib/cms/field-patch.js.map +1 -0
  259. package/dist/lib/cms/published-documents.d.ts +1 -1
  260. package/dist/lib/cms/published-documents.js +1 -1
  261. package/dist/lib/cms/published-documents.js.map +1 -1
  262. package/dist/lib/cms-views/view-model.d.ts +3 -3
  263. package/dist/lib/collection-view/__fixtures__/document-writes.d.ts +6 -0
  264. package/dist/lib/collection-view/__fixtures__/document-writes.js +23 -0
  265. package/dist/lib/collection-view/__fixtures__/document-writes.js.map +1 -0
  266. package/dist/lib/collection-view/collection-view-source.d.ts +69 -6
  267. package/dist/lib/collection-view/collection-view-source.js.map +1 -1
  268. package/dist/lib/data/collection-content.d.ts +4 -13
  269. package/dist/lib/data/collection-content.js +11 -5
  270. package/dist/lib/data/collection-content.js.map +1 -1
  271. package/dist/lib/dnd/drop-rules.d.ts +25 -31
  272. package/dist/lib/dnd/drop-rules.js +53 -59
  273. package/dist/lib/dnd/drop-rules.js.map +1 -1
  274. package/dist/lib/engine-version.d.ts +1 -1
  275. package/dist/lib/engine-version.js +1 -1
  276. package/dist/lib/engine-version.js.map +1 -1
  277. package/dist/lib/i18n/messages.de.js +72 -15
  278. package/dist/lib/i18n/messages.de.js.map +1 -1
  279. package/dist/lib/i18n/messages.en.d.ts +61 -12
  280. package/dist/lib/i18n/messages.en.js +76 -16
  281. package/dist/lib/i18n/messages.en.js.map +1 -1
  282. package/dist/lib/operations/__fixtures__/write-document.d.ts +30 -0
  283. package/dist/lib/operations/__fixtures__/write-document.js +16 -0
  284. package/dist/lib/operations/__fixtures__/write-document.js.map +1 -0
  285. package/dist/lib/operations/cms-persistence.d.ts +124 -31
  286. package/dist/lib/operations/cms-persistence.js +447 -68
  287. package/dist/lib/operations/cms-persistence.js.map +1 -1
  288. package/dist/lib/operations/config-manifest-lookup.d.ts +35 -0
  289. package/dist/lib/operations/config-manifest-lookup.js +56 -0
  290. package/dist/lib/operations/config-manifest-lookup.js.map +1 -0
  291. package/dist/lib/operations/project-runtime-config.d.ts +39 -0
  292. package/dist/lib/operations/project-runtime-config.js +54 -0
  293. package/dist/lib/operations/project-runtime-config.js.map +1 -0
  294. package/dist/lib/operations/publish-with-revision.d.ts +64 -0
  295. package/dist/lib/operations/publish-with-revision.js +66 -0
  296. package/dist/lib/operations/publish-with-revision.js.map +1 -0
  297. package/dist/lib/operations/runtime-validator.d.ts +32 -0
  298. package/dist/lib/operations/runtime-validator.js +18 -0
  299. package/dist/lib/operations/runtime-validator.js.map +1 -0
  300. package/dist/lib/operations/session-hydration.d.ts +31 -14
  301. package/dist/lib/operations/session-hydration.js +12 -16
  302. package/dist/lib/operations/session-hydration.js.map +1 -1
  303. package/dist/lib/operations/studio-operations.d.ts +59 -2
  304. package/dist/lib/operations/studio-operations.js +76 -14
  305. package/dist/lib/operations/studio-operations.js.map +1 -1
  306. package/dist/lib/persistence/convex-adapter.d.ts +13 -5
  307. package/dist/lib/persistence/convex-adapter.js +189 -74
  308. package/dist/lib/persistence/convex-adapter.js.map +1 -1
  309. package/dist/lib/persistence/convex-client.d.ts +65 -5
  310. package/dist/lib/persistence/convex-client.js +106 -3
  311. package/dist/lib/persistence/convex-client.js.map +1 -1
  312. package/dist/lib/persistence/index.js +17 -18
  313. package/dist/lib/persistence/index.js.map +1 -1
  314. package/dist/lib/persona/labels.js +4 -1
  315. package/dist/lib/persona/labels.js.map +1 -1
  316. package/dist/lib/query/operations-query.d.ts +1 -1
  317. package/dist/lib/query/operations-query.js +1 -1
  318. package/dist/lib/query/operations-query.js.map +1 -1
  319. package/dist/lib/references/impact.d.ts +21 -15
  320. package/dist/lib/references/impact.js +13 -26
  321. package/dist/lib/references/impact.js.map +1 -1
  322. package/dist/lib/references/references-store.d.ts +9 -0
  323. package/dist/lib/references/references-store.js +24 -4
  324. package/dist/lib/references/references-store.js.map +1 -1
  325. package/dist/lib/routes/route-issues.d.ts +6 -6
  326. package/dist/lib/routes/route-issues.js +26 -80
  327. package/dist/lib/routes/route-issues.js.map +1 -1
  328. package/dist/lib/workspace/config-issues.js +2 -1
  329. package/dist/lib/workspace/config-issues.js.map +1 -1
  330. package/dist/lib/workspace/index.d.ts +31 -1
  331. package/dist/lib/workspace/index.js +64 -17
  332. package/dist/lib/workspace/index.js.map +1 -1
  333. package/dist/lib/workspace/switcher-source.d.ts +3 -3
  334. package/dist/lib/workspace/switcher-source.js.map +1 -1
  335. package/dist/lib/workspace/workspace-source.d.ts +2 -2
  336. package/dist/lib/workspace/workspace-source.js +2 -1
  337. package/dist/lib/workspace/workspace-source.js.map +1 -1
  338. package/package.json +3 -3
  339. package/dist/components/cms/cms-composition-validators.d.ts +0 -16
  340. package/dist/components/cms/cms-composition-validators.js +0 -133
  341. package/dist/components/cms/cms-composition-validators.js.map +0 -1
  342. package/dist/components/cms/publish-clean.d.ts +0 -28
  343. package/dist/components/cms/publish-clean.js +0 -38
  344. package/dist/components/cms/publish-clean.js.map +0 -1
  345. package/dist/components/cms/use-save-mode.d.ts +0 -35
  346. package/dist/components/cms/use-save-mode.js +0 -54
  347. package/dist/components/cms/use-save-mode.js.map +0 -1
  348. package/dist/lib/assets/asset-placements.d.ts +0 -28
  349. package/dist/lib/assets/asset-placements.js +0 -83
  350. package/dist/lib/assets/asset-placements.js.map +0 -1
  351. package/dist/lib/cms/document-gate.d.ts +0 -47
  352. package/dist/lib/cms/document-gate.js +0 -61
  353. package/dist/lib/cms/document-gate.js.map +0 -1
@@ -1,23 +1,22 @@
1
1
  import { ConvexError } from 'convex/values';
2
- import { documentKey, documentSchema } from '@elytracms/core/cms-core';
3
- import { RecordNotFoundError, PersistenceValidationError, assetCreateDefaults, assetRecordFromRow, compareReferenceEntries, highestRole, isKnownRole, persistenceIssueSchema, presentWriteKeys, redirectEntryFromRow, validateForPersistence, } from '@elytracms/core/persistence';
2
+ import { DRAFT_VARIANT, documentKey, documentSchema } from '@elytracms/core/cms-core';
3
+ import { PRESENCE_TTL_MS, RecordNotFoundError, PersistenceValidationError, presenceInputSchema, assetCreateDefaults, assetRecordFromRow, compareReferenceEntries, highestRole, isKnownRole, persistenceIssueSchema, presentWriteKeys, redirectEntryFromRow, validateForPersistence, versionNotPinnableError, } from '@elytracms/core/persistence';
4
4
  import { normalizeInviteEmail } from '@elytracms/core/persistence';
5
- import { queryRef, mutationRef } from './convex-client.js';
5
+ import { createUuidIdFactory } from '@elytracms/core/determinism';
6
+ import { SUBSCRIPTION_UNAUTHENTICATED_MESSAGE, queryRef, mutationRef } from './convex-client.js';
6
7
  import { ConvexBlobStorage } from './convex-blob-storage.js';
7
8
  const realClock = () => new Date().toISOString();
8
- function newId(prefix) {
9
- const rand = typeof globalThis.crypto?.randomUUID === 'function'
10
- ? globalThis.crypto.randomUUID()
11
- : Math.abs(Date.parse(new Date().toISOString())).toString(36);
12
- return `${prefix}_${rand}`;
13
- }
14
- // --- CMS, Assets ----------------------------------------------------------
15
9
  /**
16
- * Deterministic, config-owned schema timestamps (EC-183). CMS collections live
17
- * in `elytra.config.ts`, not the backend, so the synthesized `CmsSchemaRecord`
18
- * carries a stable placeholder rather than DB-row timestamps.
10
+ * EC-578: asset and redirect ids come from the ONE id factory documents and nodes use
11
+ * (`createUuidIdFactory`, `crypto.getRandomValues`). The old fallback minted a
12
+ * millisecond timestamp when `crypto.randomUUID` was missing (any non-secure context),
13
+ * so two writes in one millisecond collided; the factory refuses loudly instead.
19
14
  */
20
- const CONFIG_SCHEMA_TIMESTAMP = '2026-01-01T00:00:00.000Z';
15
+ let mintUuid;
16
+ function newId(prefix) {
17
+ mintUuid ??= createUuidIdFactory();
18
+ return `${prefix}_${mintUuid()}`;
19
+ }
21
20
  /**
22
21
  * Row → contract record. `projectId` is the caller's (the row has none): before
23
22
  * EC-577 this copied the row's nonexistent `projectId`, so every connected
@@ -27,6 +26,8 @@ function docRecordFromDoc(doc, projectId) {
27
26
  return {
28
27
  projectId,
29
28
  document: doc.document,
29
+ variant: doc.variant ?? DRAFT_VARIANT,
30
+ revision: doc.revision,
30
31
  ...(doc.publishedVersion !== undefined ? { publishedVersion: doc.publishedVersion } : {}),
31
32
  // EC-447: carry the materialized publish instant onto the contract record so the
32
33
  // studio's $publishedAt column + its reconcile read the authoritative value.
@@ -51,66 +52,168 @@ function versionRecordFromDoc(doc, projectId) {
51
52
  createdAt: doc.createdAt,
52
53
  ...(doc.label ? { label: doc.label } : {}),
53
54
  ...(doc.origin ? { origin: doc.origin } : {}),
55
+ ...(doc.kind ? { kind: doc.kind } : {}),
54
56
  };
55
57
  }
58
+ /**
59
+ * The Convex CMS repository — the client half of the revisioned aggregate (EC-595). It
60
+ * maps the handlers' outcomes onto the provider-neutral contract (a conflict is DATA,
61
+ * never a throw) and keeps the EC-153 not-found taxonomy client-side. EC-583: documents
62
+ * only — no schema methods (the collections ride the operations context).
63
+ */
56
64
  class ConvexCmsRepository {
57
65
  client;
58
- collections;
66
+ reactive;
59
67
  clock;
60
- /**
61
- * @param collections the active project's config-owned CMS collections
62
- * (EC-183, AD-11), injected from `elytra.config.ts`. The Convex backend has
63
- * no `cmsSchemas` table — `findSchema`/`getSchema` serve these instead.
64
- */
65
- constructor(client, collections = [], clock = realClock) {
68
+ constructor(client, reactive, clock = realClock) {
66
69
  this.client = client;
67
- this.collections = collections;
70
+ this.reactive = reactive;
68
71
  this.clock = clock;
69
72
  }
70
- async saveSchema(_projectId, _collections) {
71
- // Read-only (EC-183, AD-11): the CMS collection schema is owned by
72
- // `elytra.config.ts`; the backend stores documents only. A write here is a
73
- // visible, structured error rather than a silent no-op (the studio's CMS
74
- // schema surfaces are read-only; legitimate writers were removed in EC-183).
75
- throw new PersistenceValidationError('CMS collection schema is owned by elytra.config.ts and cannot be written to the backend.', [
76
- {
77
- code: 'schema-config-owned',
78
- message: 'Edit elytra.config.ts to change collections; the backend stores documents only.',
79
- path: [],
80
- domain: 'cms',
81
- },
82
- ]);
83
- }
84
- /** Config-owned schema (EC-183): the active project's collections from config. */
85
- configSchema(projectId) {
73
+ async replaceDocument(projectId, document, precondition, _options) {
74
+ // A pure document store (EC-183, AD-11): shape-parse only. The actor the patch log
75
+ // records is the server's authenticated caller, never a client claim.
76
+ const parsedDoc = documentSchema.parse(document);
77
+ const result = (await this.client.mutation(mutationRef('cms:replaceDocument'), {
78
+ collection: parsedDoc.collection,
79
+ docId: parsedDoc.id,
80
+ document: parsedDoc,
81
+ precondition,
82
+ at: this.clock(),
83
+ }));
84
+ if (result.status === 'ok')
85
+ return { status: 'ok', record: docRecordFromDoc(result.row, projectId) };
86
86
  return {
87
- projectId,
88
- collections: [...this.collections],
89
- createdAt: CONFIG_SCHEMA_TIMESTAMP,
90
- updatedAt: CONFIG_SCHEMA_TIMESTAMP,
87
+ status: 'conflict',
88
+ current: result.current ? docRecordFromDoc(result.current, projectId) : undefined,
89
+ ...(result.existing ? { existing: docRecordFromDoc(result.existing, projectId) } : {}),
91
90
  };
92
91
  }
93
- async getSchema(projectId) {
94
- return this.configSchema(projectId);
92
+ async patchDocument(projectId, ref, patches, baseRevision, _options) {
93
+ const result = (await this.client.mutation(mutationRef('cms:patchDocument'), {
94
+ collection: ref.collection,
95
+ docId: ref.id,
96
+ patches: [...patches],
97
+ baseRevision,
98
+ at: this.clock(),
99
+ }));
100
+ if (result.status === 'missing') {
101
+ throw new RecordNotFoundError('cms-document', `${projectId}/${documentKey(ref)}`);
102
+ }
103
+ const record = docRecordFromDoc(result.row, projectId);
104
+ return result.status === 'ok'
105
+ ? { status: 'ok', record }
106
+ : { status: 'conflict', record, refused: result.refused, appliedCount: result.appliedCount };
95
107
  }
96
- async findSchema(projectId) {
97
- return this.configSchema(projectId);
108
+ async publishDocument(projectId, ref, expectedRevision, options = {}) {
109
+ const result = (await this.client.mutation(mutationRef('cms:publishDocument'), {
110
+ collection: ref.collection,
111
+ docId: ref.id,
112
+ expectedRevision,
113
+ ...(options.label ? { label: options.label } : {}),
114
+ ...(options.origin ? { origin: options.origin } : {}),
115
+ at: this.clock(),
116
+ }));
117
+ if (result.status === 'missing') {
118
+ throw new RecordNotFoundError('cms-document', `${projectId}/${documentKey(ref)}`);
119
+ }
120
+ if (result.status === 'conflict') {
121
+ return { status: 'conflict', current: docRecordFromDoc(result.current, projectId) };
122
+ }
123
+ return {
124
+ status: 'ok',
125
+ record: docRecordFromDoc(result.row, projectId),
126
+ version: versionRecordFromDoc(result.version, projectId),
127
+ };
98
128
  }
99
- async upsertDocument(projectId, document) {
100
- // The backend is a pure document store (EC-183, AD-11): collection-membership
101
- // validation lives in `@elytracms/core/cms-core` `validateDocument`, which the studio
102
- // runs pre-dispatch (cell edits, imports). We still parse the canonical
103
- // document shape so malformed blobs never reach storage.
104
- const parsedDoc = documentSchema.parse(document);
105
- const now = this.clock();
106
- const doc = (await this.client.mutation(mutationRef('cms:upsertDocument'), {
107
- collection: parsedDoc.collection,
108
- docId: parsedDoc.id,
109
- document: parsedDoc,
110
- createdAt: now,
111
- updatedAt: now,
129
+ async listDocumentPatches(projectId, ref, afterRevision) {
130
+ const rows = (await this.client.query(queryRef('cms:listDocumentPatches'), {
131
+ collection: ref.collection,
132
+ docId: ref.id,
133
+ ...(afterRevision !== undefined ? { afterRevision } : {}),
134
+ }));
135
+ return rows.map((row) => ({
136
+ projectId,
137
+ collection: row.collection,
138
+ docId: row.docId,
139
+ variant: row.variant,
140
+ revision: row.revision,
141
+ kind: row.kind,
142
+ ...(row.actor !== undefined ? { actor: row.actor } : {}),
143
+ at: row.at,
144
+ patches: row.patches,
112
145
  }));
113
- return docRecordFromDoc(doc, projectId);
146
+ }
147
+ /**
148
+ * EC-595 binding point 7 — the narrow document subscription over the REACTIVE client:
149
+ * TWO reactive queries behind this one subject — `cms:watchDocument` (the draft row)
150
+ * and `cms:watchPresence` (its heartbeats) — merged into one snapshot stream, the
151
+ * first snapshot once both have answered (EC-595 A2: one query re-pushed the whole row
152
+ * on every heartbeat — measured in `convex/presence-cost.test.ts`). A rejected session
153
+ * token surfaces as `onError(Unauthenticated …)` — EC-403's session-expired predicate
154
+ * matches it — never as a silently stopped stream. Without a reactive client the
155
+ * subscription fails loudly through `onError`.
156
+ */
157
+ subscribeDocument(projectId, ref, listener) {
158
+ if (!this.reactive) {
159
+ listener.onError?.(new Error('subscribeDocument: this Convex adapter was built without a reactive client.'));
160
+ return () => { };
161
+ }
162
+ const reactive = this.reactive();
163
+ const args = { collection: ref.collection, docId: ref.id };
164
+ let row;
165
+ let presence;
166
+ // One failure (a dropped socket, an expired token) reaches BOTH queries: report it
167
+ // once, until a query answers again.
168
+ let reported;
169
+ const fail = (error) => {
170
+ if (reported === error.message)
171
+ return;
172
+ reported = error.message;
173
+ listener.onError?.(error);
174
+ };
175
+ const emit = () => {
176
+ reported = undefined;
177
+ if (row === undefined || presence === undefined)
178
+ return;
179
+ listener.onSnapshot({
180
+ record: row.value ? docRecordFromDoc(row.value, projectId) : undefined,
181
+ presence,
182
+ });
183
+ };
184
+ const unsubscribeRow = reactive.onUpdate(queryRef('cms:watchDocument'), args, (value) => {
185
+ row = { value: value.row };
186
+ emit();
187
+ }, fail);
188
+ const unsubscribePresence = reactive.onUpdate(queryRef('cms:watchPresence'), args, (value) => {
189
+ presence = value;
190
+ emit();
191
+ }, fail);
192
+ const unhookAuth = reactive.onAuthLost(() => fail(new Error(SUBSCRIPTION_UNAUTHENTICATED_MESSAGE)));
193
+ return () => {
194
+ unhookAuth();
195
+ unsubscribeRow();
196
+ unsubscribePresence();
197
+ };
198
+ }
199
+ async reportPresence(projectId, ref, presence) {
200
+ const parsed = presenceInputSchema.parse(presence);
201
+ await this.client.mutation(mutationRef('cms:reportPresence'), {
202
+ collection: ref.collection,
203
+ docId: ref.id,
204
+ sessionId: parsed.sessionId,
205
+ ...(parsed.actorName !== undefined ? { actorName: parsed.actorName } : {}),
206
+ ...(parsed.path !== undefined ? { path: parsed.path } : {}),
207
+ at: this.clock(),
208
+ ttlMs: PRESENCE_TTL_MS,
209
+ });
210
+ }
211
+ async clearPresence(projectId, ref, sessionId) {
212
+ await this.client.mutation(mutationRef('cms:clearPresence'), {
213
+ collection: ref.collection,
214
+ docId: ref.id,
215
+ sessionId,
216
+ });
114
217
  }
115
218
  async getDocument(projectId, ref) {
116
219
  const found = await this.findDocument(projectId, ref);
@@ -193,22 +296,28 @@ class ConvexCmsRepository {
193
296
  return versionRecordFromDoc(doc, projectId);
194
297
  }
195
298
  async publishDocumentVersion(projectId, ref, version) {
196
- // Taxonomy enforced adapter-side (the handler is lenient): the document and
197
- // the pinned version must both exist (each throws RecordNotFoundError).
198
- await this.getDocument(projectId, ref);
199
- await this.getDocumentVersion(projectId, ref, version);
200
- // EC-447: pass a fresh publish-action instant as the SET-ONCE candidate (the
201
- // handler writes it only when the row has no `publishedAt` yet). One `now` for
202
- // both stamps — this IS the publish moment.
299
+ // EC-595: pin integrity is enforced IN the transaction (the version must exist for
300
+ // this document); the outcomes map onto the EC-153 not-found taxonomy here. EC-447:
301
+ // one `now` for both stamps — the publish-action instant is the SET-ONCE candidate.
203
302
  const now = this.clock();
204
- const doc = (await this.client.mutation(mutationRef('cms:publishDocumentVersion'), {
303
+ const result = (await this.client.mutation(mutationRef('cms:publishDocumentVersion'), {
205
304
  collection: ref.collection,
206
305
  docId: ref.id,
207
306
  version,
208
307
  updatedAt: now,
209
308
  publishedAt: now,
210
309
  }));
211
- return docRecordFromDoc(doc, projectId);
310
+ if (result.status === 'missing') {
311
+ throw new RecordNotFoundError('cms-document', `${projectId}/${documentKey(ref)}`);
312
+ }
313
+ if (result.status === 'missing-version') {
314
+ throw new RecordNotFoundError('cms-document-version', `${projectId}/${documentKey(ref)}#${version}`);
315
+ }
316
+ // EC-585: the version was recorded by a snapshot, never by a publish — the same
317
+ // refusal the in-memory store raises.
318
+ if (result.status === 'not-pinnable')
319
+ throw versionNotPinnableError(`${projectId}/${documentKey(ref)}`, version);
320
+ return docRecordFromDoc(result.row, projectId);
212
321
  }
213
322
  async unpublishDocument(projectId, ref) {
214
323
  const existing = await this.getDocument(projectId, ref);
@@ -715,13 +824,19 @@ export class ConvexPersistenceAdapter {
715
824
  * @param client the Convex client bound to this deployment (one project).
716
825
  * @param projectId the slug of this deployment's one project (EC-179). Used to
717
826
  * stamp deployment-scoped member records onto the still-project-keyed contract.
718
- * @param collections the active project's config-owned CMS collections (EC-183,
719
- * AD-11), from `elytra.config.ts`. The backend has no `cmsSchemas` table, so
720
- * the CMS repository serves the schema from these.
827
+ *
828
+ * @param options.reactive the reactive client a document subscription runs on (EC-595),
829
+ * built lazily — the studio passes `() => getReactiveConvexClient(url)`. Absent (a
830
+ * seed script, a test double without push), `subscribeDocument` fails loudly.
831
+ *
832
+ * EC-583: the third argument is no longer the collections. They only fed the
833
+ * synthesized schema record (`getSchema`/`findSchema`), which the contract no longer
834
+ * has — collections reach the operations layer on its context
835
+ * (`createOperations({ collections })`).
721
836
  */
722
- constructor(client, projectId, collections = []) {
837
+ constructor(client, projectId, options = {}) {
723
838
  this.client = client;
724
- this.cms = new ConvexCmsRepository(client, collections);
839
+ this.cms = new ConvexCmsRepository(client, options.reactive);
725
840
  this.assets = new ConvexAssetRepository(client);
726
841
  this.references = new ConvexReferenceIndexRepository(client);
727
842
  this.members = new ConvexMembersRepository(client, projectId);