convex-cms 0.0.2 → 0.0.3

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 (265) hide show
  1. package/admin-dist/nitro.json +15 -0
  2. package/admin-dist/public/assets/CmsEmptyState-CRswfTzk.js +5 -0
  3. package/admin-dist/public/assets/CmsPageHeader-CirpXndm.js +1 -0
  4. package/admin-dist/public/assets/CmsStatusBadge-CbEUpQu-.js +1 -0
  5. package/admin-dist/public/assets/CmsToolbar-BI2nZOXp.js +1 -0
  6. package/admin-dist/public/assets/ContentEntryEditor-CBeCyK_m.js +4 -0
  7. package/admin-dist/public/assets/ErrorState-BIVaWmom.js +1 -0
  8. package/admin-dist/public/assets/TaxonomyFilter-ChaY6Y_x.js +1 -0
  9. package/admin-dist/public/assets/_contentTypeId-DQ8k_Rvw.js +1 -0
  10. package/admin-dist/public/assets/_entryId-CKU_glsK.js +1 -0
  11. package/admin-dist/public/assets/alert-BXjTqrwQ.js +1 -0
  12. package/admin-dist/public/assets/badge-hvUOzpVZ.js +1 -0
  13. package/admin-dist/public/assets/circle-check-big-CF_pR17r.js +1 -0
  14. package/admin-dist/public/assets/command-DU82cJlt.js +1 -0
  15. package/admin-dist/public/assets/content-_LXl3pp7.js +1 -0
  16. package/admin-dist/public/assets/content-types-KjxaXGxY.js +2 -0
  17. package/admin-dist/public/assets/globals-CS6BZ0zp.css +1 -0
  18. package/admin-dist/public/assets/index-DNGIZHL-.js +1 -0
  19. package/admin-dist/public/assets/label-KNtpL71g.js +1 -0
  20. package/admin-dist/public/assets/link-2-Bw2aI4V4.js +1 -0
  21. package/admin-dist/public/assets/list-sYepHjt_.js +1 -0
  22. package/admin-dist/public/assets/main-CKj5yfEi.js +97 -0
  23. package/admin-dist/public/assets/media-Bkrkffm7.js +1 -0
  24. package/admin-dist/public/assets/new._contentTypeId-C3LstjNs.js +1 -0
  25. package/admin-dist/public/assets/plus-DUn8v_Xf.js +1 -0
  26. package/admin-dist/public/assets/rotate-ccw-DJEoHcRI.js +1 -0
  27. package/admin-dist/public/assets/scroll-area-DfIlT0in.js +1 -0
  28. package/admin-dist/public/assets/search-MuAUDJKR.js +1 -0
  29. package/admin-dist/public/assets/select-BD29IXCI.js +1 -0
  30. package/admin-dist/public/assets/settings-DmMyn_6A.js +1 -0
  31. package/admin-dist/public/assets/switch-h3Rrnl5i.js +1 -0
  32. package/admin-dist/public/assets/tabs-imc8h-Dp.js +1 -0
  33. package/admin-dist/public/assets/taxonomies-dAsrT65H.js +1 -0
  34. package/admin-dist/public/assets/textarea-BTy7nwzR.js +1 -0
  35. package/admin-dist/public/assets/trash-SAWKZZHv.js +1 -0
  36. package/admin-dist/public/assets/triangle-alert-E52Vfeuh.js +1 -0
  37. package/admin-dist/public/assets/useBreadcrumbLabel-BECBMCzM.js +1 -0
  38. package/admin-dist/public/assets/usePermissions-Basjs9BT.js +1 -0
  39. package/admin-dist/public/favicon.ico +0 -0
  40. package/admin-dist/server/_chunks/_libs/@date-fns/tz.mjs +217 -0
  41. package/admin-dist/server/_chunks/_libs/@floating-ui/core.mjs +719 -0
  42. package/admin-dist/server/_chunks/_libs/@floating-ui/dom.mjs +622 -0
  43. package/admin-dist/server/_chunks/_libs/@floating-ui/react-dom.mjs +292 -0
  44. package/admin-dist/server/_chunks/_libs/@floating-ui/utils.mjs +320 -0
  45. package/admin-dist/server/_chunks/_libs/@radix-ui/number.mjs +6 -0
  46. package/admin-dist/server/_chunks/_libs/@radix-ui/primitive.mjs +11 -0
  47. package/admin-dist/server/_chunks/_libs/@radix-ui/react-arrow.mjs +23 -0
  48. package/admin-dist/server/_chunks/_libs/@radix-ui/react-avatar.mjs +119 -0
  49. package/admin-dist/server/_chunks/_libs/@radix-ui/react-checkbox.mjs +270 -0
  50. package/admin-dist/server/_chunks/_libs/@radix-ui/react-collection.mjs +69 -0
  51. package/admin-dist/server/_chunks/_libs/@radix-ui/react-compose-refs.mjs +39 -0
  52. package/admin-dist/server/_chunks/_libs/@radix-ui/react-context.mjs +137 -0
  53. package/admin-dist/server/_chunks/_libs/@radix-ui/react-dialog.mjs +325 -0
  54. package/admin-dist/server/_chunks/_libs/@radix-ui/react-direction.mjs +9 -0
  55. package/admin-dist/server/_chunks/_libs/@radix-ui/react-dismissable-layer.mjs +210 -0
  56. package/admin-dist/server/_chunks/_libs/@radix-ui/react-dropdown-menu.mjs +253 -0
  57. package/admin-dist/server/_chunks/_libs/@radix-ui/react-focus-guards.mjs +29 -0
  58. package/admin-dist/server/_chunks/_libs/@radix-ui/react-focus-scope.mjs +206 -0
  59. package/admin-dist/server/_chunks/_libs/@radix-ui/react-id.mjs +14 -0
  60. package/admin-dist/server/_chunks/_libs/@radix-ui/react-label.mjs +23 -0
  61. package/admin-dist/server/_chunks/_libs/@radix-ui/react-menu.mjs +812 -0
  62. package/admin-dist/server/_chunks/_libs/@radix-ui/react-popover.mjs +300 -0
  63. package/admin-dist/server/_chunks/_libs/@radix-ui/react-popper.mjs +286 -0
  64. package/admin-dist/server/_chunks/_libs/@radix-ui/react-portal.mjs +16 -0
  65. package/admin-dist/server/_chunks/_libs/@radix-ui/react-presence.mjs +128 -0
  66. package/admin-dist/server/_chunks/_libs/@radix-ui/react-primitive.mjs +141 -0
  67. package/admin-dist/server/_chunks/_libs/@radix-ui/react-roving-focus.mjs +224 -0
  68. package/admin-dist/server/_chunks/_libs/@radix-ui/react-scroll-area.mjs +721 -0
  69. package/admin-dist/server/_chunks/_libs/@radix-ui/react-select.mjs +1163 -0
  70. package/admin-dist/server/_chunks/_libs/@radix-ui/react-separator.mjs +28 -0
  71. package/admin-dist/server/_chunks/_libs/@radix-ui/react-slot.mjs +601 -0
  72. package/admin-dist/server/_chunks/_libs/@radix-ui/react-switch.mjs +152 -0
  73. package/admin-dist/server/_chunks/_libs/@radix-ui/react-tabs.mjs +189 -0
  74. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-callback-ref.mjs +11 -0
  75. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-controllable-state.mjs +69 -0
  76. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-effect-event.mjs +1 -0
  77. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-escape-keydown.mjs +17 -0
  78. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-is-hydrated.mjs +15 -0
  79. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-layout-effect.mjs +6 -0
  80. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-previous.mjs +14 -0
  81. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-size.mjs +39 -0
  82. package/admin-dist/server/_chunks/_libs/@radix-ui/react-visually-hidden.mjs +33 -0
  83. package/admin-dist/server/_chunks/_libs/@tanstack/history.mjs +409 -0
  84. package/admin-dist/server/_chunks/_libs/@tanstack/react-router.mjs +1711 -0
  85. package/admin-dist/server/_chunks/_libs/@tanstack/react-store.mjs +56 -0
  86. package/admin-dist/server/_chunks/_libs/@tanstack/router-core.mjs +4829 -0
  87. package/admin-dist/server/_chunks/_libs/@tanstack/store.mjs +134 -0
  88. package/admin-dist/server/_chunks/_libs/react-dom.mjs +10781 -0
  89. package/admin-dist/server/_chunks/_libs/react.mjs +513 -0
  90. package/admin-dist/server/_libs/aria-hidden.mjs +122 -0
  91. package/admin-dist/server/_libs/class-variance-authority.mjs +44 -0
  92. package/admin-dist/server/_libs/clsx.mjs +16 -0
  93. package/admin-dist/server/_libs/cmdk.mjs +315 -0
  94. package/admin-dist/server/_libs/convex.mjs +4841 -0
  95. package/admin-dist/server/_libs/cookie-es.mjs +58 -0
  96. package/admin-dist/server/_libs/croner.mjs +1 -0
  97. package/admin-dist/server/_libs/crossws.mjs +1 -0
  98. package/admin-dist/server/_libs/date-fns.mjs +1716 -0
  99. package/admin-dist/server/_libs/detect-node-es.mjs +1 -0
  100. package/admin-dist/server/_libs/get-nonce.mjs +9 -0
  101. package/admin-dist/server/_libs/h3-v2.mjs +277 -0
  102. package/admin-dist/server/_libs/h3.mjs +401 -0
  103. package/admin-dist/server/_libs/hookable.mjs +1 -0
  104. package/admin-dist/server/_libs/isbot.mjs +20 -0
  105. package/admin-dist/server/_libs/lucide-react.mjs +850 -0
  106. package/admin-dist/server/_libs/ohash.mjs +1 -0
  107. package/admin-dist/server/_libs/react-day-picker.mjs +2201 -0
  108. package/admin-dist/server/_libs/react-remove-scroll-bar.mjs +82 -0
  109. package/admin-dist/server/_libs/react-remove-scroll.mjs +328 -0
  110. package/admin-dist/server/_libs/react-style-singleton.mjs +69 -0
  111. package/admin-dist/server/_libs/rou3.mjs +8 -0
  112. package/admin-dist/server/_libs/seroval-plugins.mjs +58 -0
  113. package/admin-dist/server/_libs/seroval.mjs +1765 -0
  114. package/admin-dist/server/_libs/srvx.mjs +719 -0
  115. package/admin-dist/server/_libs/tailwind-merge.mjs +3010 -0
  116. package/admin-dist/server/_libs/tiny-invariant.mjs +12 -0
  117. package/admin-dist/server/_libs/tiny-warning.mjs +5 -0
  118. package/admin-dist/server/_libs/tslib.mjs +39 -0
  119. package/admin-dist/server/_libs/ufo.mjs +54 -0
  120. package/admin-dist/server/_libs/unctx.mjs +1 -0
  121. package/admin-dist/server/_libs/unstorage.mjs +1 -0
  122. package/admin-dist/server/_libs/use-callback-ref.mjs +66 -0
  123. package/admin-dist/server/_libs/use-sidecar.mjs +106 -0
  124. package/admin-dist/server/_libs/use-sync-external-store.mjs +139 -0
  125. package/admin-dist/server/_libs/zod.mjs +4223 -0
  126. package/admin-dist/server/_ssr/CmsEmptyState-DU7-7-mV.mjs +290 -0
  127. package/admin-dist/server/_ssr/CmsPageHeader-CseW0AHm.mjs +24 -0
  128. package/admin-dist/server/_ssr/CmsStatusBadge-B_pi4KCp.mjs +127 -0
  129. package/admin-dist/server/_ssr/CmsToolbar-X75ex6ek.mjs +49 -0
  130. package/admin-dist/server/_ssr/ContentEntryEditor-CepusRsA.mjs +3720 -0
  131. package/admin-dist/server/_ssr/ErrorState-cI-bKLez.mjs +89 -0
  132. package/admin-dist/server/_ssr/TaxonomyFilter-Bwrq0-cz.mjs +188 -0
  133. package/admin-dist/server/_ssr/_contentTypeId-BqYKEcLr.mjs +379 -0
  134. package/admin-dist/server/_ssr/_entryId-CRfnqeDf.mjs +161 -0
  135. package/admin-dist/server/_ssr/_tanstack-start-manifest_v-BwDlABVk.mjs +4 -0
  136. package/admin-dist/server/_ssr/alert-CVt45UUP.mjs +92 -0
  137. package/admin-dist/server/_ssr/badge-6BsP37vG.mjs +125 -0
  138. package/admin-dist/server/_ssr/command-fy8epIKf.mjs +128 -0
  139. package/admin-dist/server/_ssr/config.server-D7JHDcDv.mjs +117 -0
  140. package/admin-dist/server/_ssr/content-B5RhL7uW.mjs +532 -0
  141. package/admin-dist/server/_ssr/content-types-BIOqCQYN.mjs +1166 -0
  142. package/admin-dist/server/_ssr/index-DHSHDPt1.mjs +193 -0
  143. package/admin-dist/server/_ssr/index.mjs +1275 -0
  144. package/admin-dist/server/_ssr/label-C8Dko1j7.mjs +22 -0
  145. package/admin-dist/server/_ssr/media-CSx3XttC.mjs +1832 -0
  146. package/admin-dist/server/_ssr/new._contentTypeId-DzanEZQM.mjs +144 -0
  147. package/admin-dist/server/_ssr/router-DDWcF-kt.mjs +1556 -0
  148. package/admin-dist/server/_ssr/scroll-area-bjPYwhXN.mjs +59 -0
  149. package/admin-dist/server/_ssr/select-BUhDDf4T.mjs +142 -0
  150. package/admin-dist/server/_ssr/settings-DAsxnw2q.mjs +348 -0
  151. package/admin-dist/server/_ssr/start-HYkvq4Ni.mjs +4 -0
  152. package/admin-dist/server/_ssr/switch-BgyRtQ1Z.mjs +31 -0
  153. package/admin-dist/server/_ssr/tabs-DzMdRB1A.mjs +628 -0
  154. package/admin-dist/server/_ssr/taxonomies-C8j8g5Q5.mjs +915 -0
  155. package/admin-dist/server/_ssr/textarea-9jNeYJSc.mjs +18 -0
  156. package/admin-dist/server/_ssr/trash-DYMxwhZB.mjs +291 -0
  157. package/admin-dist/server/_ssr/useBreadcrumbLabel-FNSAr2Ha.mjs +16 -0
  158. package/admin-dist/server/_ssr/usePermissions-BJGGahrJ.mjs +68 -0
  159. package/admin-dist/server/favicon.ico +0 -0
  160. package/admin-dist/server/index.mjs +627 -0
  161. package/dist/cli/index.js +0 -0
  162. package/dist/client/admin-config.d.ts +0 -1
  163. package/dist/client/admin-config.d.ts.map +1 -1
  164. package/dist/client/admin-config.js +0 -1
  165. package/dist/client/admin-config.js.map +1 -1
  166. package/dist/client/adminApi.d.ts.map +1 -1
  167. package/dist/client/agentTools.d.ts +1237 -135
  168. package/dist/client/agentTools.d.ts.map +1 -1
  169. package/dist/client/agentTools.js +33 -9
  170. package/dist/client/agentTools.js.map +1 -1
  171. package/dist/client/index.d.ts +1 -1
  172. package/dist/client/index.d.ts.map +1 -1
  173. package/dist/client/index.js.map +1 -1
  174. package/dist/component/_generated/component.d.ts +9 -0
  175. package/dist/component/_generated/component.d.ts.map +1 -1
  176. package/dist/component/mediaAssets.d.ts +35 -0
  177. package/dist/component/mediaAssets.d.ts.map +1 -1
  178. package/dist/component/mediaAssets.js +81 -0
  179. package/dist/component/mediaAssets.js.map +1 -1
  180. package/dist/test.d.ts.map +1 -1
  181. package/dist/test.js +2 -1
  182. package/dist/test.js.map +1 -1
  183. package/package.json +9 -5
  184. package/dist/component/auditLog.d.ts +0 -410
  185. package/dist/component/auditLog.d.ts.map +0 -1
  186. package/dist/component/auditLog.js +0 -607
  187. package/dist/component/auditLog.js.map +0 -1
  188. package/dist/component/types.d.ts +0 -4
  189. package/dist/component/types.d.ts.map +0 -1
  190. package/dist/component/types.js +0 -2
  191. package/dist/component/types.js.map +0 -1
  192. package/src/cli/commands/admin.ts +0 -104
  193. package/src/cli/index.ts +0 -21
  194. package/src/cli/utils/detectConvexUrl.ts +0 -54
  195. package/src/cli/utils/openBrowser.ts +0 -16
  196. package/src/client/admin-config.ts +0 -138
  197. package/src/client/adminApi.ts +0 -942
  198. package/src/client/agentTools.ts +0 -1311
  199. package/src/client/argTypes.ts +0 -316
  200. package/src/client/field-types.ts +0 -187
  201. package/src/client/index.ts +0 -1301
  202. package/src/client/queryBuilder.ts +0 -1100
  203. package/src/client/schema/codegen.ts +0 -500
  204. package/src/client/schema/defineContentType.ts +0 -501
  205. package/src/client/schema/index.ts +0 -169
  206. package/src/client/schema/schemaDrift.ts +0 -574
  207. package/src/client/schema/typedClient.ts +0 -688
  208. package/src/client/schema/types.ts +0 -666
  209. package/src/client/types.ts +0 -723
  210. package/src/client/workflows.ts +0 -141
  211. package/src/client/wrapper.ts +0 -4304
  212. package/src/component/_generated/api.ts +0 -140
  213. package/src/component/_generated/component.ts +0 -5029
  214. package/src/component/_generated/dataModel.ts +0 -60
  215. package/src/component/_generated/server.ts +0 -156
  216. package/src/component/authorization.ts +0 -647
  217. package/src/component/authorizationHooks.ts +0 -668
  218. package/src/component/bulkOperations.ts +0 -687
  219. package/src/component/contentEntries.ts +0 -1976
  220. package/src/component/contentEntryMutations.ts +0 -1223
  221. package/src/component/contentEntryValidation.ts +0 -707
  222. package/src/component/contentLock.ts +0 -550
  223. package/src/component/contentTypeMigration.ts +0 -1064
  224. package/src/component/contentTypeMutations.ts +0 -969
  225. package/src/component/contentTypes.ts +0 -346
  226. package/src/component/convex.config.ts +0 -44
  227. package/src/component/documentTypes.ts +0 -240
  228. package/src/component/eventEmitter.ts +0 -485
  229. package/src/component/exportImport.ts +0 -1169
  230. package/src/component/index.ts +0 -491
  231. package/src/component/lib/deepReferenceResolver.ts +0 -999
  232. package/src/component/lib/errors.ts +0 -816
  233. package/src/component/lib/index.ts +0 -145
  234. package/src/component/lib/mediaReferenceResolver.ts +0 -495
  235. package/src/component/lib/metadataExtractor.ts +0 -792
  236. package/src/component/lib/mutationAuth.ts +0 -199
  237. package/src/component/lib/queries.ts +0 -79
  238. package/src/component/lib/ragContentChunker.ts +0 -1371
  239. package/src/component/lib/referenceResolver.ts +0 -430
  240. package/src/component/lib/slugGenerator.ts +0 -262
  241. package/src/component/lib/slugUniqueness.ts +0 -333
  242. package/src/component/lib/softDelete.ts +0 -44
  243. package/src/component/localeFallbackChain.ts +0 -673
  244. package/src/component/localeFields.ts +0 -896
  245. package/src/component/mediaAssetMutations.ts +0 -725
  246. package/src/component/mediaAssets.ts +0 -932
  247. package/src/component/mediaFolderMutations.ts +0 -1046
  248. package/src/component/mediaUploadMutations.ts +0 -224
  249. package/src/component/mediaVariantMutations.ts +0 -900
  250. package/src/component/mediaVariants.ts +0 -793
  251. package/src/component/ragContentIndexer.ts +0 -1067
  252. package/src/component/rateLimitHooks.ts +0 -572
  253. package/src/component/roles.ts +0 -1360
  254. package/src/component/scheduledPublish.ts +0 -358
  255. package/src/component/schema.ts +0 -617
  256. package/src/component/taxonomies.ts +0 -949
  257. package/src/component/taxonomyMutations.ts +0 -1210
  258. package/src/component/trash.ts +0 -724
  259. package/src/component/userContext.ts +0 -898
  260. package/src/component/validation.ts +0 -1388
  261. package/src/component/validators.ts +0 -949
  262. package/src/component/versionMutations.ts +0 -392
  263. package/src/component/webhookTrigger.ts +0 -1922
  264. package/src/react/index.ts +0 -898
  265. package/src/test.ts +0 -1580
@@ -1,1100 +0,0 @@
1
- /**
2
- * Fluent Query Builder for Content Entries
3
- *
4
- * Provides a chainable, type-safe interface for constructing complex
5
- * content queries. Supports filtering, sorting, pagination, and search
6
- * with TypeScript inference.
7
- *
8
- * @example
9
- * ```typescript
10
- * // Simple query with chaining
11
- * const posts = await cms.contentEntries
12
- * .query()
13
- * .contentType("blog_post")
14
- * .status("published")
15
- * .limit(10)
16
- * .execute(ctx);
17
- *
18
- * // Complex query with field filters
19
- * const featured = await cms.contentEntries
20
- * .query()
21
- * .contentType("blog_post")
22
- * .where("category", "eq", "technology")
23
- * .where("featured", "eq", true)
24
- * .whereIn("tags", ["javascript", "typescript"])
25
- * .orderBy("_creationTime", "desc")
26
- * .limit(5)
27
- * .execute(ctx);
28
- *
29
- * // Pagination with cursor
30
- * const page1 = await cms.contentEntries
31
- * .query()
32
- * .contentType("blog_post")
33
- * .limit(20)
34
- * .execute(ctx);
35
- *
36
- * const page2 = await cms.contentEntries
37
- * .query()
38
- * .contentType("blog_post")
39
- * .limit(20)
40
- * .cursor(page1.continueCursor)
41
- * .execute(ctx);
42
- * ```
43
- */
44
-
45
- import type {
46
- ContentEntry,
47
- ContentStatus,
48
- FieldFilter,
49
- FilterOperator,
50
- PaginationResult,
51
- ContentQueryOptions,
52
- } from "./types.js";
53
- import type { ConvexContext, TypedComponentApi } from "./wrapper.js";
54
-
55
- // =============================================================================
56
- // Sort Direction Type
57
- // =============================================================================
58
-
59
- /**
60
- * Sort direction for query results.
61
- */
62
- export type SortDirection = "asc" | "desc";
63
-
64
- /**
65
- * Sortable fields for content entries.
66
- * Currently supports creation time; can be extended for custom field sorting.
67
- */
68
- export type SortableField = "_creationTime" | "_id" | string;
69
-
70
- // =============================================================================
71
- // Query Builder Options (Internal State)
72
- // =============================================================================
73
-
74
- /**
75
- * Internal state for the query builder.
76
- * Accumulates all query options before execution.
77
- */
78
- interface QueryBuilderState {
79
- contentTypeId?: string;
80
- contentTypeName?: string;
81
- status?: ContentStatus;
82
- statusIn?: ContentStatus[];
83
- locale?: string;
84
- search?: string;
85
- includeDeleted?: boolean;
86
- fieldFilters: FieldFilter[];
87
- sortField?: SortableField;
88
- sortDirection?: SortDirection;
89
- numItems: number;
90
- cursorValue?: string | null;
91
- }
92
-
93
- // =============================================================================
94
- // Query Builder Result Types
95
- // =============================================================================
96
-
97
- /**
98
- * Result from executing a query builder.
99
- * Extends PaginationResult with convenience methods.
100
- */
101
- export interface QueryBuilderResult<T> extends PaginationResult<T> {
102
- /**
103
- * Whether there are more results available.
104
- * Alias for !isDone for convenience.
105
- */
106
- hasMore: boolean;
107
- }
108
-
109
- // =============================================================================
110
- // Query Builder Class
111
- // =============================================================================
112
-
113
- /**
114
- * Fluent query builder for constructing content entry queries.
115
- *
116
- * Provides a chainable API for building complex queries with:
117
- * - Content type filtering
118
- * - Status filtering (single or multiple)
119
- * - Field-level filters with various operators
120
- * - Full-text search
121
- * - Locale filtering
122
- * - Cursor-based pagination
123
- * - Sort direction
124
- *
125
- * All methods return `this` for chaining, except terminal methods
126
- * (`execute()`, `first()`, `count()`) which execute the query.
127
- *
128
- * @example
129
- * ```typescript
130
- * // The query builder is obtained from contentEntries.query()
131
- * const builder = cms.contentEntries.query();
132
- *
133
- * // Chain methods to build the query
134
- * const result = await builder
135
- * .contentType("blog_post")
136
- * .status("published")
137
- * .where("author", "eq", "user_123")
138
- * .search("typescript")
139
- * .limit(10)
140
- * .execute(ctx);
141
- * ```
142
- */
143
- export class ContentQueryBuilder {
144
- private state: QueryBuilderState;
145
- private readonly api: TypedComponentApi;
146
-
147
- constructor(api: TypedComponentApi) {
148
- this.api = api;
149
- this.state = {
150
- fieldFilters: [],
151
- numItems: 50, // Default page size
152
- };
153
- }
154
-
155
- // ===========================================================================
156
- // Content Type Filtering
157
- // ===========================================================================
158
-
159
- /**
160
- * Filter by content type name.
161
- *
162
- * @param name - The content type name (e.g., "blog_post")
163
- * @returns this for chaining
164
- *
165
- * @example
166
- * ```typescript
167
- * const posts = await cms.contentEntries
168
- * .query()
169
- * .contentType("blog_post")
170
- * .execute(ctx);
171
- * ```
172
- */
173
- contentType(name: string): this {
174
- this.state.contentTypeName = name;
175
- this.state.contentTypeId = undefined; // Clear ID if name is set
176
- return this;
177
- }
178
-
179
- /**
180
- * Filter by content type ID.
181
- *
182
- * @param id - The content type ID
183
- * @returns this for chaining
184
- *
185
- * @example
186
- * ```typescript
187
- * const posts = await cms.contentEntries
188
- * .query()
189
- * .contentTypeById(typeId)
190
- * .execute(ctx);
191
- * ```
192
- */
193
- contentTypeById(id: string): this {
194
- this.state.contentTypeId = id;
195
- this.state.contentTypeName = undefined; // Clear name if ID is set
196
- return this;
197
- }
198
-
199
- // ===========================================================================
200
- // Status Filtering
201
- // ===========================================================================
202
-
203
- /**
204
- * Filter by a single status.
205
- *
206
- * @param status - The status to filter by
207
- * @returns this for chaining
208
- *
209
- * @example
210
- * ```typescript
211
- * const published = await cms.contentEntries
212
- * .query()
213
- * .status("published")
214
- * .execute(ctx);
215
- * ```
216
- */
217
- status(status: ContentStatus): this {
218
- this.state.status = status;
219
- this.state.statusIn = undefined; // Clear statusIn if single status is set
220
- return this;
221
- }
222
-
223
- /**
224
- * Filter by multiple statuses (OR logic).
225
- *
226
- * Useful for admin views that need to show content in various states.
227
- *
228
- * @param statuses - Array of statuses to include
229
- * @returns this for chaining
230
- *
231
- * @example
232
- * ```typescript
233
- * // Show all non-archived content in admin
234
- * const editorial = await cms.contentEntries
235
- * .query()
236
- * .statusIn(["draft", "published", "scheduled"])
237
- * .execute(ctx);
238
- * ```
239
- */
240
- statusIn(statuses: ContentStatus[]): this {
241
- this.state.statusIn = statuses;
242
- this.state.status = undefined; // Clear single status if statusIn is set
243
- return this;
244
- }
245
-
246
- /**
247
- * Shorthand for status("published").
248
- *
249
- * @returns this for chaining
250
- */
251
- published(): this {
252
- return this.status("published");
253
- }
254
-
255
- /**
256
- * Shorthand for status("draft").
257
- *
258
- * @returns this for chaining
259
- */
260
- drafts(): this {
261
- return this.status("draft");
262
- }
263
-
264
- /**
265
- * Shorthand for status("archived").
266
- *
267
- * @returns this for chaining
268
- */
269
- archived(): this {
270
- return this.status("archived");
271
- }
272
-
273
- /**
274
- * Shorthand for status("scheduled").
275
- *
276
- * @returns this for chaining
277
- */
278
- scheduled(): this {
279
- return this.status("scheduled");
280
- }
281
-
282
- // ===========================================================================
283
- // Locale Filtering
284
- // ===========================================================================
285
-
286
- /**
287
- * Filter by locale.
288
- *
289
- * @param locale - The locale code (e.g., "en-US", "es-ES")
290
- * @returns this for chaining
291
- *
292
- * @example
293
- * ```typescript
294
- * const spanishPosts = await cms.contentEntries
295
- * .query()
296
- * .contentType("blog_post")
297
- * .locale("es-ES")
298
- * .execute(ctx);
299
- * ```
300
- */
301
- locale(locale: string): this {
302
- this.state.locale = locale;
303
- return this;
304
- }
305
-
306
- // ===========================================================================
307
- // Soft Delete Filtering
308
- // ===========================================================================
309
-
310
- /**
311
- * Include soft-deleted entries in results.
312
- *
313
- * By default, soft-deleted entries are excluded.
314
- *
315
- * @param include - Whether to include deleted entries (default: true)
316
- * @returns this for chaining
317
- *
318
- * @example
319
- * ```typescript
320
- * // Show all entries including deleted ones
321
- * const all = await cms.contentEntries
322
- * .query()
323
- * .includeDeleted()
324
- * .execute(ctx);
325
- *
326
- * // Explicitly exclude deleted (same as default)
327
- * const active = await cms.contentEntries
328
- * .query()
329
- * .includeDeleted(false)
330
- * .execute(ctx);
331
- * ```
332
- */
333
- includeDeleted(include: boolean = true): this {
334
- this.state.includeDeleted = include;
335
- return this;
336
- }
337
-
338
- /**
339
- * Only return soft-deleted entries.
340
- *
341
- * @returns this for chaining
342
- *
343
- * @example
344
- * ```typescript
345
- * const trash = await cms.contentEntries
346
- * .query()
347
- * .onlyDeleted()
348
- * .execute(ctx);
349
- * ```
350
- */
351
- onlyDeleted(): this {
352
- this.state.includeDeleted = true;
353
- // Add a field filter for deletedAt being defined
354
- // This is handled in the component layer
355
- return this;
356
- }
357
-
358
- // ===========================================================================
359
- // Full-Text Search
360
- // ===========================================================================
361
-
362
- /**
363
- * Search content using full-text search.
364
- *
365
- * Searches indexed fields in the content entry data.
366
- * Requires the searchIndexing feature to be enabled.
367
- *
368
- * @param query - The search query string
369
- * @returns this for chaining
370
- *
371
- * @example
372
- * ```typescript
373
- * const results = await cms.contentEntries
374
- * .query()
375
- * .contentType("blog_post")
376
- * .search("typescript tutorial")
377
- * .execute(ctx);
378
- * ```
379
- */
380
- search(query: string): this {
381
- this.state.search = query;
382
- return this;
383
- }
384
-
385
- // ===========================================================================
386
- // Field Filters
387
- // ===========================================================================
388
-
389
- /**
390
- * Add a field filter condition.
391
- *
392
- * Multiple filters are combined with AND logic.
393
- *
394
- * @param field - The field name in the content data
395
- * @param operator - The comparison operator
396
- * @param value - The value to compare against
397
- * @returns this for chaining
398
- *
399
- * @example
400
- * ```typescript
401
- * const techPosts = await cms.contentEntries
402
- * .query()
403
- * .contentType("blog_post")
404
- * .where("category", "eq", "technology")
405
- * .where("views", "gte", 100)
406
- * .execute(ctx);
407
- * ```
408
- */
409
- where(field: string, operator: FilterOperator, value: unknown): this {
410
- this.state.fieldFilters.push({ field, operator, value });
411
- return this;
412
- }
413
-
414
- /**
415
- * Filter where field equals value.
416
- * Shorthand for where(field, "eq", value).
417
- *
418
- * @param field - The field name
419
- * @param value - The value to match
420
- * @returns this for chaining
421
- *
422
- * @example
423
- * ```typescript
424
- * const featured = await cms.contentEntries
425
- * .query()
426
- * .whereEquals("featured", true)
427
- * .execute(ctx);
428
- * ```
429
- */
430
- whereEquals(field: string, value: unknown): this {
431
- return this.where(field, "eq", value);
432
- }
433
-
434
- /**
435
- * Filter where field does not equal value.
436
- * Shorthand for where(field, "ne", value).
437
- *
438
- * @param field - The field name
439
- * @param value - The value to exclude
440
- * @returns this for chaining
441
- */
442
- whereNotEquals(field: string, value: unknown): this {
443
- return this.where(field, "ne", value);
444
- }
445
-
446
- /**
447
- * Filter where field is greater than value.
448
- * Shorthand for where(field, "gt", value).
449
- *
450
- * @param field - The field name
451
- * @param value - The minimum value (exclusive)
452
- * @returns this for chaining
453
- *
454
- * @example
455
- * ```typescript
456
- * const expensive = await cms.contentEntries
457
- * .query()
458
- * .contentType("product")
459
- * .whereGreaterThan("price", 100)
460
- * .execute(ctx);
461
- * ```
462
- */
463
- whereGreaterThan(field: string, value: number | Date): this {
464
- const filterValue = value instanceof Date ? value.getTime() : value;
465
- return this.where(field, "gt", filterValue);
466
- }
467
-
468
- /**
469
- * Filter where field is greater than or equal to value.
470
- * Shorthand for where(field, "gte", value).
471
- *
472
- * @param field - The field name
473
- * @param value - The minimum value (inclusive)
474
- * @returns this for chaining
475
- */
476
- whereGreaterThanOrEquals(field: string, value: number | Date): this {
477
- const filterValue = value instanceof Date ? value.getTime() : value;
478
- return this.where(field, "gte", filterValue);
479
- }
480
-
481
- /**
482
- * Filter where field is less than value.
483
- * Shorthand for where(field, "lt", value).
484
- *
485
- * @param field - The field name
486
- * @param value - The maximum value (exclusive)
487
- * @returns this for chaining
488
- */
489
- whereLessThan(field: string, value: number | Date): this {
490
- const filterValue = value instanceof Date ? value.getTime() : value;
491
- return this.where(field, "lt", filterValue);
492
- }
493
-
494
- /**
495
- * Filter where field is less than or equal to value.
496
- * Shorthand for where(field, "lte", value).
497
- *
498
- * @param field - The field name
499
- * @param value - The maximum value (inclusive)
500
- * @returns this for chaining
501
- */
502
- whereLessThanOrEquals(field: string, value: number | Date): this {
503
- const filterValue = value instanceof Date ? value.getTime() : value;
504
- return this.where(field, "lte", filterValue);
505
- }
506
-
507
- /**
508
- * Filter where field is in a range (inclusive).
509
- *
510
- * @param field - The field name
511
- * @param min - The minimum value (inclusive)
512
- * @param max - The maximum value (inclusive)
513
- * @returns this for chaining
514
- *
515
- * @example
516
- * ```typescript
517
- * const midRange = await cms.contentEntries
518
- * .query()
519
- * .contentType("product")
520
- * .whereBetween("price", 50, 150)
521
- * .execute(ctx);
522
- * ```
523
- */
524
- whereBetween(field: string, min: number | Date, max: number | Date): this {
525
- const minValue = min instanceof Date ? min.getTime() : min;
526
- const maxValue = max instanceof Date ? max.getTime() : max;
527
- return this
528
- .where(field, "gte", minValue)
529
- .where(field, "lte", maxValue);
530
- }
531
-
532
- /**
533
- * Filter where field value is in an array of allowed values.
534
- * Shorthand for where(field, "in", values).
535
- *
536
- * @param field - The field name
537
- * @param values - Array of allowed values
538
- * @returns this for chaining
539
- *
540
- * @example
541
- * ```typescript
542
- * const categoryPosts = await cms.contentEntries
543
- * .query()
544
- * .whereIn("category", ["tech", "science", "design"])
545
- * .execute(ctx);
546
- * ```
547
- */
548
- whereIn(field: string, values: unknown[]): this {
549
- return this.where(field, "in", values);
550
- }
551
-
552
- /**
553
- * Filter where field value is NOT in an array of values.
554
- * Shorthand for where(field, "notIn", values).
555
- *
556
- * @param field - The field name
557
- * @param values - Array of excluded values
558
- * @returns this for chaining
559
- */
560
- whereNotIn(field: string, values: unknown[]): this {
561
- return this.where(field, "notIn", values);
562
- }
563
-
564
- /**
565
- * Filter where string field contains a substring.
566
- * For array fields, checks if the array contains the value.
567
- * Shorthand for where(field, "contains", value).
568
- *
569
- * @param field - The field name
570
- * @param value - The substring or array value to find
571
- * @returns this for chaining
572
- *
573
- * @example
574
- * ```typescript
575
- * // String contains
576
- * const results = await cms.contentEntries
577
- * .query()
578
- * .whereContains("title", "guide")
579
- * .execute(ctx);
580
- *
581
- * // Array contains
582
- * const tagged = await cms.contentEntries
583
- * .query()
584
- * .whereContains("tags", "featured")
585
- * .execute(ctx);
586
- * ```
587
- */
588
- whereContains(field: string, value: unknown): this {
589
- return this.where(field, "contains", value);
590
- }
591
-
592
- /**
593
- * Filter where string field starts with a prefix.
594
- * Shorthand for where(field, "startsWith", prefix).
595
- *
596
- * @param field - The field name
597
- * @param prefix - The prefix to match
598
- * @returns this for chaining
599
- *
600
- * @example
601
- * ```typescript
602
- * const year2026 = await cms.contentEntries
603
- * .query()
604
- * .whereStartsWith("slug", "2026-")
605
- * .execute(ctx);
606
- * ```
607
- */
608
- whereStartsWith(field: string, prefix: string): this {
609
- return this.where(field, "startsWith", prefix);
610
- }
611
-
612
- /**
613
- * Filter where string field ends with a suffix.
614
- * Shorthand for where(field, "endsWith", suffix).
615
- *
616
- * @param field - The field name
617
- * @param suffix - The suffix to match
618
- * @returns this for chaining
619
- */
620
- whereEndsWith(field: string, suffix: string): this {
621
- return this.where(field, "endsWith", suffix);
622
- }
623
-
624
- // ===========================================================================
625
- // Sorting
626
- // ===========================================================================
627
-
628
- /**
629
- * Set the sort order for results.
630
- *
631
- * Supports sorting by:
632
- * - System fields: "_creationTime", "_id", "firstPublishedAt", "lastPublishedAt", "scheduledPublishAt", "version"
633
- * - Custom data fields: Use "data.fieldName" format (e.g., "data.price", "data.sortOrder")
634
- *
635
- * Results are sorted in descending order by default.
636
- *
637
- * @param field - The field to sort by (system field or "data.fieldName" for custom fields)
638
- * @param direction - Sort direction ("asc" or "desc")
639
- * @returns this for chaining
640
- *
641
- * @example
642
- * ```typescript
643
- * // Newest first (default)
644
- * const newest = await cms.contentEntries
645
- * .query()
646
- * .orderBy("_creationTime", "desc")
647
- * .execute(ctx);
648
- *
649
- * // Oldest first
650
- * const oldest = await cms.contentEntries
651
- * .query()
652
- * .orderBy("_creationTime", "asc")
653
- * .execute(ctx);
654
- *
655
- * // Sort by publish date
656
- * const byPublishDate = await cms.contentEntries
657
- * .query()
658
- * .orderBy("firstPublishedAt", "desc")
659
- * .execute(ctx);
660
- *
661
- * // Sort by custom field (price, low to high)
662
- * const byPrice = await cms.contentEntries
663
- * .query()
664
- * .contentType("product")
665
- * .orderBy("data.price", "asc")
666
- * .execute(ctx);
667
- *
668
- * // Sort by custom order field
669
- * const byOrder = await cms.contentEntries
670
- * .query()
671
- * .contentType("menu_item")
672
- * .orderBy("data.sortOrder", "asc")
673
- * .execute(ctx);
674
- * ```
675
- */
676
- orderBy(field: SortableField, direction: SortDirection = "desc"): this {
677
- this.state.sortField = field;
678
- this.state.sortDirection = direction;
679
- return this;
680
- }
681
-
682
- /**
683
- * Sort by first publish date (newest published first).
684
- * Useful for showing recently published content.
685
- *
686
- * @param direction - Sort direction ("asc" or "desc", default "desc")
687
- * @returns this for chaining
688
- *
689
- * @example
690
- * ```typescript
691
- * const recentlyPublished = await cms.contentEntries
692
- * .query()
693
- * .status("published")
694
- * .byPublishDate()
695
- * .execute(ctx);
696
- * ```
697
- */
698
- byPublishDate(direction: SortDirection = "desc"): this {
699
- return this.orderBy("firstPublishedAt", direction);
700
- }
701
-
702
- /**
703
- * Sort by last publish date (most recently updated first).
704
- * Useful for showing recently updated content.
705
- *
706
- * @param direction - Sort direction ("asc" or "desc", default "desc")
707
- * @returns this for chaining
708
- */
709
- byLastPublishDate(direction: SortDirection = "desc"): this {
710
- return this.orderBy("lastPublishedAt", direction);
711
- }
712
-
713
- /**
714
- * Sort by a custom data field.
715
- * Convenience method that automatically prefixes field name with "data.".
716
- *
717
- * @param fieldName - The field name in the content entry's data object
718
- * @param direction - Sort direction ("asc" or "desc", default "desc")
719
- * @returns this for chaining
720
- *
721
- * @example
722
- * ```typescript
723
- * // Sort by price (low to high)
724
- * const cheapest = await cms.contentEntries
725
- * .query()
726
- * .contentType("product")
727
- * .orderByField("price", "asc")
728
- * .execute(ctx);
729
- *
730
- * // Sort by title alphabetically
731
- * const alphabetical = await cms.contentEntries
732
- * .query()
733
- * .orderByField("title", "asc")
734
- * .execute(ctx);
735
- * ```
736
- */
737
- orderByField(fieldName: string, direction: SortDirection = "desc"): this {
738
- return this.orderBy(`data.${fieldName}`, direction);
739
- }
740
-
741
- /**
742
- * Sort by newest first (descending creation time).
743
- * Shorthand for orderBy("_creationTime", "desc").
744
- *
745
- * @returns this for chaining
746
- */
747
- newestFirst(): this {
748
- return this.orderBy("_creationTime", "desc");
749
- }
750
-
751
- /**
752
- * Sort by oldest first (ascending creation time).
753
- * Shorthand for orderBy("_creationTime", "asc").
754
- *
755
- * @returns this for chaining
756
- */
757
- oldestFirst(): this {
758
- return this.orderBy("_creationTime", "asc");
759
- }
760
-
761
- // ===========================================================================
762
- // Pagination
763
- // ===========================================================================
764
-
765
- /**
766
- * Set the maximum number of results to return.
767
- *
768
- * @param count - Number of items per page (1-250)
769
- * @returns this for chaining
770
- *
771
- * @example
772
- * ```typescript
773
- * const firstTen = await cms.contentEntries
774
- * .query()
775
- * .limit(10)
776
- * .execute(ctx);
777
- * ```
778
- */
779
- limit(count: number): this {
780
- this.state.numItems = Math.max(1, Math.min(250, count));
781
- return this;
782
- }
783
-
784
- /**
785
- * Set the pagination cursor for fetching the next page.
786
- *
787
- * Use the `continueCursor` from the previous query result.
788
- *
789
- * @param cursor - The cursor from the previous page
790
- * @returns this for chaining
791
- *
792
- * @example
793
- * ```typescript
794
- * const page1 = await cms.contentEntries
795
- * .query()
796
- * .limit(20)
797
- * .execute(ctx);
798
- *
799
- * if (!page1.isDone) {
800
- * const page2 = await cms.contentEntries
801
- * .query()
802
- * .limit(20)
803
- * .cursor(page1.continueCursor)
804
- * .execute(ctx);
805
- * }
806
- * ```
807
- */
808
- cursor(cursor: string | null | undefined): this {
809
- this.state.cursorValue = cursor;
810
- return this;
811
- }
812
-
813
- /**
814
- * Alias for cursor() for more natural chaining.
815
- *
816
- * @param cursor - The cursor from the previous page
817
- * @returns this for chaining
818
- */
819
- after(cursor: string | null | undefined): this {
820
- return this.cursor(cursor);
821
- }
822
-
823
- // ===========================================================================
824
- // Build Query Options
825
- // ===========================================================================
826
-
827
- /**
828
- * Build the query options object from the current state.
829
- *
830
- * This is primarily for internal use, but can be useful for debugging
831
- * or when you need to pass options to the underlying API directly.
832
- *
833
- * @returns The compiled ContentQueryOptions
834
- *
835
- * @example
836
- * ```typescript
837
- * const options = cms.contentEntries
838
- * .query()
839
- * .contentType("blog_post")
840
- * .status("published")
841
- * .orderBy("data.price", "asc")
842
- * .toOptions();
843
- *
844
- * console.log(options);
845
- * // {
846
- * // contentTypeName: "blog_post",
847
- * // status: "published",
848
- * // sortField: "data.price",
849
- * // sortDirection: "asc",
850
- * // fieldFilters: [],
851
- * // paginationOpts: { numItems: 50 }
852
- * // }
853
- * ```
854
- */
855
- toOptions(): ContentQueryOptions {
856
- const options: ContentQueryOptions = {
857
- paginationOpts: {
858
- numItems: this.state.numItems,
859
- // Convert undefined to null since the API expects string | null, not undefined
860
- cursor: this.state.cursorValue ?? null,
861
- },
862
- };
863
-
864
- if (this.state.contentTypeId) {
865
- options.contentTypeId = this.state.contentTypeId;
866
- }
867
- if (this.state.contentTypeName) {
868
- options.contentTypeName = this.state.contentTypeName;
869
- }
870
- if (this.state.status) {
871
- options.status = this.state.status;
872
- }
873
- if (this.state.statusIn && this.state.statusIn.length > 0) {
874
- options.statusIn = this.state.statusIn;
875
- }
876
- if (this.state.locale) {
877
- options.locale = this.state.locale;
878
- }
879
- if (this.state.search) {
880
- options.search = this.state.search;
881
- }
882
- if (this.state.includeDeleted !== undefined) {
883
- options.includeDeleted = this.state.includeDeleted;
884
- }
885
- if (this.state.fieldFilters.length > 0) {
886
- options.fieldFilters = this.state.fieldFilters;
887
- }
888
- // Include sort options if set
889
- if (this.state.sortField) {
890
- options.sortField = this.state.sortField;
891
- }
892
- if (this.state.sortDirection) {
893
- options.sortDirection = this.state.sortDirection;
894
- }
895
-
896
- return options;
897
- }
898
-
899
- // ===========================================================================
900
- // Terminal Methods (Execute Query)
901
- // ===========================================================================
902
-
903
- /**
904
- * Execute the query and return paginated results.
905
- *
906
- * This is the primary terminal method that runs the built query.
907
- *
908
- * @param ctx - Convex query context
909
- * @returns Promise resolving to paginated results
910
- *
911
- * @example
912
- * ```typescript
913
- * const result = await cms.contentEntries
914
- * .query()
915
- * .contentType("blog_post")
916
- * .status("published")
917
- * .limit(10)
918
- * .execute(ctx);
919
- *
920
- * console.log(result.page); // Array of entries
921
- * console.log(result.isDone); // true if no more results
922
- * console.log(result.continueCursor); // Cursor for next page
923
- * console.log(result.hasMore); // Convenience: !isDone
924
- * ```
925
- */
926
- async execute(ctx: ConvexContext): Promise<QueryBuilderResult<ContentEntry>> {
927
- const options = this.toOptions();
928
- const result = await ctx.runQuery(
929
- this.api.contentEntries.list,
930
- options
931
- );
932
-
933
- return {
934
- page: result.page,
935
- continueCursor: result.continueCursor,
936
- isDone: result.isDone,
937
- hasMore: !result.isDone,
938
- };
939
- }
940
-
941
- /**
942
- * Execute the query and return only the first result.
943
- *
944
- * Convenience method that sets limit(1) and returns the first item or null.
945
- *
946
- * @param ctx - Convex query context
947
- * @returns Promise resolving to the first entry or null
948
- *
949
- * @example
950
- * ```typescript
951
- * const latest = await cms.contentEntries
952
- * .query()
953
- * .contentType("blog_post")
954
- * .status("published")
955
- * .newestFirst()
956
- * .first(ctx);
957
- *
958
- * if (latest) {
959
- * console.log(latest.data.title);
960
- * }
961
- * ```
962
- */
963
- async first(ctx: ConvexContext): Promise<ContentEntry | null> {
964
- this.state.numItems = 1;
965
- const result = await this.execute(ctx);
966
- return result.page[0] ?? null;
967
- }
968
-
969
- /**
970
- * Execute the query and check if any results exist.
971
- *
972
- * Convenience method that sets limit(1) and returns boolean.
973
- *
974
- * @param ctx - Convex query context
975
- * @returns Promise resolving to true if results exist
976
- *
977
- * @example
978
- * ```typescript
979
- * const hasPublished = await cms.contentEntries
980
- * .query()
981
- * .contentType("blog_post")
982
- * .status("published")
983
- * .exists(ctx);
984
- *
985
- * if (!hasPublished) {
986
- * console.log("No published posts yet");
987
- * }
988
- * ```
989
- */
990
- async exists(ctx: ConvexContext): Promise<boolean> {
991
- const entry = await this.first(ctx);
992
- return entry !== null;
993
- }
994
-
995
- /**
996
- * Execute the query and collect all results into a single array.
997
- *
998
- * WARNING: This fetches ALL matching results, potentially making
999
- * multiple paginated requests. Use with caution on large datasets.
1000
- *
1001
- * @param ctx - Convex query context
1002
- * @param maxPages - Maximum number of pages to fetch (default: 10)
1003
- * @returns Promise resolving to array of all entries
1004
- *
1005
- * @example
1006
- * ```typescript
1007
- * // Get all published posts (use with caution!)
1008
- * const allPosts = await cms.contentEntries
1009
- * .query()
1010
- * .contentType("blog_post")
1011
- * .status("published")
1012
- * .all(ctx);
1013
- * ```
1014
- */
1015
- async all(ctx: ConvexContext, maxPages: number = 10): Promise<ContentEntry[]> {
1016
- const results: ContentEntry[] = [];
1017
- let currentCursor: string | null | undefined = this.state.cursorValue;
1018
- let pageCount = 0;
1019
-
1020
- while (pageCount < maxPages) {
1021
- this.state.cursorValue = currentCursor;
1022
- const result = await this.execute(ctx);
1023
- results.push(...result.page);
1024
-
1025
- if (result.isDone || !result.continueCursor) {
1026
- break;
1027
- }
1028
-
1029
- currentCursor = result.continueCursor;
1030
- pageCount++;
1031
- }
1032
-
1033
- return results;
1034
- }
1035
-
1036
- // ===========================================================================
1037
- // Clone / Reset
1038
- // ===========================================================================
1039
-
1040
- /**
1041
- * Create a copy of this query builder with the current state.
1042
- *
1043
- * Useful for creating variations of a base query.
1044
- *
1045
- * @returns A new QueryBuilder with the same state
1046
- *
1047
- * @example
1048
- * ```typescript
1049
- * const baseQuery = cms.contentEntries
1050
- * .query()
1051
- * .contentType("blog_post");
1052
- *
1053
- * const published = await baseQuery.clone()
1054
- * .status("published")
1055
- * .execute(ctx);
1056
- *
1057
- * const drafts = await baseQuery.clone()
1058
- * .status("draft")
1059
- * .execute(ctx);
1060
- * ```
1061
- */
1062
- clone(): ContentQueryBuilder {
1063
- const cloned = new ContentQueryBuilder(this.api);
1064
- cloned.state = {
1065
- ...this.state,
1066
- fieldFilters: [...this.state.fieldFilters],
1067
- statusIn: this.state.statusIn ? [...this.state.statusIn] : undefined,
1068
- };
1069
- return cloned;
1070
- }
1071
-
1072
- /**
1073
- * Reset the query builder to its initial state.
1074
- *
1075
- * @returns this for chaining
1076
- */
1077
- reset(): this {
1078
- this.state = {
1079
- fieldFilters: [],
1080
- numItems: 50,
1081
- };
1082
- return this;
1083
- }
1084
- }
1085
-
1086
- // =============================================================================
1087
- // Factory Function
1088
- // =============================================================================
1089
-
1090
- /**
1091
- * Create a new content query builder.
1092
- *
1093
- * This is typically called internally by ContentEntriesApi.query().
1094
- *
1095
- * @param api - The typed component API
1096
- * @returns A new ContentQueryBuilder instance
1097
- */
1098
- export function createQueryBuilder(api: TypedComponentApi): ContentQueryBuilder {
1099
- return new ContentQueryBuilder(api);
1100
- }