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,687 @@
1
+ /**
2
+ * Bulk Operations for Content Entries
3
+ *
4
+ * Provides mutations for performing bulk operations on content entries:
5
+ * - bulkPublish: Publish multiple entries at once
6
+ * - bulkUnpublish: Revert multiple entries to draft
7
+ * - bulkDelete: Delete multiple entries (soft or hard)
8
+ * - bulkUpdate: Update multiple entries with the same changes
9
+ *
10
+ * All operations process entries in a single transaction for atomicity,
11
+ * respecting Convex limits (max 16,000 documents written per transaction).
12
+ * The BULK_OPERATION_BATCH_SIZE constant defines the maximum entries per call.
13
+ *
14
+ * For larger datasets, callers should batch IDs into chunks of BULK_OPERATION_BATCH_SIZE
15
+ * and call the appropriate bulk operation for each batch.
16
+ */
17
+
18
+ import { v } from "convex/values";
19
+ import { mutation } from "./_generated/server.js";
20
+ import {
21
+ bulkPublishArgs,
22
+ bulkUnpublishArgs,
23
+ bulkDeleteArgs,
24
+ bulkUpdateArgs,
25
+ bulkOperationResult,
26
+ BULK_OPERATION_BATCH_SIZE,
27
+ } from "./validators.js";
28
+ import {
29
+ validateContentData,
30
+ ContentTypeSchema,
31
+ FieldDefinition,
32
+ } from "./validation.js";
33
+ import { Id } from "./_generated/dataModel.js";
34
+ import { isDeleted } from "./lib/softDelete.js";
35
+
36
+ // =============================================================================
37
+ // Types
38
+ // =============================================================================
39
+
40
+ interface BulkOperationItemResult {
41
+ id: Id<"contentEntries">;
42
+ success: boolean;
43
+ error?: string;
44
+ }
45
+
46
+ interface BulkOperationResult {
47
+ total: number;
48
+ succeeded: number;
49
+ failed: number;
50
+ results: BulkOperationItemResult[];
51
+ }
52
+
53
+ // =============================================================================
54
+ // Bulk Publish Mutation
55
+ // =============================================================================
56
+
57
+ /**
58
+ * Mutation to publish multiple content entries in a single transaction.
59
+ *
60
+ * Publishes entries that are in draft or scheduled status. Already published
61
+ * entries are skipped with a success status. Deleted or archived entries
62
+ * will fail with an error message.
63
+ *
64
+ * For each entry published:
65
+ * - Status is set to "published"
66
+ * - firstPublishedAt is set (if first publication)
67
+ * - lastPublishedAt is updated
68
+ * - Version is incremented
69
+ * - A version snapshot is created
70
+ *
71
+ * @param ids - Array of content entry IDs to publish (max BULK_OPERATION_BATCH_SIZE)
72
+ * @param changeDescription - Optional description for version history
73
+ * @param updatedBy - User ID for audit trail
74
+ *
75
+ * @returns BulkOperationResult with success/failure details for each entry
76
+ *
77
+ * @example
78
+ * ```typescript
79
+ * const result = await ctx.runMutation(api.bulkOperations.bulkPublish, {
80
+ * ids: [entry1._id, entry2._id, entry3._id],
81
+ * changeDescription: "Publishing launch content",
82
+ * updatedBy: currentUserId,
83
+ * });
84
+ * console.log(`Published ${result.succeeded} of ${result.total} entries`);
85
+ * ```
86
+ */
87
+ export const bulkPublish = mutation({
88
+ args: bulkPublishArgs.fields,
89
+ returns: bulkOperationResult,
90
+ handler: async (ctx, args): Promise<BulkOperationResult> => {
91
+ const { ids, changeDescription, updatedBy } = args;
92
+
93
+ // Validate batch size
94
+ if (ids.length > BULK_OPERATION_BATCH_SIZE) {
95
+ throw new Error(
96
+ `Batch size exceeds limit. Maximum ${BULK_OPERATION_BATCH_SIZE} entries per operation, got ${ids.length}.`,
97
+ );
98
+ }
99
+
100
+ if (ids.length === 0) {
101
+ return { total: 0, succeeded: 0, failed: 0, results: [] };
102
+ }
103
+
104
+ const results: BulkOperationItemResult[] = [];
105
+ const now = Date.now();
106
+
107
+ for (const id of ids) {
108
+ try {
109
+ const entry = await ctx.db.get(id);
110
+
111
+ if (!entry) {
112
+ results.push({ id, success: false, error: "Entry not found" });
113
+ continue;
114
+ }
115
+
116
+ if (isDeleted(entry)) {
117
+ results.push({ id, success: false, error: "Entry has been deleted" });
118
+ continue;
119
+ }
120
+
121
+ if (entry.status === "published") {
122
+ // Already published - treat as success (idempotent)
123
+ results.push({ id, success: true });
124
+ continue;
125
+ }
126
+
127
+ if (entry.status === "archived") {
128
+ results.push({
129
+ id,
130
+ success: false,
131
+ error: "Cannot publish archived content. Restore it first.",
132
+ });
133
+ continue;
134
+ }
135
+
136
+ // Create version snapshot before publishing
137
+ await ctx.db.insert("contentVersions", {
138
+ entryId: id,
139
+ versionNumber: entry.version,
140
+ data: entry.data,
141
+ slug: entry.slug,
142
+ status: entry.status,
143
+ changeDescription,
144
+ createdBy: updatedBy,
145
+ wasPublished: true,
146
+ publishedAt: now,
147
+ });
148
+
149
+ // Build the update object
150
+ const updates: Record<string, unknown> = {
151
+ status: "published",
152
+ lastPublishedAt: now,
153
+ version: entry.version + 1,
154
+ updatedBy,
155
+ scheduledPublishAt: undefined,
156
+ };
157
+
158
+ // Set firstPublishedAt only on first publication
159
+ if (entry.firstPublishedAt === undefined) {
160
+ updates.firstPublishedAt = now;
161
+ }
162
+
163
+ await ctx.db.patch(id, updates);
164
+ results.push({ id, success: true });
165
+ } catch (error) {
166
+ results.push({
167
+ id,
168
+ success: false,
169
+ error: error instanceof Error ? error.message : "Unknown error",
170
+ });
171
+ }
172
+ }
173
+
174
+ const succeeded = results.filter((r) => r.success).length;
175
+ return {
176
+ total: ids.length,
177
+ succeeded,
178
+ failed: ids.length - succeeded,
179
+ results,
180
+ };
181
+ },
182
+ });
183
+
184
+ // =============================================================================
185
+ // Bulk Unpublish Mutation
186
+ // =============================================================================
187
+
188
+ /**
189
+ * Mutation to unpublish multiple content entries in a single transaction.
190
+ *
191
+ * Reverts published entries to draft status. Non-published entries are
192
+ * skipped with a success status (idempotent behavior). Deleted entries
193
+ * will fail with an error message.
194
+ *
195
+ * For each entry unpublished:
196
+ * - Status is set to "draft"
197
+ * - Version is incremented
198
+ * - Publication timestamps are preserved for history
199
+ *
200
+ * @param ids - Array of content entry IDs to unpublish (max BULK_OPERATION_BATCH_SIZE)
201
+ * @param updatedBy - User ID for audit trail
202
+ *
203
+ * @returns BulkOperationResult with success/failure details for each entry
204
+ *
205
+ * @example
206
+ * ```typescript
207
+ * const result = await ctx.runMutation(api.bulkOperations.bulkUnpublish, {
208
+ * ids: [entry1._id, entry2._id],
209
+ * updatedBy: currentUserId,
210
+ * });
211
+ * ```
212
+ */
213
+ export const bulkUnpublish = mutation({
214
+ args: bulkUnpublishArgs.fields,
215
+ returns: bulkOperationResult,
216
+ handler: async (ctx, args): Promise<BulkOperationResult> => {
217
+ const { ids, updatedBy } = args;
218
+
219
+ // Validate batch size
220
+ if (ids.length > BULK_OPERATION_BATCH_SIZE) {
221
+ throw new Error(
222
+ `Batch size exceeds limit. Maximum ${BULK_OPERATION_BATCH_SIZE} entries per operation, got ${ids.length}.`,
223
+ );
224
+ }
225
+
226
+ if (ids.length === 0) {
227
+ return { total: 0, succeeded: 0, failed: 0, results: [] };
228
+ }
229
+
230
+ const results: BulkOperationItemResult[] = [];
231
+
232
+ for (const id of ids) {
233
+ try {
234
+ const entry = await ctx.db.get(id);
235
+
236
+ if (!entry) {
237
+ results.push({ id, success: false, error: "Entry not found" });
238
+ continue;
239
+ }
240
+
241
+ if (isDeleted(entry)) {
242
+ results.push({ id, success: false, error: "Entry has been deleted" });
243
+ continue;
244
+ }
245
+
246
+ if (entry.status !== "published") {
247
+ // Not published - treat as success (idempotent)
248
+ results.push({ id, success: true });
249
+ continue;
250
+ }
251
+
252
+ await ctx.db.patch(id, {
253
+ status: "draft",
254
+ version: entry.version + 1,
255
+ updatedBy,
256
+ });
257
+
258
+ results.push({ id, success: true });
259
+ } catch (error) {
260
+ results.push({
261
+ id,
262
+ success: false,
263
+ error: error instanceof Error ? error.message : "Unknown error",
264
+ });
265
+ }
266
+ }
267
+
268
+ const succeeded = results.filter((r) => r.success).length;
269
+ return {
270
+ total: ids.length,
271
+ succeeded,
272
+ failed: ids.length - succeeded,
273
+ results,
274
+ };
275
+ },
276
+ });
277
+
278
+ // =============================================================================
279
+ // Bulk Delete Mutation
280
+ // =============================================================================
281
+
282
+ /**
283
+ * Mutation to delete multiple content entries in a single transaction.
284
+ *
285
+ * By default, performs soft delete by setting deletedAt timestamp.
286
+ * When hardDelete is true, permanently removes entries and all versions.
287
+ *
288
+ * Soft Delete:
289
+ * - Sets deletedAt timestamp
290
+ * - Entries can be restored later
291
+ * - Already deleted entries are skipped
292
+ *
293
+ * Hard Delete:
294
+ * - Permanently removes entry document
295
+ * - Deletes all version snapshots
296
+ * - Cannot be undone
297
+ *
298
+ * @param ids - Array of content entry IDs to delete (max BULK_OPERATION_BATCH_SIZE)
299
+ * @param deletedBy - User ID for audit trail
300
+ * @param hardDelete - If true, permanently delete entries and versions
301
+ *
302
+ * @returns BulkOperationResult with success/failure details for each entry
303
+ *
304
+ * @example
305
+ * ```typescript
306
+ * // Soft delete (default)
307
+ * const result = await ctx.runMutation(api.bulkOperations.bulkDelete, {
308
+ * ids: [entry1._id, entry2._id],
309
+ * deletedBy: currentUserId,
310
+ * });
311
+ *
312
+ * // Hard delete
313
+ * const result = await ctx.runMutation(api.bulkOperations.bulkDelete, {
314
+ * ids: [entry1._id, entry2._id],
315
+ * deletedBy: currentUserId,
316
+ * hardDelete: true,
317
+ * });
318
+ * ```
319
+ */
320
+ export const bulkDelete = mutation({
321
+ args: bulkDeleteArgs.fields,
322
+ returns: bulkOperationResult,
323
+ handler: async (ctx, args): Promise<BulkOperationResult> => {
324
+ const { ids, deletedBy, hardDelete = false } = args;
325
+
326
+ // Validate batch size
327
+ if (ids.length > BULK_OPERATION_BATCH_SIZE) {
328
+ throw new Error(
329
+ `Batch size exceeds limit. Maximum ${BULK_OPERATION_BATCH_SIZE} entries per operation, got ${ids.length}.`,
330
+ );
331
+ }
332
+
333
+ if (ids.length === 0) {
334
+ return { total: 0, succeeded: 0, failed: 0, results: [] };
335
+ }
336
+
337
+ const results: BulkOperationItemResult[] = [];
338
+ const now = Date.now();
339
+
340
+ for (const id of ids) {
341
+ try {
342
+ const entry = await ctx.db.get(id);
343
+
344
+ if (!entry) {
345
+ results.push({ id, success: false, error: "Entry not found" });
346
+ continue;
347
+ }
348
+
349
+ // For soft delete, skip already deleted entries
350
+ if (!hardDelete && isDeleted(entry)) {
351
+ // Already deleted - treat as success (idempotent)
352
+ results.push({ id, success: true });
353
+ continue;
354
+ }
355
+
356
+ if (hardDelete) {
357
+ // Hard delete: remove all versions first
358
+ const versions = await ctx.db
359
+ .query("contentVersions")
360
+ .withIndex("by_entry", (q) => q.eq("entryId", id))
361
+ .collect();
362
+
363
+ for (const version of versions) {
364
+ await ctx.db.delete(version._id);
365
+ }
366
+
367
+ // Delete the entry itself
368
+ await ctx.db.delete(id);
369
+ } else {
370
+ // Soft delete: set deletedAt timestamp
371
+ await ctx.db.patch(id, {
372
+ deletedAt: now,
373
+ updatedBy: deletedBy,
374
+ });
375
+ }
376
+
377
+ results.push({ id, success: true });
378
+ } catch (error) {
379
+ results.push({
380
+ id,
381
+ success: false,
382
+ error: error instanceof Error ? error.message : "Unknown error",
383
+ });
384
+ }
385
+ }
386
+
387
+ const succeeded = results.filter((r) => r.success).length;
388
+ return {
389
+ total: ids.length,
390
+ succeeded,
391
+ failed: ids.length - succeeded,
392
+ results,
393
+ };
394
+ },
395
+ });
396
+
397
+ // =============================================================================
398
+ // Bulk Update Mutation
399
+ // =============================================================================
400
+
401
+ /**
402
+ * Mutation to update multiple content entries with the same changes.
403
+ *
404
+ * Applies the same data updates and/or status change to all specified entries.
405
+ * Each entry is validated against its content type schema before updating.
406
+ *
407
+ * Data is merged with existing data for each entry (partial updates).
408
+ * Status can be changed independently of data updates.
409
+ *
410
+ * @param ids - Array of content entry IDs to update (max BULK_OPERATION_BATCH_SIZE)
411
+ * @param data - Data to merge into each entry
412
+ * @param status - New status to apply to all entries
413
+ * @param updatedBy - User ID for audit trail
414
+ *
415
+ * @returns BulkOperationResult with success/failure details for each entry
416
+ *
417
+ * @example
418
+ * ```typescript
419
+ * // Update data for multiple entries
420
+ * const result = await ctx.runMutation(api.bulkOperations.bulkUpdate, {
421
+ * ids: [entry1._id, entry2._id, entry3._id],
422
+ * data: { featured: true, category: "news" },
423
+ * updatedBy: currentUserId,
424
+ * });
425
+ *
426
+ * // Change status for multiple entries
427
+ * const result = await ctx.runMutation(api.bulkOperations.bulkUpdate, {
428
+ * ids: [entry1._id, entry2._id],
429
+ * status: "archived",
430
+ * updatedBy: currentUserId,
431
+ * });
432
+ * ```
433
+ */
434
+ export const bulkUpdate = mutation({
435
+ args: bulkUpdateArgs.fields,
436
+ returns: bulkOperationResult,
437
+ handler: async (ctx, args): Promise<BulkOperationResult> => {
438
+ const { ids, data, status, updatedBy } = args;
439
+
440
+ // Validate batch size
441
+ if (ids.length > BULK_OPERATION_BATCH_SIZE) {
442
+ throw new Error(
443
+ `Batch size exceeds limit. Maximum ${BULK_OPERATION_BATCH_SIZE} entries per operation, got ${ids.length}.`,
444
+ );
445
+ }
446
+
447
+ if (ids.length === 0) {
448
+ return { total: 0, succeeded: 0, failed: 0, results: [] };
449
+ }
450
+
451
+ // Check that at least one update field is provided
452
+ if (data === undefined && status === undefined) {
453
+ throw new Error(
454
+ "At least one of 'data' or 'status' must be provided for bulk update",
455
+ );
456
+ }
457
+
458
+ const results: BulkOperationItemResult[] = [];
459
+
460
+ // Cache content types to avoid repeated lookups
461
+ const contentTypeCache = new Map<
462
+ string,
463
+ {
464
+ name: string;
465
+ displayName: string;
466
+ description?: string;
467
+ fields: FieldDefinition[];
468
+ titleField?: string;
469
+ slugField?: string;
470
+ singleton?: boolean;
471
+ }
472
+ >();
473
+
474
+ for (const id of ids) {
475
+ try {
476
+ const entry = await ctx.db.get(id);
477
+
478
+ if (!entry) {
479
+ results.push({ id, success: false, error: "Entry not found" });
480
+ continue;
481
+ }
482
+
483
+ if (isDeleted(entry)) {
484
+ results.push({ id, success: false, error: "Entry has been deleted" });
485
+ continue;
486
+ }
487
+
488
+ // Build updates object
489
+ const updates: Record<string, unknown> = {
490
+ updatedBy,
491
+ version: entry.version + 1,
492
+ };
493
+
494
+ // Handle data update with validation
495
+ if (data !== undefined) {
496
+ // Get content type (from cache or database)
497
+ const contentTypeId = entry.contentTypeId.toString();
498
+ let contentType = contentTypeCache.get(contentTypeId);
499
+
500
+ if (!contentType) {
501
+ const dbContentType = await ctx.db.get(entry.contentTypeId);
502
+ if (!dbContentType) {
503
+ results.push({
504
+ id,
505
+ success: false,
506
+ error: "Content type not found",
507
+ });
508
+ continue;
509
+ }
510
+ if (isDeleted(dbContentType)) {
511
+ results.push({
512
+ id,
513
+ success: false,
514
+ error: "Content type has been deleted",
515
+ });
516
+ continue;
517
+ }
518
+
519
+ contentType = {
520
+ name: dbContentType.name,
521
+ displayName: dbContentType.displayName,
522
+ description: dbContentType.description,
523
+ fields: dbContentType.fields as FieldDefinition[],
524
+ titleField: dbContentType.titleField,
525
+ slugField: dbContentType.slugField,
526
+ singleton: dbContentType.singleton,
527
+ };
528
+ contentTypeCache.set(contentTypeId, contentType);
529
+ }
530
+
531
+ // Merge data with existing
532
+ const mergedData = {
533
+ ...(entry.data as Record<string, unknown>),
534
+ ...(data as Record<string, unknown>),
535
+ };
536
+
537
+ // Validate merged data against schema
538
+ const schema: ContentTypeSchema = {
539
+ name: contentType.name,
540
+ displayName: contentType.displayName,
541
+ description: contentType.description,
542
+ fields: contentType.fields,
543
+ titleField: contentType.titleField,
544
+ slugField: contentType.slugField,
545
+ singleton: contentType.singleton,
546
+ };
547
+
548
+ const validationResult = validateContentData(mergedData, schema);
549
+ if (!validationResult.valid) {
550
+ const errorMessages = validationResult.errors
551
+ .map((e) => `${e.field}: ${e.message}`)
552
+ .join("; ");
553
+ results.push({
554
+ id,
555
+ success: false,
556
+ error: `Validation failed: ${errorMessages}`,
557
+ });
558
+ continue;
559
+ }
560
+
561
+ updates.data = mergedData;
562
+
563
+ // Regenerate searchText from searchable fields
564
+ let searchText = "";
565
+ for (const field of contentType.fields) {
566
+ if (field.searchable && mergedData[field.name]) {
567
+ const value = mergedData[field.name];
568
+ if (typeof value === "string") {
569
+ searchText += ` ${value}`;
570
+ }
571
+ }
572
+ }
573
+ updates.searchText = searchText.trim() || undefined;
574
+ }
575
+
576
+ // Handle status update
577
+ if (status !== undefined) {
578
+ updates.status = status;
579
+ }
580
+
581
+ await ctx.db.patch(id, updates);
582
+ results.push({ id, success: true });
583
+ } catch (error) {
584
+ results.push({
585
+ id,
586
+ success: false,
587
+ error: error instanceof Error ? error.message : "Unknown error",
588
+ });
589
+ }
590
+ }
591
+
592
+ const succeeded = results.filter((r) => r.success).length;
593
+ return {
594
+ total: ids.length,
595
+ succeeded,
596
+ failed: ids.length - succeeded,
597
+ results,
598
+ };
599
+ },
600
+ });
601
+
602
+ // =============================================================================
603
+ // Bulk Restore Mutation
604
+ // =============================================================================
605
+
606
+ /**
607
+ * Mutation to restore multiple soft-deleted content entries.
608
+ *
609
+ * Removes the deletedAt marker from entries, making them active again.
610
+ * Only works for soft-deleted entries. Non-deleted entries are skipped
611
+ * with a success status (idempotent behavior).
612
+ *
613
+ * @param ids - Array of content entry IDs to restore (max BULK_OPERATION_BATCH_SIZE)
614
+ * @param restoredBy - User ID for audit trail
615
+ *
616
+ * @returns BulkOperationResult with success/failure details for each entry
617
+ *
618
+ * @example
619
+ * ```typescript
620
+ * const result = await ctx.runMutation(api.bulkOperations.bulkRestore, {
621
+ * ids: [deletedEntry1._id, deletedEntry2._id],
622
+ * restoredBy: currentUserId,
623
+ * });
624
+ * ```
625
+ */
626
+ export const bulkRestore = mutation({
627
+ args: {
628
+ ids: v.array(v.id("contentEntries")),
629
+ restoredBy: v.optional(v.string()),
630
+ },
631
+ returns: bulkOperationResult,
632
+ handler: async (ctx, args): Promise<BulkOperationResult> => {
633
+ const { ids, restoredBy } = args;
634
+
635
+ // Validate batch size
636
+ if (ids.length > BULK_OPERATION_BATCH_SIZE) {
637
+ throw new Error(
638
+ `Batch size exceeds limit. Maximum ${BULK_OPERATION_BATCH_SIZE} entries per operation, got ${ids.length}.`,
639
+ );
640
+ }
641
+
642
+ if (ids.length === 0) {
643
+ return { total: 0, succeeded: 0, failed: 0, results: [] };
644
+ }
645
+
646
+ const results: BulkOperationItemResult[] = [];
647
+
648
+ for (const id of ids) {
649
+ try {
650
+ const entry = await ctx.db.get(id);
651
+
652
+ if (!entry) {
653
+ results.push({ id, success: false, error: "Entry not found" });
654
+ continue;
655
+ }
656
+
657
+ if (!isDeleted(entry)) {
658
+ // Not deleted - treat as success (idempotent)
659
+ results.push({ id, success: true });
660
+ continue;
661
+ }
662
+
663
+ // Restore the entry
664
+ await ctx.db.patch(id, {
665
+ deletedAt: undefined,
666
+ updatedBy: restoredBy,
667
+ });
668
+
669
+ results.push({ id, success: true });
670
+ } catch (error) {
671
+ results.push({
672
+ id,
673
+ success: false,
674
+ error: error instanceof Error ? error.message : "Unknown error",
675
+ });
676
+ }
677
+ }
678
+
679
+ const succeeded = results.filter((r) => r.success).length;
680
+ return {
681
+ total: ids.length,
682
+ succeeded,
683
+ failed: ids.length - succeeded,
684
+ results,
685
+ };
686
+ },
687
+ });