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,572 @@
1
+ /**
2
+ * Rate Limit Hooks Execution Module
3
+ *
4
+ * This module provides the infrastructure for executing rate limit hooks
5
+ * that enable custom rate limiting logic for CMS operations.
6
+ *
7
+ * Rate limiting hooks allow parent applications to:
8
+ * - Implement custom rate limiting strategies (token bucket, fixed window, etc.)
9
+ * - Define different limits for different user tiers
10
+ * - Apply limits per operation, category, or globally
11
+ * - Integrate with external rate limiting services
12
+ *
13
+ * Hook execution order:
14
+ * 1. getConfig hook (optional) - Get dynamic rate limit configuration
15
+ * 2. check hook - Check if operation is rate limited
16
+ * 3. consume hook - Record rate limit usage (only if check passed)
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * import { executeRateLimitHooks, RateLimitedError } from './rateLimitHooks';
21
+ *
22
+ * // In a mutation handler
23
+ * const rateLimitResult = await executeRateLimitHooks({
24
+ * hooks: config.rateLimitHooks,
25
+ * context: {
26
+ * operation: 'contentEntries.create',
27
+ * operationCategory: 'write',
28
+ * userId: args.createdBy,
29
+ * role: userRole,
30
+ * timestamp: Date.now(),
31
+ * },
32
+ * });
33
+ *
34
+ * if (!rateLimitResult.allowed) {
35
+ * throw new RateLimitedError(rateLimitResult.reason ?? 'Rate limit exceeded', {
36
+ * retryAt: rateLimitResult.retryAt,
37
+ * });
38
+ * }
39
+ * ```
40
+ */
41
+
42
+ import type {
43
+ RateLimitHooks,
44
+ RateLimitHookContext,
45
+ RateLimitCheckResult,
46
+ RateLimitConsumeResult,
47
+ RateLimitConfigResult,
48
+ CmsOperation,
49
+ OperationCategory,
50
+ } from "../client/types.js";
51
+
52
+ // =============================================================================
53
+ // Types
54
+ // =============================================================================
55
+
56
+ /**
57
+ * Options for executing the rate limit hook chain.
58
+ */
59
+ export interface ExecuteRateLimitOptions {
60
+ /**
61
+ * The rate limit hooks configuration from ComponentConfig.
62
+ */
63
+ hooks?: RateLimitHooks;
64
+
65
+ /**
66
+ * The context for this rate limit check.
67
+ */
68
+ context: RateLimitHookContext;
69
+ }
70
+
71
+ /**
72
+ * Result from executing the rate limit hook chain.
73
+ */
74
+ export interface RateLimitResult {
75
+ /**
76
+ * Whether the operation is allowed (not rate limited).
77
+ */
78
+ allowed: boolean;
79
+
80
+ /**
81
+ * The timestamp when the operation can be retried (if rate limited).
82
+ * In milliseconds since epoch.
83
+ */
84
+ retryAt?: number;
85
+
86
+ /**
87
+ * The reason for rate limiting (if rate limited).
88
+ */
89
+ reason?: string;
90
+
91
+ /**
92
+ * Whether rate limiting was skipped entirely.
93
+ * True if no hooks were configured, operation was excluded, or user was exempt.
94
+ */
95
+ skipped: boolean;
96
+
97
+ /**
98
+ * Information about the rate limit state.
99
+ */
100
+ rateLimitInfo?: RateLimitCheckResult["rateLimitInfo"];
101
+
102
+ /**
103
+ * The configuration that was used for rate limiting.
104
+ */
105
+ config?: RateLimitConfigResult;
106
+ }
107
+
108
+ /**
109
+ * Error thrown when a rate limit is exceeded.
110
+ */
111
+ export class RateLimitedError extends Error {
112
+ readonly code = "RATE_LIMITED";
113
+ readonly retryAt?: number;
114
+ readonly operation?: CmsOperation;
115
+ readonly operationCategory?: OperationCategory;
116
+ readonly rateLimitInfo?: RateLimitCheckResult["rateLimitInfo"];
117
+
118
+ constructor(
119
+ message: string,
120
+ options?: {
121
+ retryAt?: number;
122
+ operation?: CmsOperation;
123
+ operationCategory?: OperationCategory;
124
+ rateLimitInfo?: RateLimitCheckResult["rateLimitInfo"];
125
+ }
126
+ ) {
127
+ super(message);
128
+ this.name = "RateLimitedError";
129
+ this.retryAt = options?.retryAt;
130
+ this.operation = options?.operation;
131
+ this.operationCategory = options?.operationCategory;
132
+ this.rateLimitInfo = options?.rateLimitInfo;
133
+ }
134
+
135
+ /**
136
+ * Returns a human-readable message with retry information.
137
+ */
138
+ toUserMessage(): string {
139
+ if (this.retryAt) {
140
+ const retryInMs = this.retryAt - Date.now();
141
+ if (retryInMs > 0) {
142
+ const retryInSeconds = Math.ceil(retryInMs / 1000);
143
+ return `${this.message}. Please retry in ${retryInSeconds} second${retryInSeconds !== 1 ? "s" : ""}.`;
144
+ }
145
+ }
146
+ return this.message;
147
+ }
148
+ }
149
+
150
+ // =============================================================================
151
+ // Helper Functions
152
+ // =============================================================================
153
+
154
+ /**
155
+ * Maps a CMS operation to its category.
156
+ */
157
+ export function operationToCategory(operation: CmsOperation): OperationCategory {
158
+ // Read operations
159
+ if (operation.endsWith(".read") || operation === "versions.read") {
160
+ return "read";
161
+ }
162
+
163
+ // Publish operations
164
+ if (
165
+ operation === "contentEntries.publish" ||
166
+ operation === "contentEntries.unpublish" ||
167
+ operation === "contentEntries.schedule"
168
+ ) {
169
+ return "publish";
170
+ }
171
+
172
+ // Media operations
173
+ if (
174
+ operation.startsWith("mediaAssets.") ||
175
+ operation.startsWith("mediaFolders.")
176
+ ) {
177
+ return "media";
178
+ }
179
+
180
+ // Admin operations (content type management)
181
+ if (operation.startsWith("contentTypes.")) {
182
+ return "admin";
183
+ }
184
+
185
+ // Write operations (default for mutations)
186
+ return "write";
187
+ }
188
+
189
+ /**
190
+ * Creates a rate limit context for a CMS operation.
191
+ */
192
+ export function createRateLimitContext(
193
+ operation: CmsOperation,
194
+ options: {
195
+ userId?: string;
196
+ role?: string | null;
197
+ contentTypeId?: string;
198
+ contentTypeName?: string;
199
+ metadata?: Record<string, unknown>;
200
+ }
201
+ ): RateLimitHookContext {
202
+ return {
203
+ operation,
204
+ operationCategory: operationToCategory(operation),
205
+ userId: options.userId,
206
+ role: options.role,
207
+ contentTypeId: options.contentTypeId,
208
+ contentTypeName: options.contentTypeName,
209
+ metadata: options.metadata,
210
+ timestamp: Date.now(),
211
+ };
212
+ }
213
+
214
+ /**
215
+ * Execute a single rate limit hook safely.
216
+ * Catches errors and converts them to allowed results to avoid blocking operations.
217
+ */
218
+ async function executeCheckHook(
219
+ hook: ((context: RateLimitHookContext) => Promise<RateLimitCheckResult> | RateLimitCheckResult) | undefined,
220
+ context: RateLimitHookContext
221
+ ): Promise<RateLimitCheckResult> {
222
+ if (!hook) {
223
+ return { allowed: true };
224
+ }
225
+
226
+ try {
227
+ return await hook(context);
228
+ } catch (error) {
229
+ // Hook threw an error - log and allow operation to proceed
230
+ // This prevents rate limit hook errors from blocking legitimate operations
231
+ console.error("Rate limit check hook error:", error);
232
+ return { allowed: true };
233
+ }
234
+ }
235
+
236
+ /**
237
+ * Execute the consume hook safely.
238
+ */
239
+ async function executeConsumeHook(
240
+ hook: ((context: RateLimitHookContext) => Promise<RateLimitConsumeResult> | RateLimitConsumeResult) | undefined,
241
+ context: RateLimitHookContext
242
+ ): Promise<RateLimitConsumeResult> {
243
+ if (!hook) {
244
+ return { allowed: true, consumed: false };
245
+ }
246
+
247
+ try {
248
+ return await hook(context);
249
+ } catch (error) {
250
+ // Hook threw an error - log and allow operation to proceed
251
+ console.error("Rate limit consume hook error:", error);
252
+ return { allowed: true, consumed: false };
253
+ }
254
+ }
255
+
256
+ /**
257
+ * Execute the config hook safely.
258
+ */
259
+ async function executeConfigHook(
260
+ hook: ((context: RateLimitHookContext) => Promise<RateLimitConfigResult> | RateLimitConfigResult) | undefined,
261
+ context: RateLimitHookContext
262
+ ): Promise<RateLimitConfigResult | undefined> {
263
+ if (!hook) {
264
+ return undefined;
265
+ }
266
+
267
+ try {
268
+ return await hook(context);
269
+ } catch (error) {
270
+ console.error("Rate limit config hook error:", error);
271
+ return undefined;
272
+ }
273
+ }
274
+
275
+ // =============================================================================
276
+ // Main Rate Limit Execution Function
277
+ // =============================================================================
278
+
279
+ /**
280
+ * Execute the full rate limit hook chain for an operation.
281
+ *
282
+ * This function orchestrates the execution of rate limit hooks in the correct order:
283
+ *
284
+ * 1. Check if rate limiting should be skipped (no hooks, excluded operation/category, admin bypass)
285
+ * 2. Get configuration from getConfig hook (if provided)
286
+ * 3. Execute check hook (or operation-specific override)
287
+ * 4. Execute consume hook if check passed (or operation-specific override)
288
+ * 5. Call onRateLimited callback if operation was denied
289
+ *
290
+ * @param options - Configuration for the rate limit execution
291
+ * @returns RateLimitResult indicating if the operation is allowed
292
+ *
293
+ * @example
294
+ * ```typescript
295
+ * const result = await executeRateLimitHooks({
296
+ * hooks: config.rateLimitHooks,
297
+ * context: {
298
+ * operation: 'contentEntries.create',
299
+ * operationCategory: 'write',
300
+ * userId: 'user123',
301
+ * role: 'editor',
302
+ * timestamp: Date.now(),
303
+ * },
304
+ * });
305
+ *
306
+ * if (!result.allowed) {
307
+ * throw new RateLimitedError(result.reason ?? 'Rate limit exceeded', {
308
+ * retryAt: result.retryAt,
309
+ * });
310
+ * }
311
+ * ```
312
+ */
313
+ export async function executeRateLimitHooks(
314
+ options: ExecuteRateLimitOptions
315
+ ): Promise<RateLimitResult> {
316
+ const { hooks, context } = options;
317
+
318
+ // -------------------------------------------------------------------------
319
+ // Step 1: Check if rate limiting should be skipped
320
+ // -------------------------------------------------------------------------
321
+
322
+ // No hooks configured - skip rate limiting
323
+ if (!hooks || (!hooks.check && !hooks.consume && !hooks.operationHooks)) {
324
+ return {
325
+ allowed: true,
326
+ skipped: true,
327
+ };
328
+ }
329
+
330
+ // Check if operation is excluded
331
+ if (hooks.excludeOperations?.includes(context.operation)) {
332
+ return {
333
+ allowed: true,
334
+ skipped: true,
335
+ };
336
+ }
337
+
338
+ // Check if category is excluded
339
+ if (hooks.excludeCategories?.includes(context.operationCategory)) {
340
+ return {
341
+ allowed: true,
342
+ skipped: true,
343
+ };
344
+ }
345
+
346
+ // Check if admin users should be exempted
347
+ if (hooks.skipForAdmin && context.role === "admin") {
348
+ return {
349
+ allowed: true,
350
+ skipped: true,
351
+ };
352
+ }
353
+
354
+ // -------------------------------------------------------------------------
355
+ // Step 2: Get operation-specific hooks or use global hooks
356
+ // -------------------------------------------------------------------------
357
+ const operationHooks = hooks.operationHooks?.[context.operation];
358
+ const checkHook = operationHooks?.check ?? hooks.check;
359
+ const consumeHook = operationHooks?.consume ?? hooks.consume;
360
+ const configHook = operationHooks?.getConfig ?? hooks.getConfig;
361
+
362
+ // No check hook configured - skip rate limiting
363
+ if (!checkHook) {
364
+ return {
365
+ allowed: true,
366
+ skipped: true,
367
+ };
368
+ }
369
+
370
+ // -------------------------------------------------------------------------
371
+ // Step 3: Get configuration (optional)
372
+ // -------------------------------------------------------------------------
373
+ const configResult = await executeConfigHook(configHook, context);
374
+
375
+ // Config hook says rate limiting is disabled
376
+ if (configResult && !configResult.enabled) {
377
+ return {
378
+ allowed: true,
379
+ skipped: true,
380
+ config: configResult,
381
+ };
382
+ }
383
+
384
+ // -------------------------------------------------------------------------
385
+ // Step 4: Execute check hook
386
+ // -------------------------------------------------------------------------
387
+ const checkResult = await executeCheckHook(checkHook, context);
388
+
389
+ if (!checkResult.allowed) {
390
+ // Rate limit exceeded - call onRateLimited callback
391
+ if (hooks.onRateLimited) {
392
+ try {
393
+ await hooks.onRateLimited(context, checkResult);
394
+ } catch (error) {
395
+ // Don't let callback errors affect the result
396
+ console.error("onRateLimited callback error:", error);
397
+ }
398
+ }
399
+
400
+ return {
401
+ allowed: false,
402
+ retryAt: checkResult.retryAt,
403
+ reason: checkResult.reason ?? "Rate limit exceeded",
404
+ skipped: false,
405
+ rateLimitInfo: checkResult.rateLimitInfo,
406
+ config: configResult,
407
+ };
408
+ }
409
+
410
+ // -------------------------------------------------------------------------
411
+ // Step 5: Execute consume hook (if provided)
412
+ // -------------------------------------------------------------------------
413
+ // If no separate consume hook, the check hook is assumed to have consumed
414
+ if (consumeHook) {
415
+ const consumeResult = await executeConsumeHook(consumeHook, context);
416
+
417
+ if (!consumeResult.allowed) {
418
+ // Consume failed (another request beat us to the limit)
419
+ if (hooks.onRateLimited) {
420
+ try {
421
+ await hooks.onRateLimited(context, consumeResult);
422
+ } catch (error) {
423
+ console.error("onRateLimited callback error:", error);
424
+ }
425
+ }
426
+
427
+ return {
428
+ allowed: false,
429
+ retryAt: consumeResult.retryAt,
430
+ reason: consumeResult.reason ?? "Rate limit exceeded",
431
+ skipped: false,
432
+ rateLimitInfo: consumeResult.rateLimitInfo,
433
+ config: configResult,
434
+ };
435
+ }
436
+ }
437
+
438
+ // -------------------------------------------------------------------------
439
+ // All checks passed
440
+ // -------------------------------------------------------------------------
441
+ return {
442
+ allowed: true,
443
+ skipped: false,
444
+ rateLimitInfo: checkResult.rateLimitInfo,
445
+ config: configResult,
446
+ };
447
+ }
448
+
449
+ /**
450
+ * Execute rate limit hooks and throw RateLimitedError if rate limited.
451
+ *
452
+ * This is a convenience wrapper that executes hooks and throws
453
+ * RateLimitedError if the operation is rate limited.
454
+ *
455
+ * @param options - Rate limit execution options
456
+ * @throws RateLimitedError if the operation is rate limited
457
+ * @returns The rate limit result (if allowed)
458
+ */
459
+ export async function requireRateLimit(
460
+ options: ExecuteRateLimitOptions
461
+ ): Promise<RateLimitResult> {
462
+ const result = await executeRateLimitHooks(options);
463
+
464
+ if (!result.allowed) {
465
+ throw new RateLimitedError(result.reason ?? "Rate limit exceeded", {
466
+ retryAt: result.retryAt,
467
+ operation: options.context.operation,
468
+ operationCategory: options.context.operationCategory,
469
+ rateLimitInfo: result.rateLimitInfo,
470
+ });
471
+ }
472
+
473
+ return result;
474
+ }
475
+
476
+ // =============================================================================
477
+ // Utility Functions for Building Rate Limit Hooks
478
+ // =============================================================================
479
+
480
+ /**
481
+ * Creates a simple rate limit key from context.
482
+ * Default format: `{userId}:{operationCategory}`
483
+ */
484
+ export function createRateLimitKey(
485
+ context: RateLimitHookContext,
486
+ options?: {
487
+ /** Include operation in key (more granular limiting) */
488
+ includeOperation?: boolean;
489
+ /** Include content type in key */
490
+ includeContentType?: boolean;
491
+ /** Custom prefix */
492
+ prefix?: string;
493
+ }
494
+ ): string {
495
+ const parts: string[] = [];
496
+
497
+ if (options?.prefix) {
498
+ parts.push(options.prefix);
499
+ }
500
+
501
+ if (context.userId) {
502
+ parts.push(context.userId);
503
+ } else {
504
+ parts.push("anonymous");
505
+ }
506
+
507
+ if (options?.includeOperation) {
508
+ parts.push(context.operation);
509
+ } else {
510
+ parts.push(context.operationCategory);
511
+ }
512
+
513
+ if (options?.includeContentType && context.contentTypeName) {
514
+ parts.push(context.contentTypeName);
515
+ }
516
+
517
+ return parts.join(":");
518
+ }
519
+
520
+ /**
521
+ * Creates a rate limit name from operation category.
522
+ * Useful for mapping to named rate limits in convex-helpers.
523
+ */
524
+ export function createRateLimitName(
525
+ context: RateLimitHookContext,
526
+ prefix = "cms"
527
+ ): string {
528
+ return `${prefix}.${context.operationCategory}`;
529
+ }
530
+
531
+ /**
532
+ * Default tier-based rate limit configurations.
533
+ * Can be used as a starting point for custom configurations.
534
+ */
535
+ export const DEFAULT_TIER_LIMITS = {
536
+ free: {
537
+ read: { rate: 100, period: 60000 }, // 100 reads per minute
538
+ write: { rate: 10, period: 60000 }, // 10 writes per minute
539
+ publish: { rate: 5, period: 60000 }, // 5 publishes per minute
540
+ media: { rate: 10, period: 60000 }, // 10 media ops per minute
541
+ admin: { rate: 5, period: 60000 }, // 5 admin ops per minute
542
+ },
543
+ pro: {
544
+ read: { rate: 500, period: 60000 },
545
+ write: { rate: 50, period: 60000 },
546
+ publish: { rate: 20, period: 60000 },
547
+ media: { rate: 50, period: 60000 },
548
+ admin: { rate: 20, period: 60000 },
549
+ },
550
+ enterprise: {
551
+ read: { rate: 2000, period: 60000 },
552
+ write: { rate: 200, period: 60000 },
553
+ publish: { rate: 100, period: 60000 },
554
+ media: { rate: 200, period: 60000 },
555
+ admin: { rate: 100, period: 60000 },
556
+ },
557
+ } as const;
558
+
559
+ /**
560
+ * Type for user tiers.
561
+ */
562
+ export type UserTier = keyof typeof DEFAULT_TIER_LIMITS;
563
+
564
+ /**
565
+ * Get rate limit configuration for a user tier and operation category.
566
+ */
567
+ export function getTierLimit(
568
+ tier: UserTier,
569
+ category: OperationCategory
570
+ ): { rate: number; period: number } {
571
+ return DEFAULT_TIER_LIMITS[tier][category];
572
+ }