convex-cms 0.0.2 → 0.0.3

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 (265) hide show
  1. package/admin-dist/nitro.json +15 -0
  2. package/admin-dist/public/assets/CmsEmptyState-CRswfTzk.js +5 -0
  3. package/admin-dist/public/assets/CmsPageHeader-CirpXndm.js +1 -0
  4. package/admin-dist/public/assets/CmsStatusBadge-CbEUpQu-.js +1 -0
  5. package/admin-dist/public/assets/CmsToolbar-BI2nZOXp.js +1 -0
  6. package/admin-dist/public/assets/ContentEntryEditor-CBeCyK_m.js +4 -0
  7. package/admin-dist/public/assets/ErrorState-BIVaWmom.js +1 -0
  8. package/admin-dist/public/assets/TaxonomyFilter-ChaY6Y_x.js +1 -0
  9. package/admin-dist/public/assets/_contentTypeId-DQ8k_Rvw.js +1 -0
  10. package/admin-dist/public/assets/_entryId-CKU_glsK.js +1 -0
  11. package/admin-dist/public/assets/alert-BXjTqrwQ.js +1 -0
  12. package/admin-dist/public/assets/badge-hvUOzpVZ.js +1 -0
  13. package/admin-dist/public/assets/circle-check-big-CF_pR17r.js +1 -0
  14. package/admin-dist/public/assets/command-DU82cJlt.js +1 -0
  15. package/admin-dist/public/assets/content-_LXl3pp7.js +1 -0
  16. package/admin-dist/public/assets/content-types-KjxaXGxY.js +2 -0
  17. package/admin-dist/public/assets/globals-CS6BZ0zp.css +1 -0
  18. package/admin-dist/public/assets/index-DNGIZHL-.js +1 -0
  19. package/admin-dist/public/assets/label-KNtpL71g.js +1 -0
  20. package/admin-dist/public/assets/link-2-Bw2aI4V4.js +1 -0
  21. package/admin-dist/public/assets/list-sYepHjt_.js +1 -0
  22. package/admin-dist/public/assets/main-CKj5yfEi.js +97 -0
  23. package/admin-dist/public/assets/media-Bkrkffm7.js +1 -0
  24. package/admin-dist/public/assets/new._contentTypeId-C3LstjNs.js +1 -0
  25. package/admin-dist/public/assets/plus-DUn8v_Xf.js +1 -0
  26. package/admin-dist/public/assets/rotate-ccw-DJEoHcRI.js +1 -0
  27. package/admin-dist/public/assets/scroll-area-DfIlT0in.js +1 -0
  28. package/admin-dist/public/assets/search-MuAUDJKR.js +1 -0
  29. package/admin-dist/public/assets/select-BD29IXCI.js +1 -0
  30. package/admin-dist/public/assets/settings-DmMyn_6A.js +1 -0
  31. package/admin-dist/public/assets/switch-h3Rrnl5i.js +1 -0
  32. package/admin-dist/public/assets/tabs-imc8h-Dp.js +1 -0
  33. package/admin-dist/public/assets/taxonomies-dAsrT65H.js +1 -0
  34. package/admin-dist/public/assets/textarea-BTy7nwzR.js +1 -0
  35. package/admin-dist/public/assets/trash-SAWKZZHv.js +1 -0
  36. package/admin-dist/public/assets/triangle-alert-E52Vfeuh.js +1 -0
  37. package/admin-dist/public/assets/useBreadcrumbLabel-BECBMCzM.js +1 -0
  38. package/admin-dist/public/assets/usePermissions-Basjs9BT.js +1 -0
  39. package/admin-dist/public/favicon.ico +0 -0
  40. package/admin-dist/server/_chunks/_libs/@date-fns/tz.mjs +217 -0
  41. package/admin-dist/server/_chunks/_libs/@floating-ui/core.mjs +719 -0
  42. package/admin-dist/server/_chunks/_libs/@floating-ui/dom.mjs +622 -0
  43. package/admin-dist/server/_chunks/_libs/@floating-ui/react-dom.mjs +292 -0
  44. package/admin-dist/server/_chunks/_libs/@floating-ui/utils.mjs +320 -0
  45. package/admin-dist/server/_chunks/_libs/@radix-ui/number.mjs +6 -0
  46. package/admin-dist/server/_chunks/_libs/@radix-ui/primitive.mjs +11 -0
  47. package/admin-dist/server/_chunks/_libs/@radix-ui/react-arrow.mjs +23 -0
  48. package/admin-dist/server/_chunks/_libs/@radix-ui/react-avatar.mjs +119 -0
  49. package/admin-dist/server/_chunks/_libs/@radix-ui/react-checkbox.mjs +270 -0
  50. package/admin-dist/server/_chunks/_libs/@radix-ui/react-collection.mjs +69 -0
  51. package/admin-dist/server/_chunks/_libs/@radix-ui/react-compose-refs.mjs +39 -0
  52. package/admin-dist/server/_chunks/_libs/@radix-ui/react-context.mjs +137 -0
  53. package/admin-dist/server/_chunks/_libs/@radix-ui/react-dialog.mjs +325 -0
  54. package/admin-dist/server/_chunks/_libs/@radix-ui/react-direction.mjs +9 -0
  55. package/admin-dist/server/_chunks/_libs/@radix-ui/react-dismissable-layer.mjs +210 -0
  56. package/admin-dist/server/_chunks/_libs/@radix-ui/react-dropdown-menu.mjs +253 -0
  57. package/admin-dist/server/_chunks/_libs/@radix-ui/react-focus-guards.mjs +29 -0
  58. package/admin-dist/server/_chunks/_libs/@radix-ui/react-focus-scope.mjs +206 -0
  59. package/admin-dist/server/_chunks/_libs/@radix-ui/react-id.mjs +14 -0
  60. package/admin-dist/server/_chunks/_libs/@radix-ui/react-label.mjs +23 -0
  61. package/admin-dist/server/_chunks/_libs/@radix-ui/react-menu.mjs +812 -0
  62. package/admin-dist/server/_chunks/_libs/@radix-ui/react-popover.mjs +300 -0
  63. package/admin-dist/server/_chunks/_libs/@radix-ui/react-popper.mjs +286 -0
  64. package/admin-dist/server/_chunks/_libs/@radix-ui/react-portal.mjs +16 -0
  65. package/admin-dist/server/_chunks/_libs/@radix-ui/react-presence.mjs +128 -0
  66. package/admin-dist/server/_chunks/_libs/@radix-ui/react-primitive.mjs +141 -0
  67. package/admin-dist/server/_chunks/_libs/@radix-ui/react-roving-focus.mjs +224 -0
  68. package/admin-dist/server/_chunks/_libs/@radix-ui/react-scroll-area.mjs +721 -0
  69. package/admin-dist/server/_chunks/_libs/@radix-ui/react-select.mjs +1163 -0
  70. package/admin-dist/server/_chunks/_libs/@radix-ui/react-separator.mjs +28 -0
  71. package/admin-dist/server/_chunks/_libs/@radix-ui/react-slot.mjs +601 -0
  72. package/admin-dist/server/_chunks/_libs/@radix-ui/react-switch.mjs +152 -0
  73. package/admin-dist/server/_chunks/_libs/@radix-ui/react-tabs.mjs +189 -0
  74. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-callback-ref.mjs +11 -0
  75. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-controllable-state.mjs +69 -0
  76. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-effect-event.mjs +1 -0
  77. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-escape-keydown.mjs +17 -0
  78. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-is-hydrated.mjs +15 -0
  79. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-layout-effect.mjs +6 -0
  80. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-previous.mjs +14 -0
  81. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-size.mjs +39 -0
  82. package/admin-dist/server/_chunks/_libs/@radix-ui/react-visually-hidden.mjs +33 -0
  83. package/admin-dist/server/_chunks/_libs/@tanstack/history.mjs +409 -0
  84. package/admin-dist/server/_chunks/_libs/@tanstack/react-router.mjs +1711 -0
  85. package/admin-dist/server/_chunks/_libs/@tanstack/react-store.mjs +56 -0
  86. package/admin-dist/server/_chunks/_libs/@tanstack/router-core.mjs +4829 -0
  87. package/admin-dist/server/_chunks/_libs/@tanstack/store.mjs +134 -0
  88. package/admin-dist/server/_chunks/_libs/react-dom.mjs +10781 -0
  89. package/admin-dist/server/_chunks/_libs/react.mjs +513 -0
  90. package/admin-dist/server/_libs/aria-hidden.mjs +122 -0
  91. package/admin-dist/server/_libs/class-variance-authority.mjs +44 -0
  92. package/admin-dist/server/_libs/clsx.mjs +16 -0
  93. package/admin-dist/server/_libs/cmdk.mjs +315 -0
  94. package/admin-dist/server/_libs/convex.mjs +4841 -0
  95. package/admin-dist/server/_libs/cookie-es.mjs +58 -0
  96. package/admin-dist/server/_libs/croner.mjs +1 -0
  97. package/admin-dist/server/_libs/crossws.mjs +1 -0
  98. package/admin-dist/server/_libs/date-fns.mjs +1716 -0
  99. package/admin-dist/server/_libs/detect-node-es.mjs +1 -0
  100. package/admin-dist/server/_libs/get-nonce.mjs +9 -0
  101. package/admin-dist/server/_libs/h3-v2.mjs +277 -0
  102. package/admin-dist/server/_libs/h3.mjs +401 -0
  103. package/admin-dist/server/_libs/hookable.mjs +1 -0
  104. package/admin-dist/server/_libs/isbot.mjs +20 -0
  105. package/admin-dist/server/_libs/lucide-react.mjs +850 -0
  106. package/admin-dist/server/_libs/ohash.mjs +1 -0
  107. package/admin-dist/server/_libs/react-day-picker.mjs +2201 -0
  108. package/admin-dist/server/_libs/react-remove-scroll-bar.mjs +82 -0
  109. package/admin-dist/server/_libs/react-remove-scroll.mjs +328 -0
  110. package/admin-dist/server/_libs/react-style-singleton.mjs +69 -0
  111. package/admin-dist/server/_libs/rou3.mjs +8 -0
  112. package/admin-dist/server/_libs/seroval-plugins.mjs +58 -0
  113. package/admin-dist/server/_libs/seroval.mjs +1765 -0
  114. package/admin-dist/server/_libs/srvx.mjs +719 -0
  115. package/admin-dist/server/_libs/tailwind-merge.mjs +3010 -0
  116. package/admin-dist/server/_libs/tiny-invariant.mjs +12 -0
  117. package/admin-dist/server/_libs/tiny-warning.mjs +5 -0
  118. package/admin-dist/server/_libs/tslib.mjs +39 -0
  119. package/admin-dist/server/_libs/ufo.mjs +54 -0
  120. package/admin-dist/server/_libs/unctx.mjs +1 -0
  121. package/admin-dist/server/_libs/unstorage.mjs +1 -0
  122. package/admin-dist/server/_libs/use-callback-ref.mjs +66 -0
  123. package/admin-dist/server/_libs/use-sidecar.mjs +106 -0
  124. package/admin-dist/server/_libs/use-sync-external-store.mjs +139 -0
  125. package/admin-dist/server/_libs/zod.mjs +4223 -0
  126. package/admin-dist/server/_ssr/CmsEmptyState-DU7-7-mV.mjs +290 -0
  127. package/admin-dist/server/_ssr/CmsPageHeader-CseW0AHm.mjs +24 -0
  128. package/admin-dist/server/_ssr/CmsStatusBadge-B_pi4KCp.mjs +127 -0
  129. package/admin-dist/server/_ssr/CmsToolbar-X75ex6ek.mjs +49 -0
  130. package/admin-dist/server/_ssr/ContentEntryEditor-CepusRsA.mjs +3720 -0
  131. package/admin-dist/server/_ssr/ErrorState-cI-bKLez.mjs +89 -0
  132. package/admin-dist/server/_ssr/TaxonomyFilter-Bwrq0-cz.mjs +188 -0
  133. package/admin-dist/server/_ssr/_contentTypeId-BqYKEcLr.mjs +379 -0
  134. package/admin-dist/server/_ssr/_entryId-CRfnqeDf.mjs +161 -0
  135. package/admin-dist/server/_ssr/_tanstack-start-manifest_v-BwDlABVk.mjs +4 -0
  136. package/admin-dist/server/_ssr/alert-CVt45UUP.mjs +92 -0
  137. package/admin-dist/server/_ssr/badge-6BsP37vG.mjs +125 -0
  138. package/admin-dist/server/_ssr/command-fy8epIKf.mjs +128 -0
  139. package/admin-dist/server/_ssr/config.server-D7JHDcDv.mjs +117 -0
  140. package/admin-dist/server/_ssr/content-B5RhL7uW.mjs +532 -0
  141. package/admin-dist/server/_ssr/content-types-BIOqCQYN.mjs +1166 -0
  142. package/admin-dist/server/_ssr/index-DHSHDPt1.mjs +193 -0
  143. package/admin-dist/server/_ssr/index.mjs +1275 -0
  144. package/admin-dist/server/_ssr/label-C8Dko1j7.mjs +22 -0
  145. package/admin-dist/server/_ssr/media-CSx3XttC.mjs +1832 -0
  146. package/admin-dist/server/_ssr/new._contentTypeId-DzanEZQM.mjs +144 -0
  147. package/admin-dist/server/_ssr/router-DDWcF-kt.mjs +1556 -0
  148. package/admin-dist/server/_ssr/scroll-area-bjPYwhXN.mjs +59 -0
  149. package/admin-dist/server/_ssr/select-BUhDDf4T.mjs +142 -0
  150. package/admin-dist/server/_ssr/settings-DAsxnw2q.mjs +348 -0
  151. package/admin-dist/server/_ssr/start-HYkvq4Ni.mjs +4 -0
  152. package/admin-dist/server/_ssr/switch-BgyRtQ1Z.mjs +31 -0
  153. package/admin-dist/server/_ssr/tabs-DzMdRB1A.mjs +628 -0
  154. package/admin-dist/server/_ssr/taxonomies-C8j8g5Q5.mjs +915 -0
  155. package/admin-dist/server/_ssr/textarea-9jNeYJSc.mjs +18 -0
  156. package/admin-dist/server/_ssr/trash-DYMxwhZB.mjs +291 -0
  157. package/admin-dist/server/_ssr/useBreadcrumbLabel-FNSAr2Ha.mjs +16 -0
  158. package/admin-dist/server/_ssr/usePermissions-BJGGahrJ.mjs +68 -0
  159. package/admin-dist/server/favicon.ico +0 -0
  160. package/admin-dist/server/index.mjs +627 -0
  161. package/dist/cli/index.js +0 -0
  162. package/dist/client/admin-config.d.ts +0 -1
  163. package/dist/client/admin-config.d.ts.map +1 -1
  164. package/dist/client/admin-config.js +0 -1
  165. package/dist/client/admin-config.js.map +1 -1
  166. package/dist/client/adminApi.d.ts.map +1 -1
  167. package/dist/client/agentTools.d.ts +1237 -135
  168. package/dist/client/agentTools.d.ts.map +1 -1
  169. package/dist/client/agentTools.js +33 -9
  170. package/dist/client/agentTools.js.map +1 -1
  171. package/dist/client/index.d.ts +1 -1
  172. package/dist/client/index.d.ts.map +1 -1
  173. package/dist/client/index.js.map +1 -1
  174. package/dist/component/_generated/component.d.ts +9 -0
  175. package/dist/component/_generated/component.d.ts.map +1 -1
  176. package/dist/component/mediaAssets.d.ts +35 -0
  177. package/dist/component/mediaAssets.d.ts.map +1 -1
  178. package/dist/component/mediaAssets.js +81 -0
  179. package/dist/component/mediaAssets.js.map +1 -1
  180. package/dist/test.d.ts.map +1 -1
  181. package/dist/test.js +2 -1
  182. package/dist/test.js.map +1 -1
  183. package/package.json +9 -5
  184. package/dist/component/auditLog.d.ts +0 -410
  185. package/dist/component/auditLog.d.ts.map +0 -1
  186. package/dist/component/auditLog.js +0 -607
  187. package/dist/component/auditLog.js.map +0 -1
  188. package/dist/component/types.d.ts +0 -4
  189. package/dist/component/types.d.ts.map +0 -1
  190. package/dist/component/types.js +0 -2
  191. package/dist/component/types.js.map +0 -1
  192. package/src/cli/commands/admin.ts +0 -104
  193. package/src/cli/index.ts +0 -21
  194. package/src/cli/utils/detectConvexUrl.ts +0 -54
  195. package/src/cli/utils/openBrowser.ts +0 -16
  196. package/src/client/admin-config.ts +0 -138
  197. package/src/client/adminApi.ts +0 -942
  198. package/src/client/agentTools.ts +0 -1311
  199. package/src/client/argTypes.ts +0 -316
  200. package/src/client/field-types.ts +0 -187
  201. package/src/client/index.ts +0 -1301
  202. package/src/client/queryBuilder.ts +0 -1100
  203. package/src/client/schema/codegen.ts +0 -500
  204. package/src/client/schema/defineContentType.ts +0 -501
  205. package/src/client/schema/index.ts +0 -169
  206. package/src/client/schema/schemaDrift.ts +0 -574
  207. package/src/client/schema/typedClient.ts +0 -688
  208. package/src/client/schema/types.ts +0 -666
  209. package/src/client/types.ts +0 -723
  210. package/src/client/workflows.ts +0 -141
  211. package/src/client/wrapper.ts +0 -4304
  212. package/src/component/_generated/api.ts +0 -140
  213. package/src/component/_generated/component.ts +0 -5029
  214. package/src/component/_generated/dataModel.ts +0 -60
  215. package/src/component/_generated/server.ts +0 -156
  216. package/src/component/authorization.ts +0 -647
  217. package/src/component/authorizationHooks.ts +0 -668
  218. package/src/component/bulkOperations.ts +0 -687
  219. package/src/component/contentEntries.ts +0 -1976
  220. package/src/component/contentEntryMutations.ts +0 -1223
  221. package/src/component/contentEntryValidation.ts +0 -707
  222. package/src/component/contentLock.ts +0 -550
  223. package/src/component/contentTypeMigration.ts +0 -1064
  224. package/src/component/contentTypeMutations.ts +0 -969
  225. package/src/component/contentTypes.ts +0 -346
  226. package/src/component/convex.config.ts +0 -44
  227. package/src/component/documentTypes.ts +0 -240
  228. package/src/component/eventEmitter.ts +0 -485
  229. package/src/component/exportImport.ts +0 -1169
  230. package/src/component/index.ts +0 -491
  231. package/src/component/lib/deepReferenceResolver.ts +0 -999
  232. package/src/component/lib/errors.ts +0 -816
  233. package/src/component/lib/index.ts +0 -145
  234. package/src/component/lib/mediaReferenceResolver.ts +0 -495
  235. package/src/component/lib/metadataExtractor.ts +0 -792
  236. package/src/component/lib/mutationAuth.ts +0 -199
  237. package/src/component/lib/queries.ts +0 -79
  238. package/src/component/lib/ragContentChunker.ts +0 -1371
  239. package/src/component/lib/referenceResolver.ts +0 -430
  240. package/src/component/lib/slugGenerator.ts +0 -262
  241. package/src/component/lib/slugUniqueness.ts +0 -333
  242. package/src/component/lib/softDelete.ts +0 -44
  243. package/src/component/localeFallbackChain.ts +0 -673
  244. package/src/component/localeFields.ts +0 -896
  245. package/src/component/mediaAssetMutations.ts +0 -725
  246. package/src/component/mediaAssets.ts +0 -932
  247. package/src/component/mediaFolderMutations.ts +0 -1046
  248. package/src/component/mediaUploadMutations.ts +0 -224
  249. package/src/component/mediaVariantMutations.ts +0 -900
  250. package/src/component/mediaVariants.ts +0 -793
  251. package/src/component/ragContentIndexer.ts +0 -1067
  252. package/src/component/rateLimitHooks.ts +0 -572
  253. package/src/component/roles.ts +0 -1360
  254. package/src/component/scheduledPublish.ts +0 -358
  255. package/src/component/schema.ts +0 -617
  256. package/src/component/taxonomies.ts +0 -949
  257. package/src/component/taxonomyMutations.ts +0 -1210
  258. package/src/component/trash.ts +0 -724
  259. package/src/component/userContext.ts +0 -898
  260. package/src/component/validation.ts +0 -1388
  261. package/src/component/validators.ts +0 -949
  262. package/src/component/versionMutations.ts +0 -392
  263. package/src/component/webhookTrigger.ts +0 -1922
  264. package/src/react/index.ts +0 -898
  265. package/src/test.ts +0 -1580
@@ -1,392 +0,0 @@
1
- /**
2
- * Version Mutation Functions
3
- *
4
- * Provides internal mutation functions for creating and managing version snapshots.
5
- * These functions are used internally by the CMS to maintain version history
6
- * for content entries.
7
- *
8
- * Version snapshots capture:
9
- * - Complete content data at a point in time
10
- * - Slug and status when the snapshot was created
11
- * - Metadata about who created it and why
12
- * - Whether the snapshot represents a published version
13
- */
14
-
15
- import { v } from "convex/values";
16
- import { isDeleted } from "./lib/softDelete.js";
17
- import { mutation, internalMutation } from "./_generated/server.js";
18
- import {
19
- createVersionSnapshotArgs,
20
- contentVersionDoc,
21
- contentEntryDoc,
22
- rollbackVersionArgs,
23
- } from "./validators.js";
24
- import {
25
- versionEntryNotFound,
26
- versionEntryDeleted,
27
- versionNotFound,
28
- versionMismatch,
29
- versionRollbackFailed,
30
- internalError,
31
- } from "./lib/errors.js";
32
-
33
- // =============================================================================
34
- // Create Version Snapshot (Internal)
35
- // =============================================================================
36
-
37
- /**
38
- * Internal mutation to create a version snapshot of a content entry.
39
- *
40
- * This function captures the complete state of a content entry at a specific
41
- * point in time, storing it in the contentVersions table. Snapshots are used
42
- * for:
43
- *
44
- * - **Version History**: Track changes over time for audit and review
45
- * - **Rollback Support**: Allow reverting to previous versions
46
- * - **Publishing Records**: Mark which versions were published
47
- * - **Content Comparison**: Enable diff/compare between versions
48
- *
49
- * The snapshot includes:
50
- * - versionNumber: Current version number from the entry
51
- * - data: Complete content data snapshot
52
- * - slug: Slug at the time of snapshot
53
- * - status: Entry status when snapshot was created
54
- * - changeDescription: Optional description of changes
55
- * - createdBy: User who triggered the snapshot
56
- * - wasPublished: Whether this is a published version
57
- * - publishedAt: Timestamp if this is a published version
58
- *
59
- * @param entryId - The ID of the content entry to snapshot
60
- * @param changeDescription - Optional description of what changed
61
- * @param createdBy - User ID for audit trail
62
- * @param wasPublished - Whether this snapshot represents a publish action
63
- *
64
- * @returns The created version snapshot document
65
- *
66
- * @throws Error if the entry does not exist
67
- * @throws Error if the entry has been soft-deleted
68
- *
69
- * @example
70
- * ```typescript
71
- * // Called internally before a destructive operation
72
- * await ctx.runMutation(internal.versionMutations.createVersionSnapshot, {
73
- * entryId: entryId,
74
- * changeDescription: "Pre-update snapshot",
75
- * createdBy: userId,
76
- * });
77
- *
78
- * // Called during publish to mark as published version
79
- * await ctx.runMutation(internal.versionMutations.createVersionSnapshot, {
80
- * entryId: entryId,
81
- * changeDescription: "Published to production",
82
- * createdBy: userId,
83
- * wasPublished: true,
84
- * });
85
- * ```
86
- */
87
- export const createVersionSnapshot = internalMutation({
88
- args: createVersionSnapshotArgs.fields,
89
- returns: contentVersionDoc,
90
- handler: async (ctx, args) => {
91
- const {
92
- entryId,
93
- changeDescription,
94
- createdBy,
95
- wasPublished = false,
96
- } = args;
97
-
98
- const entry = await ctx.db.get(entryId);
99
-
100
- if (!entry) {
101
- throw versionEntryNotFound((entryId as unknown) as string);
102
- }
103
-
104
- // Do not allow snapshots of deleted entries
105
- if (isDeleted(entry)) {
106
- throw versionEntryDeleted((entryId as unknown) as string);
107
- }
108
-
109
- const now = Date.now();
110
-
111
- // Create the version snapshot with complete entry state
112
- const versionId = await ctx.db.insert("contentVersions", {
113
- entryId,
114
- versionNumber: entry.version,
115
- data: entry.data,
116
- slug: entry.slug,
117
- status: entry.status,
118
- changeDescription,
119
- createdBy,
120
- wasPublished,
121
- publishedAt: wasPublished ? now : undefined,
122
- });
123
-
124
- // Retrieve and return the created version
125
- const version = await ctx.db.get(versionId);
126
-
127
- if (!version) {
128
- throw internalError("Failed to create version snapshot");
129
- }
130
-
131
- return version;
132
- },
133
- });
134
-
135
- // =============================================================================
136
- // Check for Duplicate Version (Internal Helper)
137
- // =============================================================================
138
-
139
- /**
140
- * Internal mutation to check if a version snapshot already exists.
141
- *
142
- * This can be used before creating a snapshot to avoid duplicates,
143
- * particularly useful when the same version might be snapshotted
144
- * multiple times (e.g., multiple publishes without content changes).
145
- *
146
- * @param entryId - The content entry ID
147
- * @param versionNumber - The version number to check for
148
- *
149
- * @returns true if a version with this number exists, false otherwise
150
- */
151
- export const versionExists = internalMutation({
152
- args: {
153
- entryId: v.id("contentEntries"),
154
- versionNumber: v.number(),
155
- },
156
- returns: v.boolean(),
157
- handler: async (ctx, args) => {
158
- const { entryId, versionNumber } = args;
159
-
160
- const existing = await ctx.db
161
- .query("contentVersions")
162
- .withIndex("by_entry_and_version", (q) =>
163
- q.eq("entryId", entryId).eq("versionNumber", versionNumber),
164
- )
165
- .first();
166
-
167
- return existing !== null;
168
- },
169
- });
170
-
171
- // =============================================================================
172
- // Create Snapshot If Changed (Internal Helper)
173
- // =============================================================================
174
-
175
- /**
176
- * Internal mutation to create a version snapshot only if the version
177
- * doesn't already exist. This is a convenience function that combines
178
- * the existence check and creation.
179
- *
180
- * Useful for scenarios where you want to snapshot but only if the
181
- * current version hasn't been captured yet (e.g., auto-save scenarios).
182
- *
183
- * @param entryId - The content entry ID
184
- * @param changeDescription - Optional description of changes
185
- * @param createdBy - User ID for audit trail
186
- * @param wasPublished - Whether this is a published version
187
- *
188
- * @returns The version snapshot (new or existing), or null if entry not found
189
- */
190
- export const createVersionSnapshotIfNotExists = internalMutation({
191
- args: createVersionSnapshotArgs.fields,
192
- returns: v.union(contentVersionDoc, v.null()),
193
- handler: async (ctx, args) => {
194
- const {
195
- entryId,
196
- changeDescription,
197
- createdBy,
198
- wasPublished = false,
199
- } = args;
200
-
201
- const entry = await ctx.db.get(entryId);
202
-
203
- if (!entry) {
204
- return null;
205
- }
206
-
207
- // Do not process deleted entries
208
- if (isDeleted(entry)) {
209
- return null;
210
- }
211
-
212
- // Check if this version already has a snapshot
213
- const existing = await ctx.db
214
- .query("contentVersions")
215
- .withIndex("by_entry_and_version", (q) =>
216
- q.eq("entryId", entryId).eq("versionNumber", entry.version),
217
- )
218
- .first();
219
-
220
- // Return existing if found
221
- if (existing) {
222
- return existing;
223
- }
224
-
225
- const now = Date.now();
226
-
227
- // Create new snapshot
228
- const versionId = await ctx.db.insert("contentVersions", {
229
- entryId,
230
- versionNumber: entry.version,
231
- data: entry.data,
232
- slug: entry.slug,
233
- status: entry.status,
234
- changeDescription,
235
- createdBy,
236
- wasPublished,
237
- publishedAt: wasPublished ? now : undefined,
238
- });
239
-
240
- const version = await ctx.db.get(versionId);
241
-
242
- return version ?? null;
243
- },
244
- });
245
-
246
- // =============================================================================
247
- // Rollback Version (Public)
248
- // =============================================================================
249
-
250
- /**
251
- * Mutation to restore a content entry to a previous version.
252
- *
253
- * This is the core rollback functionality that allows users to revert content
254
- * to any previously captured version state. Importantly, rollback is a
255
- * **non-destructive operation** - it creates a new version with the restored
256
- * content rather than actually "going back in time."
257
- *
258
- * ## How Rollback Works
259
- *
260
- * 1. **Validate**: Ensure the entry and target version exist and are accessible
261
- * 2. **Snapshot Current State**: Create a version snapshot of the current state
262
- * before making any changes (preserves ability to "undo" the rollback)
263
- * 3. **Restore Content**: Update the entry's `data` and `slug` from the target version
264
- * 4. **Increment Version**: The entry's version number is incremented (normal update behavior)
265
- * 5. **Create Rollback Snapshot**: Create a new version snapshot documenting the rollback
266
- *
267
- * ## What Gets Restored
268
- *
269
- * - **data**: The complete content data object from the target version
270
- * - **slug**: The URL-friendly slug from the target version
271
- *
272
- * ## What Does NOT Get Restored
273
- *
274
- * - **status**: The current publish status is preserved (a rollback doesn't
275
- * unpublish or publish content automatically)
276
- * - **scheduledPublishAt**: Scheduling is not affected
277
- * - **Publishing timestamps**: firstPublishedAt/lastPublishedAt are preserved
278
- *
279
- * ## Use Cases
280
- *
281
- * - **Accidental Changes**: Undo unwanted edits by restoring to a known good state
282
- * - **Content Review**: Compare and restore to previously approved versions
283
- * - **A/B Testing**: Switch between content variants by rolling back
284
- * - **Emergency Fixes**: Quickly revert problematic changes in production
285
- *
286
- * @param entryId - The ID of the content entry to roll back
287
- * @param versionNumber - The version number to restore to
288
- * @param updatedBy - Optional user ID for audit trail
289
- *
290
- * @returns The updated content entry with restored content
291
- *
292
- * @throws Error if the entry does not exist
293
- * @throws Error if the entry has been soft-deleted
294
- * @throws Error if the target version does not exist
295
- * @throws Error if the target version doesn't belong to this entry
296
- *
297
- * @example
298
- * ```typescript
299
- * // Restore entry to version 3
300
- * const restored = await ctx.runMutation(api.versionMutations.rollbackVersion, {
301
- * entryId: myEntryId,
302
- * versionNumber: 3,
303
- * updatedBy: currentUserId,
304
- * });
305
- *
306
- * console.log(`Rolled back to version 3, now at version ${restored.version}`);
307
- * // Note: The entry is now at a new version number (e.g., 7), not version 3
308
- * // The content matches what was in version 3
309
- * ```
310
- */
311
- export const rollbackVersion = mutation({
312
- args: rollbackVersionArgs.fields,
313
- returns: contentEntryDoc,
314
- handler: async (ctx, args) => {
315
- const { entryId, versionNumber, updatedBy } = args;
316
-
317
- // Step 1: Validate the entry exists and is not deleted
318
- const entry = await ctx.db.get(entryId);
319
-
320
- if (!entry) {
321
- throw versionEntryNotFound((entryId as unknown) as string);
322
- }
323
-
324
- if (isDeleted(entry)) {
325
- throw versionEntryDeleted((entryId as unknown) as string);
326
- }
327
-
328
- // Step 2: Retrieve the target version to restore
329
- const targetVersion = await ctx.db
330
- .query("contentVersions")
331
- .withIndex("by_entry_and_version", (q) =>
332
- q.eq("entryId", entryId).eq("versionNumber", versionNumber),
333
- )
334
- .first();
335
-
336
- if (!targetVersion) {
337
- throw versionNotFound((entryId as unknown) as string, versionNumber);
338
- }
339
-
340
- // Security: Verify the version belongs to this entry (defensive check)
341
- if (targetVersion.entryId !== entryId) {
342
- throw versionMismatch(
343
- (entryId as unknown) as string,
344
- (targetVersion._id as unknown) as string,
345
- );
346
- }
347
-
348
- // Step 3: Snapshot the current state before rollback (for undo capability)
349
- // This allows users to "undo" a rollback by rolling back to this snapshot
350
- await ctx.db.insert("contentVersions", {
351
- entryId,
352
- versionNumber: entry.version,
353
- data: entry.data,
354
- slug: entry.slug,
355
- status: entry.status,
356
- changeDescription: `Pre-rollback snapshot (before restoring to version ${versionNumber})`,
357
- createdBy: updatedBy,
358
- wasPublished: false,
359
- });
360
-
361
- // Step 4: Update the entry with restored content
362
- // Note: We restore data and slug, but preserve the current status
363
- const newVersionNumber = entry.version + 1;
364
-
365
- await ctx.db.patch(entryId, {
366
- data: targetVersion.data,
367
- slug: targetVersion.slug,
368
- version: newVersionNumber,
369
- updatedBy,
370
- });
371
-
372
- // Step 5: Create a snapshot documenting the rollback
373
- await ctx.db.insert("contentVersions", {
374
- entryId,
375
- versionNumber: newVersionNumber,
376
- data: targetVersion.data,
377
- slug: targetVersion.slug,
378
- status: entry.status, // Preserve current status
379
- changeDescription: `Rolled back to version ${versionNumber}`,
380
- createdBy: updatedBy,
381
- wasPublished: false,
382
- });
383
-
384
- const updatedEntry = await ctx.db.get(entryId);
385
-
386
- if (!updatedEntry) {
387
- throw versionRollbackFailed((entryId as unknown) as string);
388
- }
389
-
390
- return updatedEntry;
391
- },
392
- });