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,574 @@
1
+ /**
2
+ * Schema Drift Detection
3
+ *
4
+ * Compares code-defined schemas against database schemas to detect
5
+ * discrepancies that could cause runtime issues.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { detectSchemaDrift } from "@convex-cms/core";
10
+ *
11
+ * const report = await detectSchemaDrift(ctx, cms, contentSchema);
12
+ *
13
+ * if (report.hasDrift) {
14
+ * console.warn("Schema drift detected:");
15
+ * console.log(report.summary);
16
+ *
17
+ * for (const diff of report.fieldDifferences) {
18
+ * console.log(`${diff.contentType}.${diff.field}: ${diff.message}`);
19
+ * }
20
+ * }
21
+ * ```
22
+ */
23
+
24
+ import type { ContentTypeDefinition } from "./types.js";
25
+ import type { ContentSchemaInstance } from "./defineContentType.js";
26
+ import { toFieldDefinitions, type DatabaseFieldDefinition } from "./defineContentType.js";
27
+ import type { ConvexContext } from "../wrapper.js";
28
+ import type { CmsClient } from "../wrapper.js";
29
+ import type { ContentType, FieldDefinition } from "../types.js";
30
+
31
+ // =============================================================================
32
+ // Types
33
+ // =============================================================================
34
+
35
+ /**
36
+ * Severity level for drift issues.
37
+ */
38
+ export type DriftSeverity = "error" | "warning" | "info";
39
+
40
+ /**
41
+ * Type of schema difference detected.
42
+ */
43
+ export type DriftType =
44
+ | "CONTENT_TYPE_MISSING_IN_DB"
45
+ | "CONTENT_TYPE_MISSING_IN_CODE"
46
+ | "FIELD_MISSING_IN_DB"
47
+ | "FIELD_MISSING_IN_CODE"
48
+ | "FIELD_TYPE_MISMATCH"
49
+ | "FIELD_REQUIRED_MISMATCH"
50
+ | "FIELD_OPTIONS_MISMATCH"
51
+ | "CONTENT_TYPE_METADATA_MISMATCH";
52
+
53
+ /**
54
+ * A single schema drift issue.
55
+ */
56
+ export interface DriftIssue {
57
+ /**
58
+ * Type of drift detected.
59
+ */
60
+ type: DriftType;
61
+
62
+ /**
63
+ * Severity level.
64
+ */
65
+ severity: DriftSeverity;
66
+
67
+ /**
68
+ * Content type name involved.
69
+ */
70
+ contentType: string;
71
+
72
+ /**
73
+ * Field name (if applicable).
74
+ */
75
+ field?: string;
76
+
77
+ /**
78
+ * Human-readable description of the issue.
79
+ */
80
+ message: string;
81
+
82
+ /**
83
+ * Expected value (from code).
84
+ */
85
+ expected?: unknown;
86
+
87
+ /**
88
+ * Actual value (from database).
89
+ */
90
+ actual?: unknown;
91
+ }
92
+
93
+ /**
94
+ * Summary statistics for the drift report.
95
+ */
96
+ export interface DriftSummary {
97
+ /**
98
+ * Number of content types only in code (not in database).
99
+ */
100
+ missingInDatabase: number;
101
+
102
+ /**
103
+ * Number of content types only in database (not in code).
104
+ */
105
+ missingInCode: number;
106
+
107
+ /**
108
+ * Number of field-level differences.
109
+ */
110
+ fieldDifferences: number;
111
+
112
+ /**
113
+ * Total number of issues found.
114
+ */
115
+ totalIssues: number;
116
+
117
+ /**
118
+ * Number of error-level issues.
119
+ */
120
+ errors: number;
121
+
122
+ /**
123
+ * Number of warning-level issues.
124
+ */
125
+ warnings: number;
126
+ }
127
+
128
+ /**
129
+ * Full schema drift detection report.
130
+ */
131
+ export interface SchemaDriftReport {
132
+ /**
133
+ * Whether any drift was detected.
134
+ */
135
+ hasDrift: boolean;
136
+
137
+ /**
138
+ * Summary statistics.
139
+ */
140
+ summary: DriftSummary;
141
+
142
+ /**
143
+ * All detected issues.
144
+ */
145
+ issues: DriftIssue[];
146
+
147
+ /**
148
+ * Content types defined in code but not in database.
149
+ */
150
+ missingInDatabase: string[];
151
+
152
+ /**
153
+ * Content types in database but not in code.
154
+ */
155
+ missingInCode: string[];
156
+
157
+ /**
158
+ * Timestamp when the check was performed.
159
+ */
160
+ checkedAt: number;
161
+ }
162
+
163
+ /**
164
+ * Options for drift detection.
165
+ */
166
+ export interface DetectDriftOptions {
167
+ /**
168
+ * Whether to include info-level issues in the report.
169
+ * @default false
170
+ */
171
+ includeInfoLevel?: boolean;
172
+
173
+ /**
174
+ * Content type names to check. If not provided, checks all.
175
+ */
176
+ contentTypes?: string[];
177
+
178
+ /**
179
+ * Whether to treat missing-in-database as errors.
180
+ * When true, code types not in DB are errors; when false, they're warnings.
181
+ * @default true
182
+ */
183
+ strictMissingInDb?: boolean;
184
+
185
+ /**
186
+ * Whether to treat missing-in-code as errors.
187
+ * When false, DB types not in code are warnings (allows admin-created types).
188
+ * @default false
189
+ */
190
+ strictMissingInCode?: boolean;
191
+ }
192
+
193
+ // =============================================================================
194
+ // Detection Functions
195
+ // =============================================================================
196
+
197
+ /**
198
+ * Detects schema drift between code-defined schemas and database state.
199
+ *
200
+ * @param ctx - Convex context
201
+ * @param cmsClient - The CMS client to use for database queries
202
+ * @param schema - The code-defined content schema
203
+ * @param options - Detection options
204
+ * @returns A drift report with all detected issues
205
+ *
206
+ * @example
207
+ * ```typescript
208
+ * const report = await detectSchemaDrift(ctx, cms, contentSchema);
209
+ *
210
+ * if (report.hasDrift) {
211
+ * console.error("Schema drift detected!");
212
+ * console.log(`Errors: ${report.summary.errors}`);
213
+ * console.log(`Warnings: ${report.summary.warnings}`);
214
+ *
215
+ * for (const issue of report.issues) {
216
+ * console.log(`[${issue.severity}] ${issue.message}`);
217
+ * }
218
+ * }
219
+ * ```
220
+ */
221
+ export async function detectSchemaDrift<
222
+ TSchema extends ContentSchemaInstance<Record<string, ContentTypeDefinition>>
223
+ >(
224
+ ctx: ConvexContext,
225
+ cmsClient: CmsClient,
226
+ schema: TSchema,
227
+ options: DetectDriftOptions = {}
228
+ ): Promise<SchemaDriftReport> {
229
+ const {
230
+ includeInfoLevel = false,
231
+ contentTypes: filterTypes,
232
+ strictMissingInDb = true,
233
+ strictMissingInCode = false,
234
+ } = options;
235
+
236
+ const issues: DriftIssue[] = [];
237
+ const missingInDatabase: string[] = [];
238
+ const missingInCode: string[] = [];
239
+
240
+ // Get all content types from database
241
+ const dbTypes = await cmsClient.contentTypes.getAll(ctx);
242
+ const dbTypeMap = new Map(dbTypes.map((t) => [t.name, t]));
243
+
244
+ // Get code-defined content types
245
+ const codeDefinitions = Object.values(schema.definitions) as ContentTypeDefinition[];
246
+ const codeTypeNames = new Set(codeDefinitions.map((d) => d.name));
247
+
248
+ // Filter if specific types requested
249
+ const typesToCheck = filterTypes
250
+ ? codeDefinitions.filter((d) => filterTypes.includes(d.name))
251
+ : codeDefinitions;
252
+
253
+ // Check code types against database
254
+ for (const codeDef of typesToCheck) {
255
+ const dbType = dbTypeMap.get(codeDef.name);
256
+
257
+ if (!dbType) {
258
+ // Code type not in database
259
+ missingInDatabase.push(codeDef.name);
260
+ issues.push({
261
+ type: "CONTENT_TYPE_MISSING_IN_DB",
262
+ severity: strictMissingInDb ? "error" : "warning",
263
+ contentType: codeDef.name,
264
+ message: `Content type "${codeDef.name}" is defined in code but not registered in the database`,
265
+ });
266
+ continue;
267
+ }
268
+
269
+ // Compare fields
270
+ const codeFields = toFieldDefinitions(codeDef);
271
+ const fieldIssues = compareFields(codeDef.name, codeFields, dbType.fields);
272
+ issues.push(...fieldIssues);
273
+
274
+ // Compare metadata (info level)
275
+ if (includeInfoLevel) {
276
+ const metaIssues = compareMetadata(codeDef, dbType);
277
+ issues.push(...metaIssues);
278
+ }
279
+ }
280
+
281
+ // Check for database types not in code
282
+ const dbTypeNames = filterTypes
283
+ ? dbTypes.filter((t) => filterTypes.includes(t.name)).map((t) => t.name)
284
+ : dbTypes.map((t) => t.name);
285
+
286
+ for (const dbTypeName of dbTypeNames) {
287
+ if (!codeTypeNames.has(dbTypeName)) {
288
+ missingInCode.push(dbTypeName);
289
+ issues.push({
290
+ type: "CONTENT_TYPE_MISSING_IN_CODE",
291
+ severity: strictMissingInCode ? "error" : "warning",
292
+ contentType: dbTypeName,
293
+ message: `Content type "${dbTypeName}" exists in database but is not defined in code`,
294
+ });
295
+ }
296
+ }
297
+
298
+ // Calculate summary
299
+ const summary: DriftSummary = {
300
+ missingInDatabase: missingInDatabase.length,
301
+ missingInCode: missingInCode.length,
302
+ fieldDifferences: issues.filter((i) => i.field !== undefined).length,
303
+ totalIssues: issues.length,
304
+ errors: issues.filter((i) => i.severity === "error").length,
305
+ warnings: issues.filter((i) => i.severity === "warning").length,
306
+ };
307
+
308
+ return {
309
+ hasDrift: issues.length > 0,
310
+ summary,
311
+ issues: includeInfoLevel ? issues : issues.filter((i) => i.severity !== "info"),
312
+ missingInDatabase,
313
+ missingInCode,
314
+ checkedAt: Date.now(),
315
+ };
316
+ }
317
+
318
+ /**
319
+ * Compare field definitions between code and database.
320
+ */
321
+ function compareFields(
322
+ contentTypeName: string,
323
+ codeFields: DatabaseFieldDefinition[],
324
+ dbFields: FieldDefinition[]
325
+ ): DriftIssue[] {
326
+ const issues: DriftIssue[] = [];
327
+
328
+ const codeFieldMap = new Map(codeFields.map((f) => [f.name, f]));
329
+ const dbFieldMap = new Map(dbFields.map((f) => [f.name, f]));
330
+
331
+ // Check code fields against database
332
+ for (const codeField of codeFields) {
333
+ const dbField = dbFieldMap.get(codeField.name);
334
+
335
+ if (!dbField) {
336
+ issues.push({
337
+ type: "FIELD_MISSING_IN_DB",
338
+ severity: "error",
339
+ contentType: contentTypeName,
340
+ field: codeField.name,
341
+ message: `Field "${codeField.name}" is defined in code but not in the database schema`,
342
+ });
343
+ continue;
344
+ }
345
+
346
+ // Check type
347
+ if (codeField.type !== dbField.type) {
348
+ issues.push({
349
+ type: "FIELD_TYPE_MISMATCH",
350
+ severity: "error",
351
+ contentType: contentTypeName,
352
+ field: codeField.name,
353
+ message: `Field "${codeField.name}" type mismatch: code expects "${codeField.type}", database has "${dbField.type}"`,
354
+ expected: codeField.type,
355
+ actual: dbField.type,
356
+ });
357
+ }
358
+
359
+ // Check required
360
+ if (codeField.required !== dbField.required) {
361
+ issues.push({
362
+ type: "FIELD_REQUIRED_MISMATCH",
363
+ severity: "warning",
364
+ contentType: contentTypeName,
365
+ field: codeField.name,
366
+ message: `Field "${codeField.name}" required mismatch: code expects ${codeField.required ? "required" : "optional"}, database has ${dbField.required ? "required" : "optional"}`,
367
+ expected: codeField.required,
368
+ actual: dbField.required,
369
+ });
370
+ }
371
+
372
+ // Check options (selective comparison)
373
+ // Cast to Record<string, unknown> since FieldOptions shape may vary
374
+ const optionsDiff = compareFieldOptions(
375
+ codeField.options as Record<string, unknown> | undefined,
376
+ dbField.options as Record<string, unknown> | undefined
377
+ );
378
+ if (optionsDiff) {
379
+ issues.push({
380
+ type: "FIELD_OPTIONS_MISMATCH",
381
+ severity: "warning",
382
+ contentType: contentTypeName,
383
+ field: codeField.name,
384
+ message: `Field "${codeField.name}" has different options: ${optionsDiff}`,
385
+ });
386
+ }
387
+ }
388
+
389
+ // Check for fields in database not in code
390
+ for (const dbField of dbFields) {
391
+ if (!codeFieldMap.has(dbField.name)) {
392
+ issues.push({
393
+ type: "FIELD_MISSING_IN_CODE",
394
+ severity: "warning",
395
+ contentType: contentTypeName,
396
+ field: dbField.name,
397
+ message: `Field "${dbField.name}" exists in database but is not defined in code`,
398
+ });
399
+ }
400
+ }
401
+
402
+ return issues;
403
+ }
404
+
405
+ /**
406
+ * Compare field options and return a description of differences.
407
+ */
408
+ function compareFieldOptions(
409
+ codeOptions: Record<string, unknown> | undefined,
410
+ dbOptions: Record<string, unknown> | undefined
411
+ ): string | null {
412
+ if (!codeOptions && !dbOptions) return null;
413
+ if (!codeOptions && dbOptions) return "database has options, code does not";
414
+ if (codeOptions && !dbOptions) return "code has options, database does not";
415
+
416
+ const differences: string[] = [];
417
+
418
+ // Check for important option differences
419
+ const importantOptions = [
420
+ "minLength",
421
+ "maxLength",
422
+ "min",
423
+ "max",
424
+ "pattern",
425
+ "allowedContentTypes",
426
+ "allowedMimeTypes",
427
+ "multiple",
428
+ "options", // for select fields
429
+ ];
430
+
431
+ for (const key of importantOptions) {
432
+ const codeValue = codeOptions![key];
433
+ const dbValue = dbOptions![key];
434
+
435
+ if (codeValue !== undefined && dbValue === undefined) {
436
+ differences.push(`${key} missing in database`);
437
+ } else if (codeValue === undefined && dbValue !== undefined) {
438
+ differences.push(`${key} missing in code`);
439
+ } else if (JSON.stringify(codeValue) !== JSON.stringify(dbValue)) {
440
+ differences.push(`${key} differs`);
441
+ }
442
+ }
443
+
444
+ return differences.length > 0 ? differences.join(", ") : null;
445
+ }
446
+
447
+ /**
448
+ * Compare content type metadata.
449
+ */
450
+ function compareMetadata(
451
+ codeDef: ContentTypeDefinition,
452
+ dbType: ContentType
453
+ ): DriftIssue[] {
454
+ const issues: DriftIssue[] = [];
455
+
456
+ if (codeDef.meta?.displayName && codeDef.meta.displayName !== dbType.displayName) {
457
+ issues.push({
458
+ type: "CONTENT_TYPE_METADATA_MISMATCH",
459
+ severity: "info",
460
+ contentType: codeDef.name,
461
+ message: `Display name mismatch: code has "${codeDef.meta.displayName}", database has "${dbType.displayName}"`,
462
+ expected: codeDef.meta.displayName,
463
+ actual: dbType.displayName,
464
+ });
465
+ }
466
+
467
+ if (codeDef.meta?.titleField && codeDef.meta.titleField !== dbType.titleField) {
468
+ issues.push({
469
+ type: "CONTENT_TYPE_METADATA_MISMATCH",
470
+ severity: "info",
471
+ contentType: codeDef.name,
472
+ message: `Title field mismatch: code has "${codeDef.meta.titleField}", database has "${dbType.titleField}"`,
473
+ expected: codeDef.meta.titleField,
474
+ actual: dbType.titleField,
475
+ });
476
+ }
477
+
478
+ return issues;
479
+ }
480
+
481
+ // =============================================================================
482
+ // Formatting Utilities
483
+ // =============================================================================
484
+
485
+ /**
486
+ * Format a drift report as a human-readable string.
487
+ *
488
+ * @param report - The drift report to format
489
+ * @returns A formatted string suitable for console output
490
+ */
491
+ export function formatDriftReport(report: SchemaDriftReport): string {
492
+ if (!report.hasDrift) {
493
+ return "No schema drift detected. Code and database schemas are in sync.";
494
+ }
495
+
496
+ const lines: string[] = [
497
+ "Schema Drift Report",
498
+ "===================",
499
+ "",
500
+ `Total Issues: ${report.summary.totalIssues}`,
501
+ ` Errors: ${report.summary.errors}`,
502
+ ` Warnings: ${report.summary.warnings}`,
503
+ "",
504
+ ];
505
+
506
+ if (report.missingInDatabase.length > 0) {
507
+ lines.push("Content Types Missing in Database:");
508
+ for (const name of report.missingInDatabase) {
509
+ lines.push(` - ${name}`);
510
+ }
511
+ lines.push("");
512
+ }
513
+
514
+ if (report.missingInCode.length > 0) {
515
+ lines.push("Content Types Missing in Code:");
516
+ for (const name of report.missingInCode) {
517
+ lines.push(` - ${name}`);
518
+ }
519
+ lines.push("");
520
+ }
521
+
522
+ const fieldIssues = report.issues.filter((i) => i.field);
523
+ if (fieldIssues.length > 0) {
524
+ lines.push("Field Differences:");
525
+ for (const issue of fieldIssues) {
526
+ const prefix = issue.severity === "error" ? "[ERROR]" : "[WARN]";
527
+ lines.push(` ${prefix} ${issue.contentType}.${issue.field}: ${issue.message}`);
528
+ }
529
+ }
530
+
531
+ return lines.join("\n");
532
+ }
533
+
534
+ /**
535
+ * Check if a drift report h errors (not just warnings).
536
+ *
537
+ * @param report - The drift report to check
538
+ * @returns true if there are error-level issues
539
+ */
540
+ export function hasErrors(report: SchemaDriftReport): boolean {
541
+ return report.summary.errors > 0;
542
+ }
543
+
544
+ /**
545
+ * Filter a drift report to only include specific content types.
546
+ *
547
+ * @param report - The full drift report
548
+ * @param contentTypes - Content type names to include
549
+ * @returns A filtered report
550
+ */
551
+ export function filterReportByContentTypes(
552
+ report: SchemaDriftReport,
553
+ contentTypes: string[]
554
+ ): SchemaDriftReport {
555
+ const typeSet = new Set(contentTypes);
556
+
557
+ const filteredIssues = report.issues.filter((i) => typeSet.has(i.contentType));
558
+
559
+ return {
560
+ ...report,
561
+ issues: filteredIssues,
562
+ missingInDatabase: report.missingInDatabase.filter((n) => typeSet.has(n)),
563
+ missingInCode: report.missingInCode.filter((n) => typeSet.has(n)),
564
+ summary: {
565
+ missingInDatabase: report.missingInDatabase.filter((n) => typeSet.has(n)).length,
566
+ missingInCode: report.missingInCode.filter((n) => typeSet.has(n)).length,
567
+ fieldDifferences: filteredIssues.filter((i) => i.field !== undefined).length,
568
+ totalIssues: filteredIssues.length,
569
+ errors: filteredIssues.filter((i) => i.severity === "error").length,
570
+ warnings: filteredIssues.filter((i) => i.severity === "warning").length,
571
+ },
572
+ hasDrift: filteredIssues.length > 0,
573
+ };
574
+ }