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.
- package/admin-dist/nitro.json +15 -0
- package/admin-dist/public/assets/CmsEmptyState-CRswfTzk.js +5 -0
- package/admin-dist/public/assets/CmsPageHeader-CirpXndm.js +1 -0
- package/admin-dist/public/assets/CmsStatusBadge-CbEUpQu-.js +1 -0
- package/admin-dist/public/assets/CmsToolbar-BI2nZOXp.js +1 -0
- package/admin-dist/public/assets/ContentEntryEditor-CBeCyK_m.js +4 -0
- package/admin-dist/public/assets/ErrorState-BIVaWmom.js +1 -0
- package/admin-dist/public/assets/TaxonomyFilter-ChaY6Y_x.js +1 -0
- package/admin-dist/public/assets/_contentTypeId-DQ8k_Rvw.js +1 -0
- package/admin-dist/public/assets/_entryId-CKU_glsK.js +1 -0
- package/admin-dist/public/assets/alert-BXjTqrwQ.js +1 -0
- package/admin-dist/public/assets/badge-hvUOzpVZ.js +1 -0
- package/admin-dist/public/assets/circle-check-big-CF_pR17r.js +1 -0
- package/admin-dist/public/assets/command-DU82cJlt.js +1 -0
- package/admin-dist/public/assets/content-_LXl3pp7.js +1 -0
- package/admin-dist/public/assets/content-types-KjxaXGxY.js +2 -0
- package/admin-dist/public/assets/globals-CS6BZ0zp.css +1 -0
- package/admin-dist/public/assets/index-DNGIZHL-.js +1 -0
- package/admin-dist/public/assets/label-KNtpL71g.js +1 -0
- package/admin-dist/public/assets/link-2-Bw2aI4V4.js +1 -0
- package/admin-dist/public/assets/list-sYepHjt_.js +1 -0
- package/admin-dist/public/assets/main-CKj5yfEi.js +97 -0
- package/admin-dist/public/assets/media-Bkrkffm7.js +1 -0
- package/admin-dist/public/assets/new._contentTypeId-C3LstjNs.js +1 -0
- package/admin-dist/public/assets/plus-DUn8v_Xf.js +1 -0
- package/admin-dist/public/assets/rotate-ccw-DJEoHcRI.js +1 -0
- package/admin-dist/public/assets/scroll-area-DfIlT0in.js +1 -0
- package/admin-dist/public/assets/search-MuAUDJKR.js +1 -0
- package/admin-dist/public/assets/select-BD29IXCI.js +1 -0
- package/admin-dist/public/assets/settings-DmMyn_6A.js +1 -0
- package/admin-dist/public/assets/switch-h3Rrnl5i.js +1 -0
- package/admin-dist/public/assets/tabs-imc8h-Dp.js +1 -0
- package/admin-dist/public/assets/taxonomies-dAsrT65H.js +1 -0
- package/admin-dist/public/assets/textarea-BTy7nwzR.js +1 -0
- package/admin-dist/public/assets/trash-SAWKZZHv.js +1 -0
- package/admin-dist/public/assets/triangle-alert-E52Vfeuh.js +1 -0
- package/admin-dist/public/assets/useBreadcrumbLabel-BECBMCzM.js +1 -0
- package/admin-dist/public/assets/usePermissions-Basjs9BT.js +1 -0
- package/admin-dist/public/favicon.ico +0 -0
- package/admin-dist/server/_chunks/_libs/@date-fns/tz.mjs +217 -0
- package/admin-dist/server/_chunks/_libs/@floating-ui/core.mjs +719 -0
- package/admin-dist/server/_chunks/_libs/@floating-ui/dom.mjs +622 -0
- package/admin-dist/server/_chunks/_libs/@floating-ui/react-dom.mjs +292 -0
- package/admin-dist/server/_chunks/_libs/@floating-ui/utils.mjs +320 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/number.mjs +6 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/primitive.mjs +11 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-arrow.mjs +23 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-avatar.mjs +119 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-checkbox.mjs +270 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-collection.mjs +69 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-compose-refs.mjs +39 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-context.mjs +137 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-dialog.mjs +325 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-direction.mjs +9 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-dismissable-layer.mjs +210 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-dropdown-menu.mjs +253 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-focus-guards.mjs +29 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-focus-scope.mjs +206 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-id.mjs +14 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-label.mjs +23 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-menu.mjs +812 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-popover.mjs +300 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-popper.mjs +286 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-portal.mjs +16 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-presence.mjs +128 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-primitive.mjs +141 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-roving-focus.mjs +224 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-scroll-area.mjs +721 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-select.mjs +1163 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-separator.mjs +28 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-slot.mjs +601 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-switch.mjs +152 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-tabs.mjs +189 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-callback-ref.mjs +11 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-controllable-state.mjs +69 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-effect-event.mjs +1 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-escape-keydown.mjs +17 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-is-hydrated.mjs +15 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-layout-effect.mjs +6 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-previous.mjs +14 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-size.mjs +39 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-visually-hidden.mjs +33 -0
- package/admin-dist/server/_chunks/_libs/@tanstack/history.mjs +409 -0
- package/admin-dist/server/_chunks/_libs/@tanstack/react-router.mjs +1711 -0
- package/admin-dist/server/_chunks/_libs/@tanstack/react-store.mjs +56 -0
- package/admin-dist/server/_chunks/_libs/@tanstack/router-core.mjs +4829 -0
- package/admin-dist/server/_chunks/_libs/@tanstack/store.mjs +134 -0
- package/admin-dist/server/_chunks/_libs/react-dom.mjs +10781 -0
- package/admin-dist/server/_chunks/_libs/react.mjs +513 -0
- package/admin-dist/server/_libs/aria-hidden.mjs +122 -0
- package/admin-dist/server/_libs/class-variance-authority.mjs +44 -0
- package/admin-dist/server/_libs/clsx.mjs +16 -0
- package/admin-dist/server/_libs/cmdk.mjs +315 -0
- package/admin-dist/server/_libs/convex.mjs +4841 -0
- package/admin-dist/server/_libs/cookie-es.mjs +58 -0
- package/admin-dist/server/_libs/croner.mjs +1 -0
- package/admin-dist/server/_libs/crossws.mjs +1 -0
- package/admin-dist/server/_libs/date-fns.mjs +1716 -0
- package/admin-dist/server/_libs/detect-node-es.mjs +1 -0
- package/admin-dist/server/_libs/get-nonce.mjs +9 -0
- package/admin-dist/server/_libs/h3-v2.mjs +277 -0
- package/admin-dist/server/_libs/h3.mjs +401 -0
- package/admin-dist/server/_libs/hookable.mjs +1 -0
- package/admin-dist/server/_libs/isbot.mjs +20 -0
- package/admin-dist/server/_libs/lucide-react.mjs +850 -0
- package/admin-dist/server/_libs/ohash.mjs +1 -0
- package/admin-dist/server/_libs/react-day-picker.mjs +2201 -0
- package/admin-dist/server/_libs/react-remove-scroll-bar.mjs +82 -0
- package/admin-dist/server/_libs/react-remove-scroll.mjs +328 -0
- package/admin-dist/server/_libs/react-style-singleton.mjs +69 -0
- package/admin-dist/server/_libs/rou3.mjs +8 -0
- package/admin-dist/server/_libs/seroval-plugins.mjs +58 -0
- package/admin-dist/server/_libs/seroval.mjs +1765 -0
- package/admin-dist/server/_libs/srvx.mjs +719 -0
- package/admin-dist/server/_libs/tailwind-merge.mjs +3010 -0
- package/admin-dist/server/_libs/tiny-invariant.mjs +12 -0
- package/admin-dist/server/_libs/tiny-warning.mjs +5 -0
- package/admin-dist/server/_libs/tslib.mjs +39 -0
- package/admin-dist/server/_libs/ufo.mjs +54 -0
- package/admin-dist/server/_libs/unctx.mjs +1 -0
- package/admin-dist/server/_libs/unstorage.mjs +1 -0
- package/admin-dist/server/_libs/use-callback-ref.mjs +66 -0
- package/admin-dist/server/_libs/use-sidecar.mjs +106 -0
- package/admin-dist/server/_libs/use-sync-external-store.mjs +139 -0
- package/admin-dist/server/_libs/zod.mjs +4223 -0
- package/admin-dist/server/_ssr/CmsEmptyState-DU7-7-mV.mjs +290 -0
- package/admin-dist/server/_ssr/CmsPageHeader-CseW0AHm.mjs +24 -0
- package/admin-dist/server/_ssr/CmsStatusBadge-B_pi4KCp.mjs +127 -0
- package/admin-dist/server/_ssr/CmsToolbar-X75ex6ek.mjs +49 -0
- package/admin-dist/server/_ssr/ContentEntryEditor-CepusRsA.mjs +3720 -0
- package/admin-dist/server/_ssr/ErrorState-cI-bKLez.mjs +89 -0
- package/admin-dist/server/_ssr/TaxonomyFilter-Bwrq0-cz.mjs +188 -0
- package/admin-dist/server/_ssr/_contentTypeId-BqYKEcLr.mjs +379 -0
- package/admin-dist/server/_ssr/_entryId-CRfnqeDf.mjs +161 -0
- package/admin-dist/server/_ssr/_tanstack-start-manifest_v-BwDlABVk.mjs +4 -0
- package/admin-dist/server/_ssr/alert-CVt45UUP.mjs +92 -0
- package/admin-dist/server/_ssr/badge-6BsP37vG.mjs +125 -0
- package/admin-dist/server/_ssr/command-fy8epIKf.mjs +128 -0
- package/admin-dist/server/_ssr/config.server-D7JHDcDv.mjs +117 -0
- package/admin-dist/server/_ssr/content-B5RhL7uW.mjs +532 -0
- package/admin-dist/server/_ssr/content-types-BIOqCQYN.mjs +1166 -0
- package/admin-dist/server/_ssr/index-DHSHDPt1.mjs +193 -0
- package/admin-dist/server/_ssr/index.mjs +1275 -0
- package/admin-dist/server/_ssr/label-C8Dko1j7.mjs +22 -0
- package/admin-dist/server/_ssr/media-CSx3XttC.mjs +1832 -0
- package/admin-dist/server/_ssr/new._contentTypeId-DzanEZQM.mjs +144 -0
- package/admin-dist/server/_ssr/router-DDWcF-kt.mjs +1556 -0
- package/admin-dist/server/_ssr/scroll-area-bjPYwhXN.mjs +59 -0
- package/admin-dist/server/_ssr/select-BUhDDf4T.mjs +142 -0
- package/admin-dist/server/_ssr/settings-DAsxnw2q.mjs +348 -0
- package/admin-dist/server/_ssr/start-HYkvq4Ni.mjs +4 -0
- package/admin-dist/server/_ssr/switch-BgyRtQ1Z.mjs +31 -0
- package/admin-dist/server/_ssr/tabs-DzMdRB1A.mjs +628 -0
- package/admin-dist/server/_ssr/taxonomies-C8j8g5Q5.mjs +915 -0
- package/admin-dist/server/_ssr/textarea-9jNeYJSc.mjs +18 -0
- package/admin-dist/server/_ssr/trash-DYMxwhZB.mjs +291 -0
- package/admin-dist/server/_ssr/useBreadcrumbLabel-FNSAr2Ha.mjs +16 -0
- package/admin-dist/server/_ssr/usePermissions-BJGGahrJ.mjs +68 -0
- package/admin-dist/server/favicon.ico +0 -0
- package/admin-dist/server/index.mjs +627 -0
- package/dist/cli/index.js +0 -0
- package/dist/client/admin-config.d.ts +0 -1
- package/dist/client/admin-config.d.ts.map +1 -1
- package/dist/client/admin-config.js +0 -1
- package/dist/client/admin-config.js.map +1 -1
- package/dist/client/adminApi.d.ts.map +1 -1
- package/dist/client/agentTools.d.ts +1237 -135
- package/dist/client/agentTools.d.ts.map +1 -1
- package/dist/client/agentTools.js +33 -9
- package/dist/client/agentTools.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/component.d.ts +9 -0
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/mediaAssets.d.ts +35 -0
- package/dist/component/mediaAssets.d.ts.map +1 -1
- package/dist/component/mediaAssets.js +81 -0
- package/dist/component/mediaAssets.js.map +1 -1
- package/dist/test.d.ts.map +1 -1
- package/dist/test.js +2 -1
- package/dist/test.js.map +1 -1
- package/package.json +9 -5
- package/dist/component/auditLog.d.ts +0 -410
- package/dist/component/auditLog.d.ts.map +0 -1
- package/dist/component/auditLog.js +0 -607
- package/dist/component/auditLog.js.map +0 -1
- package/dist/component/types.d.ts +0 -4
- package/dist/component/types.d.ts.map +0 -1
- package/dist/component/types.js +0 -2
- package/dist/component/types.js.map +0 -1
- package/src/cli/commands/admin.ts +0 -104
- package/src/cli/index.ts +0 -21
- package/src/cli/utils/detectConvexUrl.ts +0 -54
- package/src/cli/utils/openBrowser.ts +0 -16
- package/src/client/admin-config.ts +0 -138
- package/src/client/adminApi.ts +0 -942
- package/src/client/agentTools.ts +0 -1311
- package/src/client/argTypes.ts +0 -316
- package/src/client/field-types.ts +0 -187
- package/src/client/index.ts +0 -1301
- package/src/client/queryBuilder.ts +0 -1100
- package/src/client/schema/codegen.ts +0 -500
- package/src/client/schema/defineContentType.ts +0 -501
- package/src/client/schema/index.ts +0 -169
- package/src/client/schema/schemaDrift.ts +0 -574
- package/src/client/schema/typedClient.ts +0 -688
- package/src/client/schema/types.ts +0 -666
- package/src/client/types.ts +0 -723
- package/src/client/workflows.ts +0 -141
- package/src/client/wrapper.ts +0 -4304
- package/src/component/_generated/api.ts +0 -140
- package/src/component/_generated/component.ts +0 -5029
- package/src/component/_generated/dataModel.ts +0 -60
- package/src/component/_generated/server.ts +0 -156
- package/src/component/authorization.ts +0 -647
- package/src/component/authorizationHooks.ts +0 -668
- package/src/component/bulkOperations.ts +0 -687
- package/src/component/contentEntries.ts +0 -1976
- package/src/component/contentEntryMutations.ts +0 -1223
- package/src/component/contentEntryValidation.ts +0 -707
- package/src/component/contentLock.ts +0 -550
- package/src/component/contentTypeMigration.ts +0 -1064
- package/src/component/contentTypeMutations.ts +0 -969
- package/src/component/contentTypes.ts +0 -346
- package/src/component/convex.config.ts +0 -44
- package/src/component/documentTypes.ts +0 -240
- package/src/component/eventEmitter.ts +0 -485
- package/src/component/exportImport.ts +0 -1169
- package/src/component/index.ts +0 -491
- package/src/component/lib/deepReferenceResolver.ts +0 -999
- package/src/component/lib/errors.ts +0 -816
- package/src/component/lib/index.ts +0 -145
- package/src/component/lib/mediaReferenceResolver.ts +0 -495
- package/src/component/lib/metadataExtractor.ts +0 -792
- package/src/component/lib/mutationAuth.ts +0 -199
- package/src/component/lib/queries.ts +0 -79
- package/src/component/lib/ragContentChunker.ts +0 -1371
- package/src/component/lib/referenceResolver.ts +0 -430
- package/src/component/lib/slugGenerator.ts +0 -262
- package/src/component/lib/slugUniqueness.ts +0 -333
- package/src/component/lib/softDelete.ts +0 -44
- package/src/component/localeFallbackChain.ts +0 -673
- package/src/component/localeFields.ts +0 -896
- package/src/component/mediaAssetMutations.ts +0 -725
- package/src/component/mediaAssets.ts +0 -932
- package/src/component/mediaFolderMutations.ts +0 -1046
- package/src/component/mediaUploadMutations.ts +0 -224
- package/src/component/mediaVariantMutations.ts +0 -900
- package/src/component/mediaVariants.ts +0 -793
- package/src/component/ragContentIndexer.ts +0 -1067
- package/src/component/rateLimitHooks.ts +0 -572
- package/src/component/roles.ts +0 -1360
- package/src/component/scheduledPublish.ts +0 -358
- package/src/component/schema.ts +0 -617
- package/src/component/taxonomies.ts +0 -949
- package/src/component/taxonomyMutations.ts +0 -1210
- package/src/component/trash.ts +0 -724
- package/src/component/userContext.ts +0 -898
- package/src/component/validation.ts +0 -1388
- package/src/component/validators.ts +0 -949
- package/src/component/versionMutations.ts +0 -392
- package/src/component/webhookTrigger.ts +0 -1922
- package/src/react/index.ts +0 -898
- 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
|
-
}
|