convex-cms 0.0.2 → 0.0.5-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (311) hide show
  1. package/README.md +109 -13
  2. package/admin-dist/nitro.json +15 -0
  3. package/admin-dist/public/assets/CmsEmptyState-CiMQwSQV.js +5 -0
  4. package/admin-dist/public/assets/CmsPageHeader-ohOq0luT.js +1 -0
  5. package/admin-dist/public/assets/CmsStatusBadge-BdNf0V9v.js +1 -0
  6. package/admin-dist/public/assets/CmsSurface-CWup6Jh7.js +1 -0
  7. package/admin-dist/public/assets/CmsToolbar-cEBlCHa3.js +1 -0
  8. package/admin-dist/public/assets/ContentEntryEditor-BY5ypfUs.js +4 -0
  9. package/admin-dist/public/assets/ErrorState-C4nJ-ml4.js +1 -0
  10. package/admin-dist/public/assets/TaxonomyFilter-BgE_SR_O.js +1 -0
  11. package/admin-dist/public/assets/_contentTypeId-DtZectcC.js +1 -0
  12. package/admin-dist/public/assets/_entryId-BpSmrfAm.js +1 -0
  13. package/admin-dist/public/assets/alert-Bf2l8kxw.js +1 -0
  14. package/admin-dist/public/assets/badge-qPrc4AUM.js +1 -0
  15. package/admin-dist/public/assets/circle-check-big-Dgozy3vV.js +1 -0
  16. package/admin-dist/public/assets/command-QOmNhlb0.js +1 -0
  17. package/admin-dist/public/assets/content-OEBGlxg1.js +1 -0
  18. package/admin-dist/public/assets/content-types-CjQliqVV.js +2 -0
  19. package/admin-dist/public/assets/globals-hAmgC66w.css +1 -0
  20. package/admin-dist/public/assets/index-BH_ECMhv.js +1 -0
  21. package/admin-dist/public/assets/label-DCsUdvFh.js +1 -0
  22. package/admin-dist/public/assets/link-2-Czw1N61H.js +1 -0
  23. package/admin-dist/public/assets/list-DtCsXj8-.js +1 -0
  24. package/admin-dist/public/assets/main-CXgkZMhe.js +97 -0
  25. package/admin-dist/public/assets/media-DTJ3-ViE.js +1 -0
  26. package/admin-dist/public/assets/new._contentTypeId-CoTDxKzf.js +1 -0
  27. package/admin-dist/public/assets/plus-xCFJK0RC.js +1 -0
  28. package/admin-dist/public/assets/rotate-ccw-DIqK63wY.js +1 -0
  29. package/admin-dist/public/assets/scroll-area-B-yrE66a.js +1 -0
  30. package/admin-dist/public/assets/search-CbCbboeU.js +1 -0
  31. package/admin-dist/public/assets/select-Co3TZFJb.js +1 -0
  32. package/admin-dist/public/assets/settings-BspTTv_o.js +1 -0
  33. package/admin-dist/public/assets/switch-CfavASmR.js +1 -0
  34. package/admin-dist/public/assets/tabs-CN5s5u2W.js +1 -0
  35. package/admin-dist/public/assets/tanstack-adapter-npeE3RdY.js +1 -0
  36. package/admin-dist/public/assets/taxonomies-CgG46fIF.js +1 -0
  37. package/admin-dist/public/assets/textarea-BJ0XFZpT.js +1 -0
  38. package/admin-dist/public/assets/trash-B3daldm5.js +1 -0
  39. package/admin-dist/public/assets/triangle-alert-BZRcqsUg.js +1 -0
  40. package/admin-dist/public/assets/useBreadcrumbLabel-DwZlwvFF.js +1 -0
  41. package/admin-dist/public/assets/usePermissions-C1JQhfqb.js +1 -0
  42. package/admin-dist/public/favicon.ico +0 -0
  43. package/admin-dist/server/_chunks/_libs/@date-fns/tz.mjs +217 -0
  44. package/admin-dist/server/_chunks/_libs/@floating-ui/core.mjs +719 -0
  45. package/admin-dist/server/_chunks/_libs/@floating-ui/dom.mjs +622 -0
  46. package/admin-dist/server/_chunks/_libs/@floating-ui/react-dom.mjs +292 -0
  47. package/admin-dist/server/_chunks/_libs/@floating-ui/utils.mjs +320 -0
  48. package/admin-dist/server/_chunks/_libs/@radix-ui/number.mjs +6 -0
  49. package/admin-dist/server/_chunks/_libs/@radix-ui/primitive.mjs +11 -0
  50. package/admin-dist/server/_chunks/_libs/@radix-ui/react-arrow.mjs +23 -0
  51. package/admin-dist/server/_chunks/_libs/@radix-ui/react-avatar.mjs +119 -0
  52. package/admin-dist/server/_chunks/_libs/@radix-ui/react-checkbox.mjs +270 -0
  53. package/admin-dist/server/_chunks/_libs/@radix-ui/react-collection.mjs +69 -0
  54. package/admin-dist/server/_chunks/_libs/@radix-ui/react-compose-refs.mjs +39 -0
  55. package/admin-dist/server/_chunks/_libs/@radix-ui/react-context.mjs +137 -0
  56. package/admin-dist/server/_chunks/_libs/@radix-ui/react-dialog.mjs +325 -0
  57. package/admin-dist/server/_chunks/_libs/@radix-ui/react-direction.mjs +9 -0
  58. package/admin-dist/server/_chunks/_libs/@radix-ui/react-dismissable-layer.mjs +210 -0
  59. package/admin-dist/server/_chunks/_libs/@radix-ui/react-dropdown-menu.mjs +253 -0
  60. package/admin-dist/server/_chunks/_libs/@radix-ui/react-focus-guards.mjs +29 -0
  61. package/admin-dist/server/_chunks/_libs/@radix-ui/react-focus-scope.mjs +206 -0
  62. package/admin-dist/server/_chunks/_libs/@radix-ui/react-id.mjs +14 -0
  63. package/admin-dist/server/_chunks/_libs/@radix-ui/react-label.mjs +23 -0
  64. package/admin-dist/server/_chunks/_libs/@radix-ui/react-menu.mjs +812 -0
  65. package/admin-dist/server/_chunks/_libs/@radix-ui/react-popover.mjs +300 -0
  66. package/admin-dist/server/_chunks/_libs/@radix-ui/react-popper.mjs +286 -0
  67. package/admin-dist/server/_chunks/_libs/@radix-ui/react-portal.mjs +16 -0
  68. package/admin-dist/server/_chunks/_libs/@radix-ui/react-presence.mjs +128 -0
  69. package/admin-dist/server/_chunks/_libs/@radix-ui/react-primitive.mjs +141 -0
  70. package/admin-dist/server/_chunks/_libs/@radix-ui/react-roving-focus.mjs +224 -0
  71. package/admin-dist/server/_chunks/_libs/@radix-ui/react-scroll-area.mjs +721 -0
  72. package/admin-dist/server/_chunks/_libs/@radix-ui/react-select.mjs +1163 -0
  73. package/admin-dist/server/_chunks/_libs/@radix-ui/react-separator.mjs +28 -0
  74. package/admin-dist/server/_chunks/_libs/@radix-ui/react-slot.mjs +601 -0
  75. package/admin-dist/server/_chunks/_libs/@radix-ui/react-switch.mjs +152 -0
  76. package/admin-dist/server/_chunks/_libs/@radix-ui/react-tabs.mjs +189 -0
  77. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-callback-ref.mjs +11 -0
  78. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-controllable-state.mjs +69 -0
  79. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-effect-event.mjs +1 -0
  80. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-escape-keydown.mjs +17 -0
  81. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-is-hydrated.mjs +15 -0
  82. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-layout-effect.mjs +6 -0
  83. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-previous.mjs +14 -0
  84. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-size.mjs +39 -0
  85. package/admin-dist/server/_chunks/_libs/@radix-ui/react-visually-hidden.mjs +33 -0
  86. package/admin-dist/server/_chunks/_libs/@tanstack/history.mjs +409 -0
  87. package/admin-dist/server/_chunks/_libs/@tanstack/react-router.mjs +1718 -0
  88. package/admin-dist/server/_chunks/_libs/@tanstack/react-store.mjs +56 -0
  89. package/admin-dist/server/_chunks/_libs/@tanstack/router-core.mjs +4829 -0
  90. package/admin-dist/server/_chunks/_libs/@tanstack/store.mjs +134 -0
  91. package/admin-dist/server/_chunks/_libs/react-dom.mjs +10781 -0
  92. package/admin-dist/server/_chunks/_libs/react.mjs +513 -0
  93. package/admin-dist/server/_libs/aria-hidden.mjs +122 -0
  94. package/admin-dist/server/_libs/class-variance-authority.mjs +44 -0
  95. package/admin-dist/server/_libs/clsx.mjs +16 -0
  96. package/admin-dist/server/_libs/cmdk.mjs +315 -0
  97. package/admin-dist/server/_libs/convex.mjs +4841 -0
  98. package/admin-dist/server/_libs/cookie-es.mjs +58 -0
  99. package/admin-dist/server/_libs/croner.mjs +1 -0
  100. package/admin-dist/server/_libs/crossws.mjs +1 -0
  101. package/admin-dist/server/_libs/date-fns.mjs +1716 -0
  102. package/admin-dist/server/_libs/detect-node-es.mjs +1 -0
  103. package/admin-dist/server/_libs/get-nonce.mjs +9 -0
  104. package/admin-dist/server/_libs/h3-v2.mjs +277 -0
  105. package/admin-dist/server/_libs/h3.mjs +401 -0
  106. package/admin-dist/server/_libs/hookable.mjs +1 -0
  107. package/admin-dist/server/_libs/isbot.mjs +20 -0
  108. package/admin-dist/server/_libs/lucide-react.mjs +850 -0
  109. package/admin-dist/server/_libs/ohash.mjs +1 -0
  110. package/admin-dist/server/_libs/react-day-picker.mjs +2201 -0
  111. package/admin-dist/server/_libs/react-remove-scroll-bar.mjs +82 -0
  112. package/admin-dist/server/_libs/react-remove-scroll.mjs +328 -0
  113. package/admin-dist/server/_libs/react-style-singleton.mjs +69 -0
  114. package/admin-dist/server/_libs/rou3.mjs +8 -0
  115. package/admin-dist/server/_libs/seroval-plugins.mjs +58 -0
  116. package/admin-dist/server/_libs/seroval.mjs +1765 -0
  117. package/admin-dist/server/_libs/srvx.mjs +719 -0
  118. package/admin-dist/server/_libs/tailwind-merge.mjs +3010 -0
  119. package/admin-dist/server/_libs/tiny-invariant.mjs +12 -0
  120. package/admin-dist/server/_libs/tiny-warning.mjs +5 -0
  121. package/admin-dist/server/_libs/tslib.mjs +39 -0
  122. package/admin-dist/server/_libs/ufo.mjs +54 -0
  123. package/admin-dist/server/_libs/unctx.mjs +1 -0
  124. package/admin-dist/server/_libs/unstorage.mjs +1 -0
  125. package/admin-dist/server/_libs/use-callback-ref.mjs +66 -0
  126. package/admin-dist/server/_libs/use-sidecar.mjs +106 -0
  127. package/admin-dist/server/_libs/use-sync-external-store.mjs +139 -0
  128. package/admin-dist/server/_libs/zod.mjs +4223 -0
  129. package/admin-dist/server/_ssr/CmsButton-B45JAKR1.mjs +125 -0
  130. package/admin-dist/server/_ssr/CmsEmptyState-D_BQFAVR.mjs +290 -0
  131. package/admin-dist/server/_ssr/CmsPageHeader-CrUZA59A.mjs +24 -0
  132. package/admin-dist/server/_ssr/CmsStatusBadge-B-sj6yaj.mjs +127 -0
  133. package/admin-dist/server/_ssr/CmsSurface-DKJZhpjk.mjs +44 -0
  134. package/admin-dist/server/_ssr/CmsToolbar-ByaW5iXf.mjs +49 -0
  135. package/admin-dist/server/_ssr/ContentEntryEditor-D3_Jb1dq.mjs +3720 -0
  136. package/admin-dist/server/_ssr/ErrorState-cI-bKLez.mjs +89 -0
  137. package/admin-dist/server/_ssr/TaxonomyFilter-BRJkuCtA.mjs +188 -0
  138. package/admin-dist/server/_ssr/_contentTypeId-B9kA6CaM.mjs +379 -0
  139. package/admin-dist/server/_ssr/_entryId-BddcMkZN.mjs +161 -0
  140. package/admin-dist/server/_ssr/_tanstack-start-manifest_v-Dd7AmelK.mjs +4 -0
  141. package/admin-dist/server/_ssr/command-CGtVr8Gb.mjs +128 -0
  142. package/admin-dist/server/_ssr/config.server-D7JHDcDv.mjs +117 -0
  143. package/admin-dist/server/_ssr/content-D1tbeOd0.mjs +647 -0
  144. package/admin-dist/server/_ssr/content-types-BZqY_BER.mjs +1342 -0
  145. package/admin-dist/server/_ssr/index-BIdq4xaC.mjs +264 -0
  146. package/admin-dist/server/_ssr/index.mjs +1275 -0
  147. package/admin-dist/server/_ssr/label-T-QNKAr6.mjs +22 -0
  148. package/admin-dist/server/_ssr/media-C-xqjBrl.mjs +1832 -0
  149. package/admin-dist/server/_ssr/new._contentTypeId-DWic9cRq.mjs +144 -0
  150. package/admin-dist/server/_ssr/router-D1BMAMJT.mjs +1556 -0
  151. package/admin-dist/server/_ssr/scroll-area-C0pic_WA.mjs +59 -0
  152. package/admin-dist/server/_ssr/select-CqmuN2F6.mjs +142 -0
  153. package/admin-dist/server/_ssr/settings-CAkncGGV.mjs +430 -0
  154. package/admin-dist/server/_ssr/start-HYkvq4Ni.mjs +4 -0
  155. package/admin-dist/server/_ssr/switch-CgmuJkT9.mjs +31 -0
  156. package/admin-dist/server/_ssr/tabs-CnMj0aRy.mjs +630 -0
  157. package/admin-dist/server/_ssr/tanstack-adapter-BXZrMauE.mjs +119 -0
  158. package/admin-dist/server/_ssr/taxonomies-thl3BfVm.mjs +1015 -0
  159. package/admin-dist/server/_ssr/textarea-4K5OJgeh.mjs +18 -0
  160. package/admin-dist/server/_ssr/trash-B40Gx5zP.mjs +411 -0
  161. package/admin-dist/server/_ssr/useBreadcrumbLabel-rn-fL4zV.mjs +16 -0
  162. package/admin-dist/server/_ssr/usePermissions-CKeM6_Vw.mjs +68 -0
  163. package/admin-dist/server/favicon.ico +0 -0
  164. package/admin-dist/server/index.mjs +641 -0
  165. package/dist/cli/commands/init.d.ts +6 -0
  166. package/dist/cli/commands/init.d.ts.map +1 -0
  167. package/dist/cli/commands/init.js +156 -0
  168. package/dist/cli/commands/init.js.map +1 -0
  169. package/dist/cli/index.js +6 -0
  170. package/dist/cli/index.js.map +1 -1
  171. package/dist/client/admin-config.d.ts +2 -3
  172. package/dist/client/admin-config.d.ts.map +1 -1
  173. package/dist/client/admin-config.js +2 -3
  174. package/dist/client/admin-config.js.map +1 -1
  175. package/dist/client/adminApi.d.ts +1877 -1851
  176. package/dist/client/adminApi.d.ts.map +1 -1
  177. package/dist/client/adminApi.js +649 -629
  178. package/dist/client/adminApi.js.map +1 -1
  179. package/dist/client/agentTools.d.ts +1231 -139
  180. package/dist/client/agentTools.d.ts.map +1 -1
  181. package/dist/client/agentTools.js +37 -13
  182. package/dist/client/agentTools.js.map +1 -1
  183. package/dist/client/index.d.ts +5 -5
  184. package/dist/client/index.d.ts.map +1 -1
  185. package/dist/client/index.js +4 -4
  186. package/dist/client/index.js.map +1 -1
  187. package/dist/client/schema/codegen.d.ts +2 -2
  188. package/dist/client/schema/codegen.d.ts.map +1 -1
  189. package/dist/client/schema/codegen.js +3 -3
  190. package/dist/client/schema/codegen.js.map +1 -1
  191. package/dist/client/schema/defineContentType.d.ts +3 -3
  192. package/dist/client/schema/defineContentType.js +3 -3
  193. package/dist/client/schema/index.d.ts +7 -7
  194. package/dist/client/schema/index.d.ts.map +1 -1
  195. package/dist/client/schema/index.js +5 -5
  196. package/dist/client/schema/index.js.map +1 -1
  197. package/dist/client/schema/schemaDrift.d.ts +1 -1
  198. package/dist/client/schema/schemaDrift.js +1 -1
  199. package/dist/client/schema/typedClient.d.ts +2 -2
  200. package/dist/client/schema/typedClient.js +2 -2
  201. package/dist/client/schema/types.d.ts +1 -1
  202. package/dist/client/schema/types.js +1 -1
  203. package/dist/client/wrapper.d.ts +108 -65
  204. package/dist/client/wrapper.d.ts.map +1 -1
  205. package/dist/client/wrapper.js +22 -22
  206. package/dist/client/wrapper.js.map +1 -1
  207. package/dist/component/_generated/component.d.ts +9 -0
  208. package/dist/component/_generated/component.d.ts.map +1 -1
  209. package/dist/component/convex.config.d.ts +2 -2
  210. package/dist/component/convex.config.js +2 -2
  211. package/dist/component/index.d.ts +1 -1
  212. package/dist/component/index.js +1 -1
  213. package/dist/component/lib/ragContentChunker.d.ts +1 -1
  214. package/dist/component/lib/ragContentChunker.js +1 -1
  215. package/dist/component/mediaAssets.d.ts +35 -0
  216. package/dist/component/mediaAssets.d.ts.map +1 -1
  217. package/dist/component/mediaAssets.js +81 -0
  218. package/dist/component/mediaAssets.js.map +1 -1
  219. package/dist/component/roles.d.ts +1 -1
  220. package/dist/component/roles.js +1 -1
  221. package/dist/react/index.d.ts +2 -2
  222. package/dist/react/index.d.ts.map +1 -1
  223. package/dist/react/index.js +13 -7
  224. package/dist/react/index.js.map +1 -1
  225. package/dist/test.d.ts +2 -2
  226. package/dist/test.d.ts.map +1 -1
  227. package/dist/test.js +4 -3
  228. package/dist/test.js.map +1 -1
  229. package/package.json +37 -13
  230. package/dist/component/auditLog.d.ts +0 -410
  231. package/dist/component/auditLog.d.ts.map +0 -1
  232. package/dist/component/auditLog.js +0 -607
  233. package/dist/component/auditLog.js.map +0 -1
  234. package/dist/component/types.d.ts +0 -4
  235. package/dist/component/types.d.ts.map +0 -1
  236. package/dist/component/types.js +0 -2
  237. package/dist/component/types.js.map +0 -1
  238. package/src/cli/commands/admin.ts +0 -104
  239. package/src/cli/index.ts +0 -21
  240. package/src/cli/utils/detectConvexUrl.ts +0 -54
  241. package/src/cli/utils/openBrowser.ts +0 -16
  242. package/src/client/admin-config.ts +0 -138
  243. package/src/client/adminApi.ts +0 -942
  244. package/src/client/agentTools.ts +0 -1311
  245. package/src/client/argTypes.ts +0 -316
  246. package/src/client/field-types.ts +0 -187
  247. package/src/client/index.ts +0 -1301
  248. package/src/client/queryBuilder.ts +0 -1100
  249. package/src/client/schema/codegen.ts +0 -500
  250. package/src/client/schema/defineContentType.ts +0 -501
  251. package/src/client/schema/index.ts +0 -169
  252. package/src/client/schema/schemaDrift.ts +0 -574
  253. package/src/client/schema/typedClient.ts +0 -688
  254. package/src/client/schema/types.ts +0 -666
  255. package/src/client/types.ts +0 -723
  256. package/src/client/workflows.ts +0 -141
  257. package/src/client/wrapper.ts +0 -4304
  258. package/src/component/_generated/api.ts +0 -140
  259. package/src/component/_generated/component.ts +0 -5029
  260. package/src/component/_generated/dataModel.ts +0 -60
  261. package/src/component/_generated/server.ts +0 -156
  262. package/src/component/authorization.ts +0 -647
  263. package/src/component/authorizationHooks.ts +0 -668
  264. package/src/component/bulkOperations.ts +0 -687
  265. package/src/component/contentEntries.ts +0 -1976
  266. package/src/component/contentEntryMutations.ts +0 -1223
  267. package/src/component/contentEntryValidation.ts +0 -707
  268. package/src/component/contentLock.ts +0 -550
  269. package/src/component/contentTypeMigration.ts +0 -1064
  270. package/src/component/contentTypeMutations.ts +0 -969
  271. package/src/component/contentTypes.ts +0 -346
  272. package/src/component/convex.config.ts +0 -44
  273. package/src/component/documentTypes.ts +0 -240
  274. package/src/component/eventEmitter.ts +0 -485
  275. package/src/component/exportImport.ts +0 -1169
  276. package/src/component/index.ts +0 -491
  277. package/src/component/lib/deepReferenceResolver.ts +0 -999
  278. package/src/component/lib/errors.ts +0 -816
  279. package/src/component/lib/index.ts +0 -145
  280. package/src/component/lib/mediaReferenceResolver.ts +0 -495
  281. package/src/component/lib/metadataExtractor.ts +0 -792
  282. package/src/component/lib/mutationAuth.ts +0 -199
  283. package/src/component/lib/queries.ts +0 -79
  284. package/src/component/lib/ragContentChunker.ts +0 -1371
  285. package/src/component/lib/referenceResolver.ts +0 -430
  286. package/src/component/lib/slugGenerator.ts +0 -262
  287. package/src/component/lib/slugUniqueness.ts +0 -333
  288. package/src/component/lib/softDelete.ts +0 -44
  289. package/src/component/localeFallbackChain.ts +0 -673
  290. package/src/component/localeFields.ts +0 -896
  291. package/src/component/mediaAssetMutations.ts +0 -725
  292. package/src/component/mediaAssets.ts +0 -932
  293. package/src/component/mediaFolderMutations.ts +0 -1046
  294. package/src/component/mediaUploadMutations.ts +0 -224
  295. package/src/component/mediaVariantMutations.ts +0 -900
  296. package/src/component/mediaVariants.ts +0 -793
  297. package/src/component/ragContentIndexer.ts +0 -1067
  298. package/src/component/rateLimitHooks.ts +0 -572
  299. package/src/component/roles.ts +0 -1360
  300. package/src/component/scheduledPublish.ts +0 -358
  301. package/src/component/schema.ts +0 -617
  302. package/src/component/taxonomies.ts +0 -949
  303. package/src/component/taxonomyMutations.ts +0 -1210
  304. package/src/component/trash.ts +0 -724
  305. package/src/component/userContext.ts +0 -898
  306. package/src/component/validation.ts +0 -1388
  307. package/src/component/validators.ts +0 -949
  308. package/src/component/versionMutations.ts +0 -392
  309. package/src/component/webhookTrigger.ts +0 -1922
  310. package/src/react/index.ts +0 -898
  311. package/src/test.ts +0 -1580
@@ -1,725 +0,0 @@
1
- /**
2
- * Media Asset Mutation Functions
3
- *
4
- * Provides mutation functions for creating, updating, and deleting media assets.
5
- * Media assets are records that link file storage references (storageId) with
6
- * metadata like filename, MIME type, dimensions, and organizational tags.
7
- *
8
- * Upload Flow:
9
- * 1. Client calls generateUploadUrl to get a temporary upload URL
10
- * 2. Client POSTs the file directly to the upload URL
11
- * 3. Client receives a storageId from the upload response
12
- * 4. Client calls createMediaAsset to save the metadata with the storageId
13
- */
14
-
15
- import { mutation, query, type MutationCtx } from "./_generated/server.js";
16
- import type { Id } from "./_generated/dataModel.js";
17
- import { v } from "convex/values";
18
- import {
19
- createMediaAssetArgs,
20
- updateMediaAssetArgs,
21
- mediaItemDoc,
22
- mediaAssetItemValidator,
23
- deleteMediaAssetArgs,
24
- restoreMediaAssetArgs,
25
- mediaAssetReference,
26
- moveMediaAssetsArgs,
27
- moveMediaAssetsResult,
28
- BULK_OPERATION_BATCH_SIZE,
29
- mutationAuthContext,
30
- } from "./validators.js";
31
- import {
32
- emitEvent,
33
- mediaAssetEventType,
34
- MediaAssetEventPayload,
35
- } from "./eventEmitter.js";
36
- import {
37
- mediaFolderNotFound,
38
- mediaFolderDeleted,
39
- mediaAssetNotFound,
40
- mediaAssetDeleted,
41
- mediaAssetNotDeleted,
42
- mediaAssetHasReferences,
43
- mediaAssetCreateFailed,
44
- mediaAssetUpdateFailed,
45
- batchSizeExceeded,
46
- internalError,
47
- } from "./lib/errors.js";
48
- import { requireMutationAuth, withResourceOwner } from "./lib/mutationAuth.js";
49
- import { classifyMimeType } from "./lib/metadataExtractor.js";
50
- import { isDeleted } from "./lib/softDelete.js";
51
-
52
- // =============================================================================
53
- // Delete Media Asset Result Type
54
- // =============================================================================
55
-
56
- const deleteMediaAssetResult = v.object({
57
- ...mediaAssetItemValidator.fields,
58
- _id: v.id("mediaItems"),
59
- _creationTime: v.number(),
60
- deletedAt: v.optional(v.number()),
61
- storageFileDeleted: v.optional(v.boolean()),
62
- });
63
-
64
- // =============================================================================
65
- // Create Media Asset Mutation
66
- // =============================================================================
67
-
68
- export const createMediaAsset = mutation({
69
- args: {
70
- ...createMediaAssetArgs.fields,
71
- _auth: v.optional(mutationAuthContext),
72
- },
73
- returns: mediaItemDoc,
74
- handler: async (ctx, args) => {
75
- const {
76
- storageId,
77
- name,
78
- mimeType,
79
- size,
80
- title,
81
- description,
82
- altText,
83
- parentId,
84
- width,
85
- height,
86
- duration,
87
- metadata,
88
- tags,
89
- createdBy,
90
- _auth,
91
- } = args;
92
-
93
- requireMutationAuth(_auth, "mediaItems", "create");
94
-
95
- // Validate folder exists if provided
96
- if (parentId !== undefined) {
97
- const folder = await ctx.db.get(parentId);
98
- if (!folder) {
99
- throw mediaFolderNotFound((parentId as unknown) as string);
100
- }
101
- if (isDeleted(folder)) {
102
- throw mediaFolderDeleted((parentId as unknown) as string);
103
- }
104
- }
105
-
106
- // Generate searchable text
107
- const searchParts: string[] = [];
108
- searchParts.push(name);
109
- if (title) searchParts.push(title);
110
- if (description) searchParts.push(description);
111
- if (tags && tags.length > 0) searchParts.push(...tags);
112
- const searchText = searchParts.join(" ").trim() || undefined;
113
-
114
- // Compute the path for the asset
115
- let path = "/";
116
- if (parentId) {
117
- const folder = await ctx.db.get(parentId);
118
- if (folder && folder.kind === "folder") {
119
- path = folder.path + name;
120
- } else {
121
- path = "/" + name;
122
- }
123
- } else {
124
- path = "/" + name;
125
- }
126
-
127
- // Create the media asset record
128
- const assetId = await ctx.db.insert("mediaItems", {
129
- kind: "asset",
130
- storageId,
131
- name,
132
- mimeType,
133
- size,
134
- title,
135
- description,
136
- altText,
137
- parentId,
138
- path,
139
- width,
140
- height,
141
- duration,
142
- metadata,
143
- tags,
144
- createdBy,
145
- searchText,
146
- });
147
-
148
- const asset = await ctx.db.get(assetId);
149
- if (!asset) {
150
- throw mediaAssetCreateFailed();
151
- }
152
-
153
- // Get folder path for event
154
- let folderPath: string | undefined;
155
- if (parentId) {
156
- const folder = await ctx.db.get(parentId);
157
- if (folder && folder.kind === "folder") {
158
- folderPath = folder.path;
159
- }
160
- }
161
-
162
- await emitEvent(ctx, {
163
- eventType: mediaAssetEventType("created"),
164
- resourceType: "mediaAsset",
165
- resourceId: (assetId as unknown) as string,
166
- action: "created",
167
- payload: {
168
- name,
169
- mimeType,
170
- type: classifyMimeType(mimeType),
171
- size: size ?? 0,
172
- parentId: (parentId as unknown) as string | undefined,
173
- path: folderPath,
174
- } as MediaAssetEventPayload,
175
- userId: createdBy,
176
- });
177
-
178
- return asset;
179
- },
180
- });
181
-
182
- // =============================================================================
183
- // Update Media Asset Mutation
184
- // =============================================================================
185
-
186
- export const updateMediaAsset = mutation({
187
- args: {
188
- ...updateMediaAssetArgs.fields,
189
- _auth: v.optional(mutationAuthContext),
190
- },
191
- returns: mediaItemDoc,
192
- handler: async (ctx, args) => {
193
- const {
194
- id,
195
- name,
196
- title,
197
- description,
198
- altText,
199
- parentId,
200
- tags,
201
- _auth,
202
- } = args;
203
-
204
- const item = await ctx.db.get(id);
205
- if (!item || item.kind !== "asset") {
206
- throw mediaAssetNotFound((id as unknown) as string);
207
- }
208
- const asset = item;
209
-
210
- if (isDeleted(asset)) {
211
- throw mediaAssetDeleted((id as unknown) as string);
212
- }
213
-
214
- requireMutationAuth(
215
- withResourceOwner(_auth, asset.createdBy),
216
- "mediaItems",
217
- "update",
218
- );
219
-
220
- // Validate folder if provided
221
- if (parentId !== undefined && parentId !== asset.parentId) {
222
- const folder = await ctx.db.get(parentId);
223
- if (!folder || folder.kind !== "folder") {
224
- throw mediaFolderNotFound((parentId as unknown) as string);
225
- }
226
- if (isDeleted(folder)) {
227
- throw mediaFolderDeleted((parentId as unknown) as string);
228
- }
229
- }
230
-
231
- const updates: Record<string, unknown> = {};
232
- if (name !== undefined) updates.name = name;
233
- if (title !== undefined) updates.title = title;
234
- if (description !== undefined) updates.description = description;
235
- if (altText !== undefined) updates.altText = altText;
236
- if (parentId !== undefined) updates.parentId = parentId;
237
- if (tags !== undefined) updates.tags = tags;
238
-
239
- // Regenerate search text if needed
240
- if (name !== undefined || title !== undefined || description !== undefined || tags !== undefined) {
241
- const searchParts: string[] = [];
242
- const effectiveName = name ?? asset.name;
243
- const effectiveTitle = title ?? asset.title;
244
- const effectiveDescription = description ?? asset.description;
245
- const effectiveTags = tags ?? asset.tags;
246
-
247
- searchParts.push(effectiveName);
248
- if (effectiveTitle) searchParts.push(effectiveTitle);
249
- if (effectiveDescription) searchParts.push(effectiveDescription);
250
- if (effectiveTags && effectiveTags.length > 0) searchParts.push(...effectiveTags);
251
-
252
- updates.searchText = searchParts.join(" ").trim() || undefined;
253
- }
254
-
255
- await ctx.db.patch(id, updates);
256
-
257
- const updatedItem = await ctx.db.get(id);
258
- if (!updatedItem || updatedItem.kind !== "asset") {
259
- throw mediaAssetUpdateFailed((id as unknown) as string);
260
- }
261
-
262
- // Emit event
263
- let folderPath: string | undefined;
264
- if (updatedItem.parentId) {
265
- const folder = await ctx.db.get(updatedItem.parentId);
266
- if (folder && folder.kind === "folder") {
267
- folderPath = folder.path;
268
- }
269
- }
270
-
271
- await emitEvent(ctx, {
272
- eventType: mediaAssetEventType("updated"),
273
- resourceType: "mediaAsset",
274
- resourceId: (id as unknown) as string,
275
- action: "updated",
276
- payload: {
277
- name: updatedItem.name,
278
- mimeType: updatedItem.mimeType,
279
- type: classifyMimeType(updatedItem.mimeType),
280
- size: updatedItem.size ?? 0,
281
- parentId: (updatedItem.parentId as unknown) as string | undefined,
282
- path: folderPath,
283
- } as MediaAssetEventPayload,
284
- userId: asset.createdBy,
285
- });
286
-
287
- return updatedItem;
288
- },
289
- });
290
-
291
- // =============================================================================
292
- // Find Media Asset References
293
- // =============================================================================
294
-
295
- export const findMediaAssetReferences = query({
296
- args: {
297
- mediaAssetId: v.id("mediaItems"),
298
- limit: v.optional(v.number()),
299
- },
300
- returns: v.array(mediaAssetReference),
301
- handler: async (ctx, args) => {
302
- const { mediaAssetId, limit = 100 } = args;
303
- const mediaIdStr = (mediaAssetId as unknown) as string;
304
- const references: Array<{
305
- entryId: Id<"contentEntries">;
306
- slug: string;
307
- contentTypeName: string;
308
- fields: string[];
309
- }> = [];
310
-
311
- const contentTypes = await ctx.db.query("contentTypes").collect();
312
- const contentTypeMap = new Map(
313
- contentTypes.map((ct) => [ct._id.toString(), ct]),
314
- );
315
-
316
- const entries = await ctx.db
317
- .query("contentEntries")
318
- .filter((q) => q.eq(q.field("deletedAt"), undefined))
319
- .take(10000);
320
-
321
- for (const entry of entries) {
322
- if (references.length >= limit) break;
323
-
324
- const contentType = contentTypeMap.get(entry.contentTypeId.toString());
325
- if (!contentType) continue;
326
-
327
- const mediaFields = contentType.fields.filter((f) => f.type === "media");
328
- const matchingFields: string[] = [];
329
-
330
- for (const field of mediaFields) {
331
- const fieldValue = (entry.data as Record<string, unknown>)?.[field.name];
332
- if (!fieldValue) continue;
333
-
334
- const isMultiple = (field.options as { multiple?: boolean } | undefined)?.multiple ?? false;
335
- if (isMultiple && Array.isArray(fieldValue)) {
336
- if (fieldValue.includes(mediaIdStr)) {
337
- matchingFields.push(field.name);
338
- }
339
- } else if (fieldValue === mediaIdStr) {
340
- matchingFields.push(field.name);
341
- }
342
- }
343
-
344
- if (matchingFields.length > 0) {
345
- references.push({
346
- entryId: entry._id,
347
- slug: entry.slug,
348
- contentTypeName: contentType.name,
349
- fields: matchingFields,
350
- });
351
- }
352
- }
353
-
354
- return references;
355
- },
356
- });
357
-
358
- // =============================================================================
359
- // Internal helper for reference checking
360
- // =============================================================================
361
-
362
- async function findReferencesInternal(
363
- ctx: MutationCtx,
364
- mediaAssetId: Id<"mediaItems">,
365
- ): Promise<Array<{
366
- entryId: Id<"contentEntries">;
367
- slug: string;
368
- contentTypeName: string;
369
- fields: string[];
370
- }>> {
371
- const mediaIdStr = mediaAssetId.toString();
372
- const references: Array<{
373
- entryId: Id<"contentEntries">;
374
- slug: string;
375
- contentTypeName: string;
376
- fields: string[];
377
- }> = [];
378
-
379
- const contentTypes = await ctx.db.query("contentTypes").collect();
380
- const contentTypeMap = new Map(
381
- contentTypes.map((ct) => [ct._id.toString(), ct]),
382
- );
383
-
384
- const entries = await ctx.db
385
- .query("contentEntries")
386
- .filter((q) => q.eq(q.field("deletedAt"), undefined))
387
- .take(10000);
388
-
389
- for (const entry of entries) {
390
- if (references.length >= 100) break;
391
-
392
- const contentType = contentTypeMap.get(entry.contentTypeId.toString());
393
- if (!contentType) continue;
394
-
395
- const mediaFields = contentType.fields.filter((f: { type: string }) => f.type === "media");
396
- const matchingFields: string[] = [];
397
-
398
- for (const field of mediaFields) {
399
- const fieldValue = (entry.data as Record<string, unknown>)?.[field.name];
400
- if (!fieldValue) continue;
401
-
402
- const isMultiple = (field as { options?: { multiple?: boolean } }).options?.multiple ?? false;
403
- if (isMultiple && Array.isArray(fieldValue)) {
404
- if (fieldValue.includes(mediaIdStr)) {
405
- matchingFields.push(field.name);
406
- }
407
- } else if (fieldValue === mediaIdStr) {
408
- matchingFields.push(field.name);
409
- }
410
- }
411
-
412
- if (matchingFields.length > 0) {
413
- references.push({
414
- entryId: entry._id,
415
- slug: entry.slug,
416
- contentTypeName: contentType.name,
417
- fields: matchingFields,
418
- });
419
- }
420
- }
421
-
422
- return references;
423
- }
424
-
425
- // =============================================================================
426
- // Delete Media Asset Mutation
427
- // =============================================================================
428
-
429
- export const deleteMediaAsset = mutation({
430
- args: {
431
- ...deleteMediaAssetArgs.fields,
432
- _auth: v.optional(mutationAuthContext),
433
- },
434
- returns: deleteMediaAssetResult,
435
- handler: async (ctx, args) => {
436
- const {
437
- id,
438
- deletedBy,
439
- hardDelete = false,
440
- forceDelete = false,
441
- _auth,
442
- } = args;
443
-
444
- const item = await ctx.db.get(id);
445
- if (!item || item.kind !== "asset") {
446
- throw mediaAssetNotFound((id as unknown) as string);
447
- }
448
- const asset = item;
449
-
450
- requireMutationAuth(
451
- withResourceOwner(_auth, asset.createdBy),
452
- "mediaItems",
453
- "delete",
454
- );
455
-
456
- if (!hardDelete && isDeleted(asset)) {
457
- throw mediaAssetDeleted((id as unknown) as string);
458
- }
459
-
460
- // Check for references
461
- if (!forceDelete) {
462
- const references = await findReferencesInternal(ctx, id);
463
- if (references.length > 0) {
464
- throw mediaAssetHasReferences(
465
- (id as unknown) as string,
466
- references.map((r) => ({
467
- type: "contentEntry",
468
- id: (r.entryId as unknown) as string,
469
- name: `${r.contentTypeName}/${r.slug}`,
470
- })),
471
- );
472
- }
473
- }
474
-
475
- // Get folder path for event
476
- let folderPath: string | undefined;
477
- if (asset.parentId) {
478
- const folder = await ctx.db.get(asset.parentId);
479
- if (folder && folder.kind === "folder") {
480
- folderPath = folder.path;
481
- }
482
- }
483
-
484
- if (hardDelete) {
485
- let storageFileDeleted = false;
486
- try {
487
- await ctx.storage.delete(asset.storageId);
488
- storageFileDeleted = true;
489
- } catch (error) {
490
- console.warn(
491
- `Could not delete storage file for asset ${id}:`,
492
- error instanceof Error ? error.message : error,
493
- );
494
- }
495
-
496
- await ctx.db.delete(id);
497
-
498
- await emitEvent(ctx, {
499
- eventType: mediaAssetEventType("deleted"),
500
- resourceType: "mediaAsset",
501
- resourceId: (id as unknown) as string,
502
- action: "deleted",
503
- payload: {
504
- name: asset.name,
505
- mimeType: asset.mimeType,
506
- type: classifyMimeType(asset.mimeType),
507
- size: asset.size ?? 0,
508
- parentId: (asset.parentId as unknown) as string | undefined,
509
- path: folderPath,
510
- } as MediaAssetEventPayload,
511
- userId: deletedBy,
512
- metadata: { hardDelete: true, storageFileDeleted },
513
- });
514
-
515
- return {
516
- ...asset,
517
- deletedAt: Date.now(),
518
- storageFileDeleted,
519
- };
520
- } else {
521
- const now = Date.now();
522
- await ctx.db.patch(id, { deletedAt: now });
523
-
524
- await emitEvent(ctx, {
525
- eventType: mediaAssetEventType("deleted"),
526
- resourceType: "mediaAsset",
527
- resourceId: (id as unknown) as string,
528
- action: "deleted",
529
- payload: {
530
- name: asset.name,
531
- mimeType: asset.mimeType,
532
- type: classifyMimeType(asset.mimeType),
533
- size: asset.size ?? 0,
534
- parentId: (asset.parentId as unknown) as string | undefined,
535
- path: folderPath,
536
- } as MediaAssetEventPayload,
537
- userId: deletedBy,
538
- metadata: { hardDelete: false },
539
- });
540
-
541
- return {
542
- ...asset,
543
- deletedAt: now,
544
- storageFileDeleted: undefined,
545
- };
546
- }
547
- },
548
- });
549
-
550
- // =============================================================================
551
- // Restore Media Asset Mutation
552
- // =============================================================================
553
-
554
- export const restoreMediaAsset = mutation({
555
- args: {
556
- ...restoreMediaAssetArgs.fields,
557
- _auth: v.optional(mutationAuthContext),
558
- },
559
- returns: mediaItemDoc,
560
- handler: async (ctx, args) => {
561
- const { id, restoredBy, _auth } = args;
562
-
563
- const item = await ctx.db.get(id);
564
- if (!item || item.kind !== "asset") {
565
- throw mediaAssetNotFound((id as unknown) as string);
566
- }
567
- const asset = item;
568
-
569
- requireMutationAuth(
570
- withResourceOwner(_auth, asset.createdBy),
571
- "mediaItems",
572
- "update",
573
- );
574
-
575
- if (asset.deletedAt === undefined) {
576
- throw mediaAssetNotDeleted((id as unknown) as string);
577
- }
578
-
579
- await ctx.db.patch(id, { deletedAt: undefined });
580
-
581
- const restoredItem = await ctx.db.get(id);
582
- if (!restoredItem || restoredItem.kind !== "asset") {
583
- throw internalError("Failed to restore media asset");
584
- }
585
-
586
- let folderPath: string | undefined;
587
- if (restoredItem.parentId) {
588
- const folder = await ctx.db.get(restoredItem.parentId);
589
- if (folder && folder.kind === "folder") {
590
- folderPath = folder.path;
591
- }
592
- }
593
-
594
- await emitEvent(ctx, {
595
- eventType: mediaAssetEventType("restored"),
596
- resourceType: "mediaAsset",
597
- resourceId: (id as unknown) as string,
598
- action: "restored",
599
- payload: {
600
- name: restoredItem.name,
601
- mimeType: restoredItem.mimeType,
602
- type: classifyMimeType(restoredItem.mimeType),
603
- size: restoredItem.size ?? 0,
604
- parentId: (restoredItem.parentId as unknown) as string | undefined,
605
- path: folderPath,
606
- } as MediaAssetEventPayload,
607
- userId: restoredBy,
608
- });
609
-
610
- return restoredItem;
611
- },
612
- });
613
-
614
- // =============================================================================
615
- // Move Media Assets Mutation
616
- // =============================================================================
617
-
618
- export const moveMediaAssets = mutation({
619
- args: {
620
- ...moveMediaAssetsArgs.fields,
621
- _auth: v.optional(mutationAuthContext),
622
- },
623
- returns: moveMediaAssetsResult,
624
- handler: async (ctx, args) => {
625
- const { assetIds, targetFolderId, movedBy, _auth } = args;
626
-
627
- requireMutationAuth(_auth, "mediaItems", "update");
628
-
629
- if (assetIds.length > BULK_OPERATION_BATCH_SIZE) {
630
- throw batchSizeExceeded(BULK_OPERATION_BATCH_SIZE, assetIds.length);
631
- }
632
-
633
- if (assetIds.length === 0) {
634
- return {
635
- total: 0,
636
- succeeded: 0,
637
- failed: 0,
638
- targetFolderId,
639
- targetFolderPath: undefined,
640
- results: [],
641
- };
642
- }
643
-
644
- let targetFolderPath: string | undefined;
645
- if (targetFolderId !== undefined) {
646
- const targetFolder = await ctx.db.get(targetFolderId);
647
- if (!targetFolder) {
648
- throw mediaFolderNotFound((targetFolderId as unknown) as string);
649
- }
650
- if (isDeleted(targetFolder)) {
651
- throw mediaFolderDeleted((targetFolderId as unknown) as string);
652
- }
653
- targetFolderPath = targetFolder.path;
654
- }
655
-
656
- const results: Array<{
657
- id: Id<"mediaItems">;
658
- success: boolean;
659
- error?: string;
660
- previousFolderId?: Id<"mediaItems">;
661
- }> = [];
662
-
663
- for (const assetId of assetIds) {
664
- try {
665
- const item = await ctx.db.get(assetId);
666
- if (!item || item.kind !== "asset") {
667
- results.push({ id: assetId, success: false, error: "Asset not found" });
668
- continue;
669
- }
670
-
671
- if (isDeleted(item)) {
672
- results.push({ id: assetId, success: false, error: "Asset has been deleted" });
673
- continue;
674
- }
675
-
676
- if (item.parentId === targetFolderId) {
677
- results.push({ id: assetId, success: true, previousFolderId: item.parentId });
678
- continue;
679
- }
680
-
681
- const previousFolderId = item.parentId;
682
- await ctx.db.patch(assetId, { parentId: targetFolderId });
683
-
684
- await emitEvent(ctx, {
685
- eventType: mediaAssetEventType("updated"),
686
- resourceType: "mediaAsset",
687
- resourceId: (assetId as unknown) as string,
688
- action: "updated",
689
- payload: {
690
- name: item.name,
691
- mimeType: item.mimeType,
692
- type: classifyMimeType(item.mimeType),
693
- size: item.size ?? 0,
694
- parentId: (targetFolderId as unknown) as string | undefined,
695
- path: targetFolderPath,
696
- } as MediaAssetEventPayload,
697
- userId: movedBy,
698
- metadata: {
699
- moveOperation: true,
700
- previousFolderId: (previousFolderId as unknown) as string | undefined,
701
- },
702
- });
703
-
704
- results.push({ id: assetId, success: true, previousFolderId });
705
- } catch (error) {
706
- results.push({
707
- id: assetId,
708
- success: false,
709
- error: error instanceof Error ? error.message : "Unknown error",
710
- });
711
- }
712
- }
713
-
714
- const succeeded = results.filter((r) => r.success).length;
715
-
716
- return {
717
- total: assetIds.length,
718
- succeeded,
719
- failed: assetIds.length - succeeded,
720
- targetFolderId,
721
- targetFolderPath,
722
- results,
723
- };
724
- },
725
- });