@elytracms/studio 0.0.1

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 (558) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +3 -0
  3. package/convex/assets.ts +107 -0
  4. package/convex/auth.config.ts +15 -0
  5. package/convex/auth.ts +50 -0
  6. package/convex/cliTokens.ts +63 -0
  7. package/convex/cms.ts +174 -0
  8. package/convex/content.ts +316 -0
  9. package/convex/delivery.ts +266 -0
  10. package/convex/functions.ts +35 -0
  11. package/convex/graphs.ts +51 -0
  12. package/convex/guard.ts +67 -0
  13. package/convex/http.ts +30 -0
  14. package/convex/members.ts +83 -0
  15. package/convex/publishing.ts +102 -0
  16. package/convex/references.ts +168 -0
  17. package/convex/schema.ts +173 -0
  18. package/convex/sync.ts +322 -0
  19. package/convex/tsconfig.json +12 -0
  20. package/convex/users.ts +23 -0
  21. package/convex/webhooks.ts +85 -0
  22. package/dist/components/assets/asset-library.d.ts +14 -0
  23. package/dist/components/assets/asset-library.js +191 -0
  24. package/dist/components/assets/asset-library.js.map +1 -0
  25. package/dist/components/assets/asset-metadata.d.ts +34 -0
  26. package/dist/components/assets/asset-metadata.js +40 -0
  27. package/dist/components/assets/asset-metadata.js.map +1 -0
  28. package/dist/components/assets/asset-search.d.ts +7 -0
  29. package/dist/components/assets/asset-search.js +12 -0
  30. package/dist/components/assets/asset-search.js.map +1 -0
  31. package/dist/components/assets/asset-selection.d.ts +17 -0
  32. package/dist/components/assets/asset-selection.js +33 -0
  33. package/dist/components/assets/asset-selection.js.map +1 -0
  34. package/dist/components/assets/assets-store.d.ts +51 -0
  35. package/dist/components/assets/assets-store.js +153 -0
  36. package/dist/components/assets/assets-store.js.map +1 -0
  37. package/dist/components/auth-context.d.ts +38 -0
  38. package/dist/components/auth-context.js +88 -0
  39. package/dist/components/auth-context.js.map +1 -0
  40. package/dist/components/backend-gate.d.ts +19 -0
  41. package/dist/components/backend-gate.js +63 -0
  42. package/dist/components/backend-gate.js.map +1 -0
  43. package/dist/components/breadcrumb-context.d.ts +39 -0
  44. package/dist/components/breadcrumb-context.js +38 -0
  45. package/dist/components/breadcrumb-context.js.map +1 -0
  46. package/dist/components/builder/blocks-model.d.ts +80 -0
  47. package/dist/components/builder/blocks-model.js +181 -0
  48. package/dist/components/builder/blocks-model.js.map +1 -0
  49. package/dist/components/builder/blocks-panel.d.ts +44 -0
  50. package/dist/components/builder/blocks-panel.js +147 -0
  51. package/dist/components/builder/blocks-panel.js.map +1 -0
  52. package/dist/components/builder/builder-store.d.ts +69 -0
  53. package/dist/components/builder/builder-store.js +294 -0
  54. package/dist/components/builder/builder-store.js.map +1 -0
  55. package/dist/components/builder/canvas-stylesheet.d.ts +4 -0
  56. package/dist/components/builder/canvas-stylesheet.js +24 -0
  57. package/dist/components/builder/canvas-stylesheet.js.map +1 -0
  58. package/dist/components/builder/canvas.d.ts +8 -0
  59. package/dist/components/builder/canvas.js +723 -0
  60. package/dist/components/builder/canvas.js.map +1 -0
  61. package/dist/components/builder/clipboard-actions.d.ts +93 -0
  62. package/dist/components/builder/clipboard-actions.js +246 -0
  63. package/dist/components/builder/clipboard-actions.js.map +1 -0
  64. package/dist/components/builder/composition-field-editor.d.ts +11 -0
  65. package/dist/components/builder/composition-field-editor.js +150 -0
  66. package/dist/components/builder/composition-field-editor.js.map +1 -0
  67. package/dist/components/builder/composition-scope.d.ts +16 -0
  68. package/dist/components/builder/composition-scope.js +7 -0
  69. package/dist/components/builder/composition-scope.js.map +1 -0
  70. package/dist/components/builder/drag-session.d.ts +41 -0
  71. package/dist/components/builder/drag-session.js +51 -0
  72. package/dist/components/builder/drag-session.js.map +1 -0
  73. package/dist/components/builder/fixture-thumbnail.d.ts +18 -0
  74. package/dist/components/builder/fixture-thumbnail.js +97 -0
  75. package/dist/components/builder/fixture-thumbnail.js.map +1 -0
  76. package/dist/components/builder/inline-edit.d.ts +142 -0
  77. package/dist/components/builder/inline-edit.js +136 -0
  78. package/dist/components/builder/inline-edit.js.map +1 -0
  79. package/dist/components/builder/insert-context.d.ts +14 -0
  80. package/dist/components/builder/insert-context.js +65 -0
  81. package/dist/components/builder/insert-context.js.map +1 -0
  82. package/dist/components/builder/insert-menu.d.ts +9 -0
  83. package/dist/components/builder/insert-menu.js +9 -0
  84. package/dist/components/builder/insert-menu.js.map +1 -0
  85. package/dist/components/builder/layout-inspector.d.ts +14 -0
  86. package/dist/components/builder/layout-inspector.js +84 -0
  87. package/dist/components/builder/layout-inspector.js.map +1 -0
  88. package/dist/components/builder/node-factory.d.ts +17 -0
  89. package/dist/components/builder/node-factory.js +90 -0
  90. package/dist/components/builder/node-factory.js.map +1 -0
  91. package/dist/components/builder/page-tree.d.ts +3 -0
  92. package/dist/components/builder/page-tree.js +207 -0
  93. package/dist/components/builder/page-tree.js.map +1 -0
  94. package/dist/components/builder/property-inspector.d.ts +1 -0
  95. package/dist/components/builder/property-inspector.js +233 -0
  96. package/dist/components/builder/property-inspector.js.map +1 -0
  97. package/dist/components/builder/rich-text-toolbar.d.ts +4 -0
  98. package/dist/components/builder/rich-text-toolbar.js +104 -0
  99. package/dist/components/builder/rich-text-toolbar.js.map +1 -0
  100. package/dist/components/builder/spacing-handles.d.ts +22 -0
  101. package/dist/components/builder/spacing-handles.js +213 -0
  102. package/dist/components/builder/spacing-handles.js.map +1 -0
  103. package/dist/components/cms/asset-picker.d.ts +12 -0
  104. package/dist/components/cms/asset-picker.js +60 -0
  105. package/dist/components/cms/asset-picker.js.map +1 -0
  106. package/dist/components/cms/bulk-actions.d.ts +60 -0
  107. package/dist/components/cms/bulk-actions.js +95 -0
  108. package/dist/components/cms/bulk-actions.js.map +1 -0
  109. package/dist/components/cms/bulk-bar.d.ts +23 -0
  110. package/dist/components/cms/bulk-bar.js +26 -0
  111. package/dist/components/cms/bulk-bar.js.map +1 -0
  112. package/dist/components/cms/cell-edit.d.ts +91 -0
  113. package/dist/components/cms/cell-edit.js +144 -0
  114. package/dist/components/cms/cell-edit.js.map +1 -0
  115. package/dist/components/cms/cell.d.ts +29 -0
  116. package/dist/components/cms/cell.js +127 -0
  117. package/dist/components/cms/cell.js.map +1 -0
  118. package/dist/components/cms/cms-index.d.ts +8 -0
  119. package/dist/components/cms/cms-index.js +23 -0
  120. package/dist/components/cms/cms-index.js.map +1 -0
  121. package/dist/components/cms/cms-store.d.ts +188 -0
  122. package/dist/components/cms/cms-store.js +702 -0
  123. package/dist/components/cms/cms-store.js.map +1 -0
  124. package/dist/components/cms/collection-table.d.ts +6 -0
  125. package/dist/components/cms/collection-table.js +250 -0
  126. package/dist/components/cms/collection-table.js.map +1 -0
  127. package/dist/components/cms/collections-sidebar.d.ts +9 -0
  128. package/dist/components/cms/collections-sidebar.js +25 -0
  129. package/dist/components/cms/collections-sidebar.js.map +1 -0
  130. package/dist/components/cms/detail-editor.d.ts +7 -0
  131. package/dist/components/cms/detail-editor.js +386 -0
  132. package/dist/components/cms/detail-editor.js.map +1 -0
  133. package/dist/components/cms/doc-state.d.ts +13 -0
  134. package/dist/components/cms/doc-state.js +18 -0
  135. package/dist/components/cms/doc-state.js.map +1 -0
  136. package/dist/components/cms/document-editor-context.d.ts +48 -0
  137. package/dist/components/cms/document-editor-context.js +23 -0
  138. package/dist/components/cms/document-editor-context.js.map +1 -0
  139. package/dist/components/cms/editable-cell.d.ts +22 -0
  140. package/dist/components/cms/editable-cell.js +176 -0
  141. package/dist/components/cms/editable-cell.js.map +1 -0
  142. package/dist/components/cms/field-control.d.ts +8 -0
  143. package/dist/components/cms/field-control.js +149 -0
  144. package/dist/components/cms/field-control.js.map +1 -0
  145. package/dist/components/cms/filter-builder.d.ts +12 -0
  146. package/dist/components/cms/filter-builder.js +84 -0
  147. package/dist/components/cms/filter-builder.js.map +1 -0
  148. package/dist/components/cms/import-export.d.ts +6 -0
  149. package/dist/components/cms/import-export.js +33 -0
  150. package/dist/components/cms/import-export.js.map +1 -0
  151. package/dist/components/cms/new-page-dialog.d.ts +22 -0
  152. package/dist/components/cms/new-page-dialog.js +42 -0
  153. package/dist/components/cms/new-page-dialog.js.map +1 -0
  154. package/dist/components/cms/reference-confirm.d.ts +25 -0
  155. package/dist/components/cms/reference-confirm.js +124 -0
  156. package/dist/components/cms/reference-confirm.js.map +1 -0
  157. package/dist/components/cms/relation-picker.d.ts +16 -0
  158. package/dist/components/cms/relation-picker.js +47 -0
  159. package/dist/components/cms/relation-picker.js.map +1 -0
  160. package/dist/components/cms/use-cell-edit.d.ts +33 -0
  161. package/dist/components/cms/use-cell-edit.js +125 -0
  162. package/dist/components/cms/use-cell-edit.js.map +1 -0
  163. package/dist/components/cms/versions-model.d.ts +15 -0
  164. package/dist/components/cms/versions-model.js +23 -0
  165. package/dist/components/cms/versions-model.js.map +1 -0
  166. package/dist/components/cms/versions-pane.d.ts +14 -0
  167. package/dist/components/cms/versions-pane.js +86 -0
  168. package/dist/components/cms/versions-pane.js.map +1 -0
  169. package/dist/components/cms/view-bar.d.ts +11 -0
  170. package/dist/components/cms/view-bar.js +80 -0
  171. package/dist/components/cms/view-bar.js.map +1 -0
  172. package/dist/components/collab/collab-context.d.ts +34 -0
  173. package/dist/components/collab/collab-context.js +52 -0
  174. package/dist/components/collab/collab-context.js.map +1 -0
  175. package/dist/components/collab/collab-seed.d.ts +27 -0
  176. package/dist/components/collab/collab-seed.js +42 -0
  177. package/dist/components/collab/collab-seed.js.map +1 -0
  178. package/dist/components/components/component-detail.d.ts +9 -0
  179. package/dist/components/components/component-detail.js +30 -0
  180. package/dist/components/components/component-detail.js.map +1 -0
  181. package/dist/components/components/component-status.d.ts +10 -0
  182. package/dist/components/components/component-status.js +30 -0
  183. package/dist/components/components/component-status.js.map +1 -0
  184. package/dist/components/components/components-index.d.ts +8 -0
  185. package/dist/components/components/components-index.js +42 -0
  186. package/dist/components/components/components-index.js.map +1 -0
  187. package/dist/components/components/field-inspector.d.ts +9 -0
  188. package/dist/components/components/field-inspector.js +38 -0
  189. package/dist/components/components/field-inspector.js.map +1 -0
  190. package/dist/components/components/fixture-runner.d.ts +10 -0
  191. package/dist/components/components/fixture-runner.js +24 -0
  192. package/dist/components/components/fixture-runner.js.map +1 -0
  193. package/dist/components/components/registry-browser.d.ts +4 -0
  194. package/dist/components/components/registry-browser.js +64 -0
  195. package/dist/components/components/registry-browser.js.map +1 -0
  196. package/dist/components/current-user-menu.d.ts +13 -0
  197. package/dist/components/current-user-menu.js +31 -0
  198. package/dist/components/current-user-menu.js.map +1 -0
  199. package/dist/components/dev-login-badge.d.ts +9 -0
  200. package/dist/components/dev-login-badge.js +18 -0
  201. package/dist/components/dev-login-badge.js.map +1 -0
  202. package/dist/components/extensions/extensions-view.d.ts +7 -0
  203. package/dist/components/extensions/extensions-view.js +126 -0
  204. package/dist/components/extensions/extensions-view.js.map +1 -0
  205. package/dist/components/guards.d.ts +27 -0
  206. package/dist/components/guards.js +46 -0
  207. package/dist/components/guards.js.map +1 -0
  208. package/dist/components/inspector/inspector-sidebar.d.ts +26 -0
  209. package/dist/components/inspector/inspector-sidebar.js +17 -0
  210. package/dist/components/inspector/inspector-sidebar.js.map +1 -0
  211. package/dist/components/operation-error.d.ts +17 -0
  212. package/dist/components/operation-error.js +54 -0
  213. package/dist/components/operation-error.js.map +1 -0
  214. package/dist/components/playground-badge.d.ts +10 -0
  215. package/dist/components/playground-badge.js +19 -0
  216. package/dist/components/playground-badge.js.map +1 -0
  217. package/dist/components/project-list.d.ts +15 -0
  218. package/dist/components/project-list.js +52 -0
  219. package/dist/components/project-list.js.map +1 -0
  220. package/dist/components/project-session.d.ts +46 -0
  221. package/dist/components/project-session.js +107 -0
  222. package/dist/components/project-session.js.map +1 -0
  223. package/dist/components/project-switcher.d.ts +11 -0
  224. package/dist/components/project-switcher.js +44 -0
  225. package/dist/components/project-switcher.js.map +1 -0
  226. package/dist/components/references/builder-deep-link.d.ts +12 -0
  227. package/dist/components/references/builder-deep-link.js +53 -0
  228. package/dist/components/references/builder-deep-link.js.map +1 -0
  229. package/dist/components/references/references-panel.d.ts +17 -0
  230. package/dist/components/references/references-panel.js +66 -0
  231. package/dist/components/references/references-panel.js.map +1 -0
  232. package/dist/components/routes/redirect-chain.d.ts +9 -0
  233. package/dist/components/routes/redirect-chain.js +16 -0
  234. package/dist/components/routes/redirect-chain.js.map +1 -0
  235. package/dist/components/routes/redirect-manager.d.ts +13 -0
  236. package/dist/components/routes/redirect-manager.js +24 -0
  237. package/dist/components/routes/redirect-manager.js.map +1 -0
  238. package/dist/components/routes/route-list.d.ts +35 -0
  239. package/dist/components/routes/route-list.js +69 -0
  240. package/dist/components/routes/route-list.js.map +1 -0
  241. package/dist/components/routes/routes-shell.d.ts +14 -0
  242. package/dist/components/routes/routes-shell.js +68 -0
  243. package/dist/components/routes/routes-shell.js.map +1 -0
  244. package/dist/components/section-nav.d.ts +17 -0
  245. package/dist/components/section-nav.js +33 -0
  246. package/dist/components/section-nav.js.map +1 -0
  247. package/dist/components/section-state.d.ts +24 -0
  248. package/dist/components/section-state.js +78 -0
  249. package/dist/components/section-state.js.map +1 -0
  250. package/dist/components/section-surface.d.ts +31 -0
  251. package/dist/components/section-surface.js +46 -0
  252. package/dist/components/section-surface.js.map +1 -0
  253. package/dist/components/sections.d.ts +38 -0
  254. package/dist/components/sections.js +72 -0
  255. package/dist/components/sections.js.map +1 -0
  256. package/dist/components/settings-form.d.ts +10 -0
  257. package/dist/components/settings-form.js +17 -0
  258. package/dist/components/settings-form.js.map +1 -0
  259. package/dist/components/studio-context.d.ts +22 -0
  260. package/dist/components/studio-context.js +48 -0
  261. package/dist/components/studio-context.js.map +1 -0
  262. package/dist/components/studio-project-screen.d.ts +19 -0
  263. package/dist/components/studio-project-screen.js +195 -0
  264. package/dist/components/studio-project-screen.js.map +1 -0
  265. package/dist/components/studio-shell.d.ts +18 -0
  266. package/dist/components/studio-shell.js +64 -0
  267. package/dist/components/studio-shell.js.map +1 -0
  268. package/dist/components/theme-context.d.ts +17 -0
  269. package/dist/components/theme-context.js +26 -0
  270. package/dist/components/theme-context.js.map +1 -0
  271. package/dist/components/workspace-gate.d.ts +12 -0
  272. package/dist/components/workspace-gate.js +27 -0
  273. package/dist/components/workspace-gate.js.map +1 -0
  274. package/dist/components/workspace-projects.d.ts +13 -0
  275. package/dist/components/workspace-projects.js +18 -0
  276. package/dist/components/workspace-projects.js.map +1 -0
  277. package/dist/lib/assets/delete-asset.d.ts +34 -0
  278. package/dist/lib/assets/delete-asset.js +34 -0
  279. package/dist/lib/assets/delete-asset.js.map +1 -0
  280. package/dist/lib/assets/image-dimensions.d.ts +18 -0
  281. package/dist/lib/assets/image-dimensions.js +26 -0
  282. package/dist/lib/assets/image-dimensions.js.map +1 -0
  283. package/dist/lib/assets/index.d.ts +9 -0
  284. package/dist/lib/assets/index.js +10 -0
  285. package/dist/lib/assets/index.js.map +1 -0
  286. package/dist/lib/assets/upload-state.d.ts +53 -0
  287. package/dist/lib/assets/upload-state.js +59 -0
  288. package/dist/lib/assets/upload-state.js.map +1 -0
  289. package/dist/lib/assets/upload.d.ts +56 -0
  290. package/dist/lib/assets/upload.js +125 -0
  291. package/dist/lib/assets/upload.js.map +1 -0
  292. package/dist/lib/auth/auth-adapter.d.ts +57 -0
  293. package/dist/lib/auth/auth-adapter.js +13 -0
  294. package/dist/lib/auth/auth-adapter.js.map +1 -0
  295. package/dist/lib/auth/convex-auth-adapter.d.ts +39 -0
  296. package/dist/lib/auth/convex-auth-adapter.js +171 -0
  297. package/dist/lib/auth/convex-auth-adapter.js.map +1 -0
  298. package/dist/lib/auth/dev-auto-login-adapter.d.ts +25 -0
  299. package/dist/lib/auth/dev-auto-login-adapter.js +40 -0
  300. package/dist/lib/auth/dev-auto-login-adapter.js.map +1 -0
  301. package/dist/lib/auth/index.d.ts +28 -0
  302. package/dist/lib/auth/index.js +61 -0
  303. package/dist/lib/auth/index.js.map +1 -0
  304. package/dist/lib/auth/local-auth-adapter.d.ts +57 -0
  305. package/dist/lib/auth/local-auth-adapter.js +78 -0
  306. package/dist/lib/auth/local-auth-adapter.js.map +1 -0
  307. package/dist/lib/auth/route-guard.d.ts +33 -0
  308. package/dist/lib/auth/route-guard.js +51 -0
  309. package/dist/lib/auth/route-guard.js.map +1 -0
  310. package/dist/lib/auth-errors.d.ts +42 -0
  311. package/dist/lib/auth-errors.js +74 -0
  312. package/dist/lib/auth-errors.js.map +1 -0
  313. package/dist/lib/bindings/section-bindings.d.ts +24 -0
  314. package/dist/lib/bindings/section-bindings.js +9 -0
  315. package/dist/lib/bindings/section-bindings.js.map +1 -0
  316. package/dist/lib/clipboard/index.d.ts +7 -0
  317. package/dist/lib/clipboard/index.js +8 -0
  318. package/dist/lib/clipboard/index.js.map +1 -0
  319. package/dist/lib/clipboard/os-clipboard.d.ts +46 -0
  320. package/dist/lib/clipboard/os-clipboard.js +105 -0
  321. package/dist/lib/clipboard/os-clipboard.js.map +1 -0
  322. package/dist/lib/clipboard/paste-issues.d.ts +34 -0
  323. package/dist/lib/clipboard/paste-issues.js +67 -0
  324. package/dist/lib/clipboard/paste-issues.js.map +1 -0
  325. package/dist/lib/clipboard/paste-target.d.ts +20 -0
  326. package/dist/lib/clipboard/paste-target.js +28 -0
  327. package/dist/lib/clipboard/paste-target.js.map +1 -0
  328. package/dist/lib/cms/published-documents.d.ts +30 -0
  329. package/dist/lib/cms/published-documents.js +75 -0
  330. package/dist/lib/cms/published-documents.js.map +1 -0
  331. package/dist/lib/cms-views/index.d.ts +9 -0
  332. package/dist/lib/cms-views/index.js +10 -0
  333. package/dist/lib/cms-views/index.js.map +1 -0
  334. package/dist/lib/cms-views/selection.d.ts +29 -0
  335. package/dist/lib/cms-views/selection.js +46 -0
  336. package/dist/lib/cms-views/selection.js.map +1 -0
  337. package/dist/lib/cms-views/view-model.d.ts +192 -0
  338. package/dist/lib/cms-views/view-model.js +353 -0
  339. package/dist/lib/cms-views/view-model.js.map +1 -0
  340. package/dist/lib/cms-views/views-store.d.ts +70 -0
  341. package/dist/lib/cms-views/views-store.js +129 -0
  342. package/dist/lib/cms-views/views-store.js.map +1 -0
  343. package/dist/lib/data/collection-content.d.ts +105 -0
  344. package/dist/lib/data/collection-content.js +196 -0
  345. package/dist/lib/data/collection-content.js.map +1 -0
  346. package/dist/lib/data/data-store.d.ts +97 -0
  347. package/dist/lib/data/data-store.js +205 -0
  348. package/dist/lib/data/data-store.js.map +1 -0
  349. package/dist/lib/data/index.d.ts +4 -0
  350. package/dist/lib/data/index.js +8 -0
  351. package/dist/lib/data/index.js.map +1 -0
  352. package/dist/lib/data/providers.d.ts +143 -0
  353. package/dist/lib/data/providers.js +255 -0
  354. package/dist/lib/data/providers.js.map +1 -0
  355. package/dist/lib/data/sample-capture.d.ts +30 -0
  356. package/dist/lib/data/sample-capture.js +70 -0
  357. package/dist/lib/data/sample-capture.js.map +1 -0
  358. package/dist/lib/data/sources-store.d.ts +42 -0
  359. package/dist/lib/data/sources-store.js +120 -0
  360. package/dist/lib/data/sources-store.js.map +1 -0
  361. package/dist/lib/dnd/drop-rules.d.ts +219 -0
  362. package/dist/lib/dnd/drop-rules.js +257 -0
  363. package/dist/lib/dnd/drop-rules.js.map +1 -0
  364. package/dist/lib/dnd/gap-drag.d.ts +66 -0
  365. package/dist/lib/dnd/gap-drag.js +95 -0
  366. package/dist/lib/dnd/gap-drag.js.map +1 -0
  367. package/dist/lib/locale/binding-fallback.d.ts +28 -0
  368. package/dist/lib/locale/binding-fallback.js +66 -0
  369. package/dist/lib/locale/binding-fallback.js.map +1 -0
  370. package/dist/lib/locale/fallback.d.ts +43 -0
  371. package/dist/lib/locale/fallback.js +52 -0
  372. package/dist/lib/locale/fallback.js.map +1 -0
  373. package/dist/lib/locale/index.d.ts +6 -0
  374. package/dist/lib/locale/index.js +7 -0
  375. package/dist/lib/locale/index.js.map +1 -0
  376. package/dist/lib/locale/locale-store.d.ts +40 -0
  377. package/dist/lib/locale/locale-store.js +61 -0
  378. package/dist/lib/locale/locale-store.js.map +1 -0
  379. package/dist/lib/locale/locale-switcher.d.ts +10 -0
  380. package/dist/lib/locale/locale-switcher.js +24 -0
  381. package/dist/lib/locale/locale-switcher.js.map +1 -0
  382. package/dist/lib/locale/route-variant.d.ts +14 -0
  383. package/dist/lib/locale/route-variant.js +11 -0
  384. package/dist/lib/locale/route-variant.js.map +1 -0
  385. package/dist/lib/locale/use-studio-locale.d.ts +10 -0
  386. package/dist/lib/locale/use-studio-locale.js +8 -0
  387. package/dist/lib/locale/use-studio-locale.js.map +1 -0
  388. package/dist/lib/operations/asset-repository.d.ts +15 -0
  389. package/dist/lib/operations/asset-repository.js +56 -0
  390. package/dist/lib/operations/asset-repository.js.map +1 -0
  391. package/dist/lib/operations/cms-persistence.d.ts +35 -0
  392. package/dist/lib/operations/cms-persistence.js +75 -0
  393. package/dist/lib/operations/cms-persistence.js.map +1 -0
  394. package/dist/lib/operations/graph-commands.d.ts +140 -0
  395. package/dist/lib/operations/graph-commands.js +86 -0
  396. package/dist/lib/operations/graph-commands.js.map +1 -0
  397. package/dist/lib/operations/graph-persistence.d.ts +27 -0
  398. package/dist/lib/operations/graph-persistence.js +35 -0
  399. package/dist/lib/operations/graph-persistence.js.map +1 -0
  400. package/dist/lib/operations/index.d.ts +13 -0
  401. package/dist/lib/operations/index.js +14 -0
  402. package/dist/lib/operations/index.js.map +1 -0
  403. package/dist/lib/operations/operations-package.d.ts +6 -0
  404. package/dist/lib/operations/operations-package.js +7 -0
  405. package/dist/lib/operations/operations-package.js.map +1 -0
  406. package/dist/lib/operations/persist-queue.d.ts +16 -0
  407. package/dist/lib/operations/persist-queue.js +18 -0
  408. package/dist/lib/operations/persist-queue.js.map +1 -0
  409. package/dist/lib/operations/session-hydration.d.ts +84 -0
  410. package/dist/lib/operations/session-hydration.js +112 -0
  411. package/dist/lib/operations/session-hydration.js.map +1 -0
  412. package/dist/lib/operations/studio-operations.d.ts +59 -0
  413. package/dist/lib/operations/studio-operations.js +92 -0
  414. package/dist/lib/operations/studio-operations.js.map +1 -0
  415. package/dist/lib/persistence/backend-mode.d.ts +69 -0
  416. package/dist/lib/persistence/backend-mode.js +120 -0
  417. package/dist/lib/persistence/backend-mode.js.map +1 -0
  418. package/dist/lib/persistence/convex-adapter.d.ts +34 -0
  419. package/dist/lib/persistence/convex-adapter.js +681 -0
  420. package/dist/lib/persistence/convex-adapter.js.map +1 -0
  421. package/dist/lib/persistence/convex-blob-storage.d.ts +57 -0
  422. package/dist/lib/persistence/convex-blob-storage.js +97 -0
  423. package/dist/lib/persistence/convex-blob-storage.js.map +1 -0
  424. package/dist/lib/persistence/convex-client.d.ts +19 -0
  425. package/dist/lib/persistence/convex-client.js +40 -0
  426. package/dist/lib/persistence/convex-client.js.map +1 -0
  427. package/dist/lib/persistence/index.d.ts +30 -0
  428. package/dist/lib/persistence/index.js +60 -0
  429. package/dist/lib/persistence/index.js.map +1 -0
  430. package/dist/lib/persona/labels.d.ts +23 -0
  431. package/dist/lib/persona/labels.js +122 -0
  432. package/dist/lib/persona/labels.js.map +1 -0
  433. package/dist/lib/rail.d.ts +11 -0
  434. package/dist/lib/rail.js +26 -0
  435. package/dist/lib/rail.js.map +1 -0
  436. package/dist/lib/references/impact.d.ts +64 -0
  437. package/dist/lib/references/impact.js +62 -0
  438. package/dist/lib/references/impact.js.map +1 -0
  439. package/dist/lib/references/index.d.ts +7 -0
  440. package/dist/lib/references/index.js +8 -0
  441. package/dist/lib/references/index.js.map +1 -0
  442. package/dist/lib/references/reference-model.d.ts +125 -0
  443. package/dist/lib/references/reference-model.js +242 -0
  444. package/dist/lib/references/reference-model.js.map +1 -0
  445. package/dist/lib/references/references-store.d.ts +65 -0
  446. package/dist/lib/references/references-store.js +221 -0
  447. package/dist/lib/references/references-store.js.map +1 -0
  448. package/dist/lib/registry/index.d.ts +65 -0
  449. package/dist/lib/registry/index.js +114 -0
  450. package/dist/lib/registry/index.js.map +1 -0
  451. package/dist/lib/registry/preview.d.ts +15 -0
  452. package/dist/lib/registry/preview.js +110 -0
  453. package/dist/lib/registry/preview.js.map +1 -0
  454. package/dist/lib/registry/project-components.d.ts +16 -0
  455. package/dist/lib/registry/project-components.js +152 -0
  456. package/dist/lib/registry/project-components.js.map +1 -0
  457. package/dist/lib/routing/index.d.ts +10 -0
  458. package/dist/lib/routing/index.js +11 -0
  459. package/dist/lib/routing/index.js.map +1 -0
  460. package/dist/lib/routing/routing-analysis.d.ts +26 -0
  461. package/dist/lib/routing/routing-analysis.js +37 -0
  462. package/dist/lib/routing/routing-analysis.js.map +1 -0
  463. package/dist/lib/routing/routing-store.d.ts +50 -0
  464. package/dist/lib/routing/routing-store.js +72 -0
  465. package/dist/lib/routing/routing-store.js.map +1 -0
  466. package/dist/lib/starters/kit-session.d.ts +5 -0
  467. package/dist/lib/starters/kit-session.js +17 -0
  468. package/dist/lib/starters/kit-session.js.map +1 -0
  469. package/dist/lib/theme.d.ts +21 -0
  470. package/dist/lib/theme.js +32 -0
  471. package/dist/lib/theme.js.map +1 -0
  472. package/dist/lib/utils.d.ts +11 -0
  473. package/dist/lib/utils.js +28 -0
  474. package/dist/lib/utils.js.map +1 -0
  475. package/dist/lib/webhooks/config.d.ts +15 -0
  476. package/dist/lib/webhooks/config.js +64 -0
  477. package/dist/lib/webhooks/config.js.map +1 -0
  478. package/dist/lib/webhooks/dispatcher.d.ts +67 -0
  479. package/dist/lib/webhooks/dispatcher.js +61 -0
  480. package/dist/lib/webhooks/dispatcher.js.map +1 -0
  481. package/dist/lib/webhooks/index.d.ts +11 -0
  482. package/dist/lib/webhooks/index.js +10 -0
  483. package/dist/lib/webhooks/index.js.map +1 -0
  484. package/dist/lib/webhooks/signing.d.ts +15 -0
  485. package/dist/lib/webhooks/signing.js +24 -0
  486. package/dist/lib/webhooks/signing.js.map +1 -0
  487. package/dist/lib/webhooks/tags.d.ts +25 -0
  488. package/dist/lib/webhooks/tags.js +173 -0
  489. package/dist/lib/webhooks/tags.js.map +1 -0
  490. package/dist/lib/working-graph.d.ts +82 -0
  491. package/dist/lib/working-graph.js +113 -0
  492. package/dist/lib/working-graph.js.map +1 -0
  493. package/dist/lib/workspace/index.d.ts +109 -0
  494. package/dist/lib/workspace/index.js +206 -0
  495. package/dist/lib/workspace/index.js.map +1 -0
  496. package/dist/lib/workspace/switcher-source.d.ts +39 -0
  497. package/dist/lib/workspace/switcher-source.js +23 -0
  498. package/dist/lib/workspace/switcher-source.js.map +1 -0
  499. package/dist/lib/workspace/workspace-source.d.ts +47 -0
  500. package/dist/lib/workspace/workspace-source.js +67 -0
  501. package/dist/lib/workspace/workspace-source.js.map +1 -0
  502. package/dist/router/index.d.ts +32 -0
  503. package/dist/router/index.js +32 -0
  504. package/dist/router/index.js.map +1 -0
  505. package/dist/router/match.d.ts +75 -0
  506. package/dist/router/match.js +87 -0
  507. package/dist/router/match.js.map +1 -0
  508. package/dist/router/search.d.ts +22 -0
  509. package/dist/router/search.js +16 -0
  510. package/dist/router/search.js.map +1 -0
  511. package/dist/ui/avatar.d.ts +17 -0
  512. package/dist/ui/avatar.js +38 -0
  513. package/dist/ui/avatar.js.map +1 -0
  514. package/dist/ui/badge.d.ts +9 -0
  515. package/dist/ui/badge.js +23 -0
  516. package/dist/ui/badge.js.map +1 -0
  517. package/dist/ui/button.d.ts +11 -0
  518. package/dist/ui/button.js +30 -0
  519. package/dist/ui/button.js.map +1 -0
  520. package/dist/ui/card.d.ts +11 -0
  521. package/dist/ui/card.js +23 -0
  522. package/dist/ui/card.js.map +1 -0
  523. package/dist/ui/dialog.d.ts +12 -0
  524. package/dist/ui/dialog.js +27 -0
  525. package/dist/ui/dialog.js.map +1 -0
  526. package/dist/ui/dropdown-menu.d.ts +15 -0
  527. package/dist/ui/dropdown-menu.js +24 -0
  528. package/dist/ui/dropdown-menu.js.map +1 -0
  529. package/dist/ui/field.d.ts +15 -0
  530. package/dist/ui/field.js +14 -0
  531. package/dist/ui/field.js.map +1 -0
  532. package/dist/ui/index.d.ts +14 -0
  533. package/dist/ui/index.js +15 -0
  534. package/dist/ui/index.js.map +1 -0
  535. package/dist/ui/input.d.ts +3 -0
  536. package/dist/ui/input.js +7 -0
  537. package/dist/ui/input.js.map +1 -0
  538. package/dist/ui/inspector.d.ts +18 -0
  539. package/dist/ui/inspector.js +14 -0
  540. package/dist/ui/inspector.js.map +1 -0
  541. package/dist/ui/layout.d.ts +45 -0
  542. package/dist/ui/layout.js +48 -0
  543. package/dist/ui/layout.js.map +1 -0
  544. package/dist/ui/panel.d.ts +12 -0
  545. package/dist/ui/panel.js +20 -0
  546. package/dist/ui/panel.js.map +1 -0
  547. package/dist/ui/separator.d.ts +6 -0
  548. package/dist/ui/separator.js +7 -0
  549. package/dist/ui/separator.js.map +1 -0
  550. package/dist/ui/toolbar.d.ts +9 -0
  551. package/dist/ui/toolbar.js +16 -0
  552. package/dist/ui/toolbar.js.map +1 -0
  553. package/dist/ui/typography.d.ts +50 -0
  554. package/dist/ui/typography.js +43 -0
  555. package/dist/ui/typography.js.map +1 -0
  556. package/package.json +149 -0
  557. package/src/styles/studio.css +101 -0
  558. package/src/styles/tokens.css +63 -0
package/convex/sync.ts ADDED
@@ -0,0 +1,322 @@
1
+ import { makeFunctionReference } from 'convex/server'
2
+ import type { GenericActionCtx, GenericDataModel } from 'convex/server'
3
+ import { v } from 'convex/values'
4
+ import { verifyCliToken } from '@elytracms/operations'
5
+ import type { VerifiedCliToken } from '@elytracms/operations'
6
+ import type { CliTokenRepository, PersistenceAdapter } from '@elytracms/persistence'
7
+ import { httpAction, internalQuery } from './functions'
8
+ import type { QueryCtx } from './functions'
9
+ import type { ProjectRole } from './guard'
10
+
11
+ /**
12
+ * EC-157 — the builder-sync HTTP endpoint. Serves the contract fixed in
13
+ * `@elytracms/export-sync`'s `registrySyncEndpoints` from this deployment's
14
+ * `.convex.site` origin, so a host repo's `builder check` / `typegen` run against
15
+ * the real builder instead of a `fetchImpl` double:
16
+ *
17
+ * - `GET /api/projects/:projectId/registry-sync/snapshot?mode=&environment=` — the
18
+ * typegen/check snapshot: locale config and the graph revision for the requested
19
+ * mode. Collection schemas and routes/redirects are config-owned (EC-183/EC-207,
20
+ * AD-11) — the CLI reads them from `elytra.config.ts`, not this snapshot.
21
+ *
22
+ * ## Package-shipped, deployment-agnostic (EC-223/EC-252)
23
+ *
24
+ * This module ships inside `@elytracms/studio`; the consumer's `convex/sync.ts` is a
25
+ * one-line `export *` shim. It therefore NEVER imports the per-deployment
26
+ * `./_generated/api` — it addresses sibling functions by STRING ref via
27
+ * `makeFunctionReference` (resolved against the consumer's deployment), exactly like
28
+ * `publishing.ts`. That decoupling is also what makes it typecheck under a cross-repo
29
+ * `link:` loop: there is no `ApiFromModules` inference to unify across two `convex`
30
+ * instances (the bug EC-252 fixes — a copied `sync.ts` collapsed `api.cliTokens.byHash`
31
+ * to `{}`).
32
+ *
33
+ * ## Auth (EC-150 CLI tokens)
34
+ *
35
+ * The endpoint requires `Authorization: Bearer <ect_…>`. Tokens are verified by
36
+ * `verifyCliToken` (`@elytracms/operations`): the presented plaintext is SHA-256-hashed
37
+ * and looked up via `cliTokens.byHash`; unknown and revoked tokens are rejected with
38
+ * a 401. Single-tenant (EC-182): a deployment hosts one project, so a token carries
39
+ * no per-project scope — it just needs at least the `viewer` capability the snapshot
40
+ * read requires.
41
+ *
42
+ * ## Error contract
43
+ *
44
+ * Failures are structured JSON bodies — `{ error: { code, message, issues? } }` —
45
+ * matching `RegistrySyncErrorBody` in `@elytracms/export-sync`; even unexpected
46
+ * exceptions return a structured body, never an opaque crash page. The CLI surfaces
47
+ * them as `transport` issues.
48
+ */
49
+
50
+ type ActionCtx = GenericActionCtx<GenericDataModel>
51
+
52
+ // Sibling functions are addressed by STRING ref (resolved against the consumer's
53
+ // deployment), never `./_generated/api` — see the module note. The explicit arg +
54
+ // return generics keep the call sites typed without `ApiFromModules` inference.
55
+ type CliTokenRow = {
56
+ tokenId: string
57
+ label: string
58
+ tokenHash: string
59
+ createdAt: string
60
+ revokedAt?: string
61
+ }
62
+ const cliTokensByHash = makeFunctionReference<
63
+ 'query',
64
+ { tokenHash: string },
65
+ CliTokenRow | null
66
+ >('cliTokens:byHash')
67
+ const syncExportSnapshot = makeFunctionReference<
68
+ 'query',
69
+ { projectId: string; mode: SnapshotMode; environment: string },
70
+ BuilderSnapshot
71
+ >('sync:exportSnapshot')
72
+
73
+ // --- Structured responses ----------------------------------------------------
74
+
75
+ interface SyncErrorIssue {
76
+ code: string
77
+ message: string
78
+ componentId?: string
79
+ }
80
+
81
+ function jsonResponse(value: unknown, status = 200): Response {
82
+ return new Response(JSON.stringify(value), {
83
+ status,
84
+ headers: { 'content-type': 'application/json' },
85
+ })
86
+ }
87
+
88
+ function errorResponse(
89
+ status: number,
90
+ code: string,
91
+ message: string,
92
+ issues?: SyncErrorIssue[],
93
+ ): Response {
94
+ return jsonResponse(
95
+ { error: { code, message, ...(issues && issues.length > 0 ? { issues } : {}) } },
96
+ status,
97
+ )
98
+ }
99
+
100
+ // --- Routing -----------------------------------------------------------------
101
+
102
+ const PROJECTS_PREFIX = '/api/projects/'
103
+
104
+ type SyncEndpoint = 'snapshot'
105
+
106
+ interface SyncRoute {
107
+ projectId: string
108
+ endpoint: SyncEndpoint
109
+ }
110
+
111
+ /** Parse `/api/projects/:projectId/registry-sync/<rest>` (Convex routes by prefix only). */
112
+ function parseSyncRoute(method: string, pathname: string): SyncRoute | undefined {
113
+ if (!pathname.startsWith(PROJECTS_PREFIX)) return undefined
114
+ const segments = pathname
115
+ .slice(PROJECTS_PREFIX.length)
116
+ .split('/')
117
+ .map((segment) => decodeURIComponent(segment))
118
+ const [projectId, scope, ...rest] = segments
119
+ if (!projectId || scope !== 'registry-sync') return undefined
120
+ const tail = rest.join('/')
121
+ if (tail === 'snapshot' && method === 'GET') return { projectId, endpoint: 'snapshot' }
122
+ return undefined
123
+ }
124
+
125
+ // --- Token auth (EC-150) -------------------------------------------------------
126
+
127
+ const ROLE_RANK: Record<ProjectRole, number> = { viewer: 0, editor: 1, admin: 2 }
128
+
129
+ /** What each endpoint requires; the snapshot read is `viewer`-level (EC-150). */
130
+ const REQUIRED_ROLE: Record<SyncEndpoint, ProjectRole> = {
131
+ snapshot: 'viewer',
132
+ }
133
+
134
+ /**
135
+ * A {@link CliTokenRepository} over this deployment's `cliTokens` table, narrowed to
136
+ * the verification path. `verifyCliToken` only ever calls `findByHash`; the write
137
+ * methods live behind the admin-only operations layer and are not reachable here.
138
+ */
139
+ function cliTokenRepository(ctx: ActionCtx): CliTokenRepository {
140
+ const unsupported = (method: string) =>
141
+ Promise.reject(new Error(`cli-token ${method} is not available on the sync endpoint`))
142
+ return {
143
+ createToken: () => unsupported('createToken'),
144
+ listTokens: () => unsupported('listTokens'),
145
+ revokeToken: () => unsupported('revokeToken'),
146
+ async findByHash(tokenHash) {
147
+ const doc = await ctx.runQuery(cliTokensByHash, { tokenHash })
148
+ if (!doc) return undefined
149
+ return {
150
+ id: doc.tokenId,
151
+ label: doc.label,
152
+ tokenHash: doc.tokenHash,
153
+ createdAt: doc.createdAt,
154
+ ...(doc.revokedAt ? { revokedAt: doc.revokedAt } : {}),
155
+ }
156
+ },
157
+ }
158
+ }
159
+
160
+ type AuthResult = { ok: true; verified: VerifiedCliToken } | { ok: false; response: Response }
161
+
162
+ /** Resolve and verify the Bearer CLI token, then check it grants the required role. */
163
+ async function authorize(
164
+ ctx: ActionCtx,
165
+ request: Request,
166
+ route: SyncRoute,
167
+ ): Promise<AuthResult> {
168
+ const header = request.headers.get('authorization')
169
+ if (!header || !header.toLowerCase().startsWith('bearer ')) {
170
+ return {
171
+ ok: false,
172
+ response: errorResponse(
173
+ 401,
174
+ 'unauthorized',
175
+ 'Missing CLI token — send "Authorization: Bearer <token>" (issue one with tokens.issue).',
176
+ ),
177
+ }
178
+ }
179
+ const token = header.slice('bearer '.length).trim()
180
+ // verifyCliToken only reads `adapter.cliTokens`; the rest of the adapter surface
181
+ // is deliberately absent here (the assertion documents exactly that contract).
182
+ const adapter = { cliTokens: cliTokenRepository(ctx) } as PersistenceAdapter
183
+ const verified = await verifyCliToken(adapter, token)
184
+ if (verified === null) {
185
+ return {
186
+ ok: false,
187
+ response: errorResponse(401, 'unauthorized', 'Unknown, malformed, or revoked CLI token.'),
188
+ }
189
+ }
190
+ if (ROLE_RANK[verified.role] < ROLE_RANK[REQUIRED_ROLE[route.endpoint]]) {
191
+ return {
192
+ ok: false,
193
+ response: errorResponse(
194
+ 403,
195
+ 'forbidden',
196
+ `This endpoint requires the "${REQUIRED_ROLE[route.endpoint]}" role (token grants "${verified.role}").`,
197
+ ),
198
+ }
199
+ }
200
+ return { ok: true, verified }
201
+ }
202
+
203
+ // --- The HTTP action ------------------------------------------------------------
204
+
205
+ export const registrySync = httpAction(async (ctx, request) => {
206
+ const pathname = new URL(request.url).pathname
207
+ try {
208
+ const route = parseSyncRoute(request.method, pathname)
209
+ if (!route) {
210
+ return errorResponse(
211
+ 404,
212
+ 'not-found',
213
+ `No registry-sync endpoint matches ${request.method} ${pathname}.`,
214
+ )
215
+ }
216
+ const auth = await authorize(ctx, request, route)
217
+ if (!auth.ok) return auth.response
218
+ const { projectId } = route
219
+
220
+ // snapshot
221
+ const params = new URL(request.url).searchParams
222
+ const mode = params.get('mode') ?? 'published'
223
+ if (mode !== 'published' && mode !== 'draft') {
224
+ return errorResponse(
225
+ 400,
226
+ 'invalid-request',
227
+ `Invalid mode "${mode}" (expected published|draft).`,
228
+ )
229
+ }
230
+ const environment = params.get('environment') ?? 'production'
231
+ const snapshot = await ctx.runQuery(syncExportSnapshot, {
232
+ projectId,
233
+ mode,
234
+ environment,
235
+ })
236
+ return jsonResponse(snapshot)
237
+ } catch (cause) {
238
+ // Even unexpected failures keep the structured contract — never an opaque crash.
239
+ return errorResponse(
240
+ 500,
241
+ 'internal',
242
+ `Registry sync failed: ${cause instanceof Error ? cause.message : String(cause)}`,
243
+ )
244
+ }
245
+ })
246
+
247
+ // --- Typegen/check snapshot -----------------------------------------------------
248
+
249
+ type SnapshotMode = 'published' | 'draft'
250
+
251
+ interface GraphRevisionPayload {
252
+ revision: number
253
+ serializedGraph: string
254
+ }
255
+
256
+ interface BuilderSnapshot {
257
+ projectId: string
258
+ mode: SnapshotMode
259
+ environment: string
260
+ project: null
261
+ localeConfig: { default: string; locales: string[] }
262
+ collections: never[]
263
+ graph: GraphRevisionPayload | null
264
+ }
265
+
266
+ /** The publishing-state-pinned revision (single-environment; mirrors `delivery.ts`). */
267
+ async function loadPinnedGraph(ctx: QueryCtx): Promise<GraphRevisionPayload | null> {
268
+ const state = await ctx.db.query('publishingState').first()
269
+ const pinned = state?.status === 'published' ? state.publishedRevision : undefined
270
+ if (pinned === undefined) return null
271
+ const row = await ctx.db
272
+ .query('graphRevisions')
273
+ .withIndex('by_revision', (q) => q.eq('revision', pinned))
274
+ .first()
275
+ return row ? { revision: row.revision, serializedGraph: row.serializedGraph } : null
276
+ }
277
+
278
+ async function loadLatestGraph(ctx: QueryCtx): Promise<GraphRevisionPayload | null> {
279
+ const row = await ctx.db
280
+ .query('graphRevisions')
281
+ .withIndex('by_revision')
282
+ .order('desc')
283
+ .first()
284
+ return row ? { revision: row.revision, serializedGraph: row.serializedGraph } : null
285
+ }
286
+
287
+ /**
288
+ * Everything `builder typegen`/`builder check` need from live builder state, in the
289
+ * `BuilderSnapshot` wire shape of `@elytracms/export-sync`'s `createHttpBuilderSource`.
290
+ * Unlike `delivery:snapshot` this is token-gated and serves exactly one graph per
291
+ * request: published = the pinned revision for `environment`, draft = latest.
292
+ * Component manifests are code-owned, so the snapshot no longer carries them.
293
+ */
294
+ export const exportSnapshot = internalQuery({
295
+ args: {
296
+ projectId: v.string(),
297
+ mode: v.union(v.literal('published'), v.literal('draft')),
298
+ environment: v.string(),
299
+ },
300
+ handler: async (ctx, { projectId, mode, environment }): Promise<BuilderSnapshot> => {
301
+ // `environment` is a vestigial wire-compat field on the CLI snapshot contract
302
+ // (EC-179 §2): a deployment IS one environment, so the pinned read is single-row.
303
+ const graph = mode === 'published' ? await loadPinnedGraph(ctx) : await loadLatestGraph(ctx)
304
+
305
+ // AD-11 (EC-182/EC-183/EC-207): project identity, locale config, CMS
306
+ // collection schemas, AND routes/redirects are all config-owned —
307
+ // `builder typegen`/`check` run in the consuming repo and read them from
308
+ // `elytra.config.ts`, not the wire. The DB carries only data (documents,
309
+ // assets, graph). `project`/`localeConfig`/`collections` stay in the wire
310
+ // shape as config-sourced placeholders until the CLI reads config directly
311
+ // (C1) — the snapshot no longer carries a schema or a route table to return.
312
+ return {
313
+ projectId,
314
+ mode,
315
+ environment,
316
+ project: null,
317
+ localeConfig: { default: 'en', locales: ['en'] },
318
+ collections: [],
319
+ graph,
320
+ }
321
+ },
322
+ })
@@ -0,0 +1,12 @@
1
+ {
2
+ "extends": "../../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "noEmit": true,
5
+ "module": "ESNext",
6
+ "moduleResolution": "Bundler",
7
+ "jsx": "react-jsx",
8
+ "skipLibCheck": true,
9
+ "types": ["node"]
10
+ },
11
+ "include": ["./**/*.ts"]
12
+ }
@@ -0,0 +1,23 @@
1
+ import { getAuthUserId } from '@convex-dev/auth/server'
2
+ import { query } from './functions'
3
+
4
+ /**
5
+ * Current-user lookup (EC-150). The Convex `AuthAdapter` implementation calls
6
+ * this after sign-in (and on session restore) to materialize the studio's
7
+ * `AuthSession`. Returns `null` when the caller is unauthenticated — never an
8
+ * error, so the adapter can treat it as "signed out".
9
+ */
10
+ export const me = query({
11
+ args: {},
12
+ handler: async (ctx) => {
13
+ const userId = await getAuthUserId(ctx)
14
+ if (userId === null) return null
15
+ const user = await ctx.db.get(userId)
16
+ if (user === null) return null
17
+ return {
18
+ id: String(userId),
19
+ email: user.email ?? '',
20
+ ...(user.name ? { name: user.name } : {}),
21
+ }
22
+ },
23
+ })
@@ -0,0 +1,85 @@
1
+ import { internalActionGeneric } from 'convex/server'
2
+ import { v } from 'convex/values'
3
+
4
+ /**
5
+ * EC-146 instant publish — Convex-side webhook delivery variant.
6
+ *
7
+ * The v1 dispatcher (`src/lib/webhooks`) runs in the browser next to the
8
+ * operations client and POSTs to the host directly (CORS + bundle-visible
9
+ * secret, documented there). For Convex-backed deployments this action keeps
10
+ * the secret server-side instead: `publishing:publish` / `publishing:unpublish`
11
+ * schedule it via `ctx.scheduler.runAfter(0, internal.webhooks.deliver, …)`
12
+ * (EC-180) with the already-computed tag set, and the action signs + POSTs from
13
+ * the Convex runtime. No CORS applies and the secret lives in Convex env config.
14
+ *
15
+ * It is an **internal** action: only server-side callers (the scheduler) may
16
+ * invoke it — a secret-signing HTTP emitter must never be client-callable.
17
+ *
18
+ * Environment (set via `npx convex env set …`):
19
+ * - `CANVAS_REVALIDATE_ENDPOINT` — the host's revalidate route URL
20
+ * - `CANVAS_REVALIDATE_SECRET` — shared HMAC secret (= host `REVALIDATE_SECRET`)
21
+ * - `CANVAS_PROJECT_SCOPE` — the host's `cache.scope` (= `ELYTRA_PROJECT_ID`),
22
+ * read by the publishing mutations to scope the sweep tag (`p:<scope>:project`)
23
+ *
24
+ * Signature scheme mirrors `src/lib/webhooks/signing.ts` and is verified by
25
+ * `@elytracms/next`'s `evaluateRevalidateRequest`: HMAC-SHA256 over the exact
26
+ * raw JSON body, hex-encoded, sent as `x-elytra-signature: sha256=<hex>`.
27
+ * Uses Web Crypto (`crypto.subtle`), which the Convex runtime provides.
28
+ */
29
+
30
+ const SIGNATURE_HEADER = 'x-elytra-signature'
31
+
32
+ function toHex(buffer: ArrayBuffer): string {
33
+ return [...new Uint8Array(buffer)].map((byte) => byte.toString(16).padStart(2, '0')).join('')
34
+ }
35
+
36
+ async function signBody(rawBody: string, secret: string): Promise<string> {
37
+ const encoder = new TextEncoder()
38
+ const key = await crypto.subtle.importKey(
39
+ 'raw',
40
+ encoder.encode(secret),
41
+ { name: 'HMAC', hash: 'SHA-256' },
42
+ false,
43
+ ['sign'],
44
+ )
45
+ return `sha256=${toHex(await crypto.subtle.sign('HMAC', key, encoder.encode(rawBody)))}`
46
+ }
47
+
48
+ export const deliver = internalActionGeneric({
49
+ args: {
50
+ projectId: v.string(),
51
+ /** Operation name carried as the webhook `event` (e.g. `publishing.publish`). */
52
+ event: v.string(),
53
+ /** Fully-formatted, scoped Next tag strings (see src/lib/webhooks/tags.ts). */
54
+ tags: v.array(v.string()),
55
+ },
56
+ handler: async (_ctx, args) => {
57
+ // The Convex runtime exposes deployment env vars on `process.env`; this
58
+ // tsconfig has no Node types, so reach it via `globalThis`.
59
+ const env =
60
+ (globalThis as { process?: { env?: Record<string, string | undefined> } }).process?.env ?? {}
61
+ const endpoint = env['CANVAS_REVALIDATE_ENDPOINT']
62
+ const secret = env['CANVAS_REVALIDATE_SECRET']
63
+ if (!endpoint || !secret) {
64
+ // Unconfigured deployments are a visible no-op, never an error — local
65
+ // dev must not depend on a reachable host site.
66
+ return { delivered: false as const, reason: 'unconfigured' as const }
67
+ }
68
+
69
+ const rawBody = JSON.stringify({
70
+ projectId: args.projectId,
71
+ event: args.event,
72
+ tags: args.tags,
73
+ timestamp: new Date().toISOString(),
74
+ })
75
+ const response = await fetch(endpoint, {
76
+ method: 'POST',
77
+ headers: {
78
+ 'content-type': 'application/json',
79
+ [SIGNATURE_HEADER]: await signBody(rawBody, secret),
80
+ },
81
+ body: rawBody,
82
+ })
83
+ return { delivered: response.ok, status: response.status }
84
+ },
85
+ })
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Asset library surface (EC-151, EC-152): file picker + drag-and-drop uploads
3
+ * with visible progress/failure/retry, a searchable grid of stored assets
4
+ * (filename/alt/title), metadata editing (alt, title — persisted through the
5
+ * operations asset facade), and reference-respecting deletion (usage count +
6
+ * block-or-confirm). All storage access goes through the persistence adapter
7
+ * via the assets store. Each card answers "where is this asset used?" via the
8
+ * shared references panel (EC-172); `focusAssetId` (the `?asset=` deep-link
9
+ * param) scrolls that card into view and opens its references dialog.
10
+ */
11
+ export declare function AssetLibrary({ projectId, focusAssetId, }: {
12
+ projectId: string;
13
+ focusAssetId?: string;
14
+ }): import("react").JSX.Element;
@@ -0,0 +1,191 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { AlertTriangle, ImageIcon, Link2, Pencil, RotateCcw, Search, Trash2, Upload, X, } from 'lucide-react';
3
+ import { useEffect, useRef, useState } from 'react';
4
+ import { buildAssetReferenceImpact } from '@elytracms/studio/lib/references';
5
+ import { cn } from '@elytracms/studio/lib/utils';
6
+ import { ReferenceImpactRows } from '../cms/reference-confirm';
7
+ import { ReferencesPanel } from '../references/references-panel';
8
+ import { Badge, Button, Code, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Field, Grid, Heading, Input, Panel, PanelBody, PanelHeader, Row, Spacer, Stack, Text, } from '@elytracms/studio/ui';
9
+ import { filterAssets } from './asset-search';
10
+ import { useAssets } from './assets-store';
11
+ /**
12
+ * Asset library surface (EC-151, EC-152): file picker + drag-and-drop uploads
13
+ * with visible progress/failure/retry, a searchable grid of stored assets
14
+ * (filename/alt/title), metadata editing (alt, title — persisted through the
15
+ * operations asset facade), and reference-respecting deletion (usage count +
16
+ * block-or-confirm). All storage access goes through the persistence adapter
17
+ * via the assets store. Each card answers "where is this asset used?" via the
18
+ * shared references panel (EC-172); `focusAssetId` (the `?asset=` deep-link
19
+ * param) scrolls that card into view and opens its references dialog.
20
+ */
21
+ export function AssetLibrary({ projectId, focusAssetId, }) {
22
+ const store = useAssets();
23
+ const fileInputRef = useRef(null);
24
+ const [dragging, setDragging] = useState(false);
25
+ const [query, setQuery] = useState('');
26
+ const onPick = (event) => {
27
+ if (event.target.files)
28
+ store.upload([...event.target.files]);
29
+ event.target.value = '';
30
+ };
31
+ const onDrop = (event) => {
32
+ event.preventDefault();
33
+ setDragging(false);
34
+ if (event.dataTransfer.files.length > 0)
35
+ store.upload([...event.dataTransfer.files]);
36
+ };
37
+ if (store.status === 'error') {
38
+ return (_jsxs(Stack, { gap: "2", className: "p-6", "data-testid": "assets-error", children: [_jsxs(Row, { gap: "2", children: [_jsx(AlertTriangle, { className: "size-4 text-destructive" }), _jsx(Text, { weight: "semibold", children: "Assets failed to load" })] }), _jsx(Text, { size: "sm", tone: "muted", children: store.errorMessage ?? 'The persistence backend is unreachable.' }), _jsx(Row, { children: _jsxs(Button, { variant: "outline", size: "sm", onClick: () => void store.refresh(), children: [_jsx(RotateCcw, {}), " Retry"] }) })] }));
39
+ }
40
+ return (_jsxs(Stack, { gap: "4", className: cn('flex-1 p-4 sm:p-6', dragging && 'outline-2 outline-dashed outline-primary/60 -outline-offset-4'), onDragOver: (event) => {
41
+ event.preventDefault();
42
+ setDragging(true);
43
+ }, onDragLeave: () => setDragging(false), onDrop: onDrop, "data-testid": "asset-library", children: [_jsxs(Row, { gap: "2", children: [_jsx(Heading, { level: 2, size: "h2", children: "Assets" }), _jsx(Badge, { variant: "outline", children: store.assets.length }), _jsx(Spacer, {}), _jsx(AssetSearchInput, { value: query, onChange: setQuery }), _jsx("input", { ref: fileInputRef, type: "file", multiple: true, className: "hidden", onChange: onPick, "data-testid": "asset-file-input" }), _jsxs(Button, { size: "sm", onClick: () => fileInputRef.current?.click(), children: [_jsx(Upload, {}), " Upload"] })] }), _jsx(DropHint, { visible: dragging || store.assets.length === 0 }), store.uploads.length > 0 ? (_jsxs(Panel, { "data-testid": "upload-queue", children: [_jsx(PanelHeader, { children: _jsx(Text, { size: "xs", weight: "semibold", tone: "muted", children: "Uploads" }) }), _jsx(PanelBody, { className: "divide-y divide-border p-0", children: store.uploads.map((entry) => (_jsx(UploadRow, { entry: entry, onRetry: () => store.retryUpload(entry.id), onDismiss: () => store.dismissUpload(entry.id) }, entry.id))) })] })) : null, store.status === 'loading' ? (_jsx(Text, { size: "sm", tone: "muted", children: "Loading assets\u2026" })) : (_jsx(AssetGrid, { assets: filterAssets(store.assets, query), filtered: query.trim().length > 0, projectId: projectId, focusAssetId: focusAssetId }))] }));
44
+ }
45
+ /** Named search primitive over filename / alt / title (EC-152). */
46
+ function AssetSearchInput({ value, onChange, }) {
47
+ return (_jsxs("span", { className: "relative", children: [_jsx(Search, { className: "pointer-events-none absolute left-2.5 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { value: value, onChange: (e) => onChange(e.target.value), placeholder: "Search assets\u2026", "aria-label": "Search assets", className: "w-56 pl-8", "data-testid": "asset-search" })] }));
48
+ }
49
+ /** Persistent drop affordance; doubles as the empty state. */
50
+ function DropHint({ visible }) {
51
+ return (_jsxs(Stack, { gap: "1", align: "center", className: cn('rounded-lg border border-dashed border-border p-6 text-center', !visible && 'hidden sm:flex'), children: [_jsx(ImageIcon, { className: "size-5 text-muted-foreground" }), _jsx(Text, { size: "sm", tone: "muted", children: "Drop files anywhere to upload, or use the Upload button." })] }));
52
+ }
53
+ const PHASE_LABEL = {
54
+ idle: 'queued',
55
+ preparing: 'preparing',
56
+ uploading: 'uploading',
57
+ confirming: 'saving metadata',
58
+ done: 'done',
59
+ failed: 'failed',
60
+ };
61
+ /** One in-flight upload: progress while transferring, explicit failure + retry. */
62
+ function UploadRow({ entry, onRetry, onDismiss, }) {
63
+ const { state } = entry;
64
+ const failed = state.phase === 'failed';
65
+ return (_jsxs(Stack, { gap: "1", className: "px-3 py-2", "data-testid": "upload-row", children: [_jsxs(Row, { gap: "2", children: [_jsx(Text, { size: "sm", weight: "medium", className: "min-w-0 flex-1 truncate", children: entry.filename }), _jsxs(Badge, { variant: failed ? 'error' : state.phase === 'done' ? 'success' : 'info', children: [PHASE_LABEL[state.phase], state.attempt > 1 ? ` · attempt ${state.attempt}` : ''] }), failed ? (_jsxs(_Fragment, { children: [state.failure?.retryable !== false ? (_jsxs(Button, { variant: "outline", size: "sm", onClick: onRetry, "data-testid": "upload-retry", children: [_jsx(RotateCcw, {}), " Retry"] })) : null, _jsx(Button, { variant: "ghost", size: "icon-sm", onClick: onDismiss, "aria-label": "Dismiss upload", children: _jsx(X, {}) })] })) : null] }), failed ? (_jsxs(Text, { size: "xs", tone: "muted", "data-testid": "upload-error", children: [state.failure?.stage, ": ", state.failure?.message] })) : (_jsx(ProgressBar, { value: state.progress }))] }));
66
+ }
67
+ /** Named progress primitive for byte transfer. */
68
+ function ProgressBar({ value }) {
69
+ return (_jsx("span", { className: "block h-1.5 w-full overflow-hidden rounded-full bg-muted", children: _jsx("span", { className: "block h-full rounded-full bg-primary transition-[width]", style: { width: `${Math.round(value * 100)}%` } }) }));
70
+ }
71
+ function AssetGrid({ assets, filtered, projectId, focusAssetId, }) {
72
+ if (assets.length === 0) {
73
+ // The unfiltered empty state is the DropHint above; the *filtered* empty
74
+ // state must say so explicitly (deterministic, reproducible).
75
+ return filtered ? (_jsx(Text, { size: "sm", tone: "muted", "data-testid": "asset-search-empty", children: "No assets match your search." })) : null;
76
+ }
77
+ return (_jsx(Grid, { minItemWidth: "220px", gap: "3", "data-testid": "asset-grid", children: assets.map((asset) => (_jsx(AssetCard, { asset: asset, projectId: projectId, focused: asset.id === focusAssetId }, asset.id))) }));
78
+ }
79
+ function formatBytes(byteSize) {
80
+ if (byteSize < 1024)
81
+ return `${byteSize} B`;
82
+ if (byteSize < 1024 * 1024)
83
+ return `${(byteSize / 1024).toFixed(1)} KB`;
84
+ return `${(byteSize / (1024 * 1024)).toFixed(1)} MB`;
85
+ }
86
+ function AssetCard({ asset, projectId, focused, }) {
87
+ const cardRef = useRef(null);
88
+ useEffect(() => {
89
+ if (focused)
90
+ cardRef.current?.scrollIntoView({ block: 'center', behavior: 'smooth' });
91
+ }, [focused]);
92
+ return (_jsx("div", { ref: cardRef, children: _jsxs(Panel, { className: cn('overflow-hidden', focused && 'ring-2 ring-primary/70'), "data-testid": "asset-card", children: [_jsx(AssetThumbnail, { asset: asset }), _jsxs(Stack, { gap: "1", className: "p-3", children: [_jsx(Text, { size: "sm", weight: "medium", className: "truncate", title: asset.filename, children: asset.title ?? asset.filename }), asset.title ? (_jsx(Text, { size: "xs", tone: "muted", className: "truncate", children: asset.filename })) : null, _jsx(Text, { size: "xs", tone: asset.alt ? 'muted' : 'subtle', className: "truncate", "data-testid": "asset-alt", children: asset.alt ? `Alt: ${asset.alt}` : 'No alt text' }), _jsxs(Row, { gap: "1", wrap: true, children: [_jsx(Code, { tone: "muted", children: asset.contentType }), asset.width && asset.height ? (_jsxs(Code, { tone: "muted", children: [asset.width, "\u00D7", asset.height] })) : null, _jsx(Code, { tone: "muted", children: formatBytes(asset.byteSize) })] }), _jsxs(Row, { gap: "1", children: [_jsx(Code, { tone: "muted", className: "min-w-0 flex-1 truncate", children: asset.id }), _jsx(AssetReferencesButton, { asset: asset, projectId: projectId, autoOpen: focused }), _jsx(EditAssetButton, { asset: asset }), _jsx(DeleteAssetButton, { asset: asset })] })] })] }) }));
93
+ }
94
+ /**
95
+ * "Where is this asset used?" (EC-172): the shared references panel, inbound
96
+ * over the EC-155 index, hosted in a dialog per asset card.
97
+ */
98
+ function AssetReferencesButton({ asset, projectId, autoOpen, }) {
99
+ const [open, setOpen] = useState(false);
100
+ useEffect(() => {
101
+ if (autoOpen)
102
+ setOpen(true);
103
+ }, [autoOpen]);
104
+ return (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": `References of ${asset.filename}`, onClick: () => setOpen(true), "data-testid": "asset-references", children: _jsx(Link2, {}) }), _jsx(Dialog, { open: open, onOpenChange: setOpen, children: _jsxs(DialogContent, { className: "max-w-lg", children: [_jsxs(DialogHeader, { children: [_jsxs(DialogTitle, { children: ["Used by \u2014 ", asset.title ?? asset.filename] }), _jsx(DialogDescription, { children: "Everything referencing this asset, with deep links to the exact field or node." })] }), open ? (_jsx(ReferencesPanel, { projectId: projectId, target: { type: 'asset', id: asset.id } })) : null] }) })] }));
105
+ }
106
+ /**
107
+ * Metadata editing (EC-152): alt + title, persisted through the operations
108
+ * asset facade (`assets.upsert`). Failures render in the dialog — never a
109
+ * silent loss.
110
+ */
111
+ function EditAssetButton({ asset }) {
112
+ const store = useAssets();
113
+ const [open, setOpen] = useState(false);
114
+ const [alt, setAlt] = useState('');
115
+ const [title, setTitle] = useState('');
116
+ const [error, setError] = useState(null);
117
+ const [busy, setBusy] = useState(false);
118
+ const openDialog = () => {
119
+ setAlt(asset.alt ?? '');
120
+ setTitle(asset.title ?? '');
121
+ setError(null);
122
+ setOpen(true);
123
+ };
124
+ const save = async () => {
125
+ setBusy(true);
126
+ setError(null);
127
+ const result = await store.updateMetadata(asset.id, { alt, title });
128
+ setBusy(false);
129
+ if (result.ok)
130
+ setOpen(false);
131
+ else
132
+ setError(result.message);
133
+ };
134
+ return (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": `Edit ${asset.filename}`, onClick: openDialog, "data-testid": "asset-edit", children: _jsx(Pencil, {}) }), _jsx(Dialog, { open: open, onOpenChange: setOpen, children: _jsxs(DialogContent, { className: "max-w-md", children: [_jsxs(DialogHeader, { children: [_jsxs(DialogTitle, { children: ["Edit ", asset.filename] }), _jsx(DialogDescription, { children: "Alt text flows through the delivery shape into rendered markup; the title is the library display name." })] }), _jsx(Field, { label: "Alt text", htmlFor: `asset-alt-${asset.id}`, children: _jsx(Input, { id: `asset-alt-${asset.id}`, value: alt, onChange: (e) => setAlt(e.target.value), placeholder: "Describe the image\u2026", "data-testid": "asset-edit-alt" }) }), _jsx(Field, { label: "Title", htmlFor: `asset-title-${asset.id}`, children: _jsx(Input, { id: `asset-title-${asset.id}`, value: title, onChange: (e) => setTitle(e.target.value), placeholder: asset.filename, "data-testid": "asset-edit-title" }) }), error ? (_jsx(Text, { size: "sm", className: "text-destructive", "data-testid": "asset-edit-error", children: error })) : null, _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: () => setOpen(false), disabled: busy, children: "Cancel" }), _jsx(Button, { size: "sm", onClick: () => void save(), disabled: busy, "data-testid": "asset-edit-save", children: "Save" })] })] }) })] }));
135
+ }
136
+ /** Image preview from the stored serve URL; icon fallback for other types. */
137
+ function AssetThumbnail({ asset }) {
138
+ const isImage = asset.contentType.startsWith('image/');
139
+ // In-memory/opaque locators (memory://, convex://) are not fetchable by <img>.
140
+ const resolvable = asset.url?.startsWith('http') ?? false;
141
+ if (isImage && asset.url && resolvable) {
142
+ return (_jsx("img", { src: asset.url, alt: asset.alt ?? asset.filename, className: "aspect-video w-full bg-muted object-cover" }));
143
+ }
144
+ return (_jsx("span", { className: "grid aspect-video w-full place-items-center bg-muted text-muted-foreground", children: _jsx(ImageIcon, { className: "size-6" }) }));
145
+ }
146
+ /**
147
+ * Reference-respecting delete (EC-151, EC-202): the dialog enumerates the exact
148
+ * inbound references — index-accurate (EC-155), with deep links to the field or
149
+ * node — using the shared EC-173 impact rows. Deleting a referenced asset is
150
+ * weak-by-default (allowed, with an explicit "delete anyway"): every reference
151
+ * dangles into a visible validation issue rather than silently breaking.
152
+ */
153
+ function DeleteAssetButton({ asset }) {
154
+ const store = useAssets();
155
+ const [open, setOpen] = useState(false);
156
+ const [usages, setUsages] = useState(null);
157
+ const [error, setError] = useState(null);
158
+ const [busy, setBusy] = useState(false);
159
+ const openDialog = async () => {
160
+ setOpen(true);
161
+ setError(null);
162
+ setUsages(null);
163
+ try {
164
+ setUsages(await store.usagesOf(asset.id));
165
+ }
166
+ catch (cause) {
167
+ setError(cause instanceof Error ? cause.message : String(cause));
168
+ }
169
+ };
170
+ const confirm = async () => {
171
+ setBusy(true);
172
+ setError(null);
173
+ const inUse = (usages?.length ?? 0) > 0;
174
+ const result = await store.remove(asset.id, { confirmInUse: inUse });
175
+ setBusy(false);
176
+ if (result.outcome === 'deleted' || result.outcome === 'not-found') {
177
+ setOpen(false);
178
+ }
179
+ else if (result.outcome === 'blocked') {
180
+ // A reference appeared between the pre-flight read and confirm (rare race).
181
+ setError(`Asset is now referenced in ${result.usageCount} place(s) — reopen to review.`);
182
+ }
183
+ else {
184
+ setError(result.message);
185
+ }
186
+ };
187
+ const impact = usages ? buildAssetReferenceImpact(usages) : null;
188
+ const usageCount = impact?.broken.length ?? 0;
189
+ return (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": `Delete ${asset.filename}`, onClick: () => void openDialog(), "data-testid": "asset-delete", children: _jsx(Trash2, {}) }), _jsx(Dialog, { open: open, onOpenChange: setOpen, children: _jsxs(DialogContent, { className: "max-h-[80vh] max-w-md overflow-y-auto", children: [_jsxs(DialogHeader, { children: [_jsxs(DialogTitle, { children: ["Delete ", asset.filename, "?"] }), _jsx(DialogDescription, { children: "Removes the stored bytes and the metadata record. This cannot be undone." })] }), usages === null && error === null ? (_jsx(Text, { size: "sm", tone: "muted", children: "Checking references\u2026" })) : usageCount > 0 ? (_jsxs(Stack, { gap: "2", "data-testid": "asset-usage-warning", children: [_jsxs(Row, { gap: "2", children: [_jsx(AlertTriangle, { className: "size-4 text-warning" }), _jsxs(Text, { size: "sm", weight: "semibold", children: ["In use in ", usageCount, " place", usageCount === 1 ? '' : 's'] })] }), _jsx(ReferenceImpactRows, { projectId: store.projectId, broken: impact.broken, onNavigate: () => setOpen(false) }), _jsx(Text, { size: "xs", tone: "muted", children: "Deleting will leave these references broken \u2014 they will surface as validation issues." })] })) : (_jsx(Text, { size: "sm", tone: "muted", children: "No references found. Safe to delete." })), error ? (_jsx(Text, { size: "sm", className: "text-destructive", "data-testid": "asset-delete-error", children: error })) : null, _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: () => setOpen(false), disabled: busy, children: "Cancel" }), _jsx(Button, { variant: "destructive", size: "sm", onClick: () => void confirm(), disabled: busy || usages === null, "data-testid": "asset-delete-confirm", children: usageCount > 0 ? 'Delete anyway' : 'Delete' })] })] }) })] }));
190
+ }
191
+ //# sourceMappingURL=asset-library.js.map