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,794 @@
1
+ /**
2
+ * RAG Content Indexer
3
+ *
4
+ * Background job system to automatically index published content for RAG pipelines.
5
+ * Triggers on content publish events and maintains sync between CMS content and vector indexes.
6
+ *
7
+ * Architecture:
8
+ * 1. Content publish events are captured via the event emitter system (cmsEvents table)
9
+ * 2. A background processor polls for unprocessed "contentEntry.published" events
10
+ * 3. For each event, content is extracted and chunked using ragContentChunker
11
+ * 4. Chunks are passed to a user-provided indexing callback (e.g., @convex-dev/rag)
12
+ * 5. Events are marked as processed after successful indexing
13
+ *
14
+ * The indexer supports:
15
+ * - Automatic indexing on publish events
16
+ * - Manual reindexing of specific entries
17
+ * - Bulk reindexing of all published content
18
+ * - Configurable chunking options
19
+ * - Index removal on unpublish/delete events
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * // In your Convex action, process pending indexing jobs:
24
+ * import { processPublishEvents } from "./ragContentIndexer";
25
+ *
26
+ * export const runIndexer = action({
27
+ * handler: async (ctx) => {
28
+ * return await processPublishEventsAction(ctx, {
29
+ * onIndex: async (entryId, chunks, metadata) => {
30
+ * // Add to your vector index (e.g., @convex-dev/rag)
31
+ * await rag.add(ctx, {
32
+ * namespace: `cms:${metadata.contentType}`,
33
+ * key: entryId,
34
+ * chunks: chunks.map(c => c.text),
35
+ * });
36
+ * },
37
+ * onRemove: async (entryId) => {
38
+ * // Remove from your vector index
39
+ * await rag.remove(ctx, { key: entryId });
40
+ * },
41
+ * });
42
+ * },
43
+ * });
44
+ * ```
45
+ *
46
+ * @module
47
+ */
48
+ import { v } from "convex/values";
49
+ import { mutation, query, internalMutation, internalQuery } from "./_generated/server.js";
50
+ import { internal } from "./_generated/api.js";
51
+ import { chunkContentEntry, } from "./lib/ragContentChunker.js";
52
+ // =============================================================================
53
+ // Default Configuration
54
+ // =============================================================================
55
+ const DEFAULT_CONFIG = {
56
+ autoIndexOnPublish: true,
57
+ autoRemoveOnUnpublish: true,
58
+ autoRemoveOnDelete: true,
59
+ batchSize: 50,
60
+ pollingIntervalMs: 60000,
61
+ extractionOptions: {},
62
+ includeContentTypes: [],
63
+ excludeContentTypes: [],
64
+ namespacePrefix: "cms",
65
+ };
66
+ // =============================================================================
67
+ // Internal Queries
68
+ // =============================================================================
69
+ /**
70
+ * Internal query to get unprocessed publish-related events.
71
+ * Returns events for indexing (published) and removal (unpublished, deleted).
72
+ */
73
+ export const getUnprocessedIndexingEvents = internalQuery({
74
+ args: {
75
+ limit: v.optional(v.number()),
76
+ includeContentTypes: v.optional(v.array(v.string())),
77
+ excludeContentTypes: v.optional(v.array(v.string())),
78
+ },
79
+ handler: async (ctx, args) => {
80
+ const { limit = 50, includeContentTypes = [], excludeContentTypes = [] } = args;
81
+ // Get unprocessed events
82
+ const events = await ctx.db
83
+ .query("cmsEvents")
84
+ .withIndex("by_processed", (q) => q.eq("processed", false))
85
+ .order("asc")
86
+ .take(limit * 2); // Over-fetch to account for filtering
87
+ // Filter to only content entry events that affect indexing
88
+ const indexingActions = ["published", "unpublished", "deleted", "restored"];
89
+ const filteredEvents = events.filter((event) => {
90
+ // Must be a content entry event
91
+ if (event.resourceType !== "contentEntry")
92
+ return false;
93
+ // Must be an indexing-related action
94
+ if (!indexingActions.includes(event.action))
95
+ return false;
96
+ // Apply content type filters if specified
97
+ const payload = event.payload;
98
+ const contentTypeName = payload?.contentTypeName;
99
+ if (contentTypeName) {
100
+ if (includeContentTypes.length > 0 && !includeContentTypes.includes(contentTypeName)) {
101
+ return false;
102
+ }
103
+ if (excludeContentTypes.length > 0 && excludeContentTypes.includes(contentTypeName)) {
104
+ return false;
105
+ }
106
+ }
107
+ return true;
108
+ });
109
+ return filteredEvents.slice(0, limit);
110
+ },
111
+ });
112
+ /**
113
+ * Internal query to get entry data for indexing.
114
+ */
115
+ export const getEntryForIndexing = internalQuery({
116
+ args: {
117
+ entryId: v.id("contentEntries"),
118
+ },
119
+ handler: async (ctx, args) => {
120
+ const entry = await ctx.db.get(args.entryId);
121
+ if (!entry)
122
+ return null;
123
+ // Get the content type
124
+ const contentType = await ctx.db.get(entry.contentTypeId);
125
+ if (!contentType)
126
+ return null;
127
+ return {
128
+ entry,
129
+ contentType,
130
+ };
131
+ },
132
+ });
133
+ /**
134
+ * Internal query to get multiple entries for batch indexing.
135
+ */
136
+ export const getEntriesForIndexing = internalQuery({
137
+ args: {
138
+ entryIds: v.array(v.id("contentEntries")),
139
+ },
140
+ handler: async (ctx, args) => {
141
+ const results = [];
142
+ // Fetch all entries and their content types
143
+ const contentTypeCache = new Map();
144
+ for (const entryId of args.entryIds) {
145
+ const entry = await ctx.db.get(entryId);
146
+ if (!entry) {
147
+ results.push(null);
148
+ continue;
149
+ }
150
+ let contentType = contentTypeCache.get(entry.contentTypeId);
151
+ if (!contentType) {
152
+ const fetchedContentType = await ctx.db.get(entry.contentTypeId);
153
+ if (fetchedContentType) {
154
+ contentType = fetchedContentType;
155
+ contentTypeCache.set(entry.contentTypeId, fetchedContentType);
156
+ }
157
+ }
158
+ if (!contentType) {
159
+ results.push(null);
160
+ continue;
161
+ }
162
+ results.push({ entry, contentType });
163
+ }
164
+ return results;
165
+ },
166
+ });
167
+ // =============================================================================
168
+ // Public Queries
169
+ // =============================================================================
170
+ /**
171
+ * Query to get statistics about the indexing state.
172
+ *
173
+ * Returns counts of published entries, pending indexing events,
174
+ * and breakdown by content type.
175
+ */
176
+ export const getIndexingStats = query({
177
+ args: {},
178
+ returns: v.object({
179
+ totalPublished: v.number(),
180
+ pendingIndexing: v.number(),
181
+ pendingRemoval: v.number(),
182
+ byContentType: v.any(),
183
+ }),
184
+ handler: async (ctx) => {
185
+ // Count published entries
186
+ const publishedEntries = await ctx.db
187
+ .query("contentEntries")
188
+ .withIndex("by_status", (q) => q.eq("status", "published"))
189
+ .filter((q) => q.eq(q.field("deletedAt"), undefined))
190
+ .collect();
191
+ // Count unprocessed publish events
192
+ const unprocessedEvents = await ctx.db
193
+ .query("cmsEvents")
194
+ .withIndex("by_processed", (q) => q.eq("processed", false))
195
+ .filter((q) => q.eq(q.field("resourceType"), "contentEntry"))
196
+ .collect();
197
+ const pendingIndexing = unprocessedEvents.filter((e) => e.action === "published").length;
198
+ const pendingRemoval = unprocessedEvents.filter((e) => ["unpublished", "deleted"].includes(e.action)).length;
199
+ // Get content types for breakdown
200
+ const contentTypes = await ctx.db.query("contentTypes").collect();
201
+ const contentTypeMap = new Map(contentTypes.map((ct) => [ct._id, ct.name]));
202
+ // Build breakdown by content type
203
+ const byContentType = {};
204
+ for (const entry of publishedEntries) {
205
+ const typeName = contentTypeMap.get(entry.contentTypeId) || "unknown";
206
+ if (!byContentType[typeName]) {
207
+ byContentType[typeName] = { published: 0, pending: 0 };
208
+ }
209
+ byContentType[typeName].published++;
210
+ }
211
+ for (const event of unprocessedEvents) {
212
+ if (event.action !== "published")
213
+ continue;
214
+ const payload = event.payload;
215
+ const typeName = payload?.contentTypeName || "unknown";
216
+ if (!byContentType[typeName]) {
217
+ byContentType[typeName] = { published: 0, pending: 0 };
218
+ }
219
+ byContentType[typeName].pending++;
220
+ }
221
+ return {
222
+ totalPublished: publishedEntries.length,
223
+ pendingIndexing,
224
+ pendingRemoval,
225
+ byContentType,
226
+ };
227
+ },
228
+ });
229
+ /**
230
+ * Query to check if an entry needs reindexing.
231
+ *
232
+ * Returns true if there are unprocessed events for the entry,
233
+ * or if the entry has been updated since last indexing.
234
+ */
235
+ export const needsReindexing = query({
236
+ args: {
237
+ entryId: v.id("contentEntries"),
238
+ },
239
+ returns: v.boolean(),
240
+ handler: async (ctx, args) => {
241
+ // Check for any unprocessed events for this entry
242
+ const events = await ctx.db
243
+ .query("cmsEvents")
244
+ .withIndex("by_resource", (q) => q.eq("resourceType", "contentEntry").eq("resourceId", args.entryId))
245
+ .filter((q) => q.eq(q.field("processed"), false))
246
+ .first();
247
+ return events !== null;
248
+ },
249
+ });
250
+ // =============================================================================
251
+ // Mutations
252
+ // =============================================================================
253
+ /**
254
+ * Mutation to prepare content for indexing.
255
+ *
256
+ * This extracts and chunks content from an entry, returning the chunks
257
+ * and metadata for the caller to pass to their vector index.
258
+ *
259
+ * @param entryId - The content entry ID to prepare for indexing
260
+ * @param options - Optional extraction options
261
+ *
262
+ * @returns Chunks and metadata for indexing, or null if entry not found/not published
263
+ */
264
+ export const prepareEntryForIndexing = query({
265
+ args: {
266
+ entryId: v.id("contentEntries"),
267
+ options: v.optional(v.object({
268
+ includeFields: v.optional(v.array(v.string())),
269
+ excludeFields: v.optional(v.array(v.string())),
270
+ maxCharsSoftLimit: v.optional(v.number()),
271
+ namespacePrefix: v.optional(v.string()),
272
+ })),
273
+ },
274
+ returns: v.union(v.object({
275
+ entryId: v.string(),
276
+ chunks: v.array(v.object({
277
+ text: v.string(),
278
+ metadata: v.any(),
279
+ })),
280
+ metadata: v.object({
281
+ entryId: v.string(),
282
+ contentType: v.string(),
283
+ contentTypeDisplayName: v.string(),
284
+ slug: v.string(),
285
+ locale: v.optional(v.string()),
286
+ version: v.number(),
287
+ title: v.optional(v.string()),
288
+ publishedAt: v.optional(v.number()),
289
+ namespace: v.string(),
290
+ }),
291
+ }), v.null()),
292
+ handler: async (ctx, args) => {
293
+ const { entryId, options = {} } = args;
294
+ // Get entry and content type
295
+ const entry = await ctx.db.get(entryId);
296
+ if (!entry)
297
+ return null;
298
+ // Only index published content
299
+ if (entry.status !== "published")
300
+ return null;
301
+ const contentType = await ctx.db.get(entry.contentTypeId);
302
+ if (!contentType)
303
+ return null;
304
+ // Build extraction options
305
+ const extractionOptions = {
306
+ includeMetadata: true,
307
+ includeFields: options.includeFields,
308
+ excludeFields: options.excludeFields,
309
+ chunkOptions: {
310
+ maxCharsSoftLimit: options.maxCharsSoftLimit ?? 1000,
311
+ },
312
+ };
313
+ // Convert to the expected types
314
+ const entryInfo = {
315
+ _id: entry._id,
316
+ contentTypeId: entry.contentTypeId,
317
+ slug: entry.slug,
318
+ status: entry.status,
319
+ data: entry.data,
320
+ locale: entry.locale,
321
+ version: entry.version,
322
+ _creationTime: entry._creationTime,
323
+ firstPublishedAt: entry.firstPublishedAt,
324
+ lastPublishedAt: entry.lastPublishedAt,
325
+ };
326
+ const contentTypeInfo = {
327
+ _id: contentType._id,
328
+ name: contentType.name,
329
+ displayName: contentType.displayName,
330
+ fields: contentType.fields,
331
+ titleField: contentType.titleField,
332
+ slugField: contentType.slugField,
333
+ };
334
+ // Extract and chunk content
335
+ const chunks = chunkContentEntry(entryInfo, contentTypeInfo, extractionOptions);
336
+ // Build namespace
337
+ const namespacePrefix = options.namespacePrefix ?? "cms";
338
+ const namespace = entry.locale
339
+ ? `${namespacePrefix}:${contentType.name}:${entry.locale}`
340
+ : `${namespacePrefix}:${contentType.name}`;
341
+ // Get title from chunks metadata or entry data
342
+ const title = chunks[0]?.metadata?.title || entry.data?.title;
343
+ return {
344
+ entryId: entry._id,
345
+ chunks: chunks.map((c) => ({
346
+ text: c.text,
347
+ metadata: c.metadata,
348
+ })),
349
+ metadata: {
350
+ entryId: entry._id,
351
+ contentType: contentType.name,
352
+ contentTypeDisplayName: contentType.displayName,
353
+ slug: entry.slug,
354
+ locale: entry.locale,
355
+ version: entry.version,
356
+ title,
357
+ publishedAt: entry.lastPublishedAt,
358
+ namespace,
359
+ },
360
+ };
361
+ },
362
+ });
363
+ /**
364
+ * Mutation to mark indexing events as processed.
365
+ *
366
+ * Call this after successfully indexing content to prevent reprocessing.
367
+ *
368
+ * @param eventIds - Array of event IDs to mark as processed
369
+ */
370
+ export const markIndexingEventsProcessed = mutation({
371
+ args: {
372
+ eventIds: v.array(v.id("cmsEvents")),
373
+ },
374
+ returns: v.object({
375
+ processedCount: v.number(),
376
+ }),
377
+ handler: async (ctx, args) => {
378
+ const { eventIds } = args;
379
+ const now = Date.now();
380
+ let processedCount = 0;
381
+ for (const eventId of eventIds) {
382
+ const event = await ctx.db.get(eventId);
383
+ if (event && !event.processed) {
384
+ await ctx.db.patch(eventId, {
385
+ processed: true,
386
+ processedAt: now,
387
+ });
388
+ processedCount++;
389
+ }
390
+ }
391
+ return { processedCount };
392
+ },
393
+ });
394
+ /**
395
+ * Internal mutation to request reindexing of an entry.
396
+ *
397
+ * Creates a synthetic "published" event to trigger reindexing.
398
+ */
399
+ export const requestReindex = internalMutation({
400
+ args: {
401
+ entryId: v.id("contentEntries"),
402
+ userId: v.optional(v.string()),
403
+ },
404
+ handler: async (ctx, args) => {
405
+ const { entryId, userId } = args;
406
+ // Get entry details
407
+ const entry = await ctx.db.get(entryId);
408
+ if (!entry) {
409
+ throw new Error(`Entry not found: ${entryId}`);
410
+ }
411
+ if (entry.status !== "published") {
412
+ throw new Error(`Entry is not published: ${entryId}`);
413
+ }
414
+ // Get content type for payload
415
+ const contentType = await ctx.db.get(entry.contentTypeId);
416
+ if (!contentType) {
417
+ throw new Error(`Content type not found: ${entry.contentTypeId}`);
418
+ }
419
+ // Create a reindex event
420
+ await ctx.db.insert("cmsEvents", {
421
+ eventType: "contentEntry.published",
422
+ resourceType: "contentEntry",
423
+ resourceId: entryId,
424
+ action: "published",
425
+ payload: {
426
+ slug: entry.slug,
427
+ contentTypeName: contentType.name,
428
+ contentTypeId: contentType._id,
429
+ status: entry.status,
430
+ version: entry.version,
431
+ locale: entry.locale,
432
+ changeDescription: "Reindex requested",
433
+ },
434
+ userId,
435
+ processed: false,
436
+ metadata: { reindexRequest: true },
437
+ });
438
+ return { success: true };
439
+ },
440
+ });
441
+ /**
442
+ * Public mutation to request reindexing of a specific entry.
443
+ */
444
+ export const requestEntryReindex = mutation({
445
+ args: {
446
+ entryId: v.id("contentEntries"),
447
+ userId: v.optional(v.string()),
448
+ },
449
+ returns: v.object({
450
+ success: v.boolean(),
451
+ message: v.string(),
452
+ }),
453
+ handler: async (ctx, args) => {
454
+ const { entryId, userId } = args;
455
+ // Get entry details
456
+ const entry = await ctx.db.get(entryId);
457
+ if (!entry) {
458
+ return { success: false, message: "Entry not found" };
459
+ }
460
+ if (entry.status !== "published") {
461
+ return { success: false, message: "Entry is not published" };
462
+ }
463
+ // Get content type for payload
464
+ const contentType = await ctx.db.get(entry.contentTypeId);
465
+ if (!contentType) {
466
+ return { success: false, message: "Content type not found" };
467
+ }
468
+ // Create a reindex event
469
+ await ctx.db.insert("cmsEvents", {
470
+ eventType: "contentEntry.published",
471
+ resourceType: "contentEntry",
472
+ resourceId: entryId,
473
+ action: "published",
474
+ payload: {
475
+ slug: entry.slug,
476
+ contentTypeName: contentType.name,
477
+ contentTypeId: contentType._id,
478
+ status: entry.status,
479
+ version: entry.version,
480
+ locale: entry.locale,
481
+ changeDescription: "Reindex requested",
482
+ },
483
+ userId,
484
+ processed: false,
485
+ metadata: { reindexRequest: true },
486
+ });
487
+ return { success: true, message: "Reindex event created" };
488
+ },
489
+ });
490
+ /**
491
+ * Mutation to request reindexing of all published content.
492
+ *
493
+ * Creates publish events for all currently published entries,
494
+ * which will be processed by the background indexer.
495
+ *
496
+ * @param contentTypeId - Optional content type to filter by
497
+ * @param batchSize - Number of entries to process per batch
498
+ * @param cursor - Pagination cursor for large datasets
499
+ */
500
+ export const requestBulkReindex = mutation({
501
+ args: {
502
+ contentTypeId: v.optional(v.id("contentTypes")),
503
+ batchSize: v.optional(v.number()),
504
+ cursor: v.optional(v.string()),
505
+ userId: v.optional(v.string()),
506
+ },
507
+ returns: v.object({
508
+ eventsCreated: v.number(),
509
+ hasMore: v.boolean(),
510
+ nextCursor: v.optional(v.string()),
511
+ }),
512
+ handler: async (ctx, args) => {
513
+ const { contentTypeId, batchSize = 100, userId } = args;
514
+ // Build query for published entries
515
+ const entriesQuery = ctx.db
516
+ .query("contentEntries")
517
+ .withIndex("by_status", (q) => q.eq("status", "published"))
518
+ .filter((q) => q.eq(q.field("deletedAt"), undefined));
519
+ // Apply content type filter if specified
520
+ const entries = await entriesQuery.take(batchSize + 1);
521
+ const hasMore = entries.length > batchSize;
522
+ const entriesToProcess = entries.slice(0, batchSize);
523
+ // Filter by content type if specified
524
+ const filteredEntries = contentTypeId
525
+ ? entriesToProcess.filter((e) => e.contentTypeId === contentTypeId)
526
+ : entriesToProcess;
527
+ // Get content types for payloads
528
+ const contentTypeIds = [...new Set(filteredEntries.map((e) => e.contentTypeId))];
529
+ const contentTypes = await Promise.all(contentTypeIds.map((id) => ctx.db.get(id)));
530
+ const contentTypeMap = new Map(contentTypes.filter(Boolean).map((ct) => [ct._id, ct]));
531
+ // Create reindex events
532
+ let eventsCreated = 0;
533
+ for (const entry of filteredEntries) {
534
+ const contentType = contentTypeMap.get(entry.contentTypeId);
535
+ if (!contentType)
536
+ continue;
537
+ await ctx.db.insert("cmsEvents", {
538
+ eventType: "contentEntry.published",
539
+ resourceType: "contentEntry",
540
+ resourceId: entry._id,
541
+ action: "published",
542
+ payload: {
543
+ slug: entry.slug,
544
+ contentTypeName: contentType.name,
545
+ contentTypeId: contentType._id,
546
+ status: entry.status,
547
+ version: entry.version,
548
+ locale: entry.locale,
549
+ changeDescription: "Bulk reindex requested",
550
+ },
551
+ userId,
552
+ processed: false,
553
+ metadata: { bulkReindex: true },
554
+ });
555
+ eventsCreated++;
556
+ }
557
+ // Calculate next cursor
558
+ const nextCursor = hasMore ? entriesToProcess[entriesToProcess.length - 1]?._id : undefined;
559
+ return {
560
+ eventsCreated,
561
+ hasMore,
562
+ nextCursor,
563
+ };
564
+ },
565
+ });
566
+ // =============================================================================
567
+ // Background Job Scheduling
568
+ // =============================================================================
569
+ /**
570
+ * Internal mutation to process pending indexing events.
571
+ *
572
+ * This is called by the background scheduler to process events in batches.
573
+ * Returns information about what was processed so the action can perform indexing.
574
+ */
575
+ export const getIndexingBatch = internalQuery({
576
+ args: {
577
+ config: v.optional(v.object({
578
+ batchSize: v.optional(v.number()),
579
+ includeContentTypes: v.optional(v.array(v.string())),
580
+ excludeContentTypes: v.optional(v.array(v.string())),
581
+ namespacePrefix: v.optional(v.string()),
582
+ })),
583
+ },
584
+ handler: async (ctx, args) => {
585
+ const config = { ...DEFAULT_CONFIG, ...(args.config || {}) };
586
+ // Get unprocessed events
587
+ const events = await ctx.db
588
+ .query("cmsEvents")
589
+ .withIndex("by_processed", (q) => q.eq("processed", false))
590
+ .order("asc")
591
+ .take(config.batchSize * 2);
592
+ // Filter to indexing-related content entry events
593
+ const indexingActions = ["published", "unpublished", "deleted", "restored"];
594
+ const filteredEvents = events.filter((event) => {
595
+ if (event.resourceType !== "contentEntry")
596
+ return false;
597
+ if (!indexingActions.includes(event.action))
598
+ return false;
599
+ const payload = event.payload;
600
+ const contentTypeName = payload?.contentTypeName;
601
+ if (contentTypeName) {
602
+ if (config.includeContentTypes.length > 0 &&
603
+ !config.includeContentTypes.includes(contentTypeName)) {
604
+ return false;
605
+ }
606
+ if (config.excludeContentTypes.length > 0 &&
607
+ config.excludeContentTypes.includes(contentTypeName)) {
608
+ return false;
609
+ }
610
+ }
611
+ return true;
612
+ }).slice(0, config.batchSize);
613
+ // Categorize events
614
+ const toIndex = [];
615
+ const toRemove = [];
616
+ for (const event of filteredEvents) {
617
+ const item = { eventId: event._id, entryId: event.resourceId };
618
+ if (event.action === "published" || event.action === "restored") {
619
+ toIndex.push(item);
620
+ }
621
+ else if (event.action === "unpublished" || event.action === "deleted") {
622
+ toRemove.push(item);
623
+ }
624
+ }
625
+ return {
626
+ toIndex,
627
+ toRemove,
628
+ hasMore: events.length > config.batchSize,
629
+ };
630
+ },
631
+ });
632
+ /**
633
+ * Schedules the next background indexing run.
634
+ *
635
+ * Call this to set up recurring background processing.
636
+ *
637
+ * @param delayMs - Delay before next run in milliseconds
638
+ */
639
+ export const scheduleNextIndexingRun = mutation({
640
+ args: {
641
+ delayMs: v.optional(v.number()),
642
+ },
643
+ returns: v.object({
644
+ scheduledAt: v.number(),
645
+ }),
646
+ handler: async (ctx, args) => {
647
+ const delayMs = args.delayMs ?? DEFAULT_CONFIG.pollingIntervalMs;
648
+ const runAt = Date.now() + delayMs;
649
+ await ctx.scheduler.runAt(runAt, internal.ragContentIndexer.triggerIndexingCheck, {});
650
+ return { scheduledAt: runAt };
651
+ },
652
+ });
653
+ /**
654
+ * Internal mutation triggered by scheduler to check for pending events.
655
+ *
656
+ * This checks if there are pending events and signals that processing is needed.
657
+ */
658
+ export const triggerIndexingCheck = internalMutation({
659
+ args: {},
660
+ handler: async (ctx) => {
661
+ // Check if there are any unprocessed indexing events
662
+ const pendingEvent = await ctx.db
663
+ .query("cmsEvents")
664
+ .withIndex("by_processed", (q) => q.eq("processed", false))
665
+ .filter((q) => q.and(q.eq(q.field("resourceType"), "contentEntry"), q.or(q.eq(q.field("action"), "published"), q.eq(q.field("action"), "unpublished"), q.eq(q.field("action"), "deleted"), q.eq(q.field("action"), "restored"))))
666
+ .first();
667
+ const hasPendingEvents = pendingEvent !== null;
668
+ // Log for monitoring
669
+ if (hasPendingEvents) {
670
+ console.log("RAG Indexer: Pending events detected, processing needed");
671
+ }
672
+ return { hasPendingEvents };
673
+ },
674
+ });
675
+ // =============================================================================
676
+ // Utility Functions
677
+ // =============================================================================
678
+ /**
679
+ * Prepares multiple entries for indexing in a single call.
680
+ * Useful for batch operations.
681
+ */
682
+ export const prepareEntriesForIndexing = query({
683
+ args: {
684
+ entryIds: v.array(v.id("contentEntries")),
685
+ options: v.optional(v.object({
686
+ includeFields: v.optional(v.array(v.string())),
687
+ excludeFields: v.optional(v.array(v.string())),
688
+ maxCharsSoftLimit: v.optional(v.number()),
689
+ namespacePrefix: v.optional(v.string()),
690
+ })),
691
+ },
692
+ returns: v.array(v.union(v.object({
693
+ entryId: v.string(),
694
+ chunks: v.array(v.object({
695
+ text: v.string(),
696
+ metadata: v.any(),
697
+ })),
698
+ metadata: v.object({
699
+ entryId: v.string(),
700
+ contentType: v.string(),
701
+ contentTypeDisplayName: v.string(),
702
+ slug: v.string(),
703
+ locale: v.optional(v.string()),
704
+ version: v.number(),
705
+ title: v.optional(v.string()),
706
+ publishedAt: v.optional(v.number()),
707
+ namespace: v.string(),
708
+ }),
709
+ }), v.null())),
710
+ handler: async (ctx, args) => {
711
+ const { entryIds, options = {} } = args;
712
+ const results = [];
713
+ // Cache content types to avoid repeated lookups
714
+ const contentTypeCache = new Map();
715
+ for (const entryId of entryIds) {
716
+ const entry = await ctx.db.get(entryId);
717
+ if (!entry || entry.status !== "published") {
718
+ results.push(null);
719
+ continue;
720
+ }
721
+ let contentType = contentTypeCache.get(entry.contentTypeId);
722
+ if (!contentType) {
723
+ const fetchedContentType = await ctx.db.get(entry.contentTypeId);
724
+ if (fetchedContentType) {
725
+ contentType = fetchedContentType;
726
+ contentTypeCache.set(entry.contentTypeId, fetchedContentType);
727
+ }
728
+ }
729
+ if (!contentType) {
730
+ results.push(null);
731
+ continue;
732
+ }
733
+ // Build extraction options
734
+ const extractionOptions = {
735
+ includeMetadata: true,
736
+ includeFields: options.includeFields,
737
+ excludeFields: options.excludeFields,
738
+ chunkOptions: {
739
+ maxCharsSoftLimit: options.maxCharsSoftLimit ?? 1000,
740
+ },
741
+ };
742
+ const entryInfo = {
743
+ _id: entry._id,
744
+ contentTypeId: entry.contentTypeId,
745
+ slug: entry.slug,
746
+ status: entry.status,
747
+ data: entry.data,
748
+ locale: entry.locale,
749
+ version: entry.version,
750
+ _creationTime: entry._creationTime,
751
+ firstPublishedAt: entry.firstPublishedAt,
752
+ lastPublishedAt: entry.lastPublishedAt,
753
+ };
754
+ const contentTypeInfo = {
755
+ _id: contentType._id,
756
+ name: contentType.name,
757
+ displayName: contentType.displayName,
758
+ fields: contentType.fields,
759
+ titleField: contentType.titleField,
760
+ slugField: contentType.slugField,
761
+ };
762
+ const chunks = chunkContentEntry(entryInfo, contentTypeInfo, extractionOptions);
763
+ const namespacePrefix = options.namespacePrefix ?? "cms";
764
+ const namespace = entry.locale
765
+ ? `${namespacePrefix}:${contentType.name}:${entry.locale}`
766
+ : `${namespacePrefix}:${contentType.name}`;
767
+ const title = chunks[0]?.metadata?.title || entry.data?.title;
768
+ results.push({
769
+ entryId: entry._id,
770
+ chunks: chunks.map((c) => ({
771
+ text: c.text,
772
+ metadata: c.metadata,
773
+ })),
774
+ metadata: {
775
+ entryId: entry._id,
776
+ contentType: contentType.name,
777
+ contentTypeDisplayName: contentType.displayName,
778
+ slug: entry.slug,
779
+ locale: entry.locale,
780
+ version: entry.version,
781
+ title,
782
+ publishedAt: entry.lastPublishedAt,
783
+ namespace,
784
+ },
785
+ });
786
+ }
787
+ return results;
788
+ },
789
+ });
790
+ // =============================================================================
791
+ // Exports
792
+ // =============================================================================
793
+ export { DEFAULT_CONFIG as DEFAULT_INDEXER_CONFIG };
794
+ //# sourceMappingURL=ragContentIndexer.js.map