convex-cms 0.0.2 → 0.0.5-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (311) hide show
  1. package/README.md +109 -13
  2. package/admin-dist/nitro.json +15 -0
  3. package/admin-dist/public/assets/CmsEmptyState-CiMQwSQV.js +5 -0
  4. package/admin-dist/public/assets/CmsPageHeader-ohOq0luT.js +1 -0
  5. package/admin-dist/public/assets/CmsStatusBadge-BdNf0V9v.js +1 -0
  6. package/admin-dist/public/assets/CmsSurface-CWup6Jh7.js +1 -0
  7. package/admin-dist/public/assets/CmsToolbar-cEBlCHa3.js +1 -0
  8. package/admin-dist/public/assets/ContentEntryEditor-BY5ypfUs.js +4 -0
  9. package/admin-dist/public/assets/ErrorState-C4nJ-ml4.js +1 -0
  10. package/admin-dist/public/assets/TaxonomyFilter-BgE_SR_O.js +1 -0
  11. package/admin-dist/public/assets/_contentTypeId-DtZectcC.js +1 -0
  12. package/admin-dist/public/assets/_entryId-BpSmrfAm.js +1 -0
  13. package/admin-dist/public/assets/alert-Bf2l8kxw.js +1 -0
  14. package/admin-dist/public/assets/badge-qPrc4AUM.js +1 -0
  15. package/admin-dist/public/assets/circle-check-big-Dgozy3vV.js +1 -0
  16. package/admin-dist/public/assets/command-QOmNhlb0.js +1 -0
  17. package/admin-dist/public/assets/content-OEBGlxg1.js +1 -0
  18. package/admin-dist/public/assets/content-types-CjQliqVV.js +2 -0
  19. package/admin-dist/public/assets/globals-hAmgC66w.css +1 -0
  20. package/admin-dist/public/assets/index-BH_ECMhv.js +1 -0
  21. package/admin-dist/public/assets/label-DCsUdvFh.js +1 -0
  22. package/admin-dist/public/assets/link-2-Czw1N61H.js +1 -0
  23. package/admin-dist/public/assets/list-DtCsXj8-.js +1 -0
  24. package/admin-dist/public/assets/main-CXgkZMhe.js +97 -0
  25. package/admin-dist/public/assets/media-DTJ3-ViE.js +1 -0
  26. package/admin-dist/public/assets/new._contentTypeId-CoTDxKzf.js +1 -0
  27. package/admin-dist/public/assets/plus-xCFJK0RC.js +1 -0
  28. package/admin-dist/public/assets/rotate-ccw-DIqK63wY.js +1 -0
  29. package/admin-dist/public/assets/scroll-area-B-yrE66a.js +1 -0
  30. package/admin-dist/public/assets/search-CbCbboeU.js +1 -0
  31. package/admin-dist/public/assets/select-Co3TZFJb.js +1 -0
  32. package/admin-dist/public/assets/settings-BspTTv_o.js +1 -0
  33. package/admin-dist/public/assets/switch-CfavASmR.js +1 -0
  34. package/admin-dist/public/assets/tabs-CN5s5u2W.js +1 -0
  35. package/admin-dist/public/assets/tanstack-adapter-npeE3RdY.js +1 -0
  36. package/admin-dist/public/assets/taxonomies-CgG46fIF.js +1 -0
  37. package/admin-dist/public/assets/textarea-BJ0XFZpT.js +1 -0
  38. package/admin-dist/public/assets/trash-B3daldm5.js +1 -0
  39. package/admin-dist/public/assets/triangle-alert-BZRcqsUg.js +1 -0
  40. package/admin-dist/public/assets/useBreadcrumbLabel-DwZlwvFF.js +1 -0
  41. package/admin-dist/public/assets/usePermissions-C1JQhfqb.js +1 -0
  42. package/admin-dist/public/favicon.ico +0 -0
  43. package/admin-dist/server/_chunks/_libs/@date-fns/tz.mjs +217 -0
  44. package/admin-dist/server/_chunks/_libs/@floating-ui/core.mjs +719 -0
  45. package/admin-dist/server/_chunks/_libs/@floating-ui/dom.mjs +622 -0
  46. package/admin-dist/server/_chunks/_libs/@floating-ui/react-dom.mjs +292 -0
  47. package/admin-dist/server/_chunks/_libs/@floating-ui/utils.mjs +320 -0
  48. package/admin-dist/server/_chunks/_libs/@radix-ui/number.mjs +6 -0
  49. package/admin-dist/server/_chunks/_libs/@radix-ui/primitive.mjs +11 -0
  50. package/admin-dist/server/_chunks/_libs/@radix-ui/react-arrow.mjs +23 -0
  51. package/admin-dist/server/_chunks/_libs/@radix-ui/react-avatar.mjs +119 -0
  52. package/admin-dist/server/_chunks/_libs/@radix-ui/react-checkbox.mjs +270 -0
  53. package/admin-dist/server/_chunks/_libs/@radix-ui/react-collection.mjs +69 -0
  54. package/admin-dist/server/_chunks/_libs/@radix-ui/react-compose-refs.mjs +39 -0
  55. package/admin-dist/server/_chunks/_libs/@radix-ui/react-context.mjs +137 -0
  56. package/admin-dist/server/_chunks/_libs/@radix-ui/react-dialog.mjs +325 -0
  57. package/admin-dist/server/_chunks/_libs/@radix-ui/react-direction.mjs +9 -0
  58. package/admin-dist/server/_chunks/_libs/@radix-ui/react-dismissable-layer.mjs +210 -0
  59. package/admin-dist/server/_chunks/_libs/@radix-ui/react-dropdown-menu.mjs +253 -0
  60. package/admin-dist/server/_chunks/_libs/@radix-ui/react-focus-guards.mjs +29 -0
  61. package/admin-dist/server/_chunks/_libs/@radix-ui/react-focus-scope.mjs +206 -0
  62. package/admin-dist/server/_chunks/_libs/@radix-ui/react-id.mjs +14 -0
  63. package/admin-dist/server/_chunks/_libs/@radix-ui/react-label.mjs +23 -0
  64. package/admin-dist/server/_chunks/_libs/@radix-ui/react-menu.mjs +812 -0
  65. package/admin-dist/server/_chunks/_libs/@radix-ui/react-popover.mjs +300 -0
  66. package/admin-dist/server/_chunks/_libs/@radix-ui/react-popper.mjs +286 -0
  67. package/admin-dist/server/_chunks/_libs/@radix-ui/react-portal.mjs +16 -0
  68. package/admin-dist/server/_chunks/_libs/@radix-ui/react-presence.mjs +128 -0
  69. package/admin-dist/server/_chunks/_libs/@radix-ui/react-primitive.mjs +141 -0
  70. package/admin-dist/server/_chunks/_libs/@radix-ui/react-roving-focus.mjs +224 -0
  71. package/admin-dist/server/_chunks/_libs/@radix-ui/react-scroll-area.mjs +721 -0
  72. package/admin-dist/server/_chunks/_libs/@radix-ui/react-select.mjs +1163 -0
  73. package/admin-dist/server/_chunks/_libs/@radix-ui/react-separator.mjs +28 -0
  74. package/admin-dist/server/_chunks/_libs/@radix-ui/react-slot.mjs +601 -0
  75. package/admin-dist/server/_chunks/_libs/@radix-ui/react-switch.mjs +152 -0
  76. package/admin-dist/server/_chunks/_libs/@radix-ui/react-tabs.mjs +189 -0
  77. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-callback-ref.mjs +11 -0
  78. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-controllable-state.mjs +69 -0
  79. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-effect-event.mjs +1 -0
  80. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-escape-keydown.mjs +17 -0
  81. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-is-hydrated.mjs +15 -0
  82. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-layout-effect.mjs +6 -0
  83. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-previous.mjs +14 -0
  84. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-size.mjs +39 -0
  85. package/admin-dist/server/_chunks/_libs/@radix-ui/react-visually-hidden.mjs +33 -0
  86. package/admin-dist/server/_chunks/_libs/@tanstack/history.mjs +409 -0
  87. package/admin-dist/server/_chunks/_libs/@tanstack/react-router.mjs +1718 -0
  88. package/admin-dist/server/_chunks/_libs/@tanstack/react-store.mjs +56 -0
  89. package/admin-dist/server/_chunks/_libs/@tanstack/router-core.mjs +4829 -0
  90. package/admin-dist/server/_chunks/_libs/@tanstack/store.mjs +134 -0
  91. package/admin-dist/server/_chunks/_libs/react-dom.mjs +10781 -0
  92. package/admin-dist/server/_chunks/_libs/react.mjs +513 -0
  93. package/admin-dist/server/_libs/aria-hidden.mjs +122 -0
  94. package/admin-dist/server/_libs/class-variance-authority.mjs +44 -0
  95. package/admin-dist/server/_libs/clsx.mjs +16 -0
  96. package/admin-dist/server/_libs/cmdk.mjs +315 -0
  97. package/admin-dist/server/_libs/convex.mjs +4841 -0
  98. package/admin-dist/server/_libs/cookie-es.mjs +58 -0
  99. package/admin-dist/server/_libs/croner.mjs +1 -0
  100. package/admin-dist/server/_libs/crossws.mjs +1 -0
  101. package/admin-dist/server/_libs/date-fns.mjs +1716 -0
  102. package/admin-dist/server/_libs/detect-node-es.mjs +1 -0
  103. package/admin-dist/server/_libs/get-nonce.mjs +9 -0
  104. package/admin-dist/server/_libs/h3-v2.mjs +277 -0
  105. package/admin-dist/server/_libs/h3.mjs +401 -0
  106. package/admin-dist/server/_libs/hookable.mjs +1 -0
  107. package/admin-dist/server/_libs/isbot.mjs +20 -0
  108. package/admin-dist/server/_libs/lucide-react.mjs +850 -0
  109. package/admin-dist/server/_libs/ohash.mjs +1 -0
  110. package/admin-dist/server/_libs/react-day-picker.mjs +2201 -0
  111. package/admin-dist/server/_libs/react-remove-scroll-bar.mjs +82 -0
  112. package/admin-dist/server/_libs/react-remove-scroll.mjs +328 -0
  113. package/admin-dist/server/_libs/react-style-singleton.mjs +69 -0
  114. package/admin-dist/server/_libs/rou3.mjs +8 -0
  115. package/admin-dist/server/_libs/seroval-plugins.mjs +58 -0
  116. package/admin-dist/server/_libs/seroval.mjs +1765 -0
  117. package/admin-dist/server/_libs/srvx.mjs +719 -0
  118. package/admin-dist/server/_libs/tailwind-merge.mjs +3010 -0
  119. package/admin-dist/server/_libs/tiny-invariant.mjs +12 -0
  120. package/admin-dist/server/_libs/tiny-warning.mjs +5 -0
  121. package/admin-dist/server/_libs/tslib.mjs +39 -0
  122. package/admin-dist/server/_libs/ufo.mjs +54 -0
  123. package/admin-dist/server/_libs/unctx.mjs +1 -0
  124. package/admin-dist/server/_libs/unstorage.mjs +1 -0
  125. package/admin-dist/server/_libs/use-callback-ref.mjs +66 -0
  126. package/admin-dist/server/_libs/use-sidecar.mjs +106 -0
  127. package/admin-dist/server/_libs/use-sync-external-store.mjs +139 -0
  128. package/admin-dist/server/_libs/zod.mjs +4223 -0
  129. package/admin-dist/server/_ssr/CmsButton-B45JAKR1.mjs +125 -0
  130. package/admin-dist/server/_ssr/CmsEmptyState-D_BQFAVR.mjs +290 -0
  131. package/admin-dist/server/_ssr/CmsPageHeader-CrUZA59A.mjs +24 -0
  132. package/admin-dist/server/_ssr/CmsStatusBadge-B-sj6yaj.mjs +127 -0
  133. package/admin-dist/server/_ssr/CmsSurface-DKJZhpjk.mjs +44 -0
  134. package/admin-dist/server/_ssr/CmsToolbar-ByaW5iXf.mjs +49 -0
  135. package/admin-dist/server/_ssr/ContentEntryEditor-D3_Jb1dq.mjs +3720 -0
  136. package/admin-dist/server/_ssr/ErrorState-cI-bKLez.mjs +89 -0
  137. package/admin-dist/server/_ssr/TaxonomyFilter-BRJkuCtA.mjs +188 -0
  138. package/admin-dist/server/_ssr/_contentTypeId-B9kA6CaM.mjs +379 -0
  139. package/admin-dist/server/_ssr/_entryId-BddcMkZN.mjs +161 -0
  140. package/admin-dist/server/_ssr/_tanstack-start-manifest_v-Dd7AmelK.mjs +4 -0
  141. package/admin-dist/server/_ssr/command-CGtVr8Gb.mjs +128 -0
  142. package/admin-dist/server/_ssr/config.server-D7JHDcDv.mjs +117 -0
  143. package/admin-dist/server/_ssr/content-D1tbeOd0.mjs +647 -0
  144. package/admin-dist/server/_ssr/content-types-BZqY_BER.mjs +1342 -0
  145. package/admin-dist/server/_ssr/index-BIdq4xaC.mjs +264 -0
  146. package/admin-dist/server/_ssr/index.mjs +1275 -0
  147. package/admin-dist/server/_ssr/label-T-QNKAr6.mjs +22 -0
  148. package/admin-dist/server/_ssr/media-C-xqjBrl.mjs +1832 -0
  149. package/admin-dist/server/_ssr/new._contentTypeId-DWic9cRq.mjs +144 -0
  150. package/admin-dist/server/_ssr/router-D1BMAMJT.mjs +1556 -0
  151. package/admin-dist/server/_ssr/scroll-area-C0pic_WA.mjs +59 -0
  152. package/admin-dist/server/_ssr/select-CqmuN2F6.mjs +142 -0
  153. package/admin-dist/server/_ssr/settings-CAkncGGV.mjs +430 -0
  154. package/admin-dist/server/_ssr/start-HYkvq4Ni.mjs +4 -0
  155. package/admin-dist/server/_ssr/switch-CgmuJkT9.mjs +31 -0
  156. package/admin-dist/server/_ssr/tabs-CnMj0aRy.mjs +630 -0
  157. package/admin-dist/server/_ssr/tanstack-adapter-BXZrMauE.mjs +119 -0
  158. package/admin-dist/server/_ssr/taxonomies-thl3BfVm.mjs +1015 -0
  159. package/admin-dist/server/_ssr/textarea-4K5OJgeh.mjs +18 -0
  160. package/admin-dist/server/_ssr/trash-B40Gx5zP.mjs +411 -0
  161. package/admin-dist/server/_ssr/useBreadcrumbLabel-rn-fL4zV.mjs +16 -0
  162. package/admin-dist/server/_ssr/usePermissions-CKeM6_Vw.mjs +68 -0
  163. package/admin-dist/server/favicon.ico +0 -0
  164. package/admin-dist/server/index.mjs +641 -0
  165. package/dist/cli/commands/init.d.ts +6 -0
  166. package/dist/cli/commands/init.d.ts.map +1 -0
  167. package/dist/cli/commands/init.js +156 -0
  168. package/dist/cli/commands/init.js.map +1 -0
  169. package/dist/cli/index.js +6 -0
  170. package/dist/cli/index.js.map +1 -1
  171. package/dist/client/admin-config.d.ts +2 -3
  172. package/dist/client/admin-config.d.ts.map +1 -1
  173. package/dist/client/admin-config.js +2 -3
  174. package/dist/client/admin-config.js.map +1 -1
  175. package/dist/client/adminApi.d.ts +1877 -1851
  176. package/dist/client/adminApi.d.ts.map +1 -1
  177. package/dist/client/adminApi.js +649 -629
  178. package/dist/client/adminApi.js.map +1 -1
  179. package/dist/client/agentTools.d.ts +1231 -139
  180. package/dist/client/agentTools.d.ts.map +1 -1
  181. package/dist/client/agentTools.js +37 -13
  182. package/dist/client/agentTools.js.map +1 -1
  183. package/dist/client/index.d.ts +5 -5
  184. package/dist/client/index.d.ts.map +1 -1
  185. package/dist/client/index.js +4 -4
  186. package/dist/client/index.js.map +1 -1
  187. package/dist/client/schema/codegen.d.ts +2 -2
  188. package/dist/client/schema/codegen.d.ts.map +1 -1
  189. package/dist/client/schema/codegen.js +3 -3
  190. package/dist/client/schema/codegen.js.map +1 -1
  191. package/dist/client/schema/defineContentType.d.ts +3 -3
  192. package/dist/client/schema/defineContentType.js +3 -3
  193. package/dist/client/schema/index.d.ts +7 -7
  194. package/dist/client/schema/index.d.ts.map +1 -1
  195. package/dist/client/schema/index.js +5 -5
  196. package/dist/client/schema/index.js.map +1 -1
  197. package/dist/client/schema/schemaDrift.d.ts +1 -1
  198. package/dist/client/schema/schemaDrift.js +1 -1
  199. package/dist/client/schema/typedClient.d.ts +2 -2
  200. package/dist/client/schema/typedClient.js +2 -2
  201. package/dist/client/schema/types.d.ts +1 -1
  202. package/dist/client/schema/types.js +1 -1
  203. package/dist/client/wrapper.d.ts +108 -65
  204. package/dist/client/wrapper.d.ts.map +1 -1
  205. package/dist/client/wrapper.js +22 -22
  206. package/dist/client/wrapper.js.map +1 -1
  207. package/dist/component/_generated/component.d.ts +9 -0
  208. package/dist/component/_generated/component.d.ts.map +1 -1
  209. package/dist/component/convex.config.d.ts +2 -2
  210. package/dist/component/convex.config.js +2 -2
  211. package/dist/component/index.d.ts +1 -1
  212. package/dist/component/index.js +1 -1
  213. package/dist/component/lib/ragContentChunker.d.ts +1 -1
  214. package/dist/component/lib/ragContentChunker.js +1 -1
  215. package/dist/component/mediaAssets.d.ts +35 -0
  216. package/dist/component/mediaAssets.d.ts.map +1 -1
  217. package/dist/component/mediaAssets.js +81 -0
  218. package/dist/component/mediaAssets.js.map +1 -1
  219. package/dist/component/roles.d.ts +1 -1
  220. package/dist/component/roles.js +1 -1
  221. package/dist/react/index.d.ts +2 -2
  222. package/dist/react/index.d.ts.map +1 -1
  223. package/dist/react/index.js +13 -7
  224. package/dist/react/index.js.map +1 -1
  225. package/dist/test.d.ts +2 -2
  226. package/dist/test.d.ts.map +1 -1
  227. package/dist/test.js +4 -3
  228. package/dist/test.js.map +1 -1
  229. package/package.json +37 -13
  230. package/dist/component/auditLog.d.ts +0 -410
  231. package/dist/component/auditLog.d.ts.map +0 -1
  232. package/dist/component/auditLog.js +0 -607
  233. package/dist/component/auditLog.js.map +0 -1
  234. package/dist/component/types.d.ts +0 -4
  235. package/dist/component/types.d.ts.map +0 -1
  236. package/dist/component/types.js +0 -2
  237. package/dist/component/types.js.map +0 -1
  238. package/src/cli/commands/admin.ts +0 -104
  239. package/src/cli/index.ts +0 -21
  240. package/src/cli/utils/detectConvexUrl.ts +0 -54
  241. package/src/cli/utils/openBrowser.ts +0 -16
  242. package/src/client/admin-config.ts +0 -138
  243. package/src/client/adminApi.ts +0 -942
  244. package/src/client/agentTools.ts +0 -1311
  245. package/src/client/argTypes.ts +0 -316
  246. package/src/client/field-types.ts +0 -187
  247. package/src/client/index.ts +0 -1301
  248. package/src/client/queryBuilder.ts +0 -1100
  249. package/src/client/schema/codegen.ts +0 -500
  250. package/src/client/schema/defineContentType.ts +0 -501
  251. package/src/client/schema/index.ts +0 -169
  252. package/src/client/schema/schemaDrift.ts +0 -574
  253. package/src/client/schema/typedClient.ts +0 -688
  254. package/src/client/schema/types.ts +0 -666
  255. package/src/client/types.ts +0 -723
  256. package/src/client/workflows.ts +0 -141
  257. package/src/client/wrapper.ts +0 -4304
  258. package/src/component/_generated/api.ts +0 -140
  259. package/src/component/_generated/component.ts +0 -5029
  260. package/src/component/_generated/dataModel.ts +0 -60
  261. package/src/component/_generated/server.ts +0 -156
  262. package/src/component/authorization.ts +0 -647
  263. package/src/component/authorizationHooks.ts +0 -668
  264. package/src/component/bulkOperations.ts +0 -687
  265. package/src/component/contentEntries.ts +0 -1976
  266. package/src/component/contentEntryMutations.ts +0 -1223
  267. package/src/component/contentEntryValidation.ts +0 -707
  268. package/src/component/contentLock.ts +0 -550
  269. package/src/component/contentTypeMigration.ts +0 -1064
  270. package/src/component/contentTypeMutations.ts +0 -969
  271. package/src/component/contentTypes.ts +0 -346
  272. package/src/component/convex.config.ts +0 -44
  273. package/src/component/documentTypes.ts +0 -240
  274. package/src/component/eventEmitter.ts +0 -485
  275. package/src/component/exportImport.ts +0 -1169
  276. package/src/component/index.ts +0 -491
  277. package/src/component/lib/deepReferenceResolver.ts +0 -999
  278. package/src/component/lib/errors.ts +0 -816
  279. package/src/component/lib/index.ts +0 -145
  280. package/src/component/lib/mediaReferenceResolver.ts +0 -495
  281. package/src/component/lib/metadataExtractor.ts +0 -792
  282. package/src/component/lib/mutationAuth.ts +0 -199
  283. package/src/component/lib/queries.ts +0 -79
  284. package/src/component/lib/ragContentChunker.ts +0 -1371
  285. package/src/component/lib/referenceResolver.ts +0 -430
  286. package/src/component/lib/slugGenerator.ts +0 -262
  287. package/src/component/lib/slugUniqueness.ts +0 -333
  288. package/src/component/lib/softDelete.ts +0 -44
  289. package/src/component/localeFallbackChain.ts +0 -673
  290. package/src/component/localeFields.ts +0 -896
  291. package/src/component/mediaAssetMutations.ts +0 -725
  292. package/src/component/mediaAssets.ts +0 -932
  293. package/src/component/mediaFolderMutations.ts +0 -1046
  294. package/src/component/mediaUploadMutations.ts +0 -224
  295. package/src/component/mediaVariantMutations.ts +0 -900
  296. package/src/component/mediaVariants.ts +0 -793
  297. package/src/component/ragContentIndexer.ts +0 -1067
  298. package/src/component/rateLimitHooks.ts +0 -572
  299. package/src/component/roles.ts +0 -1360
  300. package/src/component/scheduledPublish.ts +0 -358
  301. package/src/component/schema.ts +0 -617
  302. package/src/component/taxonomies.ts +0 -949
  303. package/src/component/taxonomyMutations.ts +0 -1210
  304. package/src/component/trash.ts +0 -724
  305. package/src/component/userContext.ts +0 -898
  306. package/src/component/validation.ts +0 -1388
  307. package/src/component/validators.ts +0 -949
  308. package/src/component/versionMutations.ts +0 -392
  309. package/src/component/webhookTrigger.ts +0 -1922
  310. package/src/react/index.ts +0 -898
  311. package/src/test.ts +0 -1580
@@ -1,707 +0,0 @@
1
- /**
2
- * Content Entry Validation Internal Functions
3
- *
4
- * Provides internal functions to validate content entry data against its
5
- * content type schema. Used by mutations before creating/updating entries,
6
- * and can be called directly for preview validation.
7
- *
8
- * Validation includes:
9
- * - Required field checks
10
- * - Type correctness validation
11
- * - Custom field constraints (min/max, pattern, etc.)
12
- * - Reference validation (existence and content type constraints)
13
- * - Media reference validation
14
- */
15
-
16
- import { v } from "convex/values";
17
- import { isDeleted } from "./lib/softDelete.js";
18
- import { internalQuery, query } from "./_generated/server.js";
19
- import { Id } from "./_generated/dataModel.js";
20
- import {
21
- validateContentData,
22
- ContentData,
23
- ContentTypeSchema,
24
- FieldDefinition,
25
- ValidationError,
26
- } from "./validation.js";
27
-
28
- // =============================================================================
29
- // Types
30
- // =============================================================================
31
-
32
- /**
33
- * Extended validation result that includes reference validation errors
34
- */
35
- export type ContentEntryValidationResult = {
36
- /** Whether the validation passed */
37
- valid: boolean;
38
- /** Array of validation errors (empty if valid) */
39
- errors: ValidationError[];
40
- /** Content type name (if found) */
41
- contentTypeName?: string;
42
- /** Content type display name (if found) */
43
- contentTypeDisplayName?: string;
44
- /** Whether reference validation was performed */
45
- referencesValidated: boolean;
46
- };
47
-
48
- /**
49
- * Options for content entry validation
50
- */
51
- export interface ValidateContentEntryOptions {
52
- /**
53
- * If true, validates that referenced entries exist and belong to allowed content types.
54
- * This requires additional database queries but provides complete validation.
55
- * Default: true
56
- */
57
- validateReferences?: boolean;
58
-
59
- /**
60
- * If true, reports unknown fields (fields not defined in the content type) as errors.
61
- * Default: false
62
- */
63
- strictFields?: boolean;
64
- }
65
-
66
- // =============================================================================
67
- // Validator Definitions
68
- // =============================================================================
69
-
70
- /**
71
- * Argument validator for validateContentEntry
72
- */
73
- const validateContentEntryArgs = v.object({
74
- /** The content type ID to validate against */
75
- contentTypeId: v.id("contentTypes"),
76
- /** The content data to validate */
77
- data: v.any(),
78
- /** Validation options */
79
- options: v.optional(
80
- v.object({
81
- validateReferences: v.optional(v.boolean()),
82
- strictFields: v.optional(v.boolean()),
83
- }),
84
- ),
85
- });
86
-
87
- /**
88
- * Argument validator for validateContentEntryByTypeName
89
- */
90
- const validateContentEntryByTypeNameArgs = v.object({
91
- /** The content type name to validate against */
92
- contentTypeName: v.string(),
93
- /** The content data to validate */
94
- data: v.any(),
95
- /** Validation options */
96
- options: v.optional(
97
- v.object({
98
- validateReferences: v.optional(v.boolean()),
99
- strictFields: v.optional(v.boolean()),
100
- }),
101
- ),
102
- });
103
-
104
- /**
105
- * Return validator for validation results
106
- */
107
- const validationResultValidator = v.object({
108
- valid: v.boolean(),
109
- errors: v.array(
110
- v.object({
111
- field: v.string(),
112
- message: v.string(),
113
- code: v.string(),
114
- }),
115
- ),
116
- contentTypeName: v.optional(v.string()),
117
- contentTypeDisplayName: v.optional(v.string()),
118
- referencesValidated: v.boolean(),
119
- });
120
-
121
- // =============================================================================
122
- // Internal Query: Validate Content Entry
123
- // =============================================================================
124
-
125
- /**
126
- * Internal query to validate content entry data against its content type schema.
127
- *
128
- * This function performs comprehensive validation including:
129
- * 1. Required field checks - ensures all required fields have values
130
- * 2. Type validation - verifies values match expected types (string, number, etc.)
131
- * 3. Constraint validation - checks min/max length, patterns, allowed values, etc.
132
- * 4. Reference validation - optionally verifies referenced entries exist and have correct types
133
- *
134
- * @param contentTypeId - The ID of the content type to validate against
135
- * @param data - The content data to validate
136
- * @param options - Optional validation options
137
- *
138
- * @returns ValidationResult with any errors found
139
- *
140
- * @example
141
- * ```typescript
142
- * // Validate blog post data before creating
143
- * const result = await ctx.runQuery(internal.contentEntryValidation.validateContentEntry, {
144
- * contentTypeId: blogTypeId,
145
- * data: {
146
- * title: "My Post",
147
- * content: "<p>Hello world!</p>",
148
- * author: authorEntryId,
149
- * },
150
- * });
151
- *
152
- * if (!result.valid) {
153
- * console.error("Validation errors:", result.errors);
154
- * }
155
- * ```
156
- */
157
- export const validateContentEntry = internalQuery({
158
- args: validateContentEntryArgs.fields,
159
- returns: validationResultValidator,
160
- handler: async (ctx, args): Promise<ContentEntryValidationResult> => {
161
- const { contentTypeId, data, options } = args;
162
- const validateReferences = options?.validateReferences ?? true;
163
- const strictFields = options?.strictFields ?? false;
164
-
165
- // Fetch the content type
166
- const contentType = await ctx.db.get(contentTypeId);
167
- if (!contentType) {
168
- return {
169
- valid: false,
170
- errors: [
171
- {
172
- field: "_contentType",
173
- message: `Content type not found: ${contentTypeId}`,
174
- code: "INVALID_CONTENT_TYPE",
175
- },
176
- ],
177
- referencesValidated: false,
178
- };
179
- }
180
-
181
- if (isDeleted(contentType)) {
182
- return {
183
- valid: false,
184
- errors: [
185
- {
186
- field: "_contentType",
187
- message: `Content type has been deleted: ${contentType.name}`,
188
- code: "INVALID_CONTENT_TYPE",
189
- },
190
- ],
191
- contentTypeName: contentType.name,
192
- contentTypeDisplayName: contentType.displayName,
193
- referencesValidated: false,
194
- };
195
- }
196
-
197
- if (!contentType.isActive) {
198
- return {
199
- valid: false,
200
- errors: [
201
- {
202
- field: "_contentType",
203
- message: `Content type is not active: ${contentType.name}`,
204
- code: "INVALID_CONTENT_TYPE",
205
- },
206
- ],
207
- contentTypeName: contentType.name,
208
- contentTypeDisplayName: contentType.displayName,
209
- referencesValidated: false,
210
- };
211
- }
212
-
213
- // Build the schema for validation
214
- const schema: ContentTypeSchema = {
215
- name: contentType.name,
216
- displayName: contentType.displayName,
217
- description: contentType.description,
218
- fields: contentType.fields as FieldDefinition[],
219
- titleField: contentType.titleField,
220
- slugField: contentType.slugField,
221
- singleton: contentType.singleton,
222
- };
223
-
224
- // Perform basic validation
225
- const contentData = data as ContentData;
226
- const basicResult = validateContentData(contentData, schema, {
227
- strictFields,
228
- });
229
-
230
- // Collect all errors
231
- const errors: ValidationError[] = [...basicResult.errors];
232
-
233
- // Perform reference validation if enabled
234
- let referencesValidated = false;
235
- if (validateReferences) {
236
- const referenceErrors = await validateReferences_internal(
237
- ctx,
238
- contentData,
239
- schema.fields,
240
- );
241
- errors.push(...referenceErrors);
242
- referencesValidated = true;
243
- }
244
-
245
- return {
246
- valid: errors.length === 0,
247
- errors,
248
- contentTypeName: contentType.name,
249
- contentTypeDisplayName: contentType.displayName,
250
- referencesValidated,
251
- };
252
- },
253
- });
254
-
255
- /**
256
- * Public query to validate content entry data.
257
- *
258
- * Same as the internal version but exposed as a public query for use
259
- * from the client (e.g., for form validation before submission).
260
- */
261
- export const validateEntry = query({
262
- args: validateContentEntryArgs.fields,
263
- returns: validationResultValidator,
264
- handler: async (ctx, args): Promise<ContentEntryValidationResult> => {
265
- const { contentTypeId, data, options } = args;
266
- const validateReferencesOption = options?.validateReferences ?? true;
267
- const strictFields = options?.strictFields ?? false;
268
-
269
- // Fetch the content type
270
- const contentType = await ctx.db.get(contentTypeId);
271
- if (!contentType) {
272
- return {
273
- valid: false,
274
- errors: [
275
- {
276
- field: "_contentType",
277
- message: `Content type not found: ${contentTypeId}`,
278
- code: "INVALID_CONTENT_TYPE",
279
- },
280
- ],
281
- referencesValidated: false,
282
- };
283
- }
284
-
285
- if (isDeleted(contentType)) {
286
- return {
287
- valid: false,
288
- errors: [
289
- {
290
- field: "_contentType",
291
- message: `Content type has been deleted: ${contentType.name}`,
292
- code: "INVALID_CONTENT_TYPE",
293
- },
294
- ],
295
- contentTypeName: contentType.name,
296
- contentTypeDisplayName: contentType.displayName,
297
- referencesValidated: false,
298
- };
299
- }
300
-
301
- if (!contentType.isActive) {
302
- return {
303
- valid: false,
304
- errors: [
305
- {
306
- field: "_contentType",
307
- message: `Content type is not active: ${contentType.name}`,
308
- code: "INVALID_CONTENT_TYPE",
309
- },
310
- ],
311
- contentTypeName: contentType.name,
312
- contentTypeDisplayName: contentType.displayName,
313
- referencesValidated: false,
314
- };
315
- }
316
-
317
- // Build the schema for validation
318
- const schema: ContentTypeSchema = {
319
- name: contentType.name,
320
- displayName: contentType.displayName,
321
- description: contentType.description,
322
- fields: contentType.fields as FieldDefinition[],
323
- titleField: contentType.titleField,
324
- slugField: contentType.slugField,
325
- singleton: contentType.singleton,
326
- };
327
-
328
- // Perform basic validation
329
- const contentData = data as ContentData;
330
- const basicResult = validateContentData(contentData, schema, {
331
- strictFields,
332
- });
333
-
334
- // Collect all errors
335
- const errors: ValidationError[] = [...basicResult.errors];
336
-
337
- // Perform reference validation if enabled
338
- let referencesValidated = false;
339
- if (validateReferencesOption) {
340
- const referenceErrors = await validateReferences_internal(
341
- ctx,
342
- contentData,
343
- schema.fields,
344
- );
345
- errors.push(...referenceErrors);
346
- referencesValidated = true;
347
- }
348
-
349
- return {
350
- valid: errors.length === 0,
351
- errors,
352
- contentTypeName: contentType.name,
353
- contentTypeDisplayName: contentType.displayName,
354
- referencesValidated,
355
- };
356
- },
357
- });
358
-
359
- /**
360
- * Internal query to validate content entry data by content type name.
361
- *
362
- * Same as validateContentEntry but accepts content type name instead of ID.
363
- * Useful when you know the type name but not the ID.
364
- */
365
- export const validateContentEntryByTypeName = internalQuery({
366
- args: validateContentEntryByTypeNameArgs.fields,
367
- returns: validationResultValidator,
368
- handler: async (ctx, args): Promise<ContentEntryValidationResult> => {
369
- const { contentTypeName, data, options } = args;
370
-
371
- // Find the content type by name
372
- const contentType = await ctx.db
373
- .query("contentTypes")
374
- .withIndex("by_name", (q) => q.eq("name", contentTypeName))
375
- .first();
376
-
377
- if (!contentType) {
378
- return {
379
- valid: false,
380
- errors: [
381
- {
382
- field: "_contentType",
383
- message: `Content type not found: ${contentTypeName}`,
384
- code: "INVALID_CONTENT_TYPE",
385
- },
386
- ],
387
- referencesValidated: false,
388
- };
389
- }
390
-
391
- // Delegate to the ID-based validation
392
- const validateReferencesOption = options?.validateReferences ?? true;
393
- const strictFields = options?.strictFields ?? false;
394
-
395
- if (isDeleted(contentType)) {
396
- return {
397
- valid: false,
398
- errors: [
399
- {
400
- field: "_contentType",
401
- message: `Content type has been deleted: ${contentType.name}`,
402
- code: "INVALID_CONTENT_TYPE",
403
- },
404
- ],
405
- contentTypeName: contentType.name,
406
- contentTypeDisplayName: contentType.displayName,
407
- referencesValidated: false,
408
- };
409
- }
410
-
411
- if (!contentType.isActive) {
412
- return {
413
- valid: false,
414
- errors: [
415
- {
416
- field: "_contentType",
417
- message: `Content type is not active: ${contentType.name}`,
418
- code: "INVALID_CONTENT_TYPE",
419
- },
420
- ],
421
- contentTypeName: contentType.name,
422
- contentTypeDisplayName: contentType.displayName,
423
- referencesValidated: false,
424
- };
425
- }
426
-
427
- // Build the schema for validation
428
- const schema: ContentTypeSchema = {
429
- name: contentType.name,
430
- displayName: contentType.displayName,
431
- description: contentType.description,
432
- fields: contentType.fields as FieldDefinition[],
433
- titleField: contentType.titleField,
434
- slugField: contentType.slugField,
435
- singleton: contentType.singleton,
436
- };
437
-
438
- // Perform basic validation
439
- const contentData = data as ContentData;
440
- const basicResult = validateContentData(contentData, schema, {
441
- strictFields,
442
- });
443
-
444
- // Collect all errors
445
- const errors: ValidationError[] = [...basicResult.errors];
446
-
447
- // Perform reference validation if enabled
448
- let referencesValidated = false;
449
- if (validateReferencesOption) {
450
- const referenceErrors = await validateReferences_internal(
451
- ctx,
452
- contentData,
453
- schema.fields,
454
- );
455
- errors.push(...referenceErrors);
456
- referencesValidated = true;
457
- }
458
-
459
- return {
460
- valid: errors.length === 0,
461
- errors,
462
- contentTypeName: contentType.name,
463
- contentTypeDisplayName: contentType.displayName,
464
- referencesValidated,
465
- };
466
- },
467
- });
468
-
469
- // =============================================================================
470
- // Helper Functions
471
- // =============================================================================
472
-
473
- /**
474
- * Validates reference and media fields by checking that referenced entries exist
475
- * and belong to allowed content types.
476
- *
477
- * @param ctx - The query context for database access
478
- * @param data - The content data being validated
479
- * @param fields - The field definitions from the content type
480
- * @returns Array of validation errors for invalid references
481
- */
482
- async function validateReferences_internal(
483
- ctx: { db: { get: (id: Id<any>) => Promise<any> } },
484
- data: ContentData,
485
- fields: FieldDefinition[],
486
- ): Promise<ValidationError[]> {
487
- const errors: ValidationError[] = [];
488
-
489
- for (const field of fields) {
490
- const value = data[field.name];
491
-
492
- // Skip if no value
493
- if (value === null || value === undefined) {
494
- continue;
495
- }
496
-
497
- if (field.type === "reference") {
498
- const multiple = field.options?.multiple ?? false;
499
- const allowedContentTypes = field.options?.allowedContentTypes;
500
-
501
- if (multiple && Array.isArray(value)) {
502
- // Multiple references
503
- for (let i = 0; i < value.length; i++) {
504
- const refId = value[i];
505
- if (typeof refId === "string") {
506
- const refErrors = await validateSingleReference(
507
- ctx,
508
- refId,
509
- field.name,
510
- allowedContentTypes,
511
- i,
512
- );
513
- errors.push(...refErrors);
514
- }
515
- }
516
- } else if (!multiple && typeof value === "string") {
517
- // Single reference
518
- const refErrors = await validateSingleReference(
519
- ctx,
520
- value,
521
- field.name,
522
- allowedContentTypes,
523
- );
524
- errors.push(...refErrors);
525
- }
526
- }
527
-
528
- if (field.type === "media") {
529
- const multiple = field.options?.multiple ?? false;
530
-
531
- if (multiple && Array.isArray(value)) {
532
- // Multiple media assets
533
- for (let i = 0; i < value.length; i++) {
534
- const assetId = value[i];
535
- if (typeof assetId === "string") {
536
- const mediaErrors = await validateSingleMediaAsset(
537
- ctx,
538
- assetId,
539
- field.name,
540
- field.options?.allowedMimeTypes,
541
- field.options?.maxFileSize,
542
- i,
543
- );
544
- errors.push(...mediaErrors);
545
- }
546
- }
547
- } else if (!multiple && typeof value === "string") {
548
- // Single media asset
549
- const mediaErrors = await validateSingleMediaAsset(
550
- ctx,
551
- value,
552
- field.name,
553
- field.options?.allowedMimeTypes,
554
- field.options?.maxFileSize,
555
- );
556
- errors.push(...mediaErrors);
557
- }
558
- }
559
- }
560
-
561
- return errors;
562
- }
563
-
564
- /**
565
- * Validates a single reference to a content entry.
566
- */
567
- async function validateSingleReference(
568
- ctx: { db: { get: (id: Id<any>) => Promise<any> } },
569
- referenceId: string,
570
- fieldName: string,
571
- allowedContentTypes?: string[],
572
- index?: number,
573
- ): Promise<ValidationError[]> {
574
- const errors: ValidationError[] = [];
575
- const fieldLabel = index !== undefined ? `${fieldName}[${index}]` : fieldName;
576
-
577
- try {
578
- // Try to get the referenced entry
579
- const entry = await ctx.db.get(referenceId as Id<"contentEntries">);
580
-
581
- if (!entry) {
582
- errors.push({
583
- field: fieldLabel,
584
- message: `Referenced entry not found: ${referenceId}`,
585
- code: "INVALID_CONTENT_TYPE",
586
- });
587
- return errors;
588
- }
589
-
590
- // Check if entry is deleted
591
- if (isDeleted(entry)) {
592
- errors.push({
593
- field: fieldLabel,
594
- message: `Referenced entry has been deleted: ${referenceId}`,
595
- code: "INVALID_CONTENT_TYPE",
596
- });
597
- return errors;
598
- }
599
-
600
- // Validate content type constraint
601
- if (allowedContentTypes && allowedContentTypes.length > 0) {
602
- const contentType = await ctx.db.get(entry.contentTypeId);
603
- if (contentType) {
604
- if (!allowedContentTypes.includes(contentType.name)) {
605
- errors.push({
606
- field: fieldLabel,
607
- message: `Reference must be of type: ${allowedContentTypes.join(
608
- ", ",
609
- )}. Got: ${contentType.name}`,
610
- code: "INVALID_CONTENT_TYPE",
611
- });
612
- }
613
- }
614
- }
615
- } catch {
616
- // Invalid ID format
617
- errors.push({
618
- field: fieldLabel,
619
- message: `Invalid reference ID format: ${referenceId}`,
620
- code: "INVALID_TYPE",
621
- });
622
- }
623
-
624
- return errors;
625
- }
626
-
627
- /**
628
- * Validates a single media asset reference.
629
- */
630
- async function validateSingleMediaAsset(
631
- ctx: { db: { get: (id: Id<any>) => Promise<any> } },
632
- assetId: string,
633
- fieldName: string,
634
- allowedMimeTypes?: string[],
635
- maxFileSize?: number,
636
- index?: number,
637
- ): Promise<ValidationError[]> {
638
- const errors: ValidationError[] = [];
639
- const fieldLabel = index !== undefined ? `${fieldName}[${index}]` : fieldName;
640
-
641
- try {
642
- // Try to get the media item
643
- const item = await ctx.db.get(assetId as Id<"mediaItems">);
644
-
645
- if (!item) {
646
- errors.push({
647
- field: fieldLabel,
648
- message: `Media asset not found: ${assetId}`,
649
- code: "INVALID_TYPE",
650
- });
651
- return errors;
652
- }
653
-
654
- // Check if it's an asset (not a folder)
655
- if (item.kind !== "asset") {
656
- errors.push({
657
- field: fieldLabel,
658
- message: `Media reference must be an asset, not a folder: ${assetId}`,
659
- code: "INVALID_TYPE",
660
- });
661
- return errors;
662
- }
663
-
664
- // Check if asset is deleted
665
- if (isDeleted(item)) {
666
- errors.push({
667
- field: fieldLabel,
668
- message: `Media asset has been deleted: ${assetId}`,
669
- code: "INVALID_TYPE",
670
- });
671
- return errors;
672
- }
673
-
674
- // Validate MIME type constraint
675
- if (allowedMimeTypes && allowedMimeTypes.length > 0) {
676
- if (!allowedMimeTypes.includes(item.mimeType)) {
677
- errors.push({
678
- field: fieldLabel,
679
- message: `Media type not allowed. Expected: ${allowedMimeTypes.join(
680
- ", ",
681
- )}. Got: ${item.mimeType}`,
682
- code: "INVALID_MIME_TYPE",
683
- });
684
- }
685
- }
686
-
687
- // Validate file size constraint
688
- if (maxFileSize !== undefined && item.size > maxFileSize) {
689
- const maxSizeKB = Math.round(maxFileSize / 1024);
690
- const actualSizeKB = Math.round(item.size / 1024);
691
- errors.push({
692
- field: fieldLabel,
693
- message: `File too large. Maximum: ${maxSizeKB}KB. Actual: ${actualSizeKB}KB`,
694
- code: "FILE_TOO_LARGE",
695
- });
696
- }
697
- } catch {
698
- // Invalid ID format
699
- errors.push({
700
- field: fieldLabel,
701
- message: `Invalid media asset ID format: ${assetId}`,
702
- code: "INVALID_TYPE",
703
- });
704
- }
705
-
706
- return errors;
707
- }