@btst/stack 5.0.0 → 5.1.0

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 (113) hide show
  1. package/dist/api/index.d.cts +2 -2
  2. package/dist/api/index.d.mts +2 -2
  3. package/dist/api/index.d.ts +2 -2
  4. package/dist/client/index.d.cts +2 -2
  5. package/dist/client/index.d.mts +2 -2
  6. package/dist/client/index.d.ts +2 -2
  7. package/dist/context/index.d.cts +1 -1
  8. package/dist/context/index.d.mts +1 -1
  9. package/dist/context/index.d.ts +1 -1
  10. package/dist/index.d.cts +1 -1
  11. package/dist/index.d.mts +1 -1
  12. package/dist/index.d.ts +1 -1
  13. package/dist/packages/stack/src/plugins/cms/api/getters.cjs +142 -6
  14. package/dist/packages/stack/src/plugins/cms/api/getters.mjs +139 -8
  15. package/dist/packages/stack/src/plugins/cms/api/operations.cjs +67 -61
  16. package/dist/packages/stack/src/plugins/cms/api/operations.mjs +69 -63
  17. package/dist/packages/stack/src/plugins/cms/api/plugin.cjs +26 -8
  18. package/dist/packages/stack/src/plugins/cms/api/plugin.mjs +27 -9
  19. package/dist/packages/stack/src/plugins/cms/api/query-key-defs.cjs +15 -1
  20. package/dist/packages/stack/src/plugins/cms/api/query-key-defs.mjs +14 -2
  21. package/dist/packages/stack/src/plugins/cms/client/hooks/cms-hooks.cjs +45 -24
  22. package/dist/packages/stack/src/plugins/cms/client/hooks/cms-hooks.mjs +45 -24
  23. package/dist/packages/stack/src/plugins/cms/schemas.cjs +22 -0
  24. package/dist/packages/stack/src/plugins/cms/schemas.mjs +20 -1
  25. package/dist/plugins/api/index.d.cts +2 -2
  26. package/dist/plugins/api/index.d.mts +2 -2
  27. package/dist/plugins/api/index.d.ts +2 -2
  28. package/dist/plugins/blog/api/index.d.cts +2 -2
  29. package/dist/plugins/blog/api/index.d.mts +2 -2
  30. package/dist/plugins/blog/api/index.d.ts +2 -2
  31. package/dist/plugins/blog/client/hooks/index.d.cts +2 -2
  32. package/dist/plugins/blog/client/hooks/index.d.mts +2 -2
  33. package/dist/plugins/blog/client/hooks/index.d.ts +2 -2
  34. package/dist/plugins/blog/client/index.d.cts +2 -2
  35. package/dist/plugins/blog/client/index.d.mts +2 -2
  36. package/dist/plugins/blog/client/index.d.ts +2 -2
  37. package/dist/plugins/blog/query-keys.d.cts +2 -2
  38. package/dist/plugins/blog/query-keys.d.mts +2 -2
  39. package/dist/plugins/blog/query-keys.d.ts +2 -2
  40. package/dist/plugins/client/index.d.cts +2 -2
  41. package/dist/plugins/client/index.d.mts +2 -2
  42. package/dist/plugins/client/index.d.ts +2 -2
  43. package/dist/plugins/cms/api/index.cjs +1 -0
  44. package/dist/plugins/cms/api/index.d.cts +43 -11
  45. package/dist/plugins/cms/api/index.d.mts +43 -11
  46. package/dist/plugins/cms/api/index.d.ts +43 -11
  47. package/dist/plugins/cms/api/index.mjs +1 -1
  48. package/dist/plugins/cms/client/hooks/index.d.cts +90 -45
  49. package/dist/plugins/cms/client/hooks/index.d.mts +90 -45
  50. package/dist/plugins/cms/client/hooks/index.d.ts +90 -45
  51. package/dist/plugins/cms/client/index.d.cts +2 -1
  52. package/dist/plugins/cms/client/index.d.mts +2 -1
  53. package/dist/plugins/cms/client/index.d.ts +2 -1
  54. package/dist/plugins/cms/query-keys.cjs +69 -13
  55. package/dist/plugins/cms/query-keys.d.cts +89 -212
  56. package/dist/plugins/cms/query-keys.d.mts +89 -212
  57. package/dist/plugins/cms/query-keys.d.ts +89 -212
  58. package/dist/plugins/cms/query-keys.mjs +70 -14
  59. package/dist/plugins/kanban/client/index.d.cts +1 -1
  60. package/dist/plugins/kanban/client/index.d.mts +1 -1
  61. package/dist/plugins/kanban/client/index.d.ts +1 -1
  62. package/dist/plugins/route-docs/client/index.d.cts +1 -1
  63. package/dist/plugins/route-docs/client/index.d.mts +1 -1
  64. package/dist/plugins/route-docs/client/index.d.ts +1 -1
  65. package/dist/plugins/ui-builder/client/hooks/index.d.cts +3 -3
  66. package/dist/plugins/ui-builder/client/hooks/index.d.mts +3 -3
  67. package/dist/plugins/ui-builder/client/hooks/index.d.ts +3 -3
  68. package/dist/plugins/ui-builder/client/index.d.cts +3 -3
  69. package/dist/plugins/ui-builder/client/index.d.mts +3 -3
  70. package/dist/plugins/ui-builder/client/index.d.ts +3 -3
  71. package/dist/plugins/ui-builder/query-keys.d.cts +4 -3
  72. package/dist/plugins/ui-builder/query-keys.d.mts +4 -3
  73. package/dist/plugins/ui-builder/query-keys.d.ts +4 -3
  74. package/dist/shared/{stack.DMj68qY0.d.mts → stack.B2jlVVhD.d.mts} +5 -5
  75. package/dist/shared/{stack.Cvnj99cs.d.ts → stack.BB9tubJJ.d.ts} +9 -3
  76. package/dist/shared/{stack.Bt3WG2yb.d.cts → stack.BWduIMKG.d.cts} +35 -35
  77. package/dist/shared/stack.Bw09Lg2z.d.ts +1420 -0
  78. package/dist/shared/{stack.CkEz7vSt.d.mts → stack.C2Pvl-RS.d.mts} +9 -3
  79. package/dist/shared/{stack.C2l2wd_X.d.ts → stack.Ch9-ppmQ.d.ts} +35 -35
  80. package/dist/shared/{stack.Ht8mhDj5.d.cts → stack.D17yFglU.d.cts} +5 -5
  81. package/dist/shared/{stack.CiD115Ov.d.mts → stack.D6fPBfYe.d.mts} +35 -35
  82. package/dist/shared/stack.DFGj0gb7.d.mts +1420 -0
  83. package/dist/shared/{stack.pi0FxoEa.d.ts → stack.Dcfg4GNN.d.ts} +5 -5
  84. package/dist/shared/{stack.3rxARhgS.d.cts → stack.DrB3L7Ih.d.cts} +52 -3
  85. package/dist/shared/{stack.3rxARhgS.d.mts → stack.DrB3L7Ih.d.mts} +52 -3
  86. package/dist/shared/{stack.3rxARhgS.d.ts → stack.DrB3L7Ih.d.ts} +52 -3
  87. package/dist/shared/stack.NNrBKBkM.d.cts +1420 -0
  88. package/dist/shared/{stack.mRRyZBUg.d.cts → stack.Rb87lHqK.d.cts} +9 -3
  89. package/package.json +1 -1
  90. package/src/__tests__/cms-query-keys.test.ts +57 -0
  91. package/src/plugins/api/index.ts +1 -0
  92. package/src/plugins/api/operation.ts +16 -3
  93. package/src/plugins/cms/__tests__/authorization.test.ts +652 -187
  94. package/src/plugins/cms/__tests__/client-runtime.browser.test.tsx +655 -0
  95. package/src/plugins/cms/__tests__/content-read.typecheck.ts +351 -0
  96. package/src/plugins/cms/__tests__/getters.test.ts +286 -0
  97. package/src/plugins/cms/api/getters.ts +270 -12
  98. package/src/plugins/cms/api/index.ts +10 -0
  99. package/src/plugins/cms/api/operations.ts +163 -75
  100. package/src/plugins/cms/api/plugin.ts +50 -12
  101. package/src/plugins/cms/api/query-key-defs.ts +29 -2
  102. package/src/plugins/cms/client/hooks/cms-hooks.tsx +400 -91
  103. package/src/plugins/cms/client/index.ts +9 -0
  104. package/src/plugins/cms/query-keys.ts +171 -16
  105. package/src/plugins/cms/schemas.ts +30 -0
  106. package/src/plugins/cms/types.ts +49 -2
  107. package/src/plugins/media/__tests__/identity-partition.test.tsx +26 -16
  108. package/dist/shared/stack.CAmVUwjV.d.cts +0 -764
  109. package/dist/shared/stack.CQK02ujt.d.ts +0 -764
  110. package/dist/shared/stack.D9x1fR5a.d.mts +0 -764
  111. package/dist/shared/{stack.CKc_Bais.d.cts → stack.Csi-4Mgz.d.cts} +7 -7
  112. package/dist/shared/{stack.CKc_Bais.d.mts → stack.Csi-4Mgz.d.mts} +7 -7
  113. package/dist/shared/{stack.CKc_Bais.d.ts → stack.Csi-4Mgz.d.ts} +7 -7
@@ -10,17 +10,28 @@ import type {
10
10
  SerializedContentItemWithType,
11
11
  PaginatedContentItems,
12
12
  InverseRelation,
13
+ CMSReadOptions,
14
+ CMSContentFields,
15
+ CMSReadView,
16
+ CMSContentItemResult,
17
+ CMSContentTypeMap,
13
18
  } from "../../types";
14
19
  import { cms } from "./cms-resource";
15
20
 
16
21
  /** Flattens infinite-query pages of `{ items, total }` envelopes. */
17
- function flattenPages<TData>(pages: PaginatedContentItems[] | undefined): {
18
- items: SerializedContentItemWithType<TData>[];
22
+ function flattenPages<
23
+ TData,
24
+ TView extends CMSReadView = undefined,
25
+ TFields extends CMSContentFields = undefined,
26
+ >(
27
+ pages: PaginatedContentItems[] | undefined,
28
+ ): {
29
+ items: CMSContentItemResult<TData, TView, TFields>[];
19
30
  total: number;
20
31
  } {
21
32
  const items = (pages?.flatMap((page) =>
22
33
  Array.isArray(page?.items) ? page.items : [],
23
- ) ?? []) as SerializedContentItemWithType<TData>[];
34
+ ) ?? []) as CMSContentItemResult<TData, TView, TFields>[];
24
35
  const total = pages?.[0]?.total ?? 0;
25
36
  return { items, total };
26
37
  }
@@ -86,7 +97,10 @@ export function useContentType(slug: string): {
86
97
 
87
98
  // ========== Content Items Hooks ==========
88
99
 
89
- export interface UseContentOptions {
100
+ export interface UseContentOptions<
101
+ TView extends CMSReadView = CMSReadView,
102
+ TFields extends CMSContentFields = CMSContentFields,
103
+ > extends CMSReadOptions<TView, TFields> {
90
104
  /** Number of items per page (default: 10) */
91
105
  limit?: number;
92
106
  /** Whether to enable the query (default: true) */
@@ -99,9 +113,13 @@ export interface UseContentOptions {
99
113
  * Result type for useContent hook with load more functionality
100
114
  * @template TData - The type of parsedData (defaults to Record<string, unknown>)
101
115
  */
102
- export interface UseContentResult<TData = Record<string, unknown>> {
116
+ export interface UseContentResult<
117
+ TData = Record<string, unknown>,
118
+ TView extends CMSReadView = undefined,
119
+ TFields extends CMSContentFields = undefined,
120
+ > {
103
121
  /** Array of all loaded content items */
104
- items: SerializedContentItemWithType<TData>[];
122
+ items: CMSContentItemResult<TData, TView, TFields>[];
105
123
  /** Total number of items available */
106
124
  total: number;
107
125
  /** Whether the initial load is in progress */
@@ -140,17 +158,49 @@ export interface UseContentResult<TData = Record<string, unknown>> {
140
158
  * // items[0].parsedData.price is number
141
159
  * ```
142
160
  */
161
+ export function useContent<
162
+ TMap extends CMSContentTypeMap = CMSContentTypeMap,
163
+ TSlug extends keyof TMap = keyof TMap,
164
+ const TFields extends readonly string[] = readonly string[],
165
+ >(
166
+ typeSlug: TSlug & string,
167
+ options: UseContentOptions<"content", TFields> & {
168
+ view: "content";
169
+ fields: TFields;
170
+ },
171
+ ): UseContentResult<TMap[TSlug], "content", TFields>;
172
+ export function useContent<
173
+ TMap extends CMSContentTypeMap = CMSContentTypeMap,
174
+ TSlug extends keyof TMap = keyof TMap,
175
+ >(
176
+ typeSlug: TSlug & string,
177
+ options: UseContentOptions<"content"> & {
178
+ view: "content";
179
+ fields?: undefined;
180
+ },
181
+ ): UseContentResult<TMap[TSlug], "content">;
182
+ export function useContent<
183
+ TMap extends CMSContentTypeMap = CMSContentTypeMap,
184
+ TSlug extends keyof TMap = keyof TMap,
185
+ TView extends CMSReadView = undefined,
186
+ TFields extends CMSContentFields = undefined,
187
+ >(
188
+ typeSlug: TSlug & string,
189
+ options?: UseContentOptions<TView, TFields>,
190
+ ): UseContentResult<TMap[TSlug], TView, TFields>;
143
191
  export function useContent<
144
192
  TMap extends Record<string, Record<string, unknown>> = Record<
145
193
  string,
146
194
  Record<string, unknown>
147
195
  >,
148
196
  TSlug extends keyof TMap = keyof TMap,
197
+ TView extends CMSReadView = undefined,
198
+ TFields extends CMSContentFields = undefined,
149
199
  >(
150
200
  typeSlug: TSlug & string,
151
- options: UseContentOptions = {},
152
- ): UseContentResult<TMap[TSlug]> {
153
- const { limit = 10, enabled = true, search } = options;
201
+ options: UseContentOptions<TView, TFields> = {},
202
+ ): UseContentResult<TMap[TSlug], TView, TFields> {
203
+ const { limit = 10, enabled = true, search, view, fields } = options;
154
204
 
155
205
  const {
156
206
  data,
@@ -160,11 +210,16 @@ export function useContent<
160
210
  hasNextPage,
161
211
  isFetchingNextPage,
162
212
  refetch,
163
- } = cms.cmsContent.list.useInfinite([{ typeSlug, limit, search }], {
164
- enabled: enabled && !!typeSlug,
165
- });
213
+ } = cms.cmsContent.list.useInfinite(
214
+ [{ typeSlug, limit, search, view, fields }],
215
+ {
216
+ enabled: enabled && !!typeSlug,
217
+ },
218
+ );
166
219
 
167
- const { items, total } = flattenPages<TMap[TSlug]>(data?.pages);
220
+ const { items, total } = flattenPages<TMap[TSlug], TView, TFields>(
221
+ data?.pages,
222
+ );
168
223
 
169
224
  return {
170
225
  items,
@@ -184,29 +239,71 @@ export function useContent<
184
239
  * @template TMap - A type map of content type slugs to their data types
185
240
  * @template TSlug - The content type slug (inferred from typeSlug parameter)
186
241
  */
242
+ type SuspenseContentResult<
243
+ TData,
244
+ TView extends CMSReadView,
245
+ TFields extends CMSContentFields = undefined,
246
+ > = {
247
+ items: CMSContentItemResult<TData, TView, TFields>[];
248
+ total: number;
249
+ loadMore: () => Promise<unknown>;
250
+ hasMore: boolean;
251
+ isLoadingMore: boolean;
252
+ refetch: () => Promise<unknown>;
253
+ };
254
+
255
+ export function useSuspenseContent<
256
+ TMap extends CMSContentTypeMap = CMSContentTypeMap,
257
+ TSlug extends keyof TMap = keyof TMap,
258
+ const TFields extends readonly string[] = readonly string[],
259
+ >(
260
+ typeSlug: TSlug & string,
261
+ options: UseContentOptions<"content", TFields> & {
262
+ view: "content";
263
+ fields: TFields;
264
+ },
265
+ ): SuspenseContentResult<TMap[TSlug], "content", TFields>;
266
+ export function useSuspenseContent<
267
+ TMap extends CMSContentTypeMap = CMSContentTypeMap,
268
+ TSlug extends keyof TMap = keyof TMap,
269
+ >(
270
+ typeSlug: TSlug & string,
271
+ options: UseContentOptions<"content"> & {
272
+ view: "content";
273
+ fields?: undefined;
274
+ },
275
+ ): SuspenseContentResult<TMap[TSlug], "content">;
276
+ export function useSuspenseContent<
277
+ TMap extends CMSContentTypeMap = CMSContentTypeMap,
278
+ TSlug extends keyof TMap = keyof TMap,
279
+ TView extends CMSReadView = undefined,
280
+ TFields extends CMSContentFields = undefined,
281
+ >(
282
+ typeSlug: TSlug & string,
283
+ options?: UseContentOptions<TView, TFields>,
284
+ ): SuspenseContentResult<TMap[TSlug], TView, TFields>;
187
285
  export function useSuspenseContent<
188
286
  TMap extends Record<string, Record<string, unknown>> = Record<
189
287
  string,
190
288
  Record<string, unknown>
191
289
  >,
192
290
  TSlug extends keyof TMap = keyof TMap,
291
+ TView extends CMSReadView = undefined,
292
+ TFields extends CMSContentFields = undefined,
193
293
  >(
194
294
  typeSlug: TSlug & string,
195
- options: UseContentOptions = {},
196
- ): {
197
- items: SerializedContentItemWithType<TMap[TSlug]>[];
198
- total: number;
199
- loadMore: () => Promise<unknown>;
200
- hasMore: boolean;
201
- isLoadingMore: boolean;
202
- refetch: () => Promise<unknown>;
203
- } {
204
- const { limit = 10, search } = options;
295
+ options: UseContentOptions<TView, TFields> = {},
296
+ ): SuspenseContentResult<TMap[TSlug], TView, TFields> {
297
+ const { limit = 10, search, view, fields } = options;
205
298
 
206
299
  const { data, fetchNextPage, hasNextPage, isFetchingNextPage, refetch } =
207
- cms.cmsContent.list.useSuspenseInfinite([{ typeSlug, limit, search }]);
300
+ cms.cmsContent.list.useSuspenseInfinite([
301
+ { typeSlug, limit, search, view, fields },
302
+ ]);
208
303
 
209
- const { items, total } = flattenPages<TMap[TSlug]>(data.pages);
304
+ const { items, total } = flattenPages<TMap[TSlug], TView, TFields>(
305
+ data.pages,
306
+ );
210
307
 
211
308
  return {
212
309
  items,
@@ -231,28 +328,69 @@ export function useSuspenseContent<
231
328
  * // item?.parsedData.name is string
232
329
  * ```
233
330
  */
331
+ type ContentItemQueryResult<
332
+ TData,
333
+ TView extends CMSReadView,
334
+ TFields extends CMSContentFields = undefined,
335
+ > = {
336
+ item: CMSContentItemResult<TData, TView, TFields> | null;
337
+ isLoading: boolean;
338
+ error: Error | null;
339
+ refetch: () => void;
340
+ };
341
+
342
+ export function useContentItem<
343
+ TMap extends CMSContentTypeMap = CMSContentTypeMap,
344
+ TSlug extends keyof TMap = keyof TMap,
345
+ const TFields extends readonly string[] = readonly string[],
346
+ >(
347
+ typeSlug: TSlug & string,
348
+ id: string,
349
+ options: CMSReadOptions<"content", TFields> & {
350
+ view: "content";
351
+ fields: TFields;
352
+ },
353
+ ): ContentItemQueryResult<TMap[TSlug], "content", TFields>;
354
+ export function useContentItem<
355
+ TMap extends CMSContentTypeMap = CMSContentTypeMap,
356
+ TSlug extends keyof TMap = keyof TMap,
357
+ >(
358
+ typeSlug: TSlug & string,
359
+ id: string,
360
+ options: CMSReadOptions<"content"> & { view: "content"; fields?: undefined },
361
+ ): ContentItemQueryResult<TMap[TSlug], "content">;
362
+ export function useContentItem<
363
+ TMap extends CMSContentTypeMap = CMSContentTypeMap,
364
+ TSlug extends keyof TMap = keyof TMap,
365
+ TView extends CMSReadView = undefined,
366
+ TFields extends CMSContentFields = undefined,
367
+ >(
368
+ typeSlug: TSlug & string,
369
+ id: string,
370
+ options?: CMSReadOptions<TView, TFields>,
371
+ ): ContentItemQueryResult<TMap[TSlug], TView, TFields>;
234
372
  export function useContentItem<
235
373
  TMap extends Record<string, Record<string, unknown>> = Record<
236
374
  string,
237
375
  Record<string, unknown>
238
376
  >,
239
377
  TSlug extends keyof TMap = keyof TMap,
378
+ TView extends CMSReadView = undefined,
379
+ TFields extends CMSContentFields = undefined,
240
380
  >(
241
381
  typeSlug: TSlug & string,
242
382
  id: string,
243
- ): {
244
- item: SerializedContentItemWithType<TMap[TSlug]> | null;
245
- isLoading: boolean;
246
- error: Error | null;
247
- refetch: () => void;
248
- } {
383
+ options?: CMSReadOptions<TView, TFields>,
384
+ ): ContentItemQueryResult<TMap[TSlug], TView, TFields> {
249
385
  const { data, isLoading, error, refetch } = cms.cmsContent.detail.use(
250
- [typeSlug, id],
386
+ [typeSlug, id, options],
251
387
  { enabled: !!typeSlug && !!id },
252
388
  );
253
389
 
254
390
  return {
255
- item: (data as SerializedContentItemWithType<TMap[TSlug]> | null) ?? null,
391
+ item:
392
+ (data as CMSContentItemResult<TMap[TSlug], TView, TFields> | null) ??
393
+ null,
256
394
  isLoading,
257
395
  error,
258
396
  refetch,
@@ -265,23 +403,68 @@ export function useContentItem<
265
403
  * @template TMap - A type map of content type slugs to their data types
266
404
  * @template TSlug - The content type slug (inferred from typeSlug parameter)
267
405
  */
406
+ type SuspenseContentItemResult<
407
+ TData,
408
+ TView extends CMSReadView,
409
+ TFields extends CMSContentFields = undefined,
410
+ > = {
411
+ item: CMSContentItemResult<TData, TView, TFields> | null;
412
+ refetch: () => Promise<unknown>;
413
+ };
414
+
415
+ export function useSuspenseContentItem<
416
+ TMap extends CMSContentTypeMap = CMSContentTypeMap,
417
+ TSlug extends keyof TMap = keyof TMap,
418
+ const TFields extends readonly string[] = readonly string[],
419
+ >(
420
+ typeSlug: TSlug & string,
421
+ id: string,
422
+ options: CMSReadOptions<"content", TFields> & {
423
+ view: "content";
424
+ fields: TFields;
425
+ },
426
+ ): SuspenseContentItemResult<TMap[TSlug], "content", TFields>;
427
+ export function useSuspenseContentItem<
428
+ TMap extends CMSContentTypeMap = CMSContentTypeMap,
429
+ TSlug extends keyof TMap = keyof TMap,
430
+ >(
431
+ typeSlug: TSlug & string,
432
+ id: string,
433
+ options: CMSReadOptions<"content"> & { view: "content"; fields?: undefined },
434
+ ): SuspenseContentItemResult<TMap[TSlug], "content">;
435
+ export function useSuspenseContentItem<
436
+ TMap extends CMSContentTypeMap = CMSContentTypeMap,
437
+ TSlug extends keyof TMap = keyof TMap,
438
+ TView extends CMSReadView = undefined,
439
+ TFields extends CMSContentFields = undefined,
440
+ >(
441
+ typeSlug: TSlug & string,
442
+ id: string,
443
+ options?: CMSReadOptions<TView, TFields>,
444
+ ): SuspenseContentItemResult<TMap[TSlug], TView, TFields>;
268
445
  export function useSuspenseContentItem<
269
446
  TMap extends Record<string, Record<string, unknown>> = Record<
270
447
  string,
271
448
  Record<string, unknown>
272
449
  >,
273
450
  TSlug extends keyof TMap = keyof TMap,
451
+ TView extends CMSReadView = undefined,
452
+ TFields extends CMSContentFields = undefined,
274
453
  >(
275
454
  typeSlug: TSlug & string,
276
455
  id: string,
277
- ): {
278
- item: SerializedContentItemWithType<TMap[TSlug]> | null;
279
- refetch: () => Promise<unknown>;
280
- } {
281
- const { data, refetch } = cms.cmsContent.detail.useSuspense([typeSlug, id]);
456
+ options?: CMSReadOptions<TView, TFields>,
457
+ ): SuspenseContentItemResult<TMap[TSlug], TView, TFields> {
458
+ const { data, refetch } = cms.cmsContent.detail.useSuspense([
459
+ typeSlug,
460
+ id,
461
+ options,
462
+ ]);
282
463
 
283
464
  return {
284
- item: (data as SerializedContentItemWithType<TMap[TSlug]> | null) ?? null,
465
+ item:
466
+ (data as CMSContentItemResult<TMap[TSlug], TView, TFields> | null) ??
467
+ null,
285
468
  refetch,
286
469
  };
287
470
  }
@@ -299,28 +482,58 @@ export function useSuspenseContentItem<
299
482
  * // item?.parsedData.price is number
300
483
  * ```
301
484
  */
485
+ export function useContentItemBySlug<
486
+ TMap extends CMSContentTypeMap = CMSContentTypeMap,
487
+ TSlug extends keyof TMap = keyof TMap,
488
+ const TFields extends readonly string[] = readonly string[],
489
+ >(
490
+ typeSlug: TSlug & string,
491
+ slug: string,
492
+ options: CMSReadOptions<"content", TFields> & {
493
+ view: "content";
494
+ fields: TFields;
495
+ },
496
+ ): ContentItemQueryResult<TMap[TSlug], "content", TFields>;
497
+ export function useContentItemBySlug<
498
+ TMap extends CMSContentTypeMap = CMSContentTypeMap,
499
+ TSlug extends keyof TMap = keyof TMap,
500
+ >(
501
+ typeSlug: TSlug & string,
502
+ slug: string,
503
+ options: CMSReadOptions<"content"> & { view: "content"; fields?: undefined },
504
+ ): ContentItemQueryResult<TMap[TSlug], "content">;
505
+ export function useContentItemBySlug<
506
+ TMap extends CMSContentTypeMap = CMSContentTypeMap,
507
+ TSlug extends keyof TMap = keyof TMap,
508
+ TView extends CMSReadView = undefined,
509
+ TFields extends CMSContentFields = undefined,
510
+ >(
511
+ typeSlug: TSlug & string,
512
+ slug: string,
513
+ options?: CMSReadOptions<TView, TFields>,
514
+ ): ContentItemQueryResult<TMap[TSlug], TView, TFields>;
302
515
  export function useContentItemBySlug<
303
516
  TMap extends Record<string, Record<string, unknown>> = Record<
304
517
  string,
305
518
  Record<string, unknown>
306
519
  >,
307
520
  TSlug extends keyof TMap = keyof TMap,
521
+ TView extends CMSReadView = undefined,
522
+ TFields extends CMSContentFields = undefined,
308
523
  >(
309
524
  typeSlug: TSlug & string,
310
525
  slug: string,
311
- ): {
312
- item: SerializedContentItemWithType<TMap[TSlug]> | null;
313
- isLoading: boolean;
314
- error: Error | null;
315
- refetch: () => void;
316
- } {
526
+ options?: CMSReadOptions<TView, TFields>,
527
+ ): ContentItemQueryResult<TMap[TSlug], TView, TFields> {
317
528
  const { data, isLoading, error, refetch } = cms.cmsContent.bySlug.use(
318
- [typeSlug, slug],
529
+ [typeSlug, slug, options],
319
530
  { enabled: !!typeSlug && !!slug },
320
531
  );
321
532
 
322
533
  return {
323
- item: (data as SerializedContentItemWithType<TMap[TSlug]> | null) ?? null,
534
+ item:
535
+ (data as CMSContentItemResult<TMap[TSlug], TView, TFields> | null) ??
536
+ null,
324
537
  isLoading,
325
538
  error,
326
539
  refetch,
@@ -455,24 +668,35 @@ export function useContentItemForm<TValues>(
455
668
  * query, with selected ids missing from the current results preloaded via
456
669
  * the `detail` query (typeSlug, id) so their labels resolve.
457
670
  *
458
- * Used by the relation picker (`RelationField`).
671
+ * Used by the relation picker (`RelationField`). Optional content view/fields
672
+ * apply to both search results and selected-ID preloads; full records remain the default.
459
673
  */
460
- export function useContentOptions(config: {
461
- /** Content type slug to search within */
462
- targetType: string;
463
- /** Currently selected item ids — preloaded when missing from options */
464
- value: string[];
465
- getOptionLabel: (item: SerializedContentItemWithType) => string;
466
- }): ResourceSelectResult<SerializedContentItemWithType> {
467
- const { targetType, value, getOptionLabel } = config;
468
-
469
- return cms.cmsContent.useSelect<SerializedContentItemWithType>({
674
+ export function useContentOptions<
675
+ TView extends CMSReadView = undefined,
676
+ const TFields extends CMSContentFields = undefined,
677
+ >(
678
+ config: {
679
+ /** Content type slug to search within. */
680
+ targetType: string;
681
+ /** Currently selected IDs, preloaded when absent from search results. */
682
+ value: string[];
683
+ getOptionLabel: (
684
+ item: CMSContentItemResult<Record<string, unknown>, TView, TFields>,
685
+ ) => string;
686
+ } & CMSReadOptions<TView, TFields>,
687
+ ): ResourceSelectResult<
688
+ CMSContentItemResult<Record<string, unknown>, TView, TFields>
689
+ > {
690
+ const { targetType, value, getOptionLabel, view, fields } = config;
691
+ return cms.cmsContent.useSelect<
692
+ CMSContentItemResult<Record<string, unknown>, TView, TFields>
693
+ >({
470
694
  query: "options",
471
- searchArgs: (search) => [{ typeSlug: targetType, search }],
695
+ searchArgs: (search) => [{ typeSlug: targetType, search, view, fields }],
472
696
  getOptionValue: (item) => item.id,
473
697
  getOptionLabel,
474
698
  value,
475
- preload: { args: (id) => [targetType, id] },
699
+ preload: { args: (id) => [targetType, id, { view, fields }] },
476
700
  enabled: !!targetType,
477
701
  });
478
702
  }
@@ -560,7 +784,10 @@ export function useSuspenseContentItemPopulated<
560
784
  /**
561
785
  * Options for useContentByRelation hook
562
786
  */
563
- export interface UseContentByRelationOptions {
787
+ export interface UseContentByRelationOptions<
788
+ TView extends CMSReadView = CMSReadView,
789
+ TFields extends CMSContentFields = CMSContentFields,
790
+ > extends CMSReadOptions<TView, TFields> {
564
791
  /** Number of items per page (default: 20) */
565
792
  limit?: number;
566
793
  /** Whether to enable the query (default: true) */
@@ -580,28 +807,57 @@ export interface UseContentByRelationOptions {
580
807
  * const { items } = useContentByRelation("resource", "categoryIds", categoryId)
581
808
  * ```
582
809
  */
810
+ export function useContentByRelation<
811
+ TMap extends CMSContentTypeMap = CMSContentTypeMap,
812
+ TSlug extends keyof TMap = keyof TMap,
813
+ const TFields extends readonly string[] = readonly string[],
814
+ >(
815
+ typeSlug: TSlug & string,
816
+ fieldName: string,
817
+ targetId: string,
818
+ options: UseContentByRelationOptions<"content", TFields> & {
819
+ view: "content";
820
+ fields: TFields;
821
+ },
822
+ ): UseContentResult<TMap[TSlug], "content", TFields>;
823
+ export function useContentByRelation<
824
+ TMap extends CMSContentTypeMap = CMSContentTypeMap,
825
+ TSlug extends keyof TMap = keyof TMap,
826
+ >(
827
+ typeSlug: TSlug & string,
828
+ fieldName: string,
829
+ targetId: string,
830
+ options: UseContentByRelationOptions<"content"> & {
831
+ view: "content";
832
+ fields?: undefined;
833
+ },
834
+ ): UseContentResult<TMap[TSlug], "content">;
835
+ export function useContentByRelation<
836
+ TMap extends CMSContentTypeMap = CMSContentTypeMap,
837
+ TSlug extends keyof TMap = keyof TMap,
838
+ TView extends CMSReadView = undefined,
839
+ TFields extends CMSContentFields = undefined,
840
+ >(
841
+ typeSlug: TSlug & string,
842
+ fieldName: string,
843
+ targetId: string,
844
+ options?: UseContentByRelationOptions<TView, TFields>,
845
+ ): UseContentResult<TMap[TSlug], TView, TFields>;
583
846
  export function useContentByRelation<
584
847
  TMap extends Record<string, Record<string, unknown>> = Record<
585
848
  string,
586
849
  Record<string, unknown>
587
850
  >,
588
851
  TSlug extends keyof TMap = keyof TMap,
852
+ TView extends CMSReadView = undefined,
853
+ TFields extends CMSContentFields = undefined,
589
854
  >(
590
855
  typeSlug: TSlug & string,
591
856
  fieldName: string,
592
857
  targetId: string,
593
- options: UseContentByRelationOptions = {},
594
- ): {
595
- items: SerializedContentItemWithType<TMap[TSlug]>[];
596
- total: number;
597
- isLoading: boolean;
598
- error: Error | null;
599
- loadMore: () => void;
600
- hasMore: boolean;
601
- isLoadingMore: boolean;
602
- refetch: () => void;
603
- } {
604
- const { limit = 20, enabled = true } = options;
858
+ options: UseContentByRelationOptions<TView, TFields> = {},
859
+ ): UseContentResult<TMap[TSlug], TView, TFields> {
860
+ const { limit = 20, enabled = true, view, fields } = options;
605
861
 
606
862
  const {
607
863
  data,
@@ -612,11 +868,13 @@ export function useContentByRelation<
612
868
  isFetchingNextPage,
613
869
  refetch,
614
870
  } = cms.cmsContent.byRelation.useInfinite(
615
- [{ typeSlug, field: fieldName, targetId, limit }],
871
+ [{ typeSlug, field: fieldName, targetId, limit, view, fields }],
616
872
  { enabled: enabled && !!typeSlug && !!fieldName && !!targetId },
617
873
  );
618
874
 
619
- const { items, total } = flattenPages<TMap[TSlug]>(data?.pages);
875
+ const { items, total } = flattenPages<TMap[TSlug], TView, TFields>(
876
+ data?.pages,
877
+ );
620
878
 
621
879
  return {
622
880
  items,
@@ -633,33 +891,66 @@ export function useContentByRelation<
633
891
  /**
634
892
  * Suspense variant of useContentByRelation
635
893
  */
894
+ export function useSuspenseContentByRelation<
895
+ TMap extends CMSContentTypeMap = CMSContentTypeMap,
896
+ TSlug extends keyof TMap = keyof TMap,
897
+ const TFields extends readonly string[] = readonly string[],
898
+ >(
899
+ typeSlug: TSlug & string,
900
+ fieldName: string,
901
+ targetId: string,
902
+ options: UseContentByRelationOptions<"content", TFields> & {
903
+ view: "content";
904
+ fields: TFields;
905
+ },
906
+ ): SuspenseContentResult<TMap[TSlug], "content", TFields>;
907
+ export function useSuspenseContentByRelation<
908
+ TMap extends CMSContentTypeMap = CMSContentTypeMap,
909
+ TSlug extends keyof TMap = keyof TMap,
910
+ >(
911
+ typeSlug: TSlug & string,
912
+ fieldName: string,
913
+ targetId: string,
914
+ options: UseContentByRelationOptions<"content"> & {
915
+ view: "content";
916
+ fields?: undefined;
917
+ },
918
+ ): SuspenseContentResult<TMap[TSlug], "content">;
919
+ export function useSuspenseContentByRelation<
920
+ TMap extends CMSContentTypeMap = CMSContentTypeMap,
921
+ TSlug extends keyof TMap = keyof TMap,
922
+ TView extends CMSReadView = undefined,
923
+ TFields extends CMSContentFields = undefined,
924
+ >(
925
+ typeSlug: TSlug & string,
926
+ fieldName: string,
927
+ targetId: string,
928
+ options?: UseContentByRelationOptions<TView, TFields>,
929
+ ): SuspenseContentResult<TMap[TSlug], TView, TFields>;
636
930
  export function useSuspenseContentByRelation<
637
931
  TMap extends Record<string, Record<string, unknown>> = Record<
638
932
  string,
639
933
  Record<string, unknown>
640
934
  >,
641
935
  TSlug extends keyof TMap = keyof TMap,
936
+ TView extends CMSReadView = undefined,
937
+ TFields extends CMSContentFields = undefined,
642
938
  >(
643
939
  typeSlug: TSlug & string,
644
940
  fieldName: string,
645
941
  targetId: string,
646
- options: UseContentByRelationOptions = {},
647
- ): {
648
- items: SerializedContentItemWithType<TMap[TSlug]>[];
649
- total: number;
650
- loadMore: () => Promise<unknown>;
651
- hasMore: boolean;
652
- isLoadingMore: boolean;
653
- refetch: () => Promise<unknown>;
654
- } {
655
- const { limit = 20 } = options;
942
+ options: UseContentByRelationOptions<TView, TFields> = {},
943
+ ): SuspenseContentResult<TMap[TSlug], TView, TFields> {
944
+ const { limit = 20, view, fields } = options;
656
945
 
657
946
  const { data, fetchNextPage, hasNextPage, isFetchingNextPage, refetch } =
658
947
  cms.cmsContent.byRelation.useSuspenseInfinite([
659
- { typeSlug, field: fieldName, targetId, limit },
948
+ { typeSlug, field: fieldName, targetId, limit, view, fields },
660
949
  ]);
661
950
 
662
- const { items, total } = flattenPages<TMap[TSlug]>(data.pages);
951
+ const { items, total } = flattenPages<TMap[TSlug], TView, TFields>(
952
+ data.pages,
953
+ );
663
954
 
664
955
  return {
665
956
  items,
@@ -703,16 +994,26 @@ export function useInverseRelations(
703
994
  * Hook for listing the items behind one inverse relation (e.g. all comments
704
995
  * whose `resourceId` points at this resource).
705
996
  */
706
- export function useInverseRelationItems(
997
+ export function useInverseRelationItems<
998
+ TView extends CMSReadView = undefined,
999
+ const TFields extends CMSContentFields = undefined,
1000
+ >(
707
1001
  params: {
708
1002
  contentTypeSlug: string;
709
1003
  sourceType: string;
710
1004
  itemId: string;
711
1005
  fieldName: string;
712
1006
  },
713
- options: { enabled?: boolean } = {},
1007
+ options: CMSReadOptions<TView, TFields> & {
1008
+ /** Whether to enable the query (default: true). */
1009
+ enabled?: boolean;
1010
+ /** Number of items per page (default: 20). */
1011
+ limit?: number;
1012
+ /** Number of matching items to skip (default: 0). */
1013
+ offset?: number;
1014
+ } = {},
714
1015
  ): {
715
- items: SerializedContentItemWithType[];
1016
+ items: CMSContentItemResult<Record<string, unknown>, TView, TFields>[];
716
1017
  total: number;
717
1018
  isLoading: boolean;
718
1019
  error: Error | null;
@@ -726,13 +1027,21 @@ export function useInverseRelationItems(
726
1027
  sourceType: params.sourceType,
727
1028
  itemId: params.itemId,
728
1029
  fieldName: params.fieldName,
1030
+ view: options.view,
1031
+ fields: options.fields,
1032
+ limit: options.limit,
1033
+ offset: options.offset,
729
1034
  },
730
1035
  ],
731
1036
  { enabled: options.enabled ?? true },
732
1037
  );
733
1038
 
734
1039
  return {
735
- items: data?.items ?? [],
1040
+ items: (data?.items ?? []) as CMSContentItemResult<
1041
+ Record<string, unknown>,
1042
+ TView,
1043
+ TFields
1044
+ >[],
736
1045
  total: data?.total ?? 0,
737
1046
  isLoading,
738
1047
  error,