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,805 @@
1
+ /**
2
+ * Content Type Migration Utility
3
+ *
4
+ * Provides utilities to safely migrate content when content type schemas change.
5
+ * Handles field additions, removals, renames, and type changes with data
6
+ * transformation functions.
7
+ *
8
+ * Key features:
9
+ * - Dry-run mode to preview changes before committing
10
+ * - Custom transformation functions for type conversions
11
+ * - Field renaming support
12
+ * - Default value assignment for new fields
13
+ * - Version snapshot creation for rollback capability
14
+ * - Batch processing to respect Convex transaction limits
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * // Migrate content when changing a field from text to number
19
+ * const result = await ctx.runMutation(api.contentTypeMigration.migrateContentType, {
20
+ * contentTypeId: typeId,
21
+ * migrations: [
22
+ * {
23
+ * type: "TRANSFORM_FIELD",
24
+ * fieldName: "price",
25
+ * transformation: "TEXT_TO_NUMBER",
26
+ * },
27
+ * {
28
+ * type: "RENAME_FIELD",
29
+ * oldFieldName: "desc",
30
+ * newFieldName: "description",
31
+ * },
32
+ * {
33
+ * type: "ADD_FIELD",
34
+ * fieldName: "featured",
35
+ * defaultValue: false,
36
+ * },
37
+ * ],
38
+ * dryRun: true, // Preview changes first
39
+ * migratedBy: currentUserId,
40
+ * });
41
+ * ```
42
+ */
43
+ import { v } from "convex/values";
44
+ import { isDeleted } from "./lib/softDelete.js";
45
+ import { mutation, query } from "./_generated/server.js";
46
+ // =============================================================================
47
+ // Transformation Functions
48
+ // =============================================================================
49
+ /**
50
+ * Convert a text value to a number.
51
+ */
52
+ function textToNumber(value) {
53
+ if (value === null || value === undefined || value === "") {
54
+ return null;
55
+ }
56
+ if (typeof value === "number") {
57
+ return value;
58
+ }
59
+ if (typeof value === "string") {
60
+ const trimmed = value.trim();
61
+ // Handle common formats
62
+ const cleaned = trimmed.replace(/[,$]/g, "");
63
+ const parsed = parseFloat(cleaned);
64
+ return isNaN(parsed) ? null : parsed;
65
+ }
66
+ return null;
67
+ }
68
+ /**
69
+ * Convert a number value to text.
70
+ */
71
+ function numberToText(value) {
72
+ if (value === null || value === undefined) {
73
+ return null;
74
+ }
75
+ if (typeof value === "string") {
76
+ return value;
77
+ }
78
+ if (typeof value === "number") {
79
+ return String(value);
80
+ }
81
+ return null;
82
+ }
83
+ /**
84
+ * Convert a text value to boolean.
85
+ */
86
+ function textToBoolean(value) {
87
+ if (value === null || value === undefined) {
88
+ return null;
89
+ }
90
+ if (typeof value === "boolean") {
91
+ return value;
92
+ }
93
+ if (typeof value === "string") {
94
+ const lower = value.toLowerCase().trim();
95
+ if (["true", "yes", "1", "on", "enabled"].includes(lower)) {
96
+ return true;
97
+ }
98
+ if (["false", "no", "0", "off", "disabled", ""].includes(lower)) {
99
+ return false;
100
+ }
101
+ return null;
102
+ }
103
+ if (typeof value === "number") {
104
+ return value !== 0;
105
+ }
106
+ return null;
107
+ }
108
+ /**
109
+ * Convert a boolean value to text.
110
+ */
111
+ function booleanToText(value) {
112
+ if (value === null || value === undefined) {
113
+ return null;
114
+ }
115
+ if (typeof value === "string") {
116
+ return value;
117
+ }
118
+ if (typeof value === "boolean") {
119
+ return value ? "true" : "false";
120
+ }
121
+ return null;
122
+ }
123
+ /**
124
+ * Convert a text value to a date timestamp.
125
+ */
126
+ function textToDate(value) {
127
+ if (value === null || value === undefined || value === "") {
128
+ return null;
129
+ }
130
+ if (typeof value === "number") {
131
+ return value;
132
+ }
133
+ if (typeof value === "string") {
134
+ const timestamp = Date.parse(value);
135
+ return isNaN(timestamp) ? null : timestamp;
136
+ }
137
+ return null;
138
+ }
139
+ /**
140
+ * Convert a date timestamp to text (ISO format).
141
+ */
142
+ function dateToText(value) {
143
+ if (value === null || value === undefined) {
144
+ return null;
145
+ }
146
+ if (typeof value === "string") {
147
+ return value;
148
+ }
149
+ if (typeof value === "number") {
150
+ return new Date(value).toISOString();
151
+ }
152
+ return null;
153
+ }
154
+ /**
155
+ * Convert a text value (JSON string) to parsed JSON.
156
+ */
157
+ function textToJson(value) {
158
+ if (value === null || value === undefined || value === "") {
159
+ return null;
160
+ }
161
+ if (typeof value === "object") {
162
+ return value;
163
+ }
164
+ if (typeof value === "string") {
165
+ try {
166
+ return JSON.parse(value);
167
+ }
168
+ catch {
169
+ // If it's not valid JSON, return as-is wrapped in object
170
+ return { value };
171
+ }
172
+ }
173
+ return { value };
174
+ }
175
+ /**
176
+ * Convert a JSON value to text (stringified).
177
+ */
178
+ function jsonToText(value) {
179
+ if (value === null || value === undefined) {
180
+ return null;
181
+ }
182
+ if (typeof value === "string") {
183
+ return value;
184
+ }
185
+ return JSON.stringify(value);
186
+ }
187
+ /**
188
+ * Convert a single value to an array containing that value.
189
+ */
190
+ function singleToArray(value) {
191
+ if (value === null || value === undefined) {
192
+ return [];
193
+ }
194
+ if (Array.isArray(value)) {
195
+ return value;
196
+ }
197
+ return [value];
198
+ }
199
+ /**
200
+ * Convert an array to its first element (or null if empty).
201
+ */
202
+ function arrayToSingle(value) {
203
+ if (value === null || value === undefined) {
204
+ return null;
205
+ }
206
+ if (Array.isArray(value)) {
207
+ return value.length > 0 ? value[0] : null;
208
+ }
209
+ return value;
210
+ }
211
+ /**
212
+ * Remap select/multiSelect values using a provided mapping.
213
+ */
214
+ function selectValueRemap(value, valueMap) {
215
+ if (value === null || value === undefined) {
216
+ return null;
217
+ }
218
+ if (Array.isArray(value)) {
219
+ // Handle multiSelect
220
+ return value.map((v) => {
221
+ if (typeof v === "string" && v in valueMap) {
222
+ return valueMap[v];
223
+ }
224
+ return v;
225
+ });
226
+ }
227
+ if (typeof value === "string" && value in valueMap) {
228
+ return valueMap[value];
229
+ }
230
+ return value;
231
+ }
232
+ /**
233
+ * Apply a built-in transformation to a value.
234
+ */
235
+ function applyTransformation(value, transformation, valueMap) {
236
+ switch (transformation) {
237
+ case "TEXT_TO_NUMBER":
238
+ return textToNumber(value);
239
+ case "NUMBER_TO_TEXT":
240
+ return numberToText(value);
241
+ case "TEXT_TO_BOOLEAN":
242
+ return textToBoolean(value);
243
+ case "BOOLEAN_TO_TEXT":
244
+ return booleanToText(value);
245
+ case "TEXT_TO_DATE":
246
+ return textToDate(value);
247
+ case "DATE_TO_TEXT":
248
+ return dateToText(value);
249
+ case "TEXT_TO_JSON":
250
+ return textToJson(value);
251
+ case "JSON_TO_TEXT":
252
+ return jsonToText(value);
253
+ case "SINGLE_TO_ARRAY":
254
+ return singleToArray(value);
255
+ case "ARRAY_TO_SINGLE":
256
+ return arrayToSingle(value);
257
+ case "SELECT_VALUE_REMAP":
258
+ return selectValueRemap(value, valueMap ?? {});
259
+ default:
260
+ return value;
261
+ }
262
+ }
263
+ // =============================================================================
264
+ // Migration Logic
265
+ // =============================================================================
266
+ /**
267
+ * Apply migration operations to a single entry's data.
268
+ *
269
+ * @param data - The current entry data
270
+ * @param operations - Migration operations to apply
271
+ * @param dryRun - If true, only compute changes without modifying
272
+ * @returns Object containing the migrated data and list of changes
273
+ */
274
+ export function applyMigrations(data, operations) {
275
+ const migratedData = { ...data };
276
+ const changes = [];
277
+ for (const op of operations) {
278
+ switch (op.type) {
279
+ case "ADD_FIELD": {
280
+ if (!op.fieldName)
281
+ continue;
282
+ const fieldName = op.fieldName;
283
+ // Only add if field doesn't exist or is empty (unless preserveEmpty)
284
+ const currentValue = migratedData[fieldName];
285
+ const isEmpty = currentValue === undefined ||
286
+ currentValue === null ||
287
+ currentValue === "";
288
+ if (isEmpty && !op.preserveEmpty) {
289
+ const newValue = op.defaultValue;
290
+ if (newValue !== undefined) {
291
+ changes.push({
292
+ fieldName,
293
+ operation: "ADD_FIELD",
294
+ oldValue: currentValue,
295
+ newValue,
296
+ });
297
+ migratedData[fieldName] = newValue;
298
+ }
299
+ }
300
+ break;
301
+ }
302
+ case "REMOVE_FIELD": {
303
+ if (!op.fieldName)
304
+ continue;
305
+ const fieldName = op.fieldName;
306
+ if (fieldName in migratedData) {
307
+ changes.push({
308
+ fieldName,
309
+ operation: "REMOVE_FIELD",
310
+ oldValue: migratedData[fieldName],
311
+ newValue: undefined,
312
+ });
313
+ delete migratedData[fieldName];
314
+ }
315
+ break;
316
+ }
317
+ case "RENAME_FIELD": {
318
+ if (!op.oldFieldName || !op.newFieldName)
319
+ continue;
320
+ const { oldFieldName, newFieldName } = op;
321
+ if (oldFieldName in migratedData) {
322
+ const value = migratedData[oldFieldName];
323
+ changes.push({
324
+ fieldName: oldFieldName,
325
+ operation: "RENAME_FIELD",
326
+ oldValue: value,
327
+ newValue: value,
328
+ });
329
+ delete migratedData[oldFieldName];
330
+ migratedData[newFieldName] = value;
331
+ }
332
+ break;
333
+ }
334
+ case "TRANSFORM_FIELD": {
335
+ if (!op.fieldName)
336
+ continue;
337
+ const fieldName = op.fieldName;
338
+ if (fieldName in migratedData && op.transformation) {
339
+ const oldValue = migratedData[fieldName];
340
+ const newValue = applyTransformation(oldValue, op.transformation, op.valueMap);
341
+ // Only record change if value actually changed
342
+ if (JSON.stringify(oldValue) !== JSON.stringify(newValue)) {
343
+ changes.push({
344
+ fieldName,
345
+ operation: "TRANSFORM_FIELD",
346
+ oldValue,
347
+ newValue,
348
+ });
349
+ migratedData[fieldName] = newValue;
350
+ }
351
+ }
352
+ break;
353
+ }
354
+ case "SET_DEFAULT": {
355
+ if (!op.fieldName)
356
+ continue;
357
+ const fieldName = op.fieldName;
358
+ const currentValue = migratedData[fieldName];
359
+ const isEmpty = currentValue === undefined ||
360
+ currentValue === null ||
361
+ currentValue === "";
362
+ if (isEmpty && !op.preserveEmpty && op.defaultValue !== undefined) {
363
+ changes.push({
364
+ fieldName,
365
+ operation: "SET_DEFAULT",
366
+ oldValue: currentValue,
367
+ newValue: op.defaultValue,
368
+ });
369
+ migratedData[fieldName] = op.defaultValue;
370
+ }
371
+ break;
372
+ }
373
+ }
374
+ }
375
+ return { migratedData, changes };
376
+ }
377
+ // =============================================================================
378
+ // Validators
379
+ // =============================================================================
380
+ /**
381
+ * Validator for migration operation type.
382
+ */
383
+ const migrationOperationTypeValidator = v.union(v.literal("ADD_FIELD"), v.literal("REMOVE_FIELD"), v.literal("RENAME_FIELD"), v.literal("TRANSFORM_FIELD"), v.literal("SET_DEFAULT"));
384
+ /**
385
+ * Validator for built-in transformation types.
386
+ */
387
+ const builtInTransformationValidator = v.union(v.literal("TEXT_TO_NUMBER"), v.literal("NUMBER_TO_TEXT"), v.literal("TEXT_TO_BOOLEAN"), v.literal("BOOLEAN_TO_TEXT"), v.literal("TEXT_TO_DATE"), v.literal("DATE_TO_TEXT"), v.literal("TEXT_TO_JSON"), v.literal("JSON_TO_TEXT"), v.literal("SINGLE_TO_ARRAY"), v.literal("ARRAY_TO_SINGLE"), v.literal("SELECT_VALUE_REMAP"));
388
+ /**
389
+ * Validator for a migration operation.
390
+ */
391
+ export const migrationOperationValidator = v.object({
392
+ type: migrationOperationTypeValidator,
393
+ fieldName: v.optional(v.string()),
394
+ oldFieldName: v.optional(v.string()),
395
+ newFieldName: v.optional(v.string()),
396
+ defaultValue: v.optional(v.any()),
397
+ transformation: v.optional(builtInTransformationValidator),
398
+ customTransformation: v.optional(v.string()),
399
+ valueMap: v.optional(v.any()),
400
+ preserveEmpty: v.optional(v.boolean()),
401
+ });
402
+ /**
403
+ * Validator for migrate content type arguments.
404
+ */
405
+ export const migrateContentTypeArgs = v.object({
406
+ /** Content type ID to migrate */
407
+ contentTypeId: v.id("contentTypes"),
408
+ /** Array of migration operations to apply */
409
+ migrations: v.array(migrationOperationValidator),
410
+ /** If true, preview changes without applying them */
411
+ dryRun: v.optional(v.boolean()),
412
+ /** Create version snapshots before migration (default: true) */
413
+ createVersionSnapshots: v.optional(v.boolean()),
414
+ /** Filter entries by status (default: all statuses) */
415
+ statusFilter: v.optional(v.array(v.union(v.literal("draft"), v.literal("published"), v.literal("archived"), v.literal("scheduled")))),
416
+ /** Only migrate entries with IDs in this list */
417
+ entryIds: v.optional(v.array(v.id("contentEntries"))),
418
+ /** User performing the migration */
419
+ migratedBy: v.optional(v.string()),
420
+ /** Description of the migration for version history */
421
+ changeDescription: v.optional(v.string()),
422
+ });
423
+ /**
424
+ * Validator for entry migration result.
425
+ */
426
+ const fieldChangeValidator = v.object({
427
+ fieldName: v.string(),
428
+ operation: migrationOperationTypeValidator,
429
+ oldValue: v.optional(v.any()),
430
+ newValue: v.optional(v.any()),
431
+ });
432
+ const entryMigrationResultValidator = v.object({
433
+ entryId: v.id("contentEntries"),
434
+ slug: v.string(),
435
+ success: v.boolean(),
436
+ error: v.optional(v.string()),
437
+ changes: v.optional(v.array(fieldChangeValidator)),
438
+ });
439
+ /**
440
+ * Validator for migration result.
441
+ */
442
+ export const migrationResultValidator = v.object({
443
+ dryRun: v.boolean(),
444
+ totalEntries: v.number(),
445
+ successCount: v.number(),
446
+ failureCount: v.number(),
447
+ skippedCount: v.number(),
448
+ results: v.array(entryMigrationResultValidator),
449
+ versionSnapshotsCreated: v.number(),
450
+ });
451
+ // =============================================================================
452
+ // Mutations
453
+ // =============================================================================
454
+ /**
455
+ * Mutation to migrate content entries when a content type schema changes.
456
+ *
457
+ * This mutation applies a series of migration operations to all entries
458
+ * of a given content type. It supports:
459
+ * - Adding new fields with default values
460
+ * - Removing fields
461
+ * - Renaming fields
462
+ * - Transforming field values (type conversions)
463
+ * - Setting default values for empty fields
464
+ *
465
+ * The mutation can run in dry-run mode to preview changes before applying them.
466
+ * By default, it creates version snapshots before modifying entries.
467
+ *
468
+ * @param contentTypeId - The content type to migrate
469
+ * @param migrations - Array of migration operations to apply
470
+ * @param dryRun - If true, preview changes without applying (default: false)
471
+ * @param createVersionSnapshots - Create version snapshots before migration (default: true)
472
+ * @param statusFilter - Only migrate entries with these statuses
473
+ * @param entryIds - Only migrate entries with these IDs
474
+ * @param migratedBy - User performing the migration
475
+ * @param changeDescription - Description for version history
476
+ *
477
+ * @returns MigrationResult with details of all changes
478
+ *
479
+ * @example
480
+ * ```typescript
481
+ * // Preview migration
482
+ * const preview = await ctx.runMutation(api.contentTypeMigration.migrateContentType, {
483
+ * contentTypeId: blogPostTypeId,
484
+ * migrations: [
485
+ * { type: "RENAME_FIELD", oldFieldName: "body", newFieldName: "content" },
486
+ * { type: "ADD_FIELD", fieldName: "featured", defaultValue: false },
487
+ * { type: "TRANSFORM_FIELD", fieldName: "viewCount", transformation: "TEXT_TO_NUMBER" },
488
+ * ],
489
+ * dryRun: true,
490
+ * });
491
+ *
492
+ * // Apply migration after reviewing preview
493
+ * const result = await ctx.runMutation(api.contentTypeMigration.migrateContentType, {
494
+ * contentTypeId: blogPostTypeId,
495
+ * migrations: [...],
496
+ * dryRun: false,
497
+ * changeDescription: "Renamed body to content, added featured flag",
498
+ * migratedBy: currentUserId,
499
+ * });
500
+ * ```
501
+ */
502
+ export const migrateContentType = mutation({
503
+ args: migrateContentTypeArgs.fields,
504
+ returns: migrationResultValidator,
505
+ handler: async (ctx, args) => {
506
+ const { contentTypeId, migrations, dryRun = false, createVersionSnapshots = true, statusFilter, entryIds, migratedBy, changeDescription = "Content type migration", } = args;
507
+ // Validate content type exists
508
+ const contentType = await ctx.db.get(contentTypeId);
509
+ if (!contentType) {
510
+ throw new Error(`Content type not found: ${contentTypeId}`);
511
+ }
512
+ if (isDeleted(contentType)) {
513
+ throw new Error(`Content type has been deleted: ${contentType.name}`);
514
+ }
515
+ // Validate migration operations
516
+ for (const op of migrations) {
517
+ if (op.type === "RENAME_FIELD" &&
518
+ (!op.oldFieldName || !op.newFieldName)) {
519
+ throw new Error("RENAME_FIELD operation requires both oldFieldName and newFieldName");
520
+ }
521
+ if ((op.type === "ADD_FIELD" ||
522
+ op.type === "REMOVE_FIELD" ||
523
+ op.type === "TRANSFORM_FIELD" ||
524
+ op.type === "SET_DEFAULT") &&
525
+ !op.fieldName) {
526
+ throw new Error(`${op.type} operation requires fieldName`);
527
+ }
528
+ if (op.type === "TRANSFORM_FIELD" && !op.transformation) {
529
+ throw new Error("TRANSFORM_FIELD operation requires transformation type");
530
+ }
531
+ }
532
+ // Build query for entries
533
+ const entriesQuery = ctx.db
534
+ .query("contentEntries")
535
+ .withIndex("by_content_type", (q) => q.eq("contentTypeId", contentTypeId))
536
+ .filter((q) => q.eq(q.field("deletedAt"), undefined));
537
+ // Collect all entries
538
+ const allEntries = await entriesQuery.collect();
539
+ // Filter by status if specified
540
+ let entries = allEntries;
541
+ if (statusFilter && statusFilter.length > 0) {
542
+ entries = entries.filter((e) => statusFilter.includes(e.status));
543
+ }
544
+ // Filter by entry IDs if specified
545
+ if (entryIds && entryIds.length > 0) {
546
+ const entryIdSet = new Set(entryIds.map((id) => id.toString()));
547
+ entries = entries.filter((e) => entryIdSet.has(e._id.toString()));
548
+ }
549
+ // Process entries
550
+ const results = [];
551
+ let successCount = 0;
552
+ let failureCount = 0;
553
+ let skippedCount = 0;
554
+ let versionSnapshotsCreated = 0;
555
+ for (const entry of entries) {
556
+ try {
557
+ const entryData = entry.data;
558
+ const { migratedData, changes } = applyMigrations(entryData, migrations);
559
+ // Skip if no changes
560
+ if (changes.length === 0) {
561
+ results.push({
562
+ entryId: entry._id,
563
+ slug: entry.slug,
564
+ success: true,
565
+ changes: [],
566
+ });
567
+ skippedCount++;
568
+ continue;
569
+ }
570
+ if (dryRun) {
571
+ // In dry run mode, just report what would change
572
+ results.push({
573
+ entryId: entry._id,
574
+ slug: entry.slug,
575
+ success: true,
576
+ changes,
577
+ });
578
+ successCount++;
579
+ }
580
+ else {
581
+ // Create version snapshot before migration
582
+ if (createVersionSnapshots) {
583
+ await ctx.db.insert("contentVersions", {
584
+ entryId: entry._id,
585
+ versionNumber: entry.version,
586
+ data: entry.data,
587
+ slug: entry.slug,
588
+ status: entry.status,
589
+ changeDescription: `Pre-migration snapshot: ${changeDescription}`,
590
+ createdBy: migratedBy,
591
+ wasPublished: entry.status === "published",
592
+ publishedAt: entry.status === "published"
593
+ ? entry.lastPublishedAt
594
+ : undefined,
595
+ });
596
+ versionSnapshotsCreated++;
597
+ }
598
+ // Apply migration
599
+ await ctx.db.patch(entry._id, {
600
+ data: migratedData,
601
+ version: entry.version + 1,
602
+ updatedBy: migratedBy,
603
+ });
604
+ results.push({
605
+ entryId: entry._id,
606
+ slug: entry.slug,
607
+ success: true,
608
+ changes,
609
+ });
610
+ successCount++;
611
+ }
612
+ }
613
+ catch (error) {
614
+ results.push({
615
+ entryId: entry._id,
616
+ slug: entry.slug,
617
+ success: false,
618
+ error: error instanceof Error ? error.message : "Unknown error",
619
+ });
620
+ failureCount++;
621
+ }
622
+ }
623
+ return {
624
+ dryRun,
625
+ totalEntries: entries.length,
626
+ successCount,
627
+ failureCount,
628
+ skippedCount,
629
+ results,
630
+ versionSnapshotsCreated,
631
+ };
632
+ },
633
+ });
634
+ // =============================================================================
635
+ // Queries
636
+ // =============================================================================
637
+ /**
638
+ * Query to preview migration without modifying data.
639
+ *
640
+ * This is a convenience wrapper that always runs in dry-run mode.
641
+ * Use this to safely preview what changes would be made.
642
+ */
643
+ export const previewMigration = query({
644
+ args: {
645
+ contentTypeId: v.id("contentTypes"),
646
+ migrations: v.array(migrationOperationValidator),
647
+ statusFilter: v.optional(v.array(v.union(v.literal("draft"), v.literal("published"), v.literal("archived"), v.literal("scheduled")))),
648
+ entryIds: v.optional(v.array(v.id("contentEntries"))),
649
+ /** Limit number of entries to preview (default: 10) */
650
+ limit: v.optional(v.number()),
651
+ },
652
+ returns: v.object({
653
+ totalEntries: v.number(),
654
+ previewedEntries: v.number(),
655
+ results: v.array(entryMigrationResultValidator),
656
+ summary: v.object({
657
+ entriesWithChanges: v.number(),
658
+ entriesWithoutChanges: v.number(),
659
+ operationCounts: v.any(),
660
+ }),
661
+ }),
662
+ handler: async (ctx, args) => {
663
+ const { contentTypeId, migrations, statusFilter, entryIds, limit = 10, } = args;
664
+ // Validate content type exists
665
+ const contentType = await ctx.db.get(contentTypeId);
666
+ if (!contentType) {
667
+ throw new Error(`Content type not found: ${contentTypeId}`);
668
+ }
669
+ // Build query for entries
670
+ let entries = await ctx.db
671
+ .query("contentEntries")
672
+ .withIndex("by_content_type", (q) => q.eq("contentTypeId", contentTypeId))
673
+ .filter((q) => q.eq(q.field("deletedAt"), undefined))
674
+ .collect();
675
+ const totalEntries = entries.length;
676
+ // Filter by status if specified
677
+ if (statusFilter && statusFilter.length > 0) {
678
+ entries = entries.filter((e) => statusFilter.includes(e.status));
679
+ }
680
+ // Filter by entry IDs if specified
681
+ if (entryIds && entryIds.length > 0) {
682
+ const entryIdSet = new Set(entryIds.map((id) => id.toString()));
683
+ entries = entries.filter((e) => entryIdSet.has(e._id.toString()));
684
+ }
685
+ // Limit entries for preview
686
+ const previewEntries = entries.slice(0, limit);
687
+ const results = [];
688
+ const operationCounts = {};
689
+ let entriesWithChanges = 0;
690
+ let entriesWithoutChanges = 0;
691
+ for (const entry of previewEntries) {
692
+ const entryData = entry.data;
693
+ const { changes } = applyMigrations(entryData, migrations);
694
+ if (changes.length > 0) {
695
+ entriesWithChanges++;
696
+ for (const change of changes) {
697
+ operationCounts[change.operation] =
698
+ (operationCounts[change.operation] || 0) + 1;
699
+ }
700
+ }
701
+ else {
702
+ entriesWithoutChanges++;
703
+ }
704
+ results.push({
705
+ entryId: entry._id,
706
+ slug: entry.slug,
707
+ success: true,
708
+ changes,
709
+ });
710
+ }
711
+ return {
712
+ totalEntries,
713
+ previewedEntries: previewEntries.length,
714
+ results,
715
+ summary: {
716
+ entriesWithChanges,
717
+ entriesWithoutChanges,
718
+ operationCounts,
719
+ },
720
+ };
721
+ },
722
+ });
723
+ /**
724
+ * Query to get available transformation types and their descriptions.
725
+ */
726
+ export const getTransformationTypes = query({
727
+ args: {},
728
+ returns: v.array(v.object({
729
+ type: v.string(),
730
+ description: v.string(),
731
+ fromType: v.string(),
732
+ toType: v.string(),
733
+ })),
734
+ handler: async () => {
735
+ return [
736
+ {
737
+ type: "TEXT_TO_NUMBER",
738
+ description: "Convert text strings to numbers (handles currency formatting)",
739
+ fromType: "text",
740
+ toType: "number",
741
+ },
742
+ {
743
+ type: "NUMBER_TO_TEXT",
744
+ description: "Convert numbers to text strings",
745
+ fromType: "number",
746
+ toType: "text",
747
+ },
748
+ {
749
+ type: "TEXT_TO_BOOLEAN",
750
+ description: "Convert text to boolean (true/false, yes/no, 1/0, on/off, enabled/disabled)",
751
+ fromType: "text",
752
+ toType: "boolean",
753
+ },
754
+ {
755
+ type: "BOOLEAN_TO_TEXT",
756
+ description: 'Convert boolean to "true" or "false" strings',
757
+ fromType: "boolean",
758
+ toType: "text",
759
+ },
760
+ {
761
+ type: "TEXT_TO_DATE",
762
+ description: "Convert date strings to timestamps (ISO 8601 and common formats)",
763
+ fromType: "text",
764
+ toType: "date/datetime",
765
+ },
766
+ {
767
+ type: "DATE_TO_TEXT",
768
+ description: "Convert timestamps to ISO 8601 date strings",
769
+ fromType: "date/datetime",
770
+ toType: "text",
771
+ },
772
+ {
773
+ type: "TEXT_TO_JSON",
774
+ description: "Parse JSON strings to objects",
775
+ fromType: "text",
776
+ toType: "json",
777
+ },
778
+ {
779
+ type: "JSON_TO_TEXT",
780
+ description: "Stringify JSON objects to text",
781
+ fromType: "json",
782
+ toType: "text",
783
+ },
784
+ {
785
+ type: "SINGLE_TO_ARRAY",
786
+ description: "Wrap single values in an array (for multiple references)",
787
+ fromType: "any",
788
+ toType: "array",
789
+ },
790
+ {
791
+ type: "ARRAY_TO_SINGLE",
792
+ description: "Extract first element from array (for single references)",
793
+ fromType: "array",
794
+ toType: "any",
795
+ },
796
+ {
797
+ type: "SELECT_VALUE_REMAP",
798
+ description: "Remap select/multiSelect values using a provided mapping",
799
+ fromType: "select/multiSelect",
800
+ toType: "select/multiSelect",
801
+ },
802
+ ];
803
+ },
804
+ });
805
+ //# sourceMappingURL=contentTypeMigration.js.map