fimo 0.4.1 → 0.5.0-experimental.2

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.
@@ -9,8 +9,11 @@
9
9
  */
10
10
  %>
11
11
  // AUTO-GENERATED ⚠️ – do not edit by hand
12
- import { useSuspenseQuery, useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
13
- import { FimoString, FimoMedia, FimoBoolean, FimoRichText, useLocale } from "fimo/ui";
12
+ import { <% if (!model.isSingleton) { %>useInfiniteQuery, <% } %>useSuspenseQuery, useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
13
+ <% if (!model.isSingleton) { -%>
14
+ import type { InfiniteData, UseInfiniteQueryResult, UseQueryResult } from "@tanstack/react-query";
15
+ <% } -%>
16
+ import { FimoString, FimoMedia, FimoBoolean, FimoRichText, useLocale<% if (!model.isSingleton) { %>, parseCollectionResponse<% } %> } from "fimo/ui";
14
17
  // @ts-ignore - virtual module provided by Fimo's Vite plugin
15
18
  import fimoConfig from "virtual:fimo-config";
16
19
  import type { <%= model.pascal %>, <%= model.pascal %>Input, <%= model.pascal %>Populate, <%= model.pascal %>Result<% if (!model.isSingleton) { %>, <%= model.pascal %>Fields, <%= model.pascal %>Query<% } %> } from "./<%= model.pascal %>.types";
@@ -215,6 +218,37 @@ function splitWritePayload(payload: Partial<<%= model.pascal %>Input>): Record<s
215
218
  }
216
219
 
217
220
  <% if (!model.isSingleton) { -%>
221
+ export interface CollectionPageInfo {
222
+ readonly limit: number;
223
+ readonly offset: number;
224
+ readonly total: number;
225
+ readonly hasMore: boolean;
226
+ readonly nextOffset: number | null;
227
+ }
228
+
229
+ export interface CollectionPage<TItem> {
230
+ readonly items: TItem[];
231
+ readonly pageInfo: CollectionPageInfo;
232
+ }
233
+
234
+ export type InfiniteListQuery<
235
+ TFields extends <%= model.pascal %>Fields | undefined = undefined,
236
+ TPopulate extends <%= model.pascal %>Populate | undefined = undefined,
237
+ > = Omit<<%= model.pascal %>Query<TFields, TPopulate>, "offset">;
238
+
239
+ export type InfiniteListResult<TItem> = UseInfiniteQueryResult<
240
+ InfiniteData<CollectionPage<TItem>>,
241
+ Error
242
+ > & {
243
+ readonly items: TItem[];
244
+ readonly total: number;
245
+ readonly hasMore: boolean;
246
+ readonly loadMore: () => ReturnType<
247
+ UseInfiniteQueryResult<InfiniteData<CollectionPage<TItem>>, Error>["fetchNextPage"]
248
+ >;
249
+ readonly isLoadingMore: boolean;
250
+ };
251
+
218
252
  export async function getById<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(
219
253
  id: string,
220
254
  options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}
@@ -282,15 +316,30 @@ function toQueryString<
282
316
  return query.toString();
283
317
  }
284
318
 
285
- export async function get<
319
+ export async function list<
286
320
  TFields extends <%= model.pascal %>Fields | undefined = undefined,
287
321
  TPopulate extends <%= model.pascal %>Populate | undefined = undefined,
288
- >(params: <%= model.pascal %>Query<TFields, TPopulate> = {} as <%= model.pascal %>Query<TFields, TPopulate>): Promise<<%= model.pascal %>Result<TFields, TPopulate>[]> {
322
+ >(params: <%= model.pascal %>Query<TFields, TPopulate> = {} as <%= model.pascal %>Query<TFields, TPopulate>): Promise<CollectionPage<<%= model.pascal %>Result<TFields, TPopulate>>> {
289
323
  const qs = toQueryString(params);
290
324
  const res = await fetch(`${base}/entries/<%= model.uid %>${qs ? "?" + qs : ""}`, { headers: fimoHeaders() });
291
325
  if (!res.ok) throw new Error(await res.text());
292
- const data = await res.json();
293
- return data.data.map(wrapWithSource) as <%= model.pascal %>Result<TFields, TPopulate>[];
326
+ const data = parseCollectionResponse(await res.json());
327
+ const items = data.data.map(wrapWithSource) as <%= model.pascal %>Result<TFields, TPopulate>[];
328
+ const pageInfo: CollectionPageInfo = {
329
+ limit: data.meta.limit,
330
+ offset: data.meta.offset,
331
+ total: data.meta.total,
332
+ hasMore: data.meta.hasMore,
333
+ nextOffset: data.meta.hasMore ? data.meta.offset + data.meta.limit : null,
334
+ };
335
+ return { items, pageInfo };
336
+ }
337
+
338
+ export async function get<
339
+ TFields extends <%= model.pascal %>Fields | undefined = undefined,
340
+ TPopulate extends <%= model.pascal %>Populate | undefined = undefined,
341
+ >(params: <%= model.pascal %>Query<TFields, TPopulate> = {} as <%= model.pascal %>Query<TFields, TPopulate>): Promise<<%= model.pascal %>Result<TFields, TPopulate>[]> {
342
+ return (await list(params)).items;
294
343
  }
295
344
 
296
345
  export async function create(payload: <%= model.pascal %>Input): Promise<<%= model.pascal %>> {
@@ -353,7 +402,11 @@ const qk = {
353
402
  byId : <TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(id: string, options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}) => ["<%= model.pascal %>", id, options.locale, options.populate] as const,
354
403
  bySlug : <TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(slug: string, options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}) => ["<%= model.pascal %>", "slug", slug, options.locale, options.populate] as const,
355
404
  byField : <TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(field: string, value: string, options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}) => ["<%= model.pascal %>", field, value, options.locale, options.populate] as const,
356
- list : (params: <%= model.pascal %>Query) => ["<%= model.pascal %>", params] as const
405
+ list : (params: <%= model.pascal %>Query) => ["<%= model.pascal %>", params] as const,
406
+ infiniteList: <
407
+ TFields extends <%= model.pascal %>Fields | undefined = undefined,
408
+ TPopulate extends <%= model.pascal %>Populate | undefined = undefined,
409
+ >(params: InfiniteListQuery<TFields, TPopulate>) => ["<%= model.pascal %>", "infinite", params] as const
357
410
  };
358
411
 
359
412
  export function useGetById<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(id: string, options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}) {
@@ -380,7 +433,46 @@ export function useGet<
380
433
  >(params: <%= model.pascal %>Query<TFields, TPopulate> = {} as <%= model.pascal %>Query<TFields, TPopulate>) {
381
434
  const locale = readLocale(params, LOCALIZED_CONTENT, useQueryLocale(params.locale));
382
435
  const localizedParams = { ...params, locale };
383
- return useQuery<<%= model.pascal %>Result<TFields, TPopulate>[], Error>({ queryKey: qk.list(localizedParams), queryFn: () => get(localizedParams) });
436
+ return useQuery<CollectionPage<<%= model.pascal %>Result<TFields, TPopulate>>, Error, <%= model.pascal %>Result<TFields, TPopulate>[]>({
437
+ queryKey: qk.list(localizedParams),
438
+ queryFn: () => list(localizedParams),
439
+ select: (page) => page.items,
440
+ });
441
+ }
442
+
443
+ export function useList<
444
+ TFields extends <%= model.pascal %>Fields | undefined = undefined,
445
+ TPopulate extends <%= model.pascal %>Populate | undefined = undefined,
446
+ >(params: <%= model.pascal %>Query<TFields, TPopulate> = {} as <%= model.pascal %>Query<TFields, TPopulate>): UseQueryResult<CollectionPage<<%= model.pascal %>Result<TFields, TPopulate>>, Error> {
447
+ const locale = readLocale(params, LOCALIZED_CONTENT, useQueryLocale(params.locale));
448
+ const localizedParams = { ...params, locale };
449
+ return useQuery<CollectionPage<<%= model.pascal %>Result<TFields, TPopulate>>, Error>({
450
+ queryKey: qk.list(localizedParams),
451
+ queryFn: () => list(localizedParams),
452
+ });
453
+ }
454
+
455
+ export function useInfiniteList<
456
+ TFields extends <%= model.pascal %>Fields | undefined = undefined,
457
+ TPopulate extends <%= model.pascal %>Populate | undefined = undefined,
458
+ >(params: InfiniteListQuery<TFields, TPopulate> = {} as InfiniteListQuery<TFields, TPopulate>): InfiniteListResult<<%= model.pascal %>Result<TFields, TPopulate>> {
459
+ const locale = readLocale(params, LOCALIZED_CONTENT, useQueryLocale(params.locale));
460
+ const localizedParams = { ...params, locale };
461
+ const query = useInfiniteQuery({
462
+ queryKey: qk.infiniteList(localizedParams),
463
+ queryFn: ({ pageParam }) => list({ ...localizedParams, offset: pageParam }),
464
+ initialPageParam: 0,
465
+ getNextPageParam: (lastPage) => lastPage.pageInfo.nextOffset ?? undefined,
466
+ });
467
+
468
+ return {
469
+ ...query,
470
+ items: query.data?.pages.flatMap((page) => page.items) ?? [],
471
+ total: query.data?.pages[0]?.pageInfo.total ?? 0,
472
+ hasMore: query.hasNextPage,
473
+ loadMore: () => query.fetchNextPage(),
474
+ isLoadingMore: query.isFetchingNextPage,
475
+ };
384
476
  }
385
477
 
386
478
  export function useSuspenseGetById<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(id: string, options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}) {
@@ -407,7 +499,11 @@ export function useSuspenseGet<
407
499
  >(params: <%= model.pascal %>Query<TFields, TPopulate> = {} as <%= model.pascal %>Query<TFields, TPopulate>) {
408
500
  const locale = readLocale(params, LOCALIZED_CONTENT, useQueryLocale(params.locale));
409
501
  const localizedParams = { ...params, locale };
410
- return useSuspenseQuery<<%= model.pascal %>Result<TFields, TPopulate>[], Error>({ queryKey: qk.list(localizedParams), queryFn: () => get(localizedParams) });
502
+ return useSuspenseQuery<CollectionPage<<%= model.pascal %>Result<TFields, TPopulate>>, Error, <%= model.pascal %>Result<TFields, TPopulate>[]>({
503
+ queryKey: qk.list(localizedParams),
504
+ queryFn: () => list(localizedParams),
505
+ select: (page) => page.items,
506
+ });
411
507
  }
412
508
 
413
509
  export function useCreate() {
@@ -476,6 +572,7 @@ export default {
476
572
  getById,
477
573
  getBySlug,
478
574
  getByField,
575
+ list,
479
576
  get,
480
577
  create,
481
578
  update,
@@ -485,6 +582,8 @@ export default {
485
582
  useGetById,
486
583
  useGetBySlug,
487
584
  useGetByField,
585
+ useList,
586
+ useInfiniteList,
488
587
  useGet,
489
588
  useSuspenseGetById,
490
589
  useSuspenseGet,
@@ -20,7 +20,7 @@ For CLI commands and workflows (creating a project, pushing schemas/forms, deplo
20
20
 
21
21
  This is the load-bearing rule for every code change. **Fimo's whole point is that content editors edit content in the dashboard, not in code.** Anything hardcoded breaks that.
22
22
 
23
- - **Content** (posts, products, pages, lists, anything structured) → declare a schema, render with the generated `useGet()` hook + `fimo/ui` components. **Never** write `const posts = [...]` in JSX.
23
+ - **Content** (posts, products, pages, lists, anything structured) → declare a schema, render collections with generated `useList()` or `useInfiniteList()` hooks + `fimo/ui` components. Use `useGet()` only for legacy fixed array queries. **Never** write `const posts = [...]` in JSX.
24
24
  - **Strings** (labels, CTAs, headings, descriptions, any visible text) → wrap in `t('key')` and create the default-locale value with `fimo labels set`. Use `--locale <locale>` only when intentionally writing a non-default locale. **Never** write `<h1>Welcome</h1>` directly — write `<Text value={t('home.title')} as="h1" />`.
25
25
  - **Media** (images, videos, files) → use `<Image>` / `<Video>` from `fimo/ui` for schema media, `<StaticImage>` for hard-coded URLs. **Never** use `<img src={...}>`.
26
26
  - **Forms** → use the generated Zod schema + `submitX` helper from `@/forms/<name>`. Never hand-roll fetch or validation.
@@ -250,6 +250,7 @@ Filtering on polymorphic fields is not supported yet.
250
250
  getById<TPopulate extends BlogPostPopulate | undefined>(id: string, options?: { populate?: TPopulate }): Promise<BlogPostResult<undefined, TPopulate>>
251
251
  getBySlug<TPopulate extends BlogPostPopulate | undefined>(slug: string, options?: { populate?: TPopulate }): Promise<BlogPostResult<undefined, TPopulate> | null>
252
252
  getByField<TPopulate extends BlogPostPopulate | undefined>(fieldName: string, value: string, options?: { populate?: TPopulate }): Promise<BlogPostResult<undefined, TPopulate> | null>
253
+ list<TFields extends BlogPostFields | undefined, TPopulate extends BlogPostPopulate | undefined>(params?: BlogPostQuery<TFields, TPopulate>): Promise<CollectionPage<BlogPostResult<TFields, TPopulate>>>
253
254
  get<TFields extends BlogPostFields | undefined, TPopulate extends BlogPostPopulate | undefined>(params?: BlogPostQuery<TFields, TPopulate>): Promise<BlogPostResult<TFields, TPopulate>[]>
254
255
  create(payload: BlogPostInput): Promise<BlogPost>
255
256
  update(id: string, payload: Partial<BlogPostInput>): Promise<BlogPost>
@@ -259,7 +260,9 @@ remove(id: string): Promise<void>
259
260
  useGetById(id, { populate? }) // → { data: BlogPostResult<undefined, TPopulate> | undefined, isLoading, ... }
260
261
  useGetBySlug(slug, { populate? }) // → { data: BlogPostResult<undefined, TPopulate> | null | undefined, ... } — ideal for /:slug detail routes
261
262
  useGetByField(fieldName, value, { populate? })
262
- useGet<TFields, TPopulate>(params?) // → { data: BlogPostResult<TFields, TPopulate>[] | undefined, isLoading, ... }
263
+ useList<TFields, TPopulate>(params?) // → { data: CollectionPage<BlogPostResult<TFields, TPopulate>> | undefined, ... }
264
+ useInfiniteList<TFields, TPopulate>(params?) // → { items, total, hasMore, loadMore, isLoadingMore, ... }
265
+ useGet<TFields, TPopulate>(params?) // → legacy array-shaped { data: BlogPostResult<TFields, TPopulate>[] | undefined, ... }
263
266
  useCreate() / useUpdate() / useRemove()
264
267
 
265
268
  // Suspense variants (same signatures, throw to a <Suspense> boundary instead of returning isLoading)
@@ -278,12 +281,37 @@ useGet(options?) / useSuspenseGet(options?) / useUpdate()
278
281
 
279
282
  Singleton clients do not expose `create`, `remove`, `getById`, or list queries. Localized singletons have one variant per locale; non-localized singletons have one locale-free row.
280
283
 
281
- Access pattern — the hooks **unwrap** the API envelope, so `get()` / `useGet()` resolve to a `BlogPost[]` directly (not a `{ data, meta }` wrapper):
284
+ Choose the collection interface from the UI behavior. Use `useList` when the UI controls a page, and read pagination state from `pageInfo`:
282
285
 
283
286
  ```tsx
284
- const { data: posts, isLoading } = useGet({ where: { featured: true }, sort: '-publishedAt', limit: 10 });
285
- if (isLoading || !posts) return null;
286
- posts.map((post) => /* ... */); // BlogPost[]
287
+ const { data: page, isLoading } = BlogPost.useList({ sort: '-publishedAt', limit: 12, offset: 24 });
288
+ if (isLoading || !page) return null;
289
+
290
+ page.items.map((post) => /* ... */);
291
+ page.pageInfo; // { limit, offset, total, hasMore, nextOffset }
292
+ ```
293
+
294
+ Use `useInfiniteList` for load-more and infinite-scroll views. It accumulates pages and exposes one typed `items` array, so UI code does not manage offsets or flatten pages:
295
+
296
+ ```tsx
297
+ const {
298
+ items: posts,
299
+ hasMore,
300
+ loadMore,
301
+ isLoadingMore,
302
+ } = BlogPost.useInfiniteList({
303
+ sort: '-publishedAt',
304
+ limit: 12,
305
+ });
306
+ ```
307
+
308
+ `get()` and `useGet()` remain available as legacy array-shaped helpers for small, deliberately bounded subsets:
309
+
310
+ ```tsx
311
+ const { data: featured = [] } = BlogPost.useGet({
312
+ where: { featured: true },
313
+ limit: 3,
314
+ });
287
315
  ```
288
316
 
289
317
  Query params supported: `limit` (1-100, default 20), `offset` (default 0), `search`, `sort`, `where`, `fields`, and `populate`. Use these canonical names in new code.
@@ -350,7 +378,7 @@ Do not type populated hook data as `BlogPost[]`: plain `BlogPost` keeps referenc
350
378
 
351
379
  Populate is depth-one. It only works for reference fields; trying to populate a scalar field returns 400. To-many reference order is preserved.
352
380
 
353
- The generated hooks do **not** surface pagination meta (`total`, `hasMore`, `limit`, `offset`). If you need it, read it from a raw fetch to `/entries/<typeName>` (see the **Read path** section below), which returns the full `{ data, meta }` envelope.
381
+ `list()` and `useList()` surface pagination metadata as `pageInfo`. `useInfiniteList()` consumes `nextOffset` internally and exposes `hasMore` plus `loadMore()`.
354
382
 
355
383
  ## Fimo primitives (what the field values are)
356
384
 
@@ -397,15 +425,15 @@ class FimoBoolean {
397
425
  ## UI code pattern
398
426
 
399
427
  ```tsx
400
- import { useGet } from '@/schemas/BlogPost';
428
+ import { useList } from '@/schemas/BlogPost';
401
429
  import { Text, RichText, Image, Date } from 'fimo/ui';
402
430
 
403
431
  export function BlogList() {
404
- const { data: posts, isLoading } = useGet({ limit: 10 });
405
- if (isLoading || !posts) return null;
432
+ const { data: page, isLoading } = useList({ limit: 10 });
433
+ if (isLoading || !page) return null;
406
434
  return (
407
435
  <ul>
408
- {posts.map((post) => (
436
+ {page.items.map((post) => (
409
437
  <li key={post.id}>
410
438
  <Text value={post.title} as="h2" />
411
439
  <Image value={post.coverImage} width={800} height={400} fit="cover" />
@@ -429,7 +457,7 @@ const { data, meta } = await res.json();
429
457
  // meta: { limit, offset, total, hasMore }
430
458
  ```
431
459
 
432
- Prefer the generated React Query hooks from `@/schemas/{Uid}` over hand-rolled fetch — they handle caching and types (returning typed `BlogPostResult<TFields, TPopulate>[]` results). Reach for this raw fetch only when you need the pagination `meta`, which the hooks don't surface.
460
+ Prefer the generated `useList` or `useInfiniteList` hooks from `@/schemas/{Uid}` over hand-rolled fetches. They preserve projected/populated item types, cache results, and expose the pagination state regular list UIs need. Use the raw endpoint only for lower-level integrations outside the generated client.
433
461
 
434
462
  ## Forms vs. schemas
435
463
 
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "bundled": true,
3
3
  "bundler": "esbuild",
4
- "bundledAt": "2026-07-14T07:18:58.576Z",
5
- "cliVersion": "0.4.1",
4
+ "bundledAt": "2026-07-14T18:04:31.595Z",
5
+ "cliVersion": "0.5.0",
6
6
  "external": [
7
7
  "oxc-parser",
8
8
  "fsevents"
package/dist/cli/index.js CHANGED
@@ -4194,7 +4194,7 @@ var init_session_refresh = __esm({
4194
4194
  }
4195
4195
  });
4196
4196
 
4197
- // ../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_bf26cb05b71455716d5beca75e2a0544/node_modules/@better-auth/core/dist/env/env-impl.mjs
4197
+ // ../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_da517a110fdd8af320b5bd05eef9033a/node_modules/@better-auth/core/dist/env/env-impl.mjs
4198
4198
  function getEnvVar(key, fallback) {
4199
4199
  if (typeof process !== "undefined" && process.env) return process.env[key] ?? fallback;
4200
4200
  if (typeof Deno !== "undefined") return Deno.env.get(key) ?? fallback;
@@ -4203,7 +4203,7 @@ function getEnvVar(key, fallback) {
4203
4203
  }
4204
4204
  var _envShim, _getEnv, env, nodeENV, ENV;
4205
4205
  var init_env_impl = __esm({
4206
- "../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_bf26cb05b71455716d5beca75e2a0544/node_modules/@better-auth/core/dist/env/env-impl.mjs"() {
4206
+ "../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_da517a110fdd8af320b5bd05eef9033a/node_modules/@better-auth/core/dist/env/env-impl.mjs"() {
4207
4207
  _envShim = /* @__PURE__ */ Object.create(null);
4208
4208
  _getEnv = /* @__PURE__ */ __name((useShim) => globalThis.process?.env || globalThis.Deno?.env.toObject() || globalThis.__env__ || (useShim ? _envShim : globalThis), "_getEnv");
4209
4209
  env = new Proxy(_envShim, {
@@ -4257,7 +4257,7 @@ var init_env_impl = __esm({
4257
4257
  }
4258
4258
  });
4259
4259
 
4260
- // ../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_bf26cb05b71455716d5beca75e2a0544/node_modules/@better-auth/core/dist/env/color-depth.mjs
4260
+ // ../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_da517a110fdd8af320b5bd05eef9033a/node_modules/@better-auth/core/dist/env/color-depth.mjs
4261
4261
  function getColorDepth() {
4262
4262
  if (getEnvVar("FORCE_COLOR") !== void 0) switch (getEnvVar("FORCE_COLOR")) {
4263
4263
  case "":
@@ -4303,7 +4303,7 @@ function getColorDepth() {
4303
4303
  }
4304
4304
  var COLORS_2, COLORS_16, COLORS_256, COLORS_16m, TERM_ENVS, CI_ENVS_MAP, TERM_ENVS_REG_EXP;
4305
4305
  var init_color_depth = __esm({
4306
- "../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_bf26cb05b71455716d5beca75e2a0544/node_modules/@better-auth/core/dist/env/color-depth.mjs"() {
4306
+ "../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_da517a110fdd8af320b5bd05eef9033a/node_modules/@better-auth/core/dist/env/color-depth.mjs"() {
4307
4307
  init_env_impl();
4308
4308
  COLORS_2 = 1;
4309
4309
  COLORS_16 = 4;
@@ -4354,13 +4354,13 @@ var init_color_depth = __esm({
4354
4354
  }
4355
4355
  });
4356
4356
 
4357
- // ../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_bf26cb05b71455716d5beca75e2a0544/node_modules/@better-auth/core/dist/env/logger.mjs
4357
+ // ../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_da517a110fdd8af320b5bd05eef9033a/node_modules/@better-auth/core/dist/env/logger.mjs
4358
4358
  function shouldPublishLog(currentLogLevel, logLevel) {
4359
4359
  return levels.indexOf(logLevel) >= levels.indexOf(currentLogLevel);
4360
4360
  }
4361
4361
  var TTY_COLORS, levels, levelColors, formatMessage, createLogger, logger;
4362
4362
  var init_logger = __esm({
4363
- "../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_bf26cb05b71455716d5beca75e2a0544/node_modules/@better-auth/core/dist/env/logger.mjs"() {
4363
+ "../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_da517a110fdd8af320b5bd05eef9033a/node_modules/@better-auth/core/dist/env/logger.mjs"() {
4364
4364
  init_color_depth();
4365
4365
  TTY_COLORS = {
4366
4366
  reset: "\x1B[0m",
@@ -4438,34 +4438,34 @@ var init_logger = __esm({
4438
4438
  }
4439
4439
  });
4440
4440
 
4441
- // ../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_bf26cb05b71455716d5beca75e2a0544/node_modules/@better-auth/core/dist/env/index.mjs
4441
+ // ../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_da517a110fdd8af320b5bd05eef9033a/node_modules/@better-auth/core/dist/env/index.mjs
4442
4442
  var init_env = __esm({
4443
- "../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_bf26cb05b71455716d5beca75e2a0544/node_modules/@better-auth/core/dist/env/index.mjs"() {
4443
+ "../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_da517a110fdd8af320b5bd05eef9033a/node_modules/@better-auth/core/dist/env/index.mjs"() {
4444
4444
  init_env_impl();
4445
4445
  init_color_depth();
4446
4446
  init_logger();
4447
4447
  }
4448
4448
  });
4449
4449
 
4450
- // ../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_bf26cb05b71455716d5beca75e2a0544/node_modules/@better-auth/core/dist/utils/error-codes.mjs
4450
+ // ../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_da517a110fdd8af320b5bd05eef9033a/node_modules/@better-auth/core/dist/utils/error-codes.mjs
4451
4451
  function defineErrorCodes(codes) {
4452
4452
  return codes;
4453
4453
  }
4454
4454
  var init_error_codes = __esm({
4455
- "../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_bf26cb05b71455716d5beca75e2a0544/node_modules/@better-auth/core/dist/utils/error-codes.mjs"() {
4455
+ "../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_da517a110fdd8af320b5bd05eef9033a/node_modules/@better-auth/core/dist/utils/error-codes.mjs"() {
4456
4456
  __name(defineErrorCodes, "defineErrorCodes");
4457
4457
  }
4458
4458
  });
4459
4459
 
4460
- // ../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_bf26cb05b71455716d5beca75e2a0544/node_modules/@better-auth/core/dist/utils/db.mjs
4460
+ // ../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_da517a110fdd8af320b5bd05eef9033a/node_modules/@better-auth/core/dist/utils/db.mjs
4461
4461
  var init_db = __esm({
4462
- "../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_bf26cb05b71455716d5beca75e2a0544/node_modules/@better-auth/core/dist/utils/db.mjs"() {
4462
+ "../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_da517a110fdd8af320b5bd05eef9033a/node_modules/@better-auth/core/dist/utils/db.mjs"() {
4463
4463
  }
4464
4464
  });
4465
4465
 
4466
- // ../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_bf26cb05b71455716d5beca75e2a0544/node_modules/@better-auth/core/dist/utils/deprecate.mjs
4466
+ // ../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_da517a110fdd8af320b5bd05eef9033a/node_modules/@better-auth/core/dist/utils/deprecate.mjs
4467
4467
  var init_deprecate = __esm({
4468
- "../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_bf26cb05b71455716d5beca75e2a0544/node_modules/@better-auth/core/dist/utils/deprecate.mjs"() {
4468
+ "../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_da517a110fdd8af320b5bd05eef9033a/node_modules/@better-auth/core/dist/utils/deprecate.mjs"() {
4469
4469
  }
4470
4470
  });
4471
4471
 
@@ -4475,42 +4475,42 @@ var init_random = __esm({
4475
4475
  }
4476
4476
  });
4477
4477
 
4478
- // ../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_bf26cb05b71455716d5beca75e2a0544/node_modules/@better-auth/core/dist/utils/id.mjs
4478
+ // ../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_da517a110fdd8af320b5bd05eef9033a/node_modules/@better-auth/core/dist/utils/id.mjs
4479
4479
  var init_id = __esm({
4480
- "../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_bf26cb05b71455716d5beca75e2a0544/node_modules/@better-auth/core/dist/utils/id.mjs"() {
4480
+ "../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_da517a110fdd8af320b5bd05eef9033a/node_modules/@better-auth/core/dist/utils/id.mjs"() {
4481
4481
  init_random();
4482
4482
  }
4483
4483
  });
4484
4484
 
4485
- // ../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_bf26cb05b71455716d5beca75e2a0544/node_modules/@better-auth/core/dist/utils/ip.mjs
4485
+ // ../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_da517a110fdd8af320b5bd05eef9033a/node_modules/@better-auth/core/dist/utils/ip.mjs
4486
4486
  var init_ip = __esm({
4487
- "../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_bf26cb05b71455716d5beca75e2a0544/node_modules/@better-auth/core/dist/utils/ip.mjs"() {
4487
+ "../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_da517a110fdd8af320b5bd05eef9033a/node_modules/@better-auth/core/dist/utils/ip.mjs"() {
4488
4488
  }
4489
4489
  });
4490
4490
 
4491
- // ../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_bf26cb05b71455716d5beca75e2a0544/node_modules/@better-auth/core/dist/utils/json.mjs
4491
+ // ../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_da517a110fdd8af320b5bd05eef9033a/node_modules/@better-auth/core/dist/utils/json.mjs
4492
4492
  var init_json = __esm({
4493
- "../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_bf26cb05b71455716d5beca75e2a0544/node_modules/@better-auth/core/dist/utils/json.mjs"() {
4493
+ "../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_da517a110fdd8af320b5bd05eef9033a/node_modules/@better-auth/core/dist/utils/json.mjs"() {
4494
4494
  init_logger();
4495
4495
  init_env();
4496
4496
  }
4497
4497
  });
4498
4498
 
4499
- // ../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_bf26cb05b71455716d5beca75e2a0544/node_modules/@better-auth/core/dist/utils/string.mjs
4499
+ // ../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_da517a110fdd8af320b5bd05eef9033a/node_modules/@better-auth/core/dist/utils/string.mjs
4500
4500
  var init_string = __esm({
4501
- "../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_bf26cb05b71455716d5beca75e2a0544/node_modules/@better-auth/core/dist/utils/string.mjs"() {
4501
+ "../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_da517a110fdd8af320b5bd05eef9033a/node_modules/@better-auth/core/dist/utils/string.mjs"() {
4502
4502
  }
4503
4503
  });
4504
4504
 
4505
- // ../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_bf26cb05b71455716d5beca75e2a0544/node_modules/@better-auth/core/dist/utils/url.mjs
4505
+ // ../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_da517a110fdd8af320b5bd05eef9033a/node_modules/@better-auth/core/dist/utils/url.mjs
4506
4506
  var init_url = __esm({
4507
- "../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_bf26cb05b71455716d5beca75e2a0544/node_modules/@better-auth/core/dist/utils/url.mjs"() {
4507
+ "../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_da517a110fdd8af320b5bd05eef9033a/node_modules/@better-auth/core/dist/utils/url.mjs"() {
4508
4508
  }
4509
4509
  });
4510
4510
 
4511
- // ../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_bf26cb05b71455716d5beca75e2a0544/node_modules/@better-auth/core/dist/utils/index.mjs
4511
+ // ../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_da517a110fdd8af320b5bd05eef9033a/node_modules/@better-auth/core/dist/utils/index.mjs
4512
4512
  var init_utils = __esm({
4513
- "../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_bf26cb05b71455716d5beca75e2a0544/node_modules/@better-auth/core/dist/utils/index.mjs"() {
4513
+ "../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_da517a110fdd8af320b5bd05eef9033a/node_modules/@better-auth/core/dist/utils/index.mjs"() {
4514
4514
  init_db();
4515
4515
  init_deprecate();
4516
4516
  init_error_codes();
@@ -4522,10 +4522,10 @@ var init_utils = __esm({
4522
4522
  }
4523
4523
  });
4524
4524
 
4525
- // ../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_bf26cb05b71455716d5beca75e2a0544/node_modules/@better-auth/core/dist/error/codes.mjs
4525
+ // ../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_da517a110fdd8af320b5bd05eef9033a/node_modules/@better-auth/core/dist/error/codes.mjs
4526
4526
  var BASE_ERROR_CODES;
4527
4527
  var init_codes = __esm({
4528
- "../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_bf26cb05b71455716d5beca75e2a0544/node_modules/@better-auth/core/dist/error/codes.mjs"() {
4528
+ "../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_da517a110fdd8af320b5bd05eef9033a/node_modules/@better-auth/core/dist/error/codes.mjs"() {
4529
4529
  init_error_codes();
4530
4530
  init_utils();
4531
4531
  BASE_ERROR_CODES = defineErrorCodes({
@@ -4576,10 +4576,10 @@ var init_codes = __esm({
4576
4576
  }
4577
4577
  });
4578
4578
 
4579
- // ../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_bf26cb05b71455716d5beca75e2a0544/node_modules/@better-auth/core/dist/error/index.mjs
4579
+ // ../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_da517a110fdd8af320b5bd05eef9033a/node_modules/@better-auth/core/dist/error/index.mjs
4580
4580
  var BetterAuthError;
4581
4581
  var init_error = __esm({
4582
- "../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_bf26cb05b71455716d5beca75e2a0544/node_modules/@better-auth/core/dist/error/index.mjs"() {
4582
+ "../../node_modules/.pnpm/@better-auth+core@1.4.18_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.21_better-cal_da517a110fdd8af320b5bd05eef9033a/node_modules/@better-auth/core/dist/error/index.mjs"() {
4583
4583
  init_codes();
4584
4584
  BetterAuthError = class extends Error {
4585
4585
  static {
@@ -44568,7 +44568,7 @@ var init_document_dto = __esm({
44568
44568
  });
44569
44569
 
44570
44570
  // ../../packages/api-contracts/dist/esm/dtos/chat.dto.js
44571
- var ChatMessageListDTOSchema, ChatDeletionDTOSchema, ChatDeletionDTO, ChatMessageListResponseSchema, ChatDeletionResponseSchema;
44571
+ var ChatMessageListDTOSchema, ChatMessagesPageDTOSchema, ChatDeletionDTOSchema, ChatDeletionDTO, ChatMessageListResponseSchema, ChatMessagesPageResponseSchema, ChatDeletionResponseSchema;
44572
44572
  var init_chat_dto = __esm({
44573
44573
  "../../packages/api-contracts/dist/esm/dtos/chat.dto.js"() {
44574
44574
  "use strict";
@@ -44577,12 +44577,18 @@ var init_chat_dto = __esm({
44577
44577
  init_response();
44578
44578
  init_project_dto();
44579
44579
  ChatMessageListDTOSchema = external_exports.array(ProjectMessageDTOSchema);
44580
+ ChatMessagesPageDTOSchema = external_exports.object({
44581
+ items: external_exports.array(ProjectMessagePublicDTOSchema),
44582
+ /** Cursor for the next (older) page — opaque base64 of `(createdAt, id)`. */
44583
+ nextCursor: external_exports.string().nullable()
44584
+ });
44580
44585
  ChatDeletionDTOSchema = external_exports.object({
44581
44586
  success: external_exports.boolean(),
44582
44587
  deletedCount: external_exports.number()
44583
44588
  });
44584
44589
  ChatDeletionDTO = createDTO(ChatDeletionDTOSchema);
44585
44590
  ChatMessageListResponseSchema = createResponseSchema(ChatMessageListDTOSchema);
44591
+ ChatMessagesPageResponseSchema = createResponseSchema(ChatMessagesPageDTOSchema);
44586
44592
  ChatDeletionResponseSchema = createResponseSchema(ChatDeletionDTOSchema);
44587
44593
  }
44588
44594
  });
@@ -0,0 +1,11 @@
1
+ export interface CollectionResponse {
2
+ readonly data: Record<string, unknown>[];
3
+ readonly meta: {
4
+ readonly limit: number;
5
+ readonly offset: number;
6
+ readonly total: number;
7
+ readonly hasMore: boolean;
8
+ };
9
+ }
10
+ export declare function parseCollectionResponse(value: unknown): CollectionResponse;
11
+ //# sourceMappingURL=parseCollectionResponse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseCollectionResponse.d.ts","sourceRoot":"","sources":["../../../src/runtime/content/parseCollectionResponse.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACzC,QAAQ,CAAC,IAAI,EAAE;QACb,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;KAC3B,CAAC;CACH;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,kBAAkB,CAE1E"}
@@ -0,0 +1,13 @@
1
+ import { z } from 'zod';
2
+ const collectionResponseSchema = z.object({
3
+ data: z.array(z.record(z.string(), z.unknown())),
4
+ meta: z.object({
5
+ limit: z.number().int().positive(),
6
+ offset: z.number().int().nonnegative(),
7
+ total: z.number().int().nonnegative(),
8
+ hasMore: z.boolean(),
9
+ }),
10
+ });
11
+ export function parseCollectionResponse(value) {
12
+ return collectionResponseSchema.parse(value);
13
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=parseCollectionResponse.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseCollectionResponse.test.d.ts","sourceRoot":"","sources":["../../../src/runtime/content/parseCollectionResponse.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,20 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { parseCollectionResponse } from './parseCollectionResponse.js';
3
+ describe('parseCollectionResponse', () => {
4
+ it('accepts the collection envelope used by generated clients', () => {
5
+ const response = parseCollectionResponse({
6
+ data: [{ id: 'post-1', title: 'Hello' }],
7
+ meta: { limit: 12, offset: 24, total: 40, hasMore: true },
8
+ });
9
+ expect(response).toEqual({
10
+ data: [{ id: 'post-1', title: 'Hello' }],
11
+ meta: { limit: 12, offset: 24, total: 40, hasMore: true },
12
+ });
13
+ });
14
+ it('rejects invalid pagination metadata', () => {
15
+ expect(() => parseCollectionResponse({
16
+ data: [],
17
+ meta: { limit: 0, offset: -1, total: '40', hasMore: 'yes' },
18
+ })).toThrow();
19
+ });
20
+ });
@@ -6,6 +6,8 @@ export type { FimoLocaleContextValue } from './i18n/FimoLocaleContext.js';
6
6
  export type { FimoLocaleResolution } from './i18n/locale.js';
7
7
  export { DataSourcesProvider } from './content/DataSourcesProvider.js';
8
8
  export { RefreshContentProvider } from './content/RefreshContentProvider.js';
9
+ export { parseCollectionResponse } from './content/parseCollectionResponse.js';
10
+ export type { CollectionResponse } from './content/parseCollectionResponse.js';
9
11
  export { AppErrorBoundary } from './core/AppErrorBoundary.js';
10
12
  export { ViteLifecycles } from './core/ViteLifecycles.js';
11
13
  export { Pageview } from './t/Pageview.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AACvF,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACvE,YAAY,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAC1E,YAAY,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAG7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAG7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG1D,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAG3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAEpE,cAAc,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AACvF,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACvE,YAAY,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAC1E,YAAY,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAG7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAC7E,OAAO,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAC/E,YAAY,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAG/E,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG1D,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAG3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAEpE,cAAc,uBAAuB,CAAC"}
@@ -5,6 +5,7 @@ export { localizeFimoPath, resolveFimoLocale } from './i18n/locale.js';
5
5
  // Content
6
6
  export { DataSourcesProvider } from './content/DataSourcesProvider.js';
7
7
  export { RefreshContentProvider } from './content/RefreshContentProvider.js';
8
+ export { parseCollectionResponse } from './content/parseCollectionResponse.js';
8
9
  // Core
9
10
  export { AppErrorBoundary } from './core/AppErrorBoundary.js';
10
11
  export { ViteLifecycles } from './core/ViteLifecycles.js';
@@ -75,6 +75,7 @@ const SCHEMAS = {
75
75
  };
76
76
  const USAGE = `// Agent-style usage of the generated types — compile-time contract.
77
77
  import type { PageResult, PageWhere } from "./Page";
78
+ import Post from "./Post";
78
79
  import type { PostResult } from "./Post";
79
80
 
80
81
  export function renderFeatured(page: PageResult<undefined, readonly ["featured", "hero"]>): string[] {
@@ -112,6 +113,45 @@ export function whereContract(): PageWhere {
112
113
  featured: "some-document-id",
113
114
  };
114
115
  }
116
+
117
+ export function paginatedPostContract(): void {
118
+ const legacy = Post.useGet({ limit: 3 });
119
+ const page = Post.useList({
120
+ fields: ["title", "author"] as const,
121
+ populate: ["author"] as const,
122
+ limit: 12,
123
+ offset: 24,
124
+ });
125
+ const infinite = Post.useInfiniteList({
126
+ fields: ["title", "author"] as const,
127
+ populate: ["author"] as const,
128
+ limit: 12,
129
+ });
130
+
131
+ const legacyPosts: PostResult[] = legacy.data ?? [];
132
+ const pagePosts: PostResult<readonly ["title", "author"], readonly ["author"]>[] = page.data?.items ?? [];
133
+ const total: number = page.data?.pageInfo.total ?? 0;
134
+ const nextOffset: number | null = page.data?.pageInfo.nextOffset ?? null;
135
+ const infinitePosts: PostResult<readonly ["title", "author"], readonly ["author"]>[] = infinite.items;
136
+ const firstAuthorName: string = infinitePosts[0]?.author ? String(infinitePosts[0].author.name) : "";
137
+ const hasMore: boolean = infinite.hasMore;
138
+ const isLoadingMore: boolean = infinite.isLoadingMore;
139
+
140
+ void infinite.loadMore();
141
+ // @ts-expect-error infinite lists own offset progression
142
+ Post.useInfiniteList({ offset: 12 });
143
+
144
+ void [legacyPosts, pagePosts, total, nextOffset, infinitePosts, firstAuthorName, hasMore, isLoadingMore];
145
+ }
146
+
147
+ export async function paginatedPostFetchContract(): Promise<void> {
148
+ const page = await Post.list({ limit: 12, offset: 24 });
149
+ const legacyPosts: PostResult[] = await Post.get({ limit: 3 });
150
+ const pagePosts: PostResult[] = page.items;
151
+ const hasMore: boolean = page.pageInfo.hasMore;
152
+
153
+ void [legacyPosts, pagePosts, hasMore];
154
+ }
115
155
  `;
116
156
  const SHIMS = `declare module "virtual:fimo-config" {
117
157
  const config: Record<string, unknown>;
@@ -95,7 +95,7 @@ describe('syncSchemas', () => {
95
95
  expect(settings).toContain('const LOCALIZED_REFERENCE_FIELDS = new Set<string>(["homepage"]);');
96
96
  expect(settings).not.toContain(' locale: entry.locale,');
97
97
  expect(settings).toContain('readLocale(params, LOCALIZED_CONTENT, useQueryLocale(params.locale))');
98
- expect(settings.match(/readLocale\(params, LOCALIZED_CONTENT, useQueryLocale\(params\.locale\)\)/g)?.length).toBe(2);
98
+ expect(settings.match(/readLocale\(params, LOCALIZED_CONTENT, useQueryLocale\(params\.locale\)\)/g)?.length).toBe(4);
99
99
  expect(settings.match(/readLocale\(options, false, useQueryLocale\(options\.locale\)\)/g)?.length).toBe(2);
100
100
  });
101
101
  it('generates singleton document APIs without collection mutations', () => {
@@ -115,6 +115,9 @@ describe('syncSchemas', () => {
115
115
  expect(settings).not.toContain('export async function create(');
116
116
  expect(settings).not.toContain('export async function remove(');
117
117
  expect(settings).not.toContain('export function useCreate(');
118
+ expect(settings).not.toContain('export function useList(');
119
+ expect(settings).not.toContain('export function useInfiniteList(');
120
+ expect(settings).not.toContain('useInfiniteQuery');
118
121
  expect(settingsTypes).toContain('siteName: string;');
119
122
  expect(settingsTypes).not.toContain('documentId?: string;');
120
123
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fimo",
3
- "version": "0.4.1",
3
+ "version": "0.5.0-experimental.2",
4
4
  "description": "Fimo CLI - create, deploy, and manage Fimo projects",
5
5
  "bin": {
6
6
  "fimo": "dist/cli/index.js"
package/release.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "source": "npm",
3
- "apiUrl": "https://api.fimo.ai",
4
- "webUrl": "https://fimo.ai"
3
+ "apiUrl": "https://api.staging.fimo.team",
4
+ "webUrl": "https://staging.fimo.team"
5
5
  }
@@ -21,7 +21,7 @@
21
21
  "cmdk": "^1.1.1",
22
22
  "date-fns": "^4.1.0",
23
23
  "embla-carousel-react": "^8.6.0",
24
- "fimo": "0.4.1",
24
+ "fimo": "0.5.0-experimental.2",
25
25
  "input-otp": "^1.4.2",
26
26
  "isbot": "^5",
27
27
  "lucide-react": "^0.577.0",