convex-cms 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 (379) hide show
  1. package/dist/cli/commands/admin.d.ts +16 -0
  2. package/dist/cli/commands/admin.d.ts.map +1 -0
  3. package/dist/cli/commands/admin.js +88 -0
  4. package/dist/cli/commands/admin.js.map +1 -0
  5. package/dist/cli/index.d.ts +3 -0
  6. package/dist/cli/index.d.ts.map +1 -0
  7. package/dist/cli/index.js +18 -0
  8. package/dist/cli/index.js.map +1 -0
  9. package/dist/cli/utils/detectConvexUrl.d.ts +13 -0
  10. package/dist/cli/utils/detectConvexUrl.d.ts.map +1 -0
  11. package/dist/cli/utils/detectConvexUrl.js +48 -0
  12. package/dist/cli/utils/detectConvexUrl.js.map +1 -0
  13. package/dist/cli/utils/openBrowser.d.ts +7 -0
  14. package/dist/cli/utils/openBrowser.d.ts.map +1 -0
  15. package/dist/cli/utils/openBrowser.js +17 -0
  16. package/dist/cli/utils/openBrowser.js.map +1 -0
  17. package/dist/client/admin-config.d.ts +126 -0
  18. package/dist/client/admin-config.d.ts.map +1 -0
  19. package/dist/client/admin-config.js +117 -0
  20. package/dist/client/admin-config.js.map +1 -0
  21. package/dist/client/adminApi.d.ts +2273 -0
  22. package/dist/client/adminApi.d.ts.map +1 -0
  23. package/dist/client/adminApi.js +716 -0
  24. package/dist/client/adminApi.js.map +1 -0
  25. package/dist/client/agentTools.d.ts +933 -0
  26. package/dist/client/agentTools.d.ts.map +1 -0
  27. package/dist/client/agentTools.js +1004 -0
  28. package/dist/client/agentTools.js.map +1 -0
  29. package/dist/client/argTypes.d.ts +212 -0
  30. package/dist/client/argTypes.d.ts.map +1 -0
  31. package/dist/client/argTypes.js +5 -0
  32. package/dist/client/argTypes.js.map +1 -0
  33. package/dist/client/field-types.d.ts +55 -0
  34. package/dist/client/field-types.d.ts.map +1 -0
  35. package/dist/client/field-types.js +152 -0
  36. package/dist/client/field-types.js.map +1 -0
  37. package/dist/client/index.d.ts +189 -0
  38. package/dist/client/index.d.ts.map +1 -0
  39. package/dist/client/index.js +668 -0
  40. package/dist/client/index.js.map +1 -0
  41. package/dist/client/queryBuilder.d.ts +765 -0
  42. package/dist/client/queryBuilder.d.ts.map +1 -0
  43. package/dist/client/queryBuilder.js +970 -0
  44. package/dist/client/queryBuilder.js.map +1 -0
  45. package/dist/client/schema/codegen.d.ts +128 -0
  46. package/dist/client/schema/codegen.d.ts.map +1 -0
  47. package/dist/client/schema/codegen.js +318 -0
  48. package/dist/client/schema/codegen.js.map +1 -0
  49. package/dist/client/schema/defineContentType.d.ts +221 -0
  50. package/dist/client/schema/defineContentType.d.ts.map +1 -0
  51. package/dist/client/schema/defineContentType.js +380 -0
  52. package/dist/client/schema/defineContentType.js.map +1 -0
  53. package/dist/client/schema/index.d.ts +85 -0
  54. package/dist/client/schema/index.d.ts.map +1 -0
  55. package/dist/client/schema/index.js +92 -0
  56. package/dist/client/schema/index.js.map +1 -0
  57. package/dist/client/schema/schemaDrift.d.ts +199 -0
  58. package/dist/client/schema/schemaDrift.d.ts.map +1 -0
  59. package/dist/client/schema/schemaDrift.js +340 -0
  60. package/dist/client/schema/schemaDrift.js.map +1 -0
  61. package/dist/client/schema/typedClient.d.ts +401 -0
  62. package/dist/client/schema/typedClient.d.ts.map +1 -0
  63. package/dist/client/schema/typedClient.js +269 -0
  64. package/dist/client/schema/typedClient.js.map +1 -0
  65. package/dist/client/schema/types.d.ts +477 -0
  66. package/dist/client/schema/types.d.ts.map +1 -0
  67. package/dist/client/schema/types.js +39 -0
  68. package/dist/client/schema/types.js.map +1 -0
  69. package/dist/client/types.d.ts +449 -0
  70. package/dist/client/types.d.ts.map +1 -0
  71. package/dist/client/types.js +149 -0
  72. package/dist/client/types.js.map +1 -0
  73. package/dist/client/workflows.d.ts +51 -0
  74. package/dist/client/workflows.d.ts.map +1 -0
  75. package/dist/client/workflows.js +103 -0
  76. package/dist/client/workflows.js.map +1 -0
  77. package/dist/client/wrapper.d.ts +2198 -0
  78. package/dist/client/wrapper.d.ts.map +1 -0
  79. package/dist/client/wrapper.js +2651 -0
  80. package/dist/client/wrapper.js.map +1 -0
  81. package/dist/component/_generated/api.d.ts +124 -0
  82. package/dist/component/_generated/api.d.ts.map +1 -0
  83. package/dist/component/_generated/api.js +31 -0
  84. package/dist/component/_generated/api.js.map +1 -0
  85. package/dist/component/_generated/component.d.ts +4321 -0
  86. package/dist/component/_generated/component.d.ts.map +1 -0
  87. package/dist/component/_generated/component.js +11 -0
  88. package/dist/component/_generated/component.js.map +1 -0
  89. package/dist/component/_generated/dataModel.d.ts +46 -0
  90. package/dist/component/_generated/dataModel.d.ts.map +1 -0
  91. package/dist/component/_generated/dataModel.js +11 -0
  92. package/dist/component/_generated/dataModel.js.map +1 -0
  93. package/dist/component/_generated/server.d.ts +121 -0
  94. package/dist/component/_generated/server.d.ts.map +1 -0
  95. package/dist/component/_generated/server.js +78 -0
  96. package/dist/component/_generated/server.js.map +1 -0
  97. package/dist/component/auditLog.d.ts +410 -0
  98. package/dist/component/auditLog.d.ts.map +1 -0
  99. package/dist/component/auditLog.js +607 -0
  100. package/dist/component/auditLog.js.map +1 -0
  101. package/dist/component/authorization.d.ts +323 -0
  102. package/dist/component/authorization.d.ts.map +1 -0
  103. package/dist/component/authorization.js +464 -0
  104. package/dist/component/authorization.js.map +1 -0
  105. package/dist/component/authorizationHooks.d.ts +184 -0
  106. package/dist/component/authorizationHooks.d.ts.map +1 -0
  107. package/dist/component/authorizationHooks.js +521 -0
  108. package/dist/component/authorizationHooks.js.map +1 -0
  109. package/dist/component/bulkOperations.d.ts +200 -0
  110. package/dist/component/bulkOperations.d.ts.map +1 -0
  111. package/dist/component/bulkOperations.js +568 -0
  112. package/dist/component/bulkOperations.js.map +1 -0
  113. package/dist/component/contentEntries.d.ts +719 -0
  114. package/dist/component/contentEntries.d.ts.map +1 -0
  115. package/dist/component/contentEntries.js +1617 -0
  116. package/dist/component/contentEntries.js.map +1 -0
  117. package/dist/component/contentEntryMutations.d.ts +505 -0
  118. package/dist/component/contentEntryMutations.d.ts.map +1 -0
  119. package/dist/component/contentEntryMutations.js +1009 -0
  120. package/dist/component/contentEntryMutations.js.map +1 -0
  121. package/dist/component/contentEntryValidation.d.ts +115 -0
  122. package/dist/component/contentEntryValidation.d.ts.map +1 -0
  123. package/dist/component/contentEntryValidation.js +546 -0
  124. package/dist/component/contentEntryValidation.js.map +1 -0
  125. package/dist/component/contentLock.d.ts +328 -0
  126. package/dist/component/contentLock.d.ts.map +1 -0
  127. package/dist/component/contentLock.js +471 -0
  128. package/dist/component/contentLock.js.map +1 -0
  129. package/dist/component/contentTypeMigration.d.ts +411 -0
  130. package/dist/component/contentTypeMigration.d.ts.map +1 -0
  131. package/dist/component/contentTypeMigration.js +805 -0
  132. package/dist/component/contentTypeMigration.js.map +1 -0
  133. package/dist/component/contentTypeMutations.d.ts +975 -0
  134. package/dist/component/contentTypeMutations.d.ts.map +1 -0
  135. package/dist/component/contentTypeMutations.js +768 -0
  136. package/dist/component/contentTypeMutations.js.map +1 -0
  137. package/dist/component/contentTypes.d.ts +538 -0
  138. package/dist/component/contentTypes.d.ts.map +1 -0
  139. package/dist/component/contentTypes.js +304 -0
  140. package/dist/component/contentTypes.js.map +1 -0
  141. package/dist/component/convex.config.d.ts +42 -0
  142. package/dist/component/convex.config.d.ts.map +1 -0
  143. package/dist/component/convex.config.js +43 -0
  144. package/dist/component/convex.config.js.map +1 -0
  145. package/dist/component/documentTypes.d.ts +186 -0
  146. package/dist/component/documentTypes.d.ts.map +1 -0
  147. package/dist/component/documentTypes.js +23 -0
  148. package/dist/component/documentTypes.js.map +1 -0
  149. package/dist/component/eventEmitter.d.ts +281 -0
  150. package/dist/component/eventEmitter.d.ts.map +1 -0
  151. package/dist/component/eventEmitter.js +300 -0
  152. package/dist/component/eventEmitter.js.map +1 -0
  153. package/dist/component/exportImport.d.ts +1120 -0
  154. package/dist/component/exportImport.d.ts.map +1 -0
  155. package/dist/component/exportImport.js +931 -0
  156. package/dist/component/exportImport.js.map +1 -0
  157. package/dist/component/index.d.ts +28 -0
  158. package/dist/component/index.d.ts.map +1 -0
  159. package/dist/component/index.js +142 -0
  160. package/dist/component/index.js.map +1 -0
  161. package/dist/component/lib/deepReferenceResolver.d.ts +252 -0
  162. package/dist/component/lib/deepReferenceResolver.d.ts.map +1 -0
  163. package/dist/component/lib/deepReferenceResolver.js +601 -0
  164. package/dist/component/lib/deepReferenceResolver.js.map +1 -0
  165. package/dist/component/lib/errors.d.ts +306 -0
  166. package/dist/component/lib/errors.d.ts.map +1 -0
  167. package/dist/component/lib/errors.js +407 -0
  168. package/dist/component/lib/errors.js.map +1 -0
  169. package/dist/component/lib/index.d.ts +10 -0
  170. package/dist/component/lib/index.d.ts.map +1 -0
  171. package/dist/component/lib/index.js +33 -0
  172. package/dist/component/lib/index.js.map +1 -0
  173. package/dist/component/lib/mediaReferenceResolver.d.ts +217 -0
  174. package/dist/component/lib/mediaReferenceResolver.d.ts.map +1 -0
  175. package/dist/component/lib/mediaReferenceResolver.js +326 -0
  176. package/dist/component/lib/mediaReferenceResolver.js.map +1 -0
  177. package/dist/component/lib/metadataExtractor.d.ts +245 -0
  178. package/dist/component/lib/metadataExtractor.d.ts.map +1 -0
  179. package/dist/component/lib/metadataExtractor.js +548 -0
  180. package/dist/component/lib/metadataExtractor.js.map +1 -0
  181. package/dist/component/lib/mutationAuth.d.ts +95 -0
  182. package/dist/component/lib/mutationAuth.d.ts.map +1 -0
  183. package/dist/component/lib/mutationAuth.js +146 -0
  184. package/dist/component/lib/mutationAuth.js.map +1 -0
  185. package/dist/component/lib/queries.d.ts +17 -0
  186. package/dist/component/lib/queries.d.ts.map +1 -0
  187. package/dist/component/lib/queries.js +49 -0
  188. package/dist/component/lib/queries.js.map +1 -0
  189. package/dist/component/lib/ragContentChunker.d.ts +423 -0
  190. package/dist/component/lib/ragContentChunker.d.ts.map +1 -0
  191. package/dist/component/lib/ragContentChunker.js +897 -0
  192. package/dist/component/lib/ragContentChunker.js.map +1 -0
  193. package/dist/component/lib/referenceResolver.d.ts +175 -0
  194. package/dist/component/lib/referenceResolver.d.ts.map +1 -0
  195. package/dist/component/lib/referenceResolver.js +293 -0
  196. package/dist/component/lib/referenceResolver.js.map +1 -0
  197. package/dist/component/lib/slugGenerator.d.ts +71 -0
  198. package/dist/component/lib/slugGenerator.d.ts.map +1 -0
  199. package/dist/component/lib/slugGenerator.js +207 -0
  200. package/dist/component/lib/slugGenerator.js.map +1 -0
  201. package/dist/component/lib/slugUniqueness.d.ts +131 -0
  202. package/dist/component/lib/slugUniqueness.d.ts.map +1 -0
  203. package/dist/component/lib/slugUniqueness.js +229 -0
  204. package/dist/component/lib/slugUniqueness.js.map +1 -0
  205. package/dist/component/lib/softDelete.d.ts +18 -0
  206. package/dist/component/lib/softDelete.d.ts.map +1 -0
  207. package/dist/component/lib/softDelete.js +29 -0
  208. package/dist/component/lib/softDelete.js.map +1 -0
  209. package/dist/component/localeFallbackChain.d.ts +410 -0
  210. package/dist/component/localeFallbackChain.d.ts.map +1 -0
  211. package/dist/component/localeFallbackChain.js +467 -0
  212. package/dist/component/localeFallbackChain.js.map +1 -0
  213. package/dist/component/localeFields.d.ts +508 -0
  214. package/dist/component/localeFields.d.ts.map +1 -0
  215. package/dist/component/localeFields.js +592 -0
  216. package/dist/component/localeFields.js.map +1 -0
  217. package/dist/component/mediaAssetMutations.d.ts +235 -0
  218. package/dist/component/mediaAssetMutations.d.ts.map +1 -0
  219. package/dist/component/mediaAssetMutations.js +558 -0
  220. package/dist/component/mediaAssetMutations.js.map +1 -0
  221. package/dist/component/mediaAssets.d.ts +168 -0
  222. package/dist/component/mediaAssets.d.ts.map +1 -0
  223. package/dist/component/mediaAssets.js +618 -0
  224. package/dist/component/mediaAssets.js.map +1 -0
  225. package/dist/component/mediaFolderMutations.d.ts +642 -0
  226. package/dist/component/mediaFolderMutations.d.ts.map +1 -0
  227. package/dist/component/mediaFolderMutations.js +849 -0
  228. package/dist/component/mediaFolderMutations.js.map +1 -0
  229. package/dist/component/mediaUploadMutations.d.ts +136 -0
  230. package/dist/component/mediaUploadMutations.d.ts.map +1 -0
  231. package/dist/component/mediaUploadMutations.js +205 -0
  232. package/dist/component/mediaUploadMutations.js.map +1 -0
  233. package/dist/component/mediaVariantMutations.d.ts +468 -0
  234. package/dist/component/mediaVariantMutations.d.ts.map +1 -0
  235. package/dist/component/mediaVariantMutations.js +737 -0
  236. package/dist/component/mediaVariantMutations.js.map +1 -0
  237. package/dist/component/mediaVariants.d.ts +525 -0
  238. package/dist/component/mediaVariants.d.ts.map +1 -0
  239. package/dist/component/mediaVariants.js +661 -0
  240. package/dist/component/mediaVariants.js.map +1 -0
  241. package/dist/component/ragContentIndexer.d.ts +595 -0
  242. package/dist/component/ragContentIndexer.d.ts.map +1 -0
  243. package/dist/component/ragContentIndexer.js +794 -0
  244. package/dist/component/ragContentIndexer.js.map +1 -0
  245. package/dist/component/rateLimitHooks.d.ts +266 -0
  246. package/dist/component/rateLimitHooks.d.ts.map +1 -0
  247. package/dist/component/rateLimitHooks.js +412 -0
  248. package/dist/component/rateLimitHooks.js.map +1 -0
  249. package/dist/component/roles.d.ts +649 -0
  250. package/dist/component/roles.d.ts.map +1 -0
  251. package/dist/component/roles.js +884 -0
  252. package/dist/component/roles.js.map +1 -0
  253. package/dist/component/scheduledPublish.d.ts +182 -0
  254. package/dist/component/scheduledPublish.d.ts.map +1 -0
  255. package/dist/component/scheduledPublish.js +304 -0
  256. package/dist/component/scheduledPublish.js.map +1 -0
  257. package/dist/component/schema.d.ts +4114 -0
  258. package/dist/component/schema.d.ts.map +1 -0
  259. package/dist/component/schema.js +469 -0
  260. package/dist/component/schema.js.map +1 -0
  261. package/dist/component/taxonomies.d.ts +476 -0
  262. package/dist/component/taxonomies.d.ts.map +1 -0
  263. package/dist/component/taxonomies.js +785 -0
  264. package/dist/component/taxonomies.js.map +1 -0
  265. package/dist/component/taxonomyMutations.d.ts +206 -0
  266. package/dist/component/taxonomyMutations.d.ts.map +1 -0
  267. package/dist/component/taxonomyMutations.js +1001 -0
  268. package/dist/component/taxonomyMutations.js.map +1 -0
  269. package/dist/component/trash.d.ts +265 -0
  270. package/dist/component/trash.d.ts.map +1 -0
  271. package/dist/component/trash.js +621 -0
  272. package/dist/component/trash.js.map +1 -0
  273. package/dist/component/types.d.ts +4 -0
  274. package/dist/component/types.d.ts.map +1 -0
  275. package/dist/component/types.js +2 -0
  276. package/dist/component/types.js.map +1 -0
  277. package/dist/component/userContext.d.ts +508 -0
  278. package/dist/component/userContext.d.ts.map +1 -0
  279. package/dist/component/userContext.js +615 -0
  280. package/dist/component/userContext.js.map +1 -0
  281. package/dist/component/validation.d.ts +387 -0
  282. package/dist/component/validation.d.ts.map +1 -0
  283. package/dist/component/validation.js +1052 -0
  284. package/dist/component/validation.js.map +1 -0
  285. package/dist/component/validators.d.ts +4645 -0
  286. package/dist/component/validators.d.ts.map +1 -0
  287. package/dist/component/validators.js +641 -0
  288. package/dist/component/validators.js.map +1 -0
  289. package/dist/component/versionMutations.d.ts +216 -0
  290. package/dist/component/versionMutations.d.ts.map +1 -0
  291. package/dist/component/versionMutations.js +321 -0
  292. package/dist/component/versionMutations.js.map +1 -0
  293. package/dist/component/webhookTrigger.d.ts +770 -0
  294. package/dist/component/webhookTrigger.d.ts.map +1 -0
  295. package/dist/component/webhookTrigger.js +1413 -0
  296. package/dist/component/webhookTrigger.js.map +1 -0
  297. package/dist/react/index.d.ts +316 -0
  298. package/dist/react/index.d.ts.map +1 -0
  299. package/dist/react/index.js +558 -0
  300. package/dist/react/index.js.map +1 -0
  301. package/dist/test.d.ts +2230 -0
  302. package/dist/test.d.ts.map +1 -0
  303. package/dist/test.js +1107 -0
  304. package/dist/test.js.map +1 -0
  305. package/package.json +95 -0
  306. package/src/cli/commands/admin.ts +104 -0
  307. package/src/cli/index.ts +21 -0
  308. package/src/cli/utils/detectConvexUrl.ts +54 -0
  309. package/src/cli/utils/openBrowser.ts +16 -0
  310. package/src/client/admin-config.ts +138 -0
  311. package/src/client/adminApi.ts +942 -0
  312. package/src/client/agentTools.ts +1311 -0
  313. package/src/client/argTypes.ts +316 -0
  314. package/src/client/field-types.ts +187 -0
  315. package/src/client/index.ts +1301 -0
  316. package/src/client/queryBuilder.ts +1100 -0
  317. package/src/client/schema/codegen.ts +500 -0
  318. package/src/client/schema/defineContentType.ts +501 -0
  319. package/src/client/schema/index.ts +169 -0
  320. package/src/client/schema/schemaDrift.ts +574 -0
  321. package/src/client/schema/typedClient.ts +688 -0
  322. package/src/client/schema/types.ts +666 -0
  323. package/src/client/types.ts +723 -0
  324. package/src/client/workflows.ts +141 -0
  325. package/src/client/wrapper.ts +4304 -0
  326. package/src/component/_generated/api.ts +140 -0
  327. package/src/component/_generated/component.ts +5029 -0
  328. package/src/component/_generated/dataModel.ts +60 -0
  329. package/src/component/_generated/server.ts +156 -0
  330. package/src/component/authorization.ts +647 -0
  331. package/src/component/authorizationHooks.ts +668 -0
  332. package/src/component/bulkOperations.ts +687 -0
  333. package/src/component/contentEntries.ts +1976 -0
  334. package/src/component/contentEntryMutations.ts +1223 -0
  335. package/src/component/contentEntryValidation.ts +707 -0
  336. package/src/component/contentLock.ts +550 -0
  337. package/src/component/contentTypeMigration.ts +1064 -0
  338. package/src/component/contentTypeMutations.ts +969 -0
  339. package/src/component/contentTypes.ts +346 -0
  340. package/src/component/convex.config.ts +44 -0
  341. package/src/component/documentTypes.ts +240 -0
  342. package/src/component/eventEmitter.ts +485 -0
  343. package/src/component/exportImport.ts +1169 -0
  344. package/src/component/index.ts +491 -0
  345. package/src/component/lib/deepReferenceResolver.ts +999 -0
  346. package/src/component/lib/errors.ts +816 -0
  347. package/src/component/lib/index.ts +145 -0
  348. package/src/component/lib/mediaReferenceResolver.ts +495 -0
  349. package/src/component/lib/metadataExtractor.ts +792 -0
  350. package/src/component/lib/mutationAuth.ts +199 -0
  351. package/src/component/lib/queries.ts +79 -0
  352. package/src/component/lib/ragContentChunker.ts +1371 -0
  353. package/src/component/lib/referenceResolver.ts +430 -0
  354. package/src/component/lib/slugGenerator.ts +262 -0
  355. package/src/component/lib/slugUniqueness.ts +333 -0
  356. package/src/component/lib/softDelete.ts +44 -0
  357. package/src/component/localeFallbackChain.ts +673 -0
  358. package/src/component/localeFields.ts +896 -0
  359. package/src/component/mediaAssetMutations.ts +725 -0
  360. package/src/component/mediaAssets.ts +932 -0
  361. package/src/component/mediaFolderMutations.ts +1046 -0
  362. package/src/component/mediaUploadMutations.ts +224 -0
  363. package/src/component/mediaVariantMutations.ts +900 -0
  364. package/src/component/mediaVariants.ts +793 -0
  365. package/src/component/ragContentIndexer.ts +1067 -0
  366. package/src/component/rateLimitHooks.ts +572 -0
  367. package/src/component/roles.ts +1360 -0
  368. package/src/component/scheduledPublish.ts +358 -0
  369. package/src/component/schema.ts +617 -0
  370. package/src/component/taxonomies.ts +949 -0
  371. package/src/component/taxonomyMutations.ts +1210 -0
  372. package/src/component/trash.ts +724 -0
  373. package/src/component/userContext.ts +898 -0
  374. package/src/component/validation.ts +1388 -0
  375. package/src/component/validators.ts +949 -0
  376. package/src/component/versionMutations.ts +392 -0
  377. package/src/component/webhookTrigger.ts +1922 -0
  378. package/src/react/index.ts +898 -0
  379. package/src/test.ts +1580 -0
@@ -0,0 +1,725 @@
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
+ });