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,942 @@
1
+ /**
2
+ * Admin API Helper for Convex CMS
3
+ *
4
+ * This module provides the `defineAdminAPI` function that creates typed
5
+ * Convex functions for the admin UI to call. Users export these functions
6
+ * from their `convex/` directory, which the admin UI then calls.
7
+ *
8
+ * This follows the standard Convex component pattern used by:
9
+ * - @convex-dev/agent's `definePlaygroundAPI`
10
+ * - template-component's `exposeApi`
11
+ * - @dodopayments/convex's class-based API
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * // convex/admin.ts
16
+ * import { defineAdminAPI } from "@convex-cms/core";
17
+ * import { components } from "./_generated/api";
18
+ *
19
+ * export const {
20
+ * contentTypes,
21
+ * entries,
22
+ * media,
23
+ * stats,
24
+ * } = defineAdminAPI(components.convexCms, {
25
+ * auth: async (ctx, operation) => {
26
+ * // Optional: validate user has admin access
27
+ * const identity = await ctx.auth.getUserIdentity();
28
+ * if (!identity) throw new Error("Unauthorized");
29
+ * return identity.subject;
30
+ * },
31
+ * });
32
+ * ```
33
+ */
34
+
35
+ import {
36
+ queryGeneric,
37
+ mutationGeneric,
38
+ type Auth,
39
+ } from "convex/server";
40
+ import { v } from "convex/values";
41
+ import type { ComponentApi } from "../component/_generated/component.js";
42
+ import type { Id } from "../component/_generated/dataModel.js";
43
+ import {
44
+ fieldDefinitionValidator,
45
+ contentStatusValidator,
46
+ mediaTypeValidator,
47
+ } from "../component/schema.js";
48
+
49
+ // =============================================================================
50
+ // Types
51
+ // =============================================================================
52
+
53
+ /**
54
+ * Operation context passed to the auth callback.
55
+ */
56
+ export type AdminOperation =
57
+ | { type: "contentTypes.list" }
58
+ | { type: "contentTypes.get"; id: string }
59
+ | { type: "contentTypes.create" }
60
+ | { type: "contentTypes.update"; id: string }
61
+ | { type: "contentTypes.delete"; id: string }
62
+ | { type: "entries.list"; contentTypeId: string }
63
+ | { type: "entries.get"; id: string }
64
+ | { type: "entries.create"; contentTypeId: string }
65
+ | { type: "entries.update"; id: string }
66
+ | { type: "entries.publish"; id: string }
67
+ | { type: "entries.unpublish"; id: string }
68
+ | { type: "entries.delete"; id: string }
69
+ | { type: "entries.duplicate"; id: string }
70
+ | { type: "entries.schedule"; id: string }
71
+ | { type: "entries.cancelSchedule"; id: string }
72
+ | { type: "entries.getScheduled" }
73
+ | { type: "media.assets.list" }
74
+ | { type: "media.assets.get"; id: string }
75
+ | { type: "media.assets.create" }
76
+ | { type: "media.assets.update"; id: string }
77
+ | { type: "media.assets.delete"; id: string }
78
+ | { type: "media.assets.restore"; id: string }
79
+ | { type: "media.assets.move" }
80
+ | { type: "media.folders.list" }
81
+ | { type: "media.folders.get"; id: string }
82
+ | { type: "media.folders.getTree" }
83
+ | { type: "media.folders.create" }
84
+ | { type: "media.folders.update"; id: string }
85
+ | { type: "media.folders.move"; id: string }
86
+ | { type: "media.folders.delete"; id: string }
87
+ | { type: "media.folders.restore"; id: string }
88
+ | { type: "media.generateUploadUrl" }
89
+ | { type: "stats.getDashboardStats" };
90
+
91
+ /**
92
+ * Options for configuring the admin API.
93
+ */
94
+ export interface AdminApiOptions {
95
+ /**
96
+ * Optional authentication callback.
97
+ *
98
+ * Called before each operation to validate access. Should throw if
99
+ * unauthorized. Returns the authenticated user's ID (or null for anonymous).
100
+ *
101
+ * If not provided, all operations are allowed (useful for development).
102
+ *
103
+ * @example
104
+ * ```typescript
105
+ * auth: async (ctx, operation) => {
106
+ * const identity = await ctx.auth.getUserIdentity();
107
+ * if (!identity) throw new Error("Unauthorized");
108
+ * // Could also check operation.type for fine-grained access control
109
+ * return identity.subject;
110
+ * }
111
+ * ```
112
+ */
113
+ auth?: (
114
+ ctx: { auth: Auth },
115
+ operation: AdminOperation
116
+ ) => Promise<string | null>;
117
+ }
118
+
119
+ // =============================================================================
120
+ // Validators (reused across functions)
121
+ // =============================================================================
122
+
123
+ const paginationOptsValidator = v.object({
124
+ numItems: v.number(),
125
+ cursor: v.union(v.string(), v.null()),
126
+ });
127
+
128
+ // =============================================================================
129
+ // defineAdminAPI
130
+ // =============================================================================
131
+
132
+ /**
133
+ * Creates typed Convex functions for the CMS admin UI.
134
+ *
135
+ * This function returns an object containing query and mutation functions
136
+ * that the admin UI calls. Users export these from their `convex/` directory.
137
+ *
138
+ * @param component - The CMS component API from `components.convexCms`
139
+ * @param options - Optional configuration including auth callback
140
+ * @returns Object with namespaced admin functions
141
+ *
142
+ * @example
143
+ * ```typescript
144
+ * // convex/admin.ts
145
+ * import { defineAdminAPI } from "@convex-cms/core";
146
+ * import { components } from "./_generated/api";
147
+ *
148
+ * export const { contentTypes, entries, media, stats } = defineAdminAPI(
149
+ * components.convexCms
150
+ * );
151
+ * ```
152
+ */
153
+ export function defineAdminAPI(
154
+ component: ComponentApi,
155
+ options: AdminApiOptions = {}
156
+ ) {
157
+ const { auth } = options;
158
+
159
+ // Helper to run auth check if configured
160
+ const checkAuth = async (
161
+ ctx: { auth: Auth },
162
+ operation: AdminOperation
163
+ ): Promise<string | null> => {
164
+ if (auth) {
165
+ return await auth(ctx, operation);
166
+ }
167
+ return null;
168
+ };
169
+
170
+ return {
171
+ // =========================================================================
172
+ // Content Types
173
+ // =========================================================================
174
+ contentTypes: {
175
+ list: queryGeneric({
176
+ args: {
177
+ isActive: v.optional(v.boolean()),
178
+ },
179
+ returns: v.any(),
180
+ handler: async (ctx, args) => {
181
+ await checkAuth(ctx, { type: "contentTypes.list" });
182
+ // Returns paginated result: { page, continueCursor, isDone }
183
+ return await ctx.runQuery(component.contentTypes.list, {
184
+ isActive: args.isActive,
185
+ });
186
+ },
187
+ }),
188
+
189
+ get: queryGeneric({
190
+ args: {
191
+ id: v.optional(v.string()),
192
+ name: v.optional(v.string()),
193
+ },
194
+ returns: v.union(v.any(), v.null()),
195
+ handler: async (ctx, args) => {
196
+ await checkAuth(ctx, { type: "contentTypes.get", id: args.id ?? "" });
197
+ return await ctx.runQuery(component.contentTypes.get, {
198
+ id: args.id,
199
+ name: args.name,
200
+ });
201
+ },
202
+ }),
203
+
204
+ create: mutationGeneric({
205
+ args: {
206
+ name: v.string(),
207
+ displayName: v.string(),
208
+ fields: v.array(fieldDefinitionValidator),
209
+ description: v.optional(v.string()),
210
+ icon: v.optional(v.string()),
211
+ singleton: v.optional(v.boolean()),
212
+ slugField: v.optional(v.string()),
213
+ titleField: v.optional(v.string()),
214
+ sortOrder: v.optional(v.number()),
215
+ /** Required for audit tracking - the user ID creating this content type */
216
+ createdBy: v.string(),
217
+ },
218
+ returns: v.any(),
219
+ handler: async (ctx, args) => {
220
+ await checkAuth(ctx, { type: "contentTypes.create" });
221
+ return await ctx.runMutation(
222
+ component.contentTypeMutations.createContentType,
223
+ {
224
+ name: args.name,
225
+ displayName: args.displayName,
226
+ fields: args.fields,
227
+ description: args.description,
228
+ icon: args.icon,
229
+ singleton: args.singleton,
230
+ slugField: args.slugField,
231
+ titleField: args.titleField,
232
+ sortOrder: args.sortOrder,
233
+ createdBy: args.createdBy,
234
+ }
235
+ );
236
+ },
237
+ }),
238
+
239
+ update: mutationGeneric({
240
+ args: {
241
+ id: v.string(),
242
+ displayName: v.optional(v.string()),
243
+ fields: v.optional(v.array(fieldDefinitionValidator)),
244
+ description: v.optional(v.string()),
245
+ icon: v.optional(v.string()),
246
+ singleton: v.optional(v.boolean()),
247
+ slugField: v.optional(v.string()),
248
+ titleField: v.optional(v.string()),
249
+ sortOrder: v.optional(v.number()),
250
+ isActive: v.optional(v.boolean()),
251
+ updatedBy: v.optional(v.string()),
252
+ },
253
+ returns: v.any(),
254
+ handler: async (ctx, args) => {
255
+ await checkAuth(ctx, { type: "contentTypes.update", id: args.id });
256
+ return await ctx.runMutation(
257
+ component.contentTypeMutations.updateContentType,
258
+ {
259
+ id: args.id,
260
+ displayName: args.displayName,
261
+ fields: args.fields,
262
+ description: args.description,
263
+ icon: args.icon,
264
+ singleton: args.singleton,
265
+ slugField: args.slugField,
266
+ titleField: args.titleField,
267
+ sortOrder: args.sortOrder,
268
+ isActive: args.isActive,
269
+ updatedBy: args.updatedBy,
270
+ }
271
+ );
272
+ },
273
+ }),
274
+
275
+ delete: mutationGeneric({
276
+ args: {
277
+ id: v.string(),
278
+ cascade: v.optional(v.boolean()),
279
+ hardDelete: v.optional(v.boolean()),
280
+ deletedBy: v.optional(v.string()),
281
+ },
282
+ returns: v.any(),
283
+ handler: async (ctx, args) => {
284
+ await checkAuth(ctx, { type: "contentTypes.delete", id: args.id });
285
+ return await ctx.runMutation(
286
+ component.contentTypeMutations.deleteContentType,
287
+ {
288
+ id: args.id ,
289
+ cascade: args.cascade,
290
+ hardDelete: args.hardDelete,
291
+ deletedBy: args.deletedBy,
292
+ }
293
+ );
294
+ },
295
+ }),
296
+ },
297
+
298
+ // =========================================================================
299
+ // Content Entries
300
+ // =========================================================================
301
+ entries: {
302
+ list: queryGeneric({
303
+ args: {
304
+ contentTypeId: v.optional(v.string()),
305
+ status: v.optional(contentStatusValidator),
306
+ search: v.optional(v.string()),
307
+ locale: v.optional(v.string()),
308
+ paginationOpts: paginationOptsValidator,
309
+ },
310
+ returns: v.any(),
311
+ handler: async (ctx, args) => {
312
+ await checkAuth(ctx, {
313
+ type: "entries.list",
314
+ contentTypeId: args.contentTypeId ?? "",
315
+ });
316
+ return await ctx.runQuery(component.contentEntries.list, {
317
+ contentTypeId: args.contentTypeId ,
318
+ status: args.status,
319
+ search: args.search,
320
+ locale: args.locale,
321
+ paginationOpts: args.paginationOpts,
322
+ });
323
+ },
324
+ }),
325
+
326
+ get: queryGeneric({
327
+ args: {
328
+ id: v.string(),
329
+ },
330
+ returns: v.union(v.any(), v.null()),
331
+ handler: async (ctx, args) => {
332
+ await checkAuth(ctx, { type: "entries.get", id: args.id });
333
+ return await ctx.runQuery(component.contentEntries.get, {
334
+ id: args.id ,
335
+ });
336
+ },
337
+ }),
338
+
339
+ create: mutationGeneric({
340
+ args: {
341
+ contentTypeId: v.string(),
342
+ data: v.any(),
343
+ slug: v.optional(v.string()),
344
+ status: v.optional(contentStatusValidator),
345
+ locale: v.optional(v.string()),
346
+ primaryEntryId: v.optional(v.string()),
347
+ createdBy: v.optional(v.string()),
348
+ },
349
+ returns: v.any(),
350
+ handler: async (ctx, args) => {
351
+ await checkAuth(ctx, {
352
+ type: "entries.create",
353
+ contentTypeId: args.contentTypeId,
354
+ });
355
+ return await ctx.runMutation(
356
+ component.contentEntryMutations.createEntry,
357
+ {
358
+ contentTypeId: args.contentTypeId ,
359
+ data: args.data,
360
+ slug: args.slug,
361
+ status: args.status,
362
+ locale: args.locale,
363
+ primaryEntryId: args.primaryEntryId ,
364
+ createdBy: args.createdBy,
365
+ }
366
+ );
367
+ },
368
+ }),
369
+
370
+ update: mutationGeneric({
371
+ args: {
372
+ id: v.string(),
373
+ data: v.optional(v.any()),
374
+ slug: v.optional(v.string()),
375
+ status: v.optional(contentStatusValidator),
376
+ scheduledPublishAt: v.optional(v.number()),
377
+ updatedBy: v.optional(v.string()),
378
+ regenerateSlug: v.optional(v.boolean()),
379
+ },
380
+ returns: v.any(),
381
+ handler: async (ctx, args) => {
382
+ await checkAuth(ctx, { type: "entries.update", id: args.id });
383
+ return await ctx.runMutation(
384
+ component.contentEntryMutations.updateEntry,
385
+ {
386
+ id: args.id ,
387
+ data: args.data,
388
+ slug: args.slug,
389
+ status: args.status,
390
+ scheduledPublishAt: args.scheduledPublishAt,
391
+ updatedBy: args.updatedBy,
392
+ regenerateSlug: args.regenerateSlug,
393
+ }
394
+ );
395
+ },
396
+ }),
397
+
398
+ publish: mutationGeneric({
399
+ args: {
400
+ id: v.string(),
401
+ changeDescription: v.optional(v.string()),
402
+ updatedBy: v.optional(v.string()),
403
+ },
404
+ returns: v.any(),
405
+ handler: async (ctx, args) => {
406
+ await checkAuth(ctx, { type: "entries.publish", id: args.id });
407
+ return await ctx.runMutation(
408
+ component.contentEntryMutations.publishEntry,
409
+ {
410
+ id: args.id ,
411
+ changeDescription: args.changeDescription,
412
+ updatedBy: args.updatedBy,
413
+ }
414
+ );
415
+ },
416
+ }),
417
+
418
+ unpublish: mutationGeneric({
419
+ args: {
420
+ id: v.string(),
421
+ updatedBy: v.optional(v.string()),
422
+ },
423
+ returns: v.any(),
424
+ handler: async (ctx, args) => {
425
+ await checkAuth(ctx, { type: "entries.unpublish", id: args.id });
426
+ return await ctx.runMutation(
427
+ component.contentEntryMutations.unpublishEntry,
428
+ {
429
+ id: args.id ,
430
+ updatedBy: args.updatedBy,
431
+ }
432
+ );
433
+ },
434
+ }),
435
+
436
+ delete: mutationGeneric({
437
+ args: {
438
+ id: v.string(),
439
+ hardDelete: v.optional(v.boolean()),
440
+ deletedBy: v.optional(v.string()),
441
+ },
442
+ returns: v.any(),
443
+ handler: async (ctx, args) => {
444
+ await checkAuth(ctx, { type: "entries.delete", id: args.id });
445
+ return await ctx.runMutation(
446
+ component.contentEntryMutations.deleteEntry,
447
+ {
448
+ id: args.id ,
449
+ hardDelete: args.hardDelete,
450
+ deletedBy: args.deletedBy,
451
+ }
452
+ );
453
+ },
454
+ }),
455
+
456
+ duplicate: mutationGeneric({
457
+ args: {
458
+ id: v.string(),
459
+ copyMediaReferences: v.optional(v.boolean()),
460
+ createdBy: v.optional(v.string()),
461
+ },
462
+ returns: v.any(),
463
+ handler: async (ctx, args) => {
464
+ await checkAuth(ctx, { type: "entries.duplicate", id: args.id });
465
+ return await ctx.runMutation(
466
+ component.contentEntryMutations.duplicateEntry,
467
+ {
468
+ sourceEntryId: args.id ,
469
+ copyMediaReferences: args.copyMediaReferences,
470
+ createdBy: args.createdBy,
471
+ }
472
+ );
473
+ },
474
+ }),
475
+
476
+ schedule: mutationGeneric({
477
+ args: {
478
+ id: v.string(),
479
+ publishAt: v.number(),
480
+ updatedBy: v.optional(v.string()),
481
+ },
482
+ returns: v.any(),
483
+ handler: async (ctx, args) => {
484
+ await checkAuth(ctx, { type: "entries.schedule", id: args.id });
485
+ return await ctx.runMutation(
486
+ component.scheduledPublish.scheduleEntry,
487
+ {
488
+ id: args.id ,
489
+ publishAt: args.publishAt,
490
+ updatedBy: args.updatedBy,
491
+ }
492
+ );
493
+ },
494
+ }),
495
+
496
+ cancelSchedule: mutationGeneric({
497
+ args: {
498
+ id: v.string(),
499
+ updatedBy: v.optional(v.string()),
500
+ },
501
+ returns: v.any(),
502
+ handler: async (ctx, args) => {
503
+ await checkAuth(ctx, { type: "entries.cancelSchedule", id: args.id });
504
+ return await ctx.runMutation(
505
+ component.scheduledPublish.cancelScheduledPublish,
506
+ {
507
+ id: args.id ,
508
+ updatedBy: args.updatedBy,
509
+ }
510
+ );
511
+ },
512
+ }),
513
+
514
+ getScheduled: queryGeneric({
515
+ args: {},
516
+ returns: v.array(v.any()),
517
+ handler: async (ctx) => {
518
+ await checkAuth(ctx, { type: "entries.getScheduled" });
519
+ return await ctx.runQuery(
520
+ component.scheduledPublish.getScheduledEntries,
521
+ {}
522
+ );
523
+ },
524
+ }),
525
+ },
526
+
527
+ // =========================================================================
528
+ // Media (Assets and Folders)
529
+ // =========================================================================
530
+ media: {
531
+ // --- Assets ---
532
+ listAssets: queryGeneric({
533
+ args: {
534
+ folderId: v.optional(v.string()),
535
+ type: v.optional(mediaTypeValidator),
536
+ search: v.optional(v.string()),
537
+ paginationOpts: paginationOptsValidator,
538
+ },
539
+ returns: v.any(),
540
+ handler: async (ctx, args) => {
541
+ await checkAuth(ctx, { type: "media.assets.list" });
542
+ return await ctx.runQuery(component.mediaAssets.list, {
543
+ folderId: args.folderId ,
544
+ type: args.type,
545
+ search: args.search,
546
+ paginationOpts: args.paginationOpts,
547
+ });
548
+ },
549
+ }),
550
+
551
+ getAsset: queryGeneric({
552
+ args: {
553
+ id: v.string(),
554
+ },
555
+ returns: v.union(v.any(), v.null()),
556
+ handler: async (ctx, args) => {
557
+ await checkAuth(ctx, { type: "media.assets.get", id: args.id });
558
+ return await ctx.runQuery(component.mediaAssets.get, {
559
+ id: args.id ,
560
+ });
561
+ },
562
+ }),
563
+
564
+ createAsset: mutationGeneric({
565
+ args: {
566
+ storageId: v.string(),
567
+ name: v.string(),
568
+ mimeType: v.string(),
569
+ size: v.number(),
570
+ parentId: v.optional(v.string()),
571
+ width: v.optional(v.number()),
572
+ height: v.optional(v.number()),
573
+ title: v.optional(v.string()),
574
+ description: v.optional(v.string()),
575
+ altText: v.optional(v.string()),
576
+ createdBy: v.optional(v.string()),
577
+ },
578
+ returns: v.any(),
579
+ handler: async (ctx, args) => {
580
+ await checkAuth(ctx, { type: "media.assets.create" });
581
+ return await ctx.runMutation(
582
+ component.mediaAssetMutations.createMediaAsset,
583
+ {
584
+ storageId: args.storageId as Id<"_storage">,
585
+ name: args.name,
586
+ mimeType: args.mimeType,
587
+ size: args.size,
588
+ parentId: args.parentId as Id<"mediaItems"> | undefined,
589
+ width: args.width,
590
+ height: args.height,
591
+ title: args.title,
592
+ description: args.description,
593
+ altText: args.altText,
594
+ createdBy: args.createdBy,
595
+ }
596
+ );
597
+ },
598
+ }),
599
+
600
+ updateAsset: mutationGeneric({
601
+ args: {
602
+ id: v.string(),
603
+ name: v.optional(v.string()),
604
+ title: v.optional(v.string()),
605
+ description: v.optional(v.string()),
606
+ altText: v.optional(v.string()),
607
+ parentId: v.optional(v.string()),
608
+ tags: v.optional(v.array(v.string())),
609
+ },
610
+ returns: v.any(),
611
+ handler: async (ctx, args) => {
612
+ await checkAuth(ctx, { type: "media.assets.update", id: args.id });
613
+ return await ctx.runMutation(
614
+ component.mediaAssetMutations.updateMediaAsset,
615
+ {
616
+ id: args.id as Id<"mediaItems">,
617
+ name: args.name,
618
+ title: args.title,
619
+ description: args.description,
620
+ altText: args.altText,
621
+ parentId: args.parentId as Id<"mediaItems"> | undefined,
622
+ tags: args.tags,
623
+ }
624
+ );
625
+ },
626
+ }),
627
+
628
+ deleteAsset: mutationGeneric({
629
+ args: {
630
+ id: v.string(),
631
+ hardDelete: v.optional(v.boolean()),
632
+ deletedBy: v.optional(v.string()),
633
+ },
634
+ returns: v.any(),
635
+ handler: async (ctx, args) => {
636
+ await checkAuth(ctx, { type: "media.assets.delete", id: args.id });
637
+ return await ctx.runMutation(
638
+ component.mediaAssetMutations.deleteMediaAsset,
639
+ {
640
+ id: args.id ,
641
+ hardDelete: args.hardDelete,
642
+ deletedBy: args.deletedBy,
643
+ }
644
+ );
645
+ },
646
+ }),
647
+
648
+ restoreAsset: mutationGeneric({
649
+ args: {
650
+ id: v.string(),
651
+ },
652
+ returns: v.any(),
653
+ handler: async (ctx, args) => {
654
+ await checkAuth(ctx, { type: "media.assets.restore", id: args.id });
655
+ return await ctx.runMutation(
656
+ component.mediaAssetMutations.restoreMediaAsset,
657
+ {
658
+ id: args.id ,
659
+ }
660
+ );
661
+ },
662
+ }),
663
+
664
+ moveAssets: mutationGeneric({
665
+ args: {
666
+ assetIds: v.array(v.string()),
667
+ targetFolderId: v.optional(v.string()),
668
+ },
669
+ returns: v.any(),
670
+ handler: async (ctx, args) => {
671
+ await checkAuth(ctx, { type: "media.assets.move" });
672
+ return await ctx.runMutation(
673
+ component.mediaAssetMutations.moveMediaAssets,
674
+ {
675
+ assetIds: args.assetIds ,
676
+ targetFolderId: args.targetFolderId ,
677
+ }
678
+ );
679
+ },
680
+ }),
681
+
682
+ // --- Folders ---
683
+ listFolders: queryGeneric({
684
+ args: {
685
+ parentId: v.optional(v.string()),
686
+ },
687
+ returns: v.array(v.any()),
688
+ handler: async (ctx, args) => {
689
+ await checkAuth(ctx, { type: "media.folders.list" });
690
+ return await ctx.runQuery(
691
+ component.mediaFolderMutations.listMediaFolders,
692
+ {
693
+ parentId: args.parentId ,
694
+ }
695
+ );
696
+ },
697
+ }),
698
+
699
+ getFolder: queryGeneric({
700
+ args: {
701
+ id: v.string(),
702
+ },
703
+ returns: v.union(v.any(), v.null()),
704
+ handler: async (ctx, args) => {
705
+ await checkAuth(ctx, { type: "media.folders.get", id: args.id });
706
+ return await ctx.runQuery(
707
+ component.mediaFolderMutations.getMediaFolder,
708
+ {
709
+ id: args.id ,
710
+ }
711
+ );
712
+ },
713
+ }),
714
+
715
+ getFolderTree: queryGeneric({
716
+ args: {},
717
+ returns: v.array(v.any()),
718
+ handler: async (ctx) => {
719
+ await checkAuth(ctx, { type: "media.folders.getTree" });
720
+ return await ctx.runQuery(
721
+ component.mediaFolderMutations.getFolderTree,
722
+ {}
723
+ );
724
+ },
725
+ }),
726
+
727
+ createFolder: mutationGeneric({
728
+ args: {
729
+ name: v.string(),
730
+ parentId: v.optional(v.string()),
731
+ description: v.optional(v.string()),
732
+ createdBy: v.optional(v.string()),
733
+ },
734
+ returns: v.any(),
735
+ handler: async (ctx, args) => {
736
+ await checkAuth(ctx, { type: "media.folders.create" });
737
+ return await ctx.runMutation(
738
+ component.mediaFolderMutations.createMediaFolder,
739
+ {
740
+ name: args.name,
741
+ parentId: args.parentId ,
742
+ description: args.description,
743
+ createdBy: args.createdBy,
744
+ }
745
+ );
746
+ },
747
+ }),
748
+
749
+ updateFolder: mutationGeneric({
750
+ args: {
751
+ id: v.string(),
752
+ name: v.optional(v.string()),
753
+ description: v.optional(v.string()),
754
+ sortOrder: v.optional(v.number()),
755
+ },
756
+ returns: v.any(),
757
+ handler: async (ctx, args) => {
758
+ await checkAuth(ctx, { type: "media.folders.update", id: args.id });
759
+ return await ctx.runMutation(
760
+ component.mediaFolderMutations.updateMediaFolder,
761
+ {
762
+ id: args.id ,
763
+ name: args.name,
764
+ description: args.description,
765
+ sortOrder: args.sortOrder,
766
+ }
767
+ );
768
+ },
769
+ }),
770
+
771
+ moveFolder: mutationGeneric({
772
+ args: {
773
+ id: v.string(),
774
+ newParentId: v.optional(v.string()),
775
+ },
776
+ returns: v.any(),
777
+ handler: async (ctx, args) => {
778
+ await checkAuth(ctx, { type: "media.folders.move", id: args.id });
779
+ return await ctx.runMutation(
780
+ component.mediaFolderMutations.moveMediaFolder,
781
+ {
782
+ id: args.id ,
783
+ newParentId: args.newParentId ,
784
+ }
785
+ );
786
+ },
787
+ }),
788
+
789
+ deleteFolder: mutationGeneric({
790
+ args: {
791
+ id: v.string(),
792
+ recursive: v.optional(v.boolean()),
793
+ hardDelete: v.optional(v.boolean()),
794
+ deletedBy: v.optional(v.string()),
795
+ },
796
+ returns: v.any(),
797
+ handler: async (ctx, args) => {
798
+ await checkAuth(ctx, { type: "media.folders.delete", id: args.id });
799
+ return await ctx.runMutation(
800
+ component.mediaFolderMutations.deleteMediaFolder,
801
+ {
802
+ id: args.id ,
803
+ recursive: args.recursive,
804
+ hardDelete: args.hardDelete,
805
+ deletedBy: args.deletedBy,
806
+ }
807
+ );
808
+ },
809
+ }),
810
+
811
+ restoreFolder: mutationGeneric({
812
+ args: {
813
+ id: v.string(),
814
+ recursive: v.optional(v.boolean()),
815
+ },
816
+ returns: v.any(),
817
+ handler: async (ctx, args) => {
818
+ await checkAuth(ctx, { type: "media.folders.restore", id: args.id });
819
+ return await ctx.runMutation(
820
+ component.mediaFolderMutations.restoreMediaFolder,
821
+ {
822
+ id: args.id ,
823
+ recursive: args.recursive,
824
+ }
825
+ );
826
+ },
827
+ }),
828
+
829
+ // --- Upload ---
830
+ generateUploadUrl: mutationGeneric({
831
+ args: {},
832
+ returns: v.string(),
833
+ handler: async (ctx) => {
834
+ await checkAuth(ctx, { type: "media.generateUploadUrl" });
835
+ return await ctx.storage.generateUploadUrl();
836
+ },
837
+ }),
838
+ },
839
+
840
+ // =========================================================================
841
+ // Dashboard Stats
842
+ // =========================================================================
843
+ stats: {
844
+ getDashboardStats: queryGeneric({
845
+ args: {},
846
+ returns: v.object({
847
+ contentTypes: v.object({
848
+ total: v.number(),
849
+ active: v.number(),
850
+ }),
851
+ entries: v.object({
852
+ total: v.number(),
853
+ published: v.number(),
854
+ draft: v.number(),
855
+ scheduled: v.number(),
856
+ }),
857
+ media: v.object({
858
+ total: v.number(),
859
+ images: v.number(),
860
+ videos: v.number(),
861
+ documents: v.number(),
862
+ }),
863
+ }),
864
+ handler: async (ctx) => {
865
+ await checkAuth(ctx, { type: "stats.getDashboardStats" });
866
+
867
+ // Fetch all content types (returns paginated result)
868
+ const contentTypesResult = await ctx.runQuery(
869
+ component.contentTypes.list,
870
+ {}
871
+ );
872
+ const contentTypes = contentTypesResult.page || [];
873
+
874
+ // Fetch all entries (paginated, get counts)
875
+ const entriesResult = await ctx.runQuery(
876
+ component.contentEntries.list,
877
+ {
878
+ paginationOpts: { numItems: 1000, cursor: null },
879
+ }
880
+ );
881
+
882
+ // Fetch all media assets
883
+ const mediaResult = await ctx.runQuery(component.mediaAssets.list, {
884
+ paginationOpts: { numItems: 1000, cursor: null },
885
+ });
886
+
887
+ // Calculate content type stats
888
+ const activeContentTypes = contentTypes.filter(
889
+ (ct: { isActive: boolean }) => ct.isActive
890
+ ).length;
891
+
892
+ // Calculate entry stats
893
+ const entries = entriesResult.page || [];
894
+ const publishedEntries = entries.filter(
895
+ (e: { status: string }) => e.status === "published"
896
+ ).length;
897
+ const draftEntries = entries.filter(
898
+ (e: { status: string }) => e.status === "draft"
899
+ ).length;
900
+ const scheduledEntries = entries.filter(
901
+ (e: { status: string }) => e.status === "scheduled"
902
+ ).length;
903
+
904
+ // Calculate media stats by filtering on mimeType prefix
905
+ const mediaAssets = (mediaResult.page || []) as Array<{ kind: string; mimeType?: string }>;
906
+ const assets = mediaAssets.filter((m) => m.kind === "asset");
907
+ const images = assets.filter(
908
+ (m) => m.mimeType?.startsWith("image/")
909
+ ).length;
910
+ const videos = assets.filter(
911
+ (m) => m.mimeType?.startsWith("video/")
912
+ ).length;
913
+ const documents = assets.filter(
914
+ (m) => m.mimeType?.startsWith("application/pdf") ||
915
+ m.mimeType?.includes("document") ||
916
+ m.mimeType?.includes("sheet") ||
917
+ m.mimeType?.includes("presentation")
918
+ ).length;
919
+
920
+ return {
921
+ contentTypes: {
922
+ total: contentTypes.length,
923
+ active: activeContentTypes,
924
+ },
925
+ entries: {
926
+ total: entries.length,
927
+ published: publishedEntries,
928
+ draft: draftEntries,
929
+ scheduled: scheduledEntries,
930
+ },
931
+ media: {
932
+ total: mediaAssets.length,
933
+ images,
934
+ videos,
935
+ documents,
936
+ },
937
+ };
938
+ },
939
+ }),
940
+ },
941
+ };
942
+ }