@elytracms/studio 0.0.93 → 0.0.94

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 (481) hide show
  1. package/convex/analytics-cost.test.ts +817 -0
  2. package/convex/auth.ts +5 -2
  3. package/convex/authority.ts +35 -11
  4. package/convex/contract-parity.test.ts +7 -0
  5. package/convex/delivery.ts +12 -9
  6. package/convex/guard.ts +9 -7
  7. package/convex/http-routes.test.ts +18 -8
  8. package/convex/http.ts +23 -17
  9. package/convex/ingest.test.ts +326 -0
  10. package/convex/ingest.ts +281 -0
  11. package/convex/invites.ts +13 -5
  12. package/convex/members.ts +10 -4
  13. package/convex/record-functions.ts +325 -0
  14. package/convex/records.test.ts +156 -0
  15. package/convex/redirect-migration.test.ts +205 -0
  16. package/convex/redirect-revalidate.test.ts +5 -1
  17. package/convex/redirects.ts +120 -79
  18. package/convex/role-policy.test.ts +315 -0
  19. package/convex/roles.ts +83 -0
  20. package/convex/schema.ts +30 -0
  21. package/convex/triggers.ts +21 -1
  22. package/dist/addon.d.ts +71 -0
  23. package/dist/addon.js +67 -0
  24. package/dist/addon.js.map +1 -0
  25. package/dist/components/account/account-settings.js +2 -2
  26. package/dist/components/account/account-settings.js.map +1 -1
  27. package/dist/components/account/credential-panel.js +2 -2
  28. package/dist/components/account/credential-panel.js.map +1 -1
  29. package/dist/components/account/self-account-schema.d.ts +6 -15
  30. package/dist/components/account/self-account-schema.js +52 -34
  31. package/dist/components/account/self-account-schema.js.map +1 -1
  32. package/dist/components/account/self-account-view-source.d.ts +1 -1
  33. package/dist/components/account/self-account-view-source.js +6 -4
  34. package/dist/components/account/self-account-view-source.js.map +1 -1
  35. package/dist/components/ai/ai-chat-panel-lazy.js +1 -1
  36. package/dist/components/ai/ai-chat-panel-lazy.js.map +1 -1
  37. package/dist/components/ai/ai-chat-panel.js +6 -6
  38. package/dist/components/ai/ai-chat-panel.js.map +1 -1
  39. package/dist/components/ai/ai-cms-panel.js +2 -2
  40. package/dist/components/ai/ai-cms-panel.js.map +1 -1
  41. package/dist/components/ai/ai-panel-toggle.js +4 -4
  42. package/dist/components/ai/ai-panel-toggle.js.map +1 -1
  43. package/dist/components/ai/chat-ui.js +2 -2
  44. package/dist/components/ai/chat-ui.js.map +1 -1
  45. package/dist/components/ai/proposal-card.d.ts +2 -2
  46. package/dist/components/ai/proposal-card.js +2 -2
  47. package/dist/components/ai/proposal-card.js.map +1 -1
  48. package/dist/components/ai/proposal-issues.js +2 -2
  49. package/dist/components/ai/proposal-issues.js.map +1 -1
  50. package/dist/components/ai/translate-document-action.js +2 -2
  51. package/dist/components/ai/translate-document-action.js.map +1 -1
  52. package/dist/components/ai/translate-document-dialog.js +6 -6
  53. package/dist/components/ai/translate-document-dialog.js.map +1 -1
  54. package/dist/components/ai/translate-skipped.d.ts +1 -1
  55. package/dist/components/ai/translate-skipped.js +2 -2
  56. package/dist/components/ai/translate-skipped.js.map +1 -1
  57. package/dist/components/app-views/default-modules.d.ts +50 -2
  58. package/dist/components/app-views/default-modules.js +65 -20
  59. package/dist/components/app-views/default-modules.js.map +1 -1
  60. package/dist/components/app-views/module.d.ts +53 -3
  61. package/dist/components/app-views/module.js +14 -7
  62. package/dist/components/app-views/module.js.map +1 -1
  63. package/dist/components/app-views/sidebar.js +1 -1
  64. package/dist/components/app-views/sidebar.js.map +1 -1
  65. package/dist/components/app-views/studio-config.d.ts +11 -2
  66. package/dist/components/app-views/studio-config.js.map +1 -1
  67. package/dist/components/assets/asset-detail.js +2 -2
  68. package/dist/components/assets/asset-detail.js.map +1 -1
  69. package/dist/components/assets/asset-grid.js +2 -2
  70. package/dist/components/assets/asset-grid.js.map +1 -1
  71. package/dist/components/assets/asset-thumb.js +3 -3
  72. package/dist/components/assets/asset-thumb.js.map +1 -1
  73. package/dist/components/assets/assets-collection-view-source.d.ts +2 -2
  74. package/dist/components/assets/assets-collection-view-source.js.map +1 -1
  75. package/dist/components/assets/assets-module.js +1 -1
  76. package/dist/components/assets/assets-module.js.map +1 -1
  77. package/dist/components/assets/assets-store.d.ts +2 -2
  78. package/dist/components/assets/assets-store.js +6 -6
  79. package/dist/components/assets/assets-store.js.map +1 -1
  80. package/dist/components/assets/assets-view-preference.js +1 -1
  81. package/dist/components/assets/assets-view-preference.js.map +1 -1
  82. package/dist/components/assets/replace-file.js +1 -1
  83. package/dist/components/assets/replace-file.js.map +1 -1
  84. package/dist/components/auth-context.d.ts +1 -1
  85. package/dist/components/auth-context.js +6 -6
  86. package/dist/components/auth-context.js.map +1 -1
  87. package/dist/components/builder/blocks-model.d.ts +1 -1
  88. package/dist/components/builder/blocks-model.js +3 -3
  89. package/dist/components/builder/blocks-model.js.map +1 -1
  90. package/dist/components/builder/blocks-panel.d.ts +1 -1
  91. package/dist/components/builder/blocks-panel.js +4 -4
  92. package/dist/components/builder/blocks-panel.js.map +1 -1
  93. package/dist/components/builder/builder-store.js +2 -2
  94. package/dist/components/builder/builder-store.js.map +1 -1
  95. package/dist/components/builder/canvas-breakpoints.js +1 -1
  96. package/dist/components/builder/canvas-breakpoints.js.map +1 -1
  97. package/dist/components/builder/canvas-dnd.d.ts +2 -2
  98. package/dist/components/builder/canvas-dnd.js +1 -1
  99. package/dist/components/builder/canvas-dnd.js.map +1 -1
  100. package/dist/components/builder/canvas-inline-editing.d.ts +1 -1
  101. package/dist/components/builder/canvas-inline-editing.js.map +1 -1
  102. package/dist/components/builder/canvas-overlay-geometry.d.ts +1 -1
  103. package/dist/components/builder/canvas-overlay-geometry.js.map +1 -1
  104. package/dist/components/builder/canvas-render-context.d.ts +1 -1
  105. package/dist/components/builder/canvas-render-context.js +4 -4
  106. package/dist/components/builder/canvas-render-context.js.map +1 -1
  107. package/dist/components/builder/canvas-theme-notice.js +2 -2
  108. package/dist/components/builder/canvas-theme-notice.js.map +1 -1
  109. package/dist/components/builder/canvas.js +3 -3
  110. package/dist/components/builder/canvas.js.map +1 -1
  111. package/dist/components/builder/clipboard-actions.d.ts +2 -2
  112. package/dist/components/builder/clipboard-actions.js +4 -4
  113. package/dist/components/builder/clipboard-actions.js.map +1 -1
  114. package/dist/components/builder/composition-field-editor-lazy.js +1 -1
  115. package/dist/components/builder/composition-field-editor-lazy.js.map +1 -1
  116. package/dist/components/builder/composition-field-editor.d.ts +1 -1
  117. package/dist/components/builder/composition-field-editor.js +3 -3
  118. package/dist/components/builder/composition-field-editor.js.map +1 -1
  119. package/dist/components/builder/drag-session.d.ts +1 -1
  120. package/dist/components/builder/drag-session.js.map +1 -1
  121. package/dist/components/builder/fixture-thumbnail.js +1 -1
  122. package/dist/components/builder/fixture-thumbnail.js.map +1 -1
  123. package/dist/components/builder/insert-context.d.ts +1 -1
  124. package/dist/components/builder/insert-context.js +3 -3
  125. package/dist/components/builder/insert-context.js.map +1 -1
  126. package/dist/components/builder/layout-inspector.js +1 -1
  127. package/dist/components/builder/layout-inspector.js.map +1 -1
  128. package/dist/components/builder/node-title.js +1 -1
  129. package/dist/components/builder/node-title.js.map +1 -1
  130. package/dist/components/builder/page-tree.d.ts +1 -1
  131. package/dist/components/builder/page-tree.js +5 -5
  132. package/dist/components/builder/page-tree.js.map +1 -1
  133. package/dist/components/builder/placement-refusal.js +3 -3
  134. package/dist/components/builder/placement-refusal.js.map +1 -1
  135. package/dist/components/builder/property-inspector.js +4 -4
  136. package/dist/components/builder/property-inspector.js.map +1 -1
  137. package/dist/components/builder/reuse-context.js +4 -4
  138. package/dist/components/builder/reuse-context.js.map +1 -1
  139. package/dist/components/builder/reuse.js +1 -1
  140. package/dist/components/builder/reuse.js.map +1 -1
  141. package/dist/components/builder/rich-text-toolbar.js +1 -1
  142. package/dist/components/builder/rich-text-toolbar.js.map +1 -1
  143. package/dist/components/builder/run-inspector.js +1 -1
  144. package/dist/components/builder/run-inspector.js.map +1 -1
  145. package/dist/components/builder/selection-overlay.js +3 -3
  146. package/dist/components/builder/selection-overlay.js.map +1 -1
  147. package/dist/components/builder/spacing-handles.d.ts +2 -2
  148. package/dist/components/builder/spacing-handles.js +3 -3
  149. package/dist/components/builder/spacing-handles.js.map +1 -1
  150. package/dist/components/cms/asset-field.js +2 -2
  151. package/dist/components/cms/asset-field.js.map +1 -1
  152. package/dist/components/cms/asset-picker.js +2 -2
  153. package/dist/components/cms/asset-picker.js.map +1 -1
  154. package/dist/components/cms/bulk-actions.d.ts +1 -1
  155. package/dist/components/cms/bulk-actions.js +2 -2
  156. package/dist/components/cms/bulk-actions.js.map +1 -1
  157. package/dist/components/cms/bulk-bar.d.ts +2 -2
  158. package/dist/components/cms/bulk-bar.js +2 -2
  159. package/dist/components/cms/bulk-bar.js.map +1 -1
  160. package/dist/components/cms/cell-edit.d.ts +1 -1
  161. package/dist/components/cms/cell-edit.js +1 -1
  162. package/dist/components/cms/cell-edit.js.map +1 -1
  163. package/dist/components/cms/cell.d.ts +1 -1
  164. package/dist/components/cms/cell.js +3 -3
  165. package/dist/components/cms/cell.js.map +1 -1
  166. package/dist/components/cms/cms-index.js +2 -2
  167. package/dist/components/cms/cms-index.js.map +1 -1
  168. package/dist/components/cms/cms-labels.js +2 -2
  169. package/dist/components/cms/cms-labels.js.map +1 -1
  170. package/dist/components/cms/cms-store.d.ts +2 -2
  171. package/dist/components/cms/cms-store.js +10 -10
  172. package/dist/components/cms/cms-store.js.map +1 -1
  173. package/dist/components/cms/collection-table.js +7 -7
  174. package/dist/components/cms/collection-table.js.map +1 -1
  175. package/dist/components/cms/detail-editor.d.ts +5 -2
  176. package/dist/components/cms/detail-editor.js +20 -14
  177. package/dist/components/cms/detail-editor.js.map +1 -1
  178. package/dist/components/cms/details-pane.js +1 -1
  179. package/dist/components/cms/details-pane.js.map +1 -1
  180. package/dist/components/cms/doc-state.js +1 -1
  181. package/dist/components/cms/doc-state.js.map +1 -1
  182. package/dist/components/cms/document-diff-view.js +5 -5
  183. package/dist/components/cms/document-diff-view.js.map +1 -1
  184. package/dist/components/cms/document-editor-context.js +1 -1
  185. package/dist/components/cms/document-editor-context.js.map +1 -1
  186. package/dist/components/cms/document-staging.js +1 -1
  187. package/dist/components/cms/document-staging.js.map +1 -1
  188. package/dist/components/cms/document-url-path.js +2 -2
  189. package/dist/components/cms/document-url-path.js.map +1 -1
  190. package/dist/components/cms/duplicate-document.d.ts +1 -1
  191. package/dist/components/cms/duplicate-document.js.map +1 -1
  192. package/dist/components/cms/editable-cell.js +3 -3
  193. package/dist/components/cms/editable-cell.js.map +1 -1
  194. package/dist/components/cms/field-collaboration.d.ts +1 -1
  195. package/dist/components/cms/field-collaboration.js +1 -1
  196. package/dist/components/cms/field-collaboration.js.map +1 -1
  197. package/dist/components/cms/field-connection.d.ts +1 -1
  198. package/dist/components/cms/field-connection.js +2 -2
  199. package/dist/components/cms/field-connection.js.map +1 -1
  200. package/dist/components/cms/field-control-lib.js +1 -1
  201. package/dist/components/cms/field-control-lib.js.map +1 -1
  202. package/dist/components/cms/field-control.js +4 -4
  203. package/dist/components/cms/field-control.js.map +1 -1
  204. package/dist/components/cms/field-renderer.d.ts +1 -1
  205. package/dist/components/cms/field-renderer.js +2 -2
  206. package/dist/components/cms/field-renderer.js.map +1 -1
  207. package/dist/components/cms/filter-builder.d.ts +1 -1
  208. package/dist/components/cms/filter-builder.js +3 -3
  209. package/dist/components/cms/filter-builder.js.map +1 -1
  210. package/dist/components/cms/gate-validation.js +1 -1
  211. package/dist/components/cms/gate-validation.js.map +1 -1
  212. package/dist/components/cms/import-export.js +2 -2
  213. package/dist/components/cms/import-export.js.map +1 -1
  214. package/dist/components/cms/issue-copy.js +1 -1
  215. package/dist/components/cms/issue-copy.js.map +1 -1
  216. package/dist/components/cms/issues-pane.js +3 -3
  217. package/dist/components/cms/issues-pane.js.map +1 -1
  218. package/dist/components/cms/object-array-field.js +2 -2
  219. package/dist/components/cms/object-array-field.js.map +1 -1
  220. package/dist/components/cms/prop-fields.js +2 -2
  221. package/dist/components/cms/prop-fields.js.map +1 -1
  222. package/dist/components/cms/reference-confirm.d.ts +1 -1
  223. package/dist/components/cms/reference-confirm.js +2 -2
  224. package/dist/components/cms/reference-confirm.js.map +1 -1
  225. package/dist/components/cms/relation-picker.js +2 -2
  226. package/dist/components/cms/relation-picker.js.map +1 -1
  227. package/dist/components/cms/rich-text/block-style.js +1 -1
  228. package/dist/components/cms/rich-text/block-style.js.map +1 -1
  229. package/dist/components/cms/rich-text/component-embed.js +4 -4
  230. package/dist/components/cms/rich-text/component-embed.js.map +1 -1
  231. package/dist/components/cms/rich-text/editor.js +3 -3
  232. package/dist/components/cms/rich-text/editor.js.map +1 -1
  233. package/dist/components/cms/rich-text/embed-picker.js +3 -3
  234. package/dist/components/cms/rich-text/embed-picker.js.map +1 -1
  235. package/dist/components/cms/rich-text/embed-props.js +3 -3
  236. package/dist/components/cms/rich-text/embed-props.js.map +1 -1
  237. package/dist/components/cms/rich-text/extensions.js +1 -1
  238. package/dist/components/cms/rich-text/extensions.js.map +1 -1
  239. package/dist/components/cms/rich-text/font-size-control.d.ts +1 -1
  240. package/dist/components/cms/rich-text/font-size-control.js +2 -2
  241. package/dist/components/cms/rich-text/font-size-control.js.map +1 -1
  242. package/dist/components/cms/rich-text/lazy.js +1 -1
  243. package/dist/components/cms/rich-text/lazy.js.map +1 -1
  244. package/dist/components/cms/rich-text/link-control.js +2 -2
  245. package/dist/components/cms/rich-text/link-control.js.map +1 -1
  246. package/dist/components/cms/rich-text/slash-command.js +3 -3
  247. package/dist/components/cms/rich-text/slash-command.js.map +1 -1
  248. package/dist/components/cms/rich-text/toolbar.js +1 -1
  249. package/dist/components/cms/rich-text/toolbar.js.map +1 -1
  250. package/dist/components/cms/rich-text/unknown-node.js +2 -2
  251. package/dist/components/cms/rich-text/unknown-node.js.map +1 -1
  252. package/dist/components/cms/rich-text-preview.js +2 -2
  253. package/dist/components/cms/rich-text-preview.js.map +1 -1
  254. package/dist/components/cms/row-actions.d.ts +1 -1
  255. package/dist/components/cms/row-actions.js.map +1 -1
  256. package/dist/components/cms/scalar-value-editor.d.ts +1 -1
  257. package/dist/components/cms/scalar-value-editor.js +2 -2
  258. package/dist/components/cms/scalar-value-editor.js.map +1 -1
  259. package/dist/components/cms/use-discard-controller.d.ts +1 -1
  260. package/dist/components/cms/use-discard-controller.js +1 -1
  261. package/dist/components/cms/use-discard-controller.js.map +1 -1
  262. package/dist/components/cms/use-document-draft.d.ts +7 -1
  263. package/dist/components/cms/use-document-draft.js +16 -2
  264. package/dist/components/cms/use-document-draft.js.map +1 -1
  265. package/dist/components/cms/use-document-versions.js +1 -1
  266. package/dist/components/cms/use-document-versions.js.map +1 -1
  267. package/dist/components/cms/use-field-focus.d.ts +1 -1
  268. package/dist/components/cms/use-field-focus.js +2 -2
  269. package/dist/components/cms/use-field-focus.js.map +1 -1
  270. package/dist/components/cms/use-live-url.d.ts +1 -1
  271. package/dist/components/cms/use-live-url.js +3 -3
  272. package/dist/components/cms/use-live-url.js.map +1 -1
  273. package/dist/components/cms/use-publish-controller.d.ts +1 -1
  274. package/dist/components/cms/use-publish-controller.js +1 -1
  275. package/dist/components/cms/use-publish-controller.js.map +1 -1
  276. package/dist/components/cms/use-published-perspective.d.ts +1 -1
  277. package/dist/components/cms/use-published-perspective.js.map +1 -1
  278. package/dist/components/cms/use-restore-version.d.ts +1 -1
  279. package/dist/components/cms/use-restore-version.js +1 -1
  280. package/dist/components/cms/use-restore-version.js.map +1 -1
  281. package/dist/components/cms/version-compare-lazy.js +1 -1
  282. package/dist/components/cms/version-compare-lazy.js.map +1 -1
  283. package/dist/components/cms/version-compare.js +1 -1
  284. package/dist/components/cms/version-compare.js.map +1 -1
  285. package/dist/components/cms/versions-pane.js +3 -3
  286. package/dist/components/cms/versions-pane.js.map +1 -1
  287. package/dist/components/cms/view-bar.js +3 -3
  288. package/dist/components/cms/view-bar.js.map +1 -1
  289. package/dist/components/collection-view/cms-collection-view-source.d.ts +1 -1
  290. package/dist/components/collection-view/cms-collection-view-source.js +2 -2
  291. package/dist/components/collection-view/cms-collection-view-source.js.map +1 -1
  292. package/dist/components/collection-view/collection-view-context.d.ts +1 -1
  293. package/dist/components/collection-view/collection-view-context.js +1 -1
  294. package/dist/components/collection-view/collection-view-context.js.map +1 -1
  295. package/dist/components/collection-view/collection-view-surface.d.ts +13 -0
  296. package/dist/components/collection-view/collection-view-surface.js +18 -0
  297. package/dist/components/collection-view/collection-view-surface.js.map +1 -0
  298. package/dist/components/components/component-detail.js +10 -10
  299. package/dist/components/components/component-detail.js.map +1 -1
  300. package/dist/components/components/component-preview.d.ts +2 -2
  301. package/dist/components/components/component-preview.js +5 -5
  302. package/dist/components/components/component-preview.js.map +1 -1
  303. package/dist/components/components/component-status.d.ts +1 -1
  304. package/dist/components/components/component-status.js +1 -1
  305. package/dist/components/components/component-status.js.map +1 -1
  306. package/dist/components/components/components-index.js +2 -2
  307. package/dist/components/components/components-index.js.map +1 -1
  308. package/dist/components/components/field-inspector.js +1 -1
  309. package/dist/components/components/field-inspector.js.map +1 -1
  310. package/dist/components/components/registry-browser.js +3 -3
  311. package/dist/components/components/registry-browser.js.map +1 -1
  312. package/dist/components/components/taxonomy.d.ts +1 -1
  313. package/dist/components/components/taxonomy.js.map +1 -1
  314. package/dist/components/components/transfer-dialog.js +3 -3
  315. package/dist/components/components/transfer-dialog.js.map +1 -1
  316. package/dist/components/current-user-menu.js +1 -1
  317. package/dist/components/current-user-menu.js.map +1 -1
  318. package/dist/components/delivery-health-banner.js +2 -2
  319. package/dist/components/delivery-health-banner.js.map +1 -1
  320. package/dist/components/deployment-setup-gate.js +3 -3
  321. package/dist/components/deployment-setup-gate.js.map +1 -1
  322. package/dist/components/dev-login-badge.js +2 -2
  323. package/dist/components/dev-login-badge.js.map +1 -1
  324. package/dist/components/guards.js +1 -1
  325. package/dist/components/guards.js.map +1 -1
  326. package/dist/components/inspector/document-panes.d.ts +54 -0
  327. package/dist/components/inspector/document-panes.js +41 -0
  328. package/dist/components/inspector/document-panes.js.map +1 -0
  329. package/dist/components/inspector/inspector-sidebar.js +1 -1
  330. package/dist/components/inspector/inspector-sidebar.js.map +1 -1
  331. package/dist/components/nav-issues-registry.d.ts +53 -0
  332. package/dist/components/nav-issues-registry.js +69 -0
  333. package/dist/components/nav-issues-registry.js.map +1 -0
  334. package/dist/components/nav-issues.js +4 -4
  335. package/dist/components/nav-issues.js.map +1 -1
  336. package/dist/components/operation-error.d.ts +1 -1
  337. package/dist/components/operation-error.js +4 -4
  338. package/dist/components/operation-error.js.map +1 -1
  339. package/dist/components/playground-badge.js +2 -2
  340. package/dist/components/playground-badge.js.map +1 -1
  341. package/dist/components/project-list.js +3 -3
  342. package/dist/components/project-list.js.map +1 -1
  343. package/dist/components/project-session.d.ts +2 -2
  344. package/dist/components/project-session.js +5 -5
  345. package/dist/components/project-session.js.map +1 -1
  346. package/dist/components/project-switcher.js +2 -2
  347. package/dist/components/project-switcher.js.map +1 -1
  348. package/dist/components/rail-store-status.js +1 -1
  349. package/dist/components/rail-store-status.js.map +1 -1
  350. package/dist/components/rail-view-source.d.ts +6 -5
  351. package/dist/components/rail-view-source.js +4 -3
  352. package/dist/components/rail-view-source.js.map +1 -1
  353. package/dist/components/records/record-lens.d.ts +51 -0
  354. package/dist/components/records/record-lens.js +156 -0
  355. package/dist/components/records/record-lens.js.map +1 -0
  356. package/dist/components/records/record-rail.d.ts +86 -0
  357. package/dist/components/records/record-rail.js +241 -0
  358. package/dist/components/records/record-rail.js.map +1 -0
  359. package/dist/components/redirects/redirects-schema.js +1 -1
  360. package/dist/components/redirects/redirects-schema.js.map +1 -1
  361. package/dist/components/references/references-panel.js +2 -2
  362. package/dist/components/references/references-panel.js.map +1 -1
  363. package/dist/components/section-nav.js +27 -11
  364. package/dist/components/section-nav.js.map +1 -1
  365. package/dist/components/section-state.js +2 -2
  366. package/dist/components/section-state.js.map +1 -1
  367. package/dist/components/section-surface.js +2 -2
  368. package/dist/components/section-surface.js.map +1 -1
  369. package/dist/components/session-expired-overlay.js +3 -3
  370. package/dist/components/session-expired-overlay.js.map +1 -1
  371. package/dist/components/settings-form.js +1 -1
  372. package/dist/components/settings-form.js.map +1 -1
  373. package/dist/components/sign-in-needed-badge.js +1 -1
  374. package/dist/components/sign-in-needed-badge.js.map +1 -1
  375. package/dist/components/sign-in-view.d.ts +2 -2
  376. package/dist/components/sign-in-view.js +5 -5
  377. package/dist/components/sign-in-view.js.map +1 -1
  378. package/dist/components/studio-context.js +3 -3
  379. package/dist/components/studio-context.js.map +1 -1
  380. package/dist/components/studio-project-screen.js +30 -3
  381. package/dist/components/studio-project-screen.js.map +1 -1
  382. package/dist/components/studio-shell.js +6 -5
  383. package/dist/components/studio-shell.js.map +1 -1
  384. package/dist/components/theme-context.d.ts +1 -1
  385. package/dist/components/theme-context.js +2 -2
  386. package/dist/components/theme-context.js.map +1 -1
  387. package/dist/components/use-hydration-phase.d.ts +1 -1
  388. package/dist/components/use-hydration-phase.js +1 -1
  389. package/dist/components/use-hydration-phase.js.map +1 -1
  390. package/dist/components/users/invite-user-bar.d.ts +27 -0
  391. package/dist/components/users/invite-user-bar.js +25 -9
  392. package/dist/components/users/invite-user-bar.js.map +1 -1
  393. package/dist/components/users/role-policy-drift-issues.d.ts +38 -0
  394. package/dist/components/users/role-policy-drift-issues.js +71 -0
  395. package/dist/components/users/role-policy-drift-issues.js.map +1 -0
  396. package/dist/components/users/users-collection-view-source.d.ts +1 -1
  397. package/dist/components/users/users-collection-view-source.js +4 -2
  398. package/dist/components/users/users-collection-view-source.js.map +1 -1
  399. package/dist/components/users/users-module.js +1 -1
  400. package/dist/components/users/users-module.js.map +1 -1
  401. package/dist/components/users/users-schema.d.ts +10 -1
  402. package/dist/components/users/users-schema.js +45 -25
  403. package/dist/components/users/users-schema.js.map +1 -1
  404. package/dist/components/users/users-store.js +3 -3
  405. package/dist/components/users/users-store.js.map +1 -1
  406. package/dist/components/workspace-projects.d.ts +1 -1
  407. package/dist/components/workspace-projects.js +1 -1
  408. package/dist/components/workspace-projects.js.map +1 -1
  409. package/dist/lib/ai/index.d.ts +1 -1
  410. package/dist/lib/ai/index.js +1 -1
  411. package/dist/lib/ai/index.js.map +1 -1
  412. package/dist/lib/ai/node-handler.d.ts +1 -1
  413. package/dist/lib/ai/node-handler.js.map +1 -1
  414. package/dist/lib/ai/translate-document.js +1 -1
  415. package/dist/lib/ai/translate-document.js.map +1 -1
  416. package/dist/lib/clipboard/paste-issues.js +1 -1
  417. package/dist/lib/clipboard/paste-issues.js.map +1 -1
  418. package/dist/lib/clipboard/paste-target.js +2 -2
  419. package/dist/lib/clipboard/paste-target.js.map +1 -1
  420. package/dist/lib/cms/published-documents.js +1 -1
  421. package/dist/lib/cms/published-documents.js.map +1 -1
  422. package/dist/lib/cms-views/view-model.d.ts +12 -12
  423. package/dist/lib/cms-views/view-model.js +2 -2
  424. package/dist/lib/cms-views/view-model.js.map +1 -1
  425. package/dist/lib/collection-view/collection-view-source.d.ts +7 -2
  426. package/dist/lib/collection-view/collection-view-source.js.map +1 -1
  427. package/dist/lib/data/content-session-refresh.d.ts +1 -1
  428. package/dist/lib/data/content-session-refresh.js.map +1 -1
  429. package/dist/lib/data/playground-unload-guard.d.ts +1 -1
  430. package/dist/lib/data/playground-unload-guard.js +1 -1
  431. package/dist/lib/data/playground-unload-guard.js.map +1 -1
  432. package/dist/lib/engine-version.d.ts +1 -1
  433. package/dist/lib/engine-version.js +1 -1
  434. package/dist/lib/engine-version.js.map +1 -1
  435. package/dist/lib/i18n/i18n.js +1 -1
  436. package/dist/lib/i18n/i18n.js.map +1 -1
  437. package/dist/lib/i18n/messages.de.js +4 -0
  438. package/dist/lib/i18n/messages.de.js.map +1 -1
  439. package/dist/lib/i18n/messages.en.d.ts +4 -0
  440. package/dist/lib/i18n/messages.en.js +8 -0
  441. package/dist/lib/i18n/messages.en.js.map +1 -1
  442. package/dist/lib/operations/project-runtime-config.d.ts +9 -0
  443. package/dist/lib/operations/project-runtime-config.js +13 -0
  444. package/dist/lib/operations/project-runtime-config.js.map +1 -1
  445. package/dist/lib/operations/studio-operations.d.ts +9 -1
  446. package/dist/lib/operations/studio-operations.js +6 -1
  447. package/dist/lib/operations/studio-operations.js.map +1 -1
  448. package/dist/lib/persistence/convex-adapter.d.ts +16 -3
  449. package/dist/lib/persistence/convex-adapter.js +78 -47
  450. package/dist/lib/persistence/convex-adapter.js.map +1 -1
  451. package/dist/lib/persistence/index.js +14 -1
  452. package/dist/lib/persistence/index.js.map +1 -1
  453. package/dist/lib/persona/labels.js +1 -1
  454. package/dist/lib/persona/labels.js.map +1 -1
  455. package/dist/lib/query/operations-query.d.ts +1 -1
  456. package/dist/lib/query/operations-query.js +1 -1
  457. package/dist/lib/query/operations-query.js.map +1 -1
  458. package/dist/lib/references/reference-model.js +1 -1
  459. package/dist/lib/references/reference-model.js.map +1 -1
  460. package/dist/lib/references/references-store.js +1 -1
  461. package/dist/lib/references/references-store.js.map +1 -1
  462. package/dist/lib/workspace/index.d.ts +6 -0
  463. package/dist/lib/workspace/index.js +8 -0
  464. package/dist/lib/workspace/index.js.map +1 -1
  465. package/dist/mount.d.ts +28 -0
  466. package/dist/mount.js +27 -0
  467. package/dist/mount.js.map +1 -0
  468. package/dist/router/root.js +10 -10
  469. package/dist/router/root.js.map +1 -1
  470. package/dist/ui/badge.d.ts +1 -1
  471. package/dist/ui/button.d.ts +2 -2
  472. package/package.json +15 -11
  473. package/dist/components/redirects/redirects-collection-view-source.d.ts +0 -53
  474. package/dist/components/redirects/redirects-collection-view-source.js +0 -146
  475. package/dist/components/redirects/redirects-collection-view-source.js.map +0 -1
  476. package/dist/components/redirects/redirects-module.d.ts +0 -21
  477. package/dist/components/redirects/redirects-module.js +0 -40
  478. package/dist/components/redirects/redirects-module.js.map +0 -1
  479. package/dist/components/redirects/redirects-store.d.ts +0 -68
  480. package/dist/components/redirects/redirects-store.js +0 -132
  481. package/dist/components/redirects/redirects-store.js.map +0 -1
package/convex/auth.ts CHANGED
@@ -4,8 +4,9 @@ import { actionGeneric } from 'convex/server'
4
4
  import type { AnyDataModel, GenericMutationCtx } from 'convex/server'
5
5
  import { ConvexError, v } from 'convex/values'
6
6
  import type { GenericId } from 'convex/values'
7
- import { classifyInviteSignup, normalizeInviteEmail } from '@elytracms/core/persistence'
7
+ import { bootstrapRole, classifyInviteSignup, normalizeInviteEmail } from '@elytracms/core/persistence'
8
8
  import type { InviteSignupDecision } from '@elytracms/core/persistence'
9
+ import { deployedRolePolicy } from './roles'
9
10
 
10
11
  /** The provider list `convexAuth` accepts, derived from its own signature. */
11
12
  type ConvexAuthProviders = Parameters<typeof convexAuth>[0]['providers']
@@ -210,7 +211,9 @@ export async function gateSessionCreation(
210
211
  case 'bootstrap-admin':
211
212
  await ctx.db.insert('members', {
212
213
  userId: args.userId,
213
- role: 'admin',
214
+ // EC-605: the deployed policy's strongest role (which the policy rules require
215
+ // to grant `project.administer`) — `admin` under the default policy.
216
+ role: bootstrapRole(deployedRolePolicy()),
214
217
  createdAt: now,
215
218
  updatedAt: now,
216
219
  })
@@ -1,12 +1,14 @@
1
1
  import { checkAuthorityFloor } from '@elytracms/core/operations'
2
2
  import {
3
3
  buildAuthorityAuditEntry,
4
- defaultRolePolicy,
4
+ isKnownRole,
5
+ policyRoleIds,
5
6
  roleGrantsCapability,
6
7
  } from '@elytracms/core/persistence'
7
8
  import type { AuthorityAuditOutcome, ProjectRole } from '@elytracms/core/persistence'
8
9
  import type { MutationCtx } from './functions'
9
10
  import { memberRole, type GuardCtx } from './guard'
11
+ import { deployedRolePolicy } from './roles'
10
12
 
11
13
  /**
12
14
  * The engine integrity FLOOR at the Convex trust boundary (EC-240 / AD-18 tenet
@@ -16,9 +18,10 @@ import { memberRole, type GuardCtx } from './guard'
16
18
  * invariants on authority-mutating handlers (`members:set`, `members:remove` —
17
19
  * and therefore `users.save`/`users.remove`, which delegate to them):
18
20
  *
19
- * - **(a) assign only roles you hold** — future-proofs the boundary for when
20
- * policy opens member-management to non-admins; today an administrator can
21
- * grant any role, so this never fires under the default policy.
21
+ * - **(a) assign only roles you hold** — never fires under the default policy (the
22
+ * only administrator, `admin`, is the strongest role); under a declared policy
23
+ * with an administering role below the strongest (EC-605) it is what stops that
24
+ * role granting — or inviting someone into (`invites:create`) — a rank above its own.
22
25
  * - **(b) the last admin can't be removed or demoted** — the hole this closes:
23
26
  * before EC-240 the handlers did a blind upsert/delete, so an admin could
24
27
  * remove the sole admin and lock everyone out (the empty-table bootstrap in
@@ -27,28 +30,32 @@ import { memberRole, type GuardCtx } from './guard'
27
30
  * The decision logic is the ONE shared `checkAuthorityFloor` primitive that the
28
31
  * engine resolver also calls — this file only supplies the primitives it reads
29
32
  * from `ctx.db`, so client and server can never drift. Denials throw
30
- * `unauthorized: …`, matching the guard convention.
33
+ * `unauthorized: …`, matching the guard convention. The policy is the deployment's
34
+ * (EC-605, `deployedRolePolicy`) — the one `requireCapability` granted by.
31
35
  */
32
36
  export async function assertAuthorityFloor(
33
37
  ctx: GuardCtx,
34
38
  params: {
35
39
  /** The caller (already `requireCapability('project.administer')`-checked). */
36
40
  readonly callerId: string
37
- /** The member whose role/membership this op changes. */
38
- readonly targetUserId: string
41
+ /**
42
+ * The member whose role/membership this op changes; absent for an invite (EC-605 —
43
+ * it grants a role to someone who is not a member yet, so only (a) can apply).
44
+ */
45
+ readonly targetUserId?: string
39
46
  /** Roles this op grants the target (empty for a pure removal). */
40
47
  readonly rolesGranted: readonly ProjectRole[]
41
48
  /** True when the op removes the membership entirely. */
42
49
  readonly removesMembership: boolean
43
50
  },
44
51
  ): Promise<void> {
52
+ const policy = deployedRolePolicy()
45
53
  const callerRole = await memberRole(ctx, params.callerId)
46
- const targetRole = await memberRole(ctx, params.targetUserId)
54
+ const targetRole = params.targetUserId !== undefined ? await memberRole(ctx, params.targetUserId) : null
47
55
  const members = await ctx.db.query('members').collect()
48
56
  // "admin" generalizes to "grants project.administer" (EC-240c) — the same
49
57
  // policy the engine resolver uses, so client and server can never drift.
50
- const isAdministrator = (role: string) =>
51
- roleGrantsCapability(role, 'project.administer', defaultRolePolicy)
58
+ const isAdministrator = (role: string) => roleGrantsCapability(role, 'project.administer', policy)
52
59
  const adminCount = members.filter((m) => isAdministrator(m.role)).length
53
60
 
54
61
  const decision = checkAuthorityFloor(
@@ -59,11 +66,28 @@ export async function assertAuthorityFloor(
59
66
  removesMembership: params.removesMembership,
60
67
  adminCount,
61
68
  },
62
- defaultRolePolicy,
69
+ policy,
63
70
  )
64
71
  if (!decision.allow) throw new Error(`unauthorized: ${decision.reason}`)
65
72
  }
66
73
 
74
+ /**
75
+ * EC-605 — refuse a role the deployed policy does not declare, at the WRITE (a member's
76
+ * role, an invite's role), visibly: `invalid-role: …` naming the declared set. The
77
+ * studio validates against the project's config first; this is the trust boundary's own
78
+ * check, so a hand-crafted client or a drifted deployment can never store a role nobody
79
+ * can enforce (an unknown role grants nothing — it would fail closed on every read,
80
+ * silently).
81
+ */
82
+ export function assertDeclaredRole(role: string): void {
83
+ const policy = deployedRolePolicy()
84
+ if (!isKnownRole(role, policy)) {
85
+ throw new Error(
86
+ `invalid-role: role "${role}" is not declared in this deployment's role policy (declared: ${policyRoleIds(policy).join(', ')}). Declare it in elytra.config.ts \`roles\` and re-provision (\`elytra init\`), or pick a declared role.`,
87
+ )
88
+ }
89
+ }
90
+
67
91
  /**
68
92
  * Append one {@link buildAuthorityAuditEntry} row to the `authorityAudit` table
69
93
  * (EC-240 H-tail item 6). Called at the floor chokepoint (`members:set` /
@@ -4,6 +4,7 @@ import type { ConvexHttpClient } from 'convex/browser'
4
4
  import type { FunctionReference } from 'convex/server'
5
5
  import { fieldPath, measureMaxDepth, nodePropPath } from '@elytracms/core/cms-core'
6
6
  import {
7
+ CONTRACT_RECORD_KINDS,
7
8
  PERSISTENCE_CONTRACT_SCENARIOS,
8
9
  publishCurrentDraft,
9
10
  replaceAtCurrentRevision,
@@ -12,6 +13,7 @@ import type { ContractContext } from '@elytracms/core/persistence'
12
13
  import { ConvexPersistenceAdapter } from '../src/lib/persistence/convex-adapter'
13
14
  import type { ReactiveDocumentClient } from '../src/lib/persistence/convex-client'
14
15
  import schema from './schema'
16
+ import { defineRecordFunctions } from './record-functions'
15
17
 
16
18
  /**
17
19
  * EC-454 — Convex-side contract parity, restored.
@@ -40,6 +42,11 @@ import schema from './schema'
40
42
  const modules = {
41
43
  ...import.meta.glob(['./**/*.ts', '!./**/*.test.ts', '!./_generated/**']),
42
44
  './_generated/api.ts': async () => ({}),
45
+ // EC-616: the contract's FIXTURE record kinds, registered as test-only modules exactly as
46
+ // an addon's convex module registers its kind (one module per kind, named by the kind).
47
+ ...Object.fromEntries(
48
+ CONTRACT_RECORD_KINDS.map((kind) => [`./${kind.module}.ts`, async () => defineRecordFunctions(kind)]),
49
+ ),
43
50
  }
44
51
 
45
52
  /** The config-owned slug this deployment's records are stamped with (EC-179). */
@@ -1,12 +1,14 @@
1
1
  import { v } from 'convex/values'
2
- import { DRAFT_PERSPECTIVE, DRAFT_VARIANT, unfoldValue, variantOf } from '@elytracms/core/cms-core'
2
+ import { DRAFT_PERSPECTIVE, DRAFT_VARIANT, liveRedirectRecords, unfoldValue, variantOf } from '@elytracms/core/cms-core'
3
3
  import type { Perspective } from '@elytracms/core/cms-core'
4
4
  import { assetRecordFromRow, deliveryAssetRecord } from '@elytracms/core/persistence'
5
+ import { constantTimeEquals } from '@elytracms/core/revalidation'
5
6
  import type { AssetRecord } from '@elytracms/core/persistence'
6
7
  import studioPackage from '../package.json'
7
8
  import { query } from './functions'
8
9
  import type { QueryCtx } from './functions'
9
10
  import { requireCapability } from './guard'
11
+ import { loadRedirectEntries } from './redirects'
10
12
 
11
13
  // EC-207: routes/redirects are config-sourced (authored in elytra.config.ts and
12
14
  // resolved by studio-core), NOT backend data. The delivery snapshot no longer
@@ -156,13 +158,12 @@ export async function loadSnapshot(
156
158
 
157
159
  const assets = await loadDeliveryAssets(ctx, projectId)
158
160
 
159
- // Editorial redirects (EC-303): only ENABLED entries serve — a disabled redirect
160
- // is stored but never live, in either perspective. Projected to the routing
161
- // `RedirectRecord` shape the router/`resolvePage` consume.
162
- const redirectRows = await ctx.db.query('redirects').withIndex('by_redirect').collect()
163
- const redirects = redirectRows
164
- .filter((row) => row.enabled)
165
- .map((row) => ({ id: row.redirectId, from: row.from, to: row.to, permanent: row.permanent }))
161
+ // Editorial redirects (EC-303): only ENABLED entries serve — a disabled redirect is
162
+ // stored but never live, in either perspective. EC-616: read through the redirect
163
+ // kind's one server-side reader (records + the marked legacy tolerant read) and
164
+ // projected by the ONE enabled-redirect reader, `liveRedirectRecords` (EC-590 / EC-627 —
165
+ // this was the last hand-built copy on Convex).
166
+ const redirects = liveRedirectRecords(await loadRedirectEntries(ctx, projectId))
166
167
 
167
168
  return {
168
169
  projectId,
@@ -214,7 +215,9 @@ export const draftSnapshot = query({
214
215
  if (expected === undefined || expected.length === 0) {
215
216
  throw new Error('unauthorized: draft preview is not configured on this deployment')
216
217
  }
217
- if (args.token !== expected) {
218
+ // EC-617: the one timing-safe comparison (`constantTimeEquals`, the MAC-compare the
219
+ // revalidation and ingest verifiers rest on) — never a string `!==`.
220
+ if (!(await constantTimeEquals(args.token, expected))) {
218
221
  throw new Error('unauthorized: invalid draft preview token')
219
222
  }
220
223
  return loadSnapshot(ctx, { projectId: args.projectId }, DRAFT_PERSPECTIVE)
package/convex/guard.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import { getAuthUserId } from '@convex-dev/auth/server'
2
- import { defaultRolePolicy, roleGrantsCapability } from '@elytracms/core/persistence'
2
+ import { roleGrantsCapability } from '@elytracms/core/persistence'
3
3
  import type { OperationCapabilityId, ProjectRole, RolePolicy } from '@elytracms/core/persistence'
4
4
  import type { QueryCtx } from './functions'
5
+ import { deployedRolePolicy } from './roles'
5
6
 
6
7
  /**
7
8
  * Server-side authorization guards (EC-150). The operations chokepoint in the
@@ -18,9 +19,10 @@ import type { QueryCtx } from './functions'
18
19
  * imported here rather than re-declared. Handlers gate on CAPABILITIES
19
20
  * (`content.read` / `content.write` / `project.administer`), not literal roles
20
21
  * (EC-240c), so a project's custom role set enforces server-side by the same
21
- * policy the operations layer uses. The Convex trust boundary reads the shipped
22
- * {@link defaultRolePolicy}; a project overriding roles injects its policy (the
23
- * documented follow-up seam).
22
+ * policy the operations layer uses. EC-605: the policy is the project's `roles`
23
+ * declaration as DEPLOYED — the copy the `elytra` CLI provisions into
24
+ * `ELYTRA_ROLE_POLICY` (`./roles` `deployedRolePolicy`: the default when unset, a
25
+ * thrown, located error when malformed — never a silent default).
24
26
  *
25
27
  * Failures throw plain `Error('unauthorized: …')` — Convex transports the
26
28
  * message; the app's adapters normalize thrown backend errors onto the
@@ -78,13 +80,13 @@ export async function memberRole(ctx: GuardCtx, userId: string): Promise<Project
78
80
  * This is the ONE role-enforcement primitive at the trust boundary — every
79
81
  * handler gates on a capability rather than a literal role, so a project's
80
82
  * custom role set is honoured by the shared policy. `policy` defaults to the
81
- * shipped {@link defaultRolePolicy}; the override seam (a project injecting its
82
- * own policy) is the documented follow-up.
83
+ * deployment's policy (EC-605, {@link deployedRolePolicy}); the parameter stays for a
84
+ * caller that must judge against another (tests).
83
85
  */
84
86
  export async function requireCapability(
85
87
  ctx: GuardCtx,
86
88
  capability: OperationCapabilityId,
87
- policy: RolePolicy = defaultRolePolicy,
89
+ policy: RolePolicy = deployedRolePolicy(),
88
90
  ): Promise<string> {
89
91
  const userId = await requireUserId(ctx)
90
92
  const actual = await memberRole(ctx, userId)
@@ -33,9 +33,20 @@ function routeTable(http: HttpRouter): string[] {
33
33
  .sort()
34
34
  }
35
35
 
36
- describe('defineStudioHttp — the engine registers no HTTP routes of its own (EC-481)', () => {
37
- it('contributes an EMPTY route table beyond what the injected auth registers', () => {
38
- expect(routeTable(defineStudioHttp({ auth: silentAuth }))).toEqual([])
36
+ /** EC-617: the engine's ONE route — the public-ingest receiver, an exact path (the EC-481 bar: a real client, a narrow prefix). */
37
+ const ENGINE_ROUTES = ['POST /elytra/ingest']
38
+
39
+ describe('defineStudioHttp — the engine registers exactly one HTTP route of its own (EC-481 → EC-617)', () => {
40
+ it('contributes ONLY the ingest receiver beyond what the injected auth registers', () => {
41
+ expect(routeTable(defineStudioHttp({ auth: silentAuth }))).toEqual(ENGINE_ROUTES)
42
+ })
43
+
44
+ it('the receiver is an EXACT path, POST only — it claims no prefix', () => {
45
+ const http = defineStudioHttp({ auth: silentAuth })
46
+ expect(http.lookup('/elytra/ingest', 'POST')).not.toBeNull()
47
+ expect(http.lookup('/elytra/ingest/anything', 'POST')).toBeNull()
48
+ expect(http.lookup('/elytra/', 'POST')).toBeNull()
49
+ expect(http.lookup('/elytra/ingest', 'GET')).toBeNull()
39
50
  })
40
51
 
41
52
  it('control: the recorder DOES see routes, so the empty result above is real', () => {
@@ -45,10 +56,9 @@ describe('defineStudioHttp — the engine registers no HTTP routes of its own (E
45
56
  http.route({ pathPrefix: '/api/auth/callback/', method: 'POST', handler })
46
57
  },
47
58
  }
48
- expect(routeTable(defineStudioHttp({ auth }))).toEqual([
49
- 'GET /.well-known/jwks.json',
50
- 'POST /api/auth/callback/*',
51
- ])
59
+ expect(routeTable(defineStudioHttp({ auth }))).toEqual(
60
+ ['GET /.well-known/jwks.json', 'POST /api/auth/callback/*', ...ENGINE_ROUTES].sort(),
61
+ )
52
62
  })
53
63
 
54
64
  it('passes the auth routes through untouched — nothing is added, dropped, or reordered', () => {
@@ -61,7 +71,7 @@ describe('defineStudioHttp — the engine registers no HTTP routes of its own (E
61
71
  }
62
72
  },
63
73
  }
64
- expect(routeTable(defineStudioHttp({ auth }))).toEqual([...registered].sort())
74
+ expect(routeTable(defineStudioHttp({ auth }))).toEqual([...registered, ...ENGINE_ROUTES].sort())
65
75
  })
66
76
  })
67
77
 
package/convex/http.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  import { httpRouter } from 'convex/server'
2
2
  import type { HttpRouter } from 'convex/server'
3
+ import { INGEST_RECEIVER_PATH } from '@elytracms/core/ingest'
4
+ import { ingestReceiver } from './ingest'
5
+ import type { IngestHandlers } from './ingest'
3
6
 
4
7
  /** The slice of the Convex Auth object the studio's HTTP routes need. */
5
8
  interface AuthHttpRoutes {
@@ -8,33 +11,36 @@ interface AuthHttpRoutes {
8
11
 
9
12
  /**
10
13
  * EC-252 — the studio's HTTP routes as a factory. Pure engine infra ships in the
11
- * package; only the consumer's `auth` (provider choice) is injected. The consumer's
12
- * `convex/http.ts` is a two-liner:
14
+ * package; only the consumer's `auth` (provider choice) and — since EC-617 — its ingest
15
+ * handlers are injected. The consumer's `convex/http.ts`:
13
16
  *
14
17
  * ```ts
15
18
  * import { auth } from './auth'
16
19
  * export default defineStudioHttp({ auth })
20
+ * // with an addon that handles ingest payloads (one import, one entry):
21
+ * // import { formsIngest } from '@elytracms/forms/convex'
22
+ * // export default defineStudioHttp({ auth, ingest: { submission: formsIngest } })
17
23
  * ```
18
24
  *
19
- * ## The engine registers NO HTTP routes of its own (EC-481)
25
+ * ## The engine registers exactly ONE route of its own (EC-481 → EC-617)
20
26
  *
21
- * Everything on this router comes from Convex Auth's `addHttpRoutes` — the JWT
22
- * discovery endpoints (`/.well-known/*`, EC-150) and, only when a consumer supplies
23
- * an OAuth provider, `/api/auth/signin/*` + `/api/auth/callback/*`.
27
+ * Everything else on this router comes from Convex Auth's `addHttpRoutes` — the JWT
28
+ * discovery endpoints (`/.well-known/*`, EC-150) and, only when a consumer supplies an
29
+ * OAuth provider, `/api/auth/signin/*` + `/api/auth/callback/*`.
24
30
  *
25
- * The engine used to add one more: `GET /api/projects/*`, the EC-157 Bearer-token
26
- * registry-sync endpoint backing `builder typegen`/`builder check`. EC-470 deleted
27
- * that CLI and with it the endpoint's only client; the handler had in any case
28
- * answered with config-owned placeholders since EC-287 retired the project graph
29
- * (`project: null`, `collections: []`). EC-481 removed the route and its handler
30
- * together rather than leave a Bearer-authenticated surface serving stub data on
31
- * every deployment. `http-routes.test.ts` pins that shut.
32
- *
33
- * A future engine-owned HTTP surface is fine — but it needs a real client and a
34
- * narrower prefix than `/api/projects/`.
31
+ * EC-481 removed the engine's former `GET /api/projects/*` (a Bearer-authenticated
32
+ * registry-sync endpoint with no client left) and set the bar for any future one: "a
33
+ * real client and a narrower prefix". EC-617's public-ingest RECEIVER is that route:
34
+ * `POST /elytra/ingest` — an EXACT path, not a prefix; its client is the site's
35
+ * `createIngestRoute` (`@elytracms/next`), which signs every payload with the
36
+ * server-held secret, and `elytra doctor`'s probe. It is mounted on every deployment,
37
+ * with or without handlers: closed (401) until `ELYTRA_INGEST_SECRET` is set, and a
38
+ * payload kind with no registered handler is refused visibly (422), never dropped.
39
+ * `http-routes.test.ts` pins the table.
35
40
  */
36
- export function defineStudioHttp({ auth }: { auth: AuthHttpRoutes }): HttpRouter {
41
+ export function defineStudioHttp({ auth, ingest }: { auth: AuthHttpRoutes; ingest?: IngestHandlers }): HttpRouter {
37
42
  const http = httpRouter()
38
43
  auth.addHttpRoutes(http)
44
+ http.route({ path: INGEST_RECEIVER_PATH, method: 'POST', handler: ingestReceiver(ingest) })
39
45
  return http
40
46
  }