fimo 0.4.0-experimental.3 → 0.4.0-experimental.5

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.
@@ -14,15 +14,15 @@ import { FimoString, FimoMedia, FimoBoolean, FimoRichText, useLocale } from "fim
14
14
  import fimoConfig from "virtual:fimo-config";
15
15
  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";
16
16
  <%
17
- if (model.relationImports && model.relationImports.length > 0) {
18
- model.relationImports.forEach((relationImport) => {
17
+ if (model.referenceImports && model.referenceImports.length > 0) {
18
+ model.referenceImports.forEach((referenceImport) => {
19
19
  -%>
20
- import { wrapWithSource as wrap<%= relationImport.pascal %>WithSource } from "./<%= relationImport.pascal %>";
20
+ import { wrapWithSource as wrap<%= referenceImport.pascal %>WithSource } from "./<%= referenceImport.pascal %>";
21
21
  <%
22
22
  });
23
23
  }
24
24
  -%>
25
- export type { <%= model.pascal %>, <%= model.pascal %>Fields, <%= model.pascal %>FimoMeta, <%= model.pascal %>Input, <%= model.pascal %>Populate, <%= model.pascal %>Query, <%= model.pascal %>Relations, <%= model.pascal %>Result, <%= model.pascal %>Sort, <%= model.pascal %>Where } from "./<%= model.pascal %>.types";
25
+ export type { <%= model.pascal %>, <%= model.pascal %>Fields, <%= model.pascal %>FimoMeta, <%= model.pascal %>Input, <%= model.pascal %>Populate, <%= model.pascal %>Query, <%= model.pascal %>References, <%= model.pascal %>Result, <%= model.pascal %>Sort, <%= model.pascal %>Where } from "./<%= model.pascal %>.types";
26
26
 
27
27
  /*───────────────────────────────────────────────────────────────*/
28
28
  /* ──────────── 🎁 Source wrapping ──────────── */
@@ -79,7 +79,7 @@ if (model.fields) {
79
79
  <%
80
80
  }
81
81
  break;
82
- case 'relation': {
82
+ case 'reference': {
83
83
  const wrapper = field.target === model.uid ? 'wrapWithSource' : `wrap${field.targetPascal}WithSource`;
84
84
  if (field.many) {
85
85
  -%>
@@ -119,7 +119,7 @@ function useQueryLocale(locale?: string): string {
119
119
  }
120
120
 
121
121
  const LOCALIZED_CONTENT = <%= model.localized ? 'true' : 'false' %>;
122
- const LOCALIZED_RELATION_FIELDS = new Set<string>(<%= JSON.stringify(model.localizedRelationFields) %>);
122
+ const LOCALIZED_REFERENCE_FIELDS = new Set<string>(<%= JSON.stringify(model.localizedReferenceFields) %>);
123
123
 
124
124
  function populateFields(populate: <%= model.pascal %>Populate | undefined): string[] {
125
125
  if (populate == null) return [];
@@ -131,7 +131,7 @@ function readLocale(
131
131
  ownerUsesLocale: boolean,
132
132
  fallbackLocale = defaultLocale()
133
133
  ): string | undefined {
134
- const populatesLocalizedTarget = populateFields(options.populate).some((field) => LOCALIZED_RELATION_FIELDS.has(field));
134
+ const populatesLocalizedTarget = populateFields(options.populate).some((field) => LOCALIZED_REFERENCE_FIELDS.has(field));
135
135
  return ownerUsesLocale || populatesLocalizedTarget ? options.locale ?? fallbackLocale : undefined;
136
136
  }
137
137
 
@@ -145,7 +145,7 @@ function fimoHeaders(extra?: Record<string, string>): Record<string, string> {
145
145
 
146
146
  function withReadOptions<TPopulate extends <%= model.pascal %>Populate | undefined>(
147
147
  url: string,
148
- options: { locale?: string; populate?: TPopulate } = {},
148
+ options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {},
149
149
  ownerUsesLocale = LOCALIZED_CONTENT
150
150
  ): string {
151
151
  const u = new URL(url, "http://fimo.local");
@@ -154,6 +154,7 @@ function withReadOptions<TPopulate extends <%= model.pascal %>Populate | undefin
154
154
  if (options.populate != null) {
155
155
  u.searchParams.set("populate", typeof options.populate === "string" ? options.populate : options.populate.join(","));
156
156
  }
157
+ if (options.localeFallbacks) u.searchParams.set("localeFallbacks", "true");
157
158
  return `${u.pathname}${u.search}`;
158
159
  }
159
160
 
@@ -178,7 +179,7 @@ function splitWritePayload(payload: Partial<<%= model.pascal %>Input>): Record<s
178
179
  <% if (!model.isSingleton) { -%>
179
180
  export async function getById<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(
180
181
  id: string,
181
- options: { locale?: string; populate?: TPopulate } = {}
182
+ options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}
182
183
  ): Promise<<%= model.pascal %>Result<undefined, TPopulate>> {
183
184
  const res = await fetch(`${base}${withReadOptions(`/entries/<%= model.uid %>/${id}`, options, false)}`, { headers: fimoHeaders() });
184
185
  if (!res.ok) throw new Error(await res.text());
@@ -188,7 +189,7 @@ export async function getById<TPopulate extends <%= model.pascal %>Populate | un
188
189
 
189
190
  export async function getBySlug<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(
190
191
  slug: string,
191
- options: { locale?: string; populate?: TPopulate } = {}
192
+ options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}
192
193
  ): Promise<<%= model.pascal %>Result<undefined, TPopulate> | null> {
193
194
  const res = await fetch(`${base}${withReadOptions(`/entries/<%= model.uid %>/slug/${encodeURIComponent(slug)}`, options)}`, { headers: fimoHeaders() });
194
195
  if (res.status === 404) return null;
@@ -200,7 +201,7 @@ export async function getBySlug<TPopulate extends <%= model.pascal %>Populate |
200
201
  export async function getByField<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(
201
202
  fieldName: string,
202
203
  value: string,
203
- options: { locale?: string; populate?: TPopulate } = {}
204
+ options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}
204
205
  ): Promise<<%= model.pascal %>Result<undefined, TPopulate> | null> {
205
206
  const res = await fetch(`${base}${withReadOptions(`/entries/<%= model.uid %>/field/${encodeURIComponent(fieldName)}/${encodeURIComponent(value)}`, options)}`, { headers: fimoHeaders() });
206
207
  if (res.status === 404) return null;
@@ -282,7 +283,7 @@ export async function remove(id: string): Promise<void> {
282
283
  }
283
284
  <% } else { -%>
284
285
  export async function get<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(
285
- options: { locale?: string; populate?: TPopulate } = {}
286
+ options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}
286
287
  ): Promise<<%= model.pascal %>Result<undefined, TPopulate>> {
287
288
  const res = await fetch(`${base}${withReadOptions(`/documents/<%= model.uid %>`, options)}`, { headers: fimoHeaders() });
288
289
  if (!res.ok) throw new Error(await res.text());
@@ -311,25 +312,25 @@ export async function update(payload: Partial<<%= model.pascal %>Input>): Promis
311
312
  <% if (!model.isSingleton) { -%>
312
313
  const qk = {
313
314
  root : ["<%= model.pascal %>"] as const,
314
- byId : <TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(id: string, options: { locale?: string; populate?: TPopulate } = {}) => ["<%= model.pascal %>", id, options.locale, options.populate] as const,
315
- bySlug : <TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(slug: string, options: { locale?: string; populate?: TPopulate } = {}) => ["<%= model.pascal %>", "slug", slug, options.locale, options.populate] as const,
316
- byField : <TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(field: string, value: string, options: { locale?: string; populate?: TPopulate } = {}) => ["<%= model.pascal %>", field, value, options.locale, options.populate] as const,
315
+ 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,
316
+ 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,
317
+ 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,
317
318
  list : (params: <%= model.pascal %>Query) => ["<%= model.pascal %>", params] as const
318
319
  };
319
320
 
320
- export function useGetById<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(id: string, options: { locale?: string; populate?: TPopulate } = {}) {
321
+ export function useGetById<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(id: string, options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}) {
321
322
  const locale = readLocale(options, false, useQueryLocale(options.locale));
322
323
  const localizedOptions = { ...options, locale };
323
324
  return useQuery<<%= model.pascal %>Result<undefined, TPopulate>, Error>({ queryKey: qk.byId(id, localizedOptions), queryFn: () => getById(id, localizedOptions) });
324
325
  }
325
326
 
326
- export function useGetBySlug<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(slug: string, options: { locale?: string; populate?: TPopulate } = {}) {
327
+ export function useGetBySlug<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(slug: string, options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}) {
327
328
  const locale = readLocale(options, LOCALIZED_CONTENT, useQueryLocale(options.locale));
328
329
  const localizedOptions = { ...options, locale };
329
330
  return useQuery<<%= model.pascal %>Result<undefined, TPopulate> | null, Error>({ queryKey: qk.bySlug(slug, localizedOptions), queryFn: () => getBySlug(slug, localizedOptions), enabled: !!slug });
330
331
  }
331
332
 
332
- export function useGetByField<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(fieldName: string, value: string, options: { locale?: string; populate?: TPopulate } = {}) {
333
+ export function useGetByField<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(fieldName: string, value: string, options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}) {
333
334
  const locale = readLocale(options, LOCALIZED_CONTENT, useQueryLocale(options.locale));
334
335
  const localizedOptions = { ...options, locale };
335
336
  return useQuery<<%= model.pascal %>Result<undefined, TPopulate> | null, Error>({ queryKey: qk.byField(fieldName, value, localizedOptions), queryFn: () => getByField(fieldName, value, localizedOptions), enabled: !!value });
@@ -344,19 +345,19 @@ export function useGet<
344
345
  return useQuery<<%= model.pascal %>Result<TFields, TPopulate>[], Error>({ queryKey: qk.list(localizedParams), queryFn: () => get(localizedParams) });
345
346
  }
346
347
 
347
- export function useSuspenseGetById<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(id: string, options: { locale?: string; populate?: TPopulate } = {}) {
348
+ export function useSuspenseGetById<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(id: string, options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}) {
348
349
  const locale = readLocale(options, false, useQueryLocale(options.locale));
349
350
  const localizedOptions = { ...options, locale };
350
351
  return useSuspenseQuery<<%= model.pascal %>Result<undefined, TPopulate>, Error>({ queryKey: qk.byId(id, localizedOptions), queryFn: () => getById(id, localizedOptions) });
351
352
  }
352
353
 
353
- export function useSuspenseGetBySlug<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(slug: string, options: { locale?: string; populate?: TPopulate } = {}) {
354
+ export function useSuspenseGetBySlug<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(slug: string, options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}) {
354
355
  const locale = readLocale(options, LOCALIZED_CONTENT, useQueryLocale(options.locale));
355
356
  const localizedOptions = { ...options, locale };
356
357
  return useSuspenseQuery<<%= model.pascal %>Result<undefined, TPopulate> | null, Error>({ queryKey: qk.bySlug(slug, localizedOptions), queryFn: () => getBySlug(slug, localizedOptions) });
357
358
  }
358
359
 
359
- export function useSuspenseGetByField<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(fieldName: string, value: string, options: { locale?: string; populate?: TPopulate } = {}) {
360
+ export function useSuspenseGetByField<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(fieldName: string, value: string, options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}) {
360
361
  const locale = readLocale(options, LOCALIZED_CONTENT, useQueryLocale(options.locale));
361
362
  const localizedOptions = { ...options, locale };
362
363
  return useSuspenseQuery<<%= model.pascal %>Result<undefined, TPopulate> | null, Error>({ queryKey: qk.byField(fieldName, value, localizedOptions), queryFn: () => getByField(fieldName, value, localizedOptions) });
@@ -403,16 +404,16 @@ export function useRemove() {
403
404
  <% } else { -%>
404
405
  const qk = {
405
406
  root: ["<%= model.pascal %>"] as const,
406
- document: <TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(options: { locale?: string; populate?: TPopulate } = {}) => ["<%= model.pascal %>", "document", options.locale, options.populate] as const
407
+ document: <TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}) => ["<%= model.pascal %>", "document", options.locale, options.populate] as const
407
408
  };
408
409
 
409
- export function useGet<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(options: { locale?: string; populate?: TPopulate } = {}) {
410
+ export function useGet<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}) {
410
411
  const locale = readLocale(options, LOCALIZED_CONTENT, useQueryLocale(options.locale));
411
412
  const localizedOptions = { ...options, locale };
412
413
  return useQuery<<%= model.pascal %>Result<undefined, TPopulate>, Error>({ queryKey: qk.document(localizedOptions), queryFn: () => get(localizedOptions) });
413
414
  }
414
415
 
415
- export function useSuspenseGet<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(options: { locale?: string; populate?: TPopulate } = {}) {
416
+ export function useSuspenseGet<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}) {
416
417
  const locale = readLocale(options, LOCALIZED_CONTENT, useQueryLocale(options.locale));
417
418
  const localizedOptions = { ...options, locale };
418
419
  return useSuspenseQuery<<%= model.pascal %>Result<undefined, TPopulate>, Error>({ queryKey: qk.document(localizedOptions), queryFn: () => get(localizedOptions) });
@@ -8,12 +8,12 @@
8
8
  */
9
9
  %>
10
10
  // AUTO-GENERATED ⚠️ – do not edit by hand
11
- import { type Fields, type FimoBoolean, type FimoMedia, type FimoReference, type FimoRichText, type FimoRichTextContent, type FimoString, type Populate, type PopulatedResult, type ProjectedWithPopulate, type Query, type Sort, type Where, type WithRelations } from "fimo/ui";
11
+ import { type Fields, type FimoBoolean, type FimoMedia, type FimoReference, type FimoRichText, type FimoRichTextContent, type FimoString, type Populate, type PopulatedResult, type ProjectedWithPopulate, type Query, type Sort, type Where, type WithReferences } from "fimo/ui";
12
12
  <%
13
- if (model.relationImports && model.relationImports.length > 0) {
14
- model.relationImports.forEach((relationImport) => {
13
+ if (model.referenceImports && model.referenceImports.length > 0) {
14
+ model.referenceImports.forEach((referenceImport) => {
15
15
  -%>
16
- import type { <%= relationImport.pascal %> } from "./<%= relationImport.pascal %>.types";
16
+ import type { <%= referenceImport.pascal %> } from "./<%= referenceImport.pascal %>.types";
17
17
  <%
18
18
  });
19
19
  }
@@ -30,7 +30,7 @@ export interface <%= model.pascal %>FimoMeta {
30
30
  contentType: string;
31
31
  }
32
32
 
33
- export interface <%= model.pascal %> extends WithRelations<<%= model.pascal %>Relations> {
33
+ export interface <%= model.pascal %> extends WithReferences<<%= model.pascal %>References> {
34
34
  id: string;
35
35
  documentId: string;
36
36
  slug: FimoString;
@@ -93,7 +93,7 @@ function getTypeForField(field) {
93
93
  return 'Record<string, any>';
94
94
  case 'media':
95
95
  return field.multiple ? 'FimoMedia[]' : 'FimoMedia';
96
- case 'relation':
96
+ case 'reference':
97
97
  return field.many ? 'FimoReference[]' : 'FimoReference | null';
98
98
  default:
99
99
  return 'any';
@@ -118,7 +118,7 @@ function getInputTypeForField(field) {
118
118
  const media = 'Pick<FimoMedia, "url" | "alt" | "name" | "mime" | "width" | "height">';
119
119
  return field.multiple ? `${media}[]` : media;
120
120
  }
121
- case 'relation':
121
+ case 'reference':
122
122
  return field.many ? 'FimoReference[]' : 'FimoReference | null';
123
123
  default:
124
124
  return 'unknown';
@@ -127,11 +127,11 @@ function getInputTypeForField(field) {
127
127
  -%>
128
128
  }
129
129
 
130
- export interface <%= model.pascal %>Relations {
130
+ export interface <%= model.pascal %>References {
131
131
  <%
132
132
  if (model.fields) {
133
133
  Object.entries(model.fields).forEach(([key, field]) => {
134
- if (field.type !== 'relation' || typeof field.targetPascal !== 'string') {
134
+ if (field.type !== 'reference' || typeof field.targetPascal !== 'string') {
135
135
  return;
136
136
  }
137
137
  const type = field.many ? `${field.targetPascal}[]` : `${field.targetPascal} | null`;
@@ -18,8 +18,8 @@ This file covers the **package surface**: the JSON shape you write, the generate
18
18
  "featured": { "type": "boolean" },
19
19
  "coverImage": { "type": "media" },
20
20
  "metadata": { "type": "json" },
21
- "author": { "type": "relation", "target": "Author" },
22
- "contributors": { "type": "relation", "target": "Author", "many": true }
21
+ "author": { "type": "reference", "target": "Author" },
22
+ "contributors": { "type": "reference", "target": "Author", "many": true }
23
23
  }
24
24
  }
25
25
  ```
@@ -45,22 +45,22 @@ something else (e.g. `handle`, `permalink`).
45
45
 
46
46
  ## Field types
47
47
 
48
- | Type | `data` JSON shape | Use for |
49
- | ---------- | ------------------------------------------------------------------------------------- | --------------------------------- |
50
- | `string` | `"..."` | Titles, short text |
51
- | `richtext` | Tiptap JSONContent (see below) | Blog body, descriptions, articles |
52
- | `number` | `42` | Counts, quantities |
53
- | `boolean` | `true` / `false` | Flags |
54
- | `date` | ISO date string, e.g. `"2026-06-03"` | Publish dates, timestamps |
55
- | `media` | `{ "source": "local"\|"external", "url": "https://...", "alt": "..." }` | Images, video |
56
- | `json` | any JSON | Structured metadata |
57
- | `relation` | `{ "documentId": "..." }`, `null`, or an array of reference objects when `many: true` | References to another schema |
48
+ | Type | `data` JSON shape | Use for |
49
+ | ----------- | ------------------------------------------------------------------------------------- | --------------------------------- |
50
+ | `string` | `"..."` | Titles, short text |
51
+ | `richtext` | Tiptap JSONContent (see below) | Blog body, descriptions, articles |
52
+ | `number` | `42` | Counts, quantities |
53
+ | `boolean` | `true` / `false` | Flags |
54
+ | `date` | ISO date string, e.g. `"2026-06-03"` | Publish dates, timestamps |
55
+ | `media` | `{ "source": "local"\|"external", "url": "https://...", "alt": "..." }` | Images, video |
56
+ | `json` | any JSON | Structured metadata |
57
+ | `reference` | `{ "documentId": "..." }`, `null`, or an array of reference objects when `many: true` | References to another schema |
58
58
 
59
59
  A `date` field currently generates as `any` in the TS client, so for typed access (and the `<Date>` component) prefer a `string` field holding an ISO date — which is what the `publishDate` examples below do.
60
60
 
61
- ## Relation fields
61
+ ## Reference fields
62
62
 
63
- Use `relation` when one content entry references another content entry. Do not store entry IDs in a `json` field as a workaround.
63
+ Use `reference` when one content entry references another content entry. Do not store entry IDs in a `json` field as a workaround.
64
64
 
65
65
  ```json
66
66
  {
@@ -68,8 +68,8 @@ Use `relation` when one content entry references another content entry. Do not s
68
68
  "name": "Blog Posts",
69
69
  "fields": {
70
70
  "title": { "type": "string" },
71
- "author": { "type": "relation", "target": "Author" },
72
- "contributors": { "type": "relation", "target": "Author", "many": true }
71
+ "author": { "type": "reference", "target": "Author" },
72
+ "contributors": { "type": "reference", "target": "Author", "many": true }
73
73
  }
74
74
  }
75
75
  ```
@@ -77,27 +77,40 @@ Use `relation` when one content entry references another content entry. Do not s
77
77
  Rules:
78
78
 
79
79
  - `target` is required and must be the PascalCase schema `uid` of the referenced content type.
80
- - Omit `many` for a to-one relation. Use `"many": true` for a to-many relation.
81
- - Self-references are allowed, e.g. a `Category` schema can have `parent: { "type": "relation", "target": "Category" }`.
80
+ - Omit `many` for a to-one reference. Use `"many": true` for a to-many reference.
81
+ - Self-references are allowed, e.g. a `Category` schema can have `parent: { "type": "reference", "target": "Category" }`.
82
82
  - Entry values store reference objects with the target `documentId`, not row `id` values: `{ "documentId": "..." }`.
83
- - Do not include `contentType` for normal fixed-target relations; the schema's `target` already defines it.
84
- - Do not add `localized` or field-level `i18n` to a relation. The relation value belongs to the entry variant that stores it.
85
- - Use `null` for an empty to-one relation and `[]` for an empty to-many relation.
86
- - The API validates that referenced target documents exist and rejects duplicate values in to-many relation arrays.
83
+ - Do not include `contentType` for normal fixed-target references; the schema's `target` already defines it.
84
+ - Do not add `localized` or field-level `i18n` to a reference. The reference value belongs to the entry variant that stores it.
85
+ - Use `null` for an empty to-one reference and `[]` for an empty to-many reference.
86
+ - The API validates that referenced target documents exist and rejects duplicate values in to-many reference arrays.
87
87
 
88
88
  Entry payload example:
89
89
 
90
90
  ```json
91
91
  {
92
92
  "data": {
93
- "slug": "relation-example",
94
- "title": "Relation example",
93
+ "slug": "reference-example",
94
+ "title": "Reference example",
95
95
  "author": { "documentId": "750e8400-e29b-41d4-a716-446655440000" },
96
96
  "contributors": [{ "documentId": "750e8400-e29b-41d4-a716-446655440000" }]
97
97
  }
98
98
  }
99
99
  ```
100
100
 
101
+ ## Schema transitions
102
+
103
+ After editing schema JSON, run `fimo schemas push {Uid}` and then `fimo validate`. Push regenerates the schema client and applies the schema change to stored content automatically:
104
+
105
+ - Adding fields and changing labels preserve entries.
106
+ - Removing a field removes only that field's values.
107
+ - A one-to-many reference change wraps each existing reference in an array.
108
+ - A many-to-one change, reference target change, scalar type change, or scalar/reference change clears only the changed field.
109
+ - Collection-to-singleton removes that type's entries and cleans references to those documents.
110
+ - Singleton-to-collection preserves the existing row.
111
+
112
+ There are no reset or data-loss flags. Do not invent one or promise that incompatible field values survive; the schema change itself selects the behavior above.
113
+
101
114
  **Default to `richtext`** for fields named `body`, `content`, `description`, `summary`, `bio`, `intro`, `details`, `excerpt` — only use `string` when the field is explicitly short (e.g. a 160-char meta description).
102
115
 
103
116
  ## Tiptap JSONContent (richtext)
@@ -209,9 +222,9 @@ For shared content, set `i18n: { "enabled": false }` on the schema and create ea
209
222
  - have no public `locale` property;
210
223
  - are returned in every locale context without duplication;
211
224
  - ignore locale on owner reads and writes;
212
- - still use the active/requested locale when a populated relation targets a localized schema.
225
+ - still use the active/requested locale when a populated reference targets a localized schema.
213
226
 
214
- Do not duplicate shared entries across locales, add field-level localization settings, or implement locale fallbacks.
227
+ Do not duplicate shared entries across locales or add field-level localization settings. Do not implement locale fallbacks by hand: reads accept an opt-in `localeFallbacks: true` option that resolves populated references missing in the requested locale to the best available variant (project default first), marked by its `locale` field. Public reads stay strict unless the option is passed.
215
228
 
216
229
  **Generated API:**
217
230
 
@@ -277,8 +290,8 @@ BlogPost.useGet({
277
290
  ```
278
291
 
279
292
  Operators: scalar equality, `$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte`, `$in`, `$nin`, `$contains`, `$startsWith`, `$null`.
280
- Relation field values are reference objects, but relation filters use the target `documentId` string, for example `where: { author: "750e..." }`; for to-many relations that means "contains this target document".
281
- For to-many relations, `$eq` and `$contains` mean membership, `$in` means any listed document is present, `$ne` and `$nin` mean none are present, and `$null: true` means empty or missing. Relation fields cannot be sorted.
293
+ Reference field values are reference objects, but reference filters use the target `documentId` string, for example `where: { author: "750e..." }`; for to-many references that means "contains this target document".
294
+ For to-many references, `$eq` and `$contains` mean membership, `$in` means any listed document is present, `$ne` and `$nin` mean none are present, and `$null: true` means empty or missing. Reference fields cannot be sorted.
282
295
 
283
296
  Use `fields` to keep list payloads small. The API always keeps `id`, and returns only the requested fields beyond that:
284
297
 
@@ -291,7 +304,7 @@ const { data: summaries = [] } = BlogPost.useGet({
291
304
  // summaries: Array<Pick<BlogPost, 'id' | 'slug' | 'title' | 'excerpt' | 'coverImage'>>
292
305
  ```
293
306
 
294
- Use `populate` to embed relation targets in read results:
307
+ Use `populate` to embed reference targets in read results:
295
308
 
296
309
  ```tsx
297
310
  const { data: posts = [] } = BlogPost.useGet({
@@ -314,11 +327,11 @@ type PostCardItem = BlogPostResult<
314
327
  >;
315
328
  ```
316
329
 
317
- Do not type populated hook data as `BlogPost[]`: plain `BlogPost` keeps relation fields as `FimoReference` objects, while `BlogPostResult<TFields, TPopulate>` models the projected/populated read result.
330
+ Do not type populated hook data as `BlogPost[]`: plain `BlogPost` keeps reference fields as `FimoReference` objects, while `BlogPostResult<TFields, TPopulate>` models the projected/populated read result.
318
331
 
319
- `BlogPostPopulate` is the generated input type for allowed populate values. It comes from the schema's relation fields, so a post schema with `author` and `contributors` only accepts those names in `populate`. Most app code never imports it directly; it is mainly used by `BlogPostQuery` and hook generics.
332
+ `BlogPostPopulate` is the generated input type for allowed populate values. It comes from the schema's reference fields, so a post schema with `author` and `contributors` only accepts those names in `populate`. Most app code never imports it directly; it is mainly used by `BlogPostQuery` and hook generics.
320
333
 
321
- Populate is depth-one. It only works for relation fields; trying to populate a scalar field returns 400. To-many relation order is preserved.
334
+ Populate is depth-one. It only works for reference fields; trying to populate a scalar field returns 400. To-many reference order is preserved.
322
335
 
323
336
  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.
324
337
 
@@ -335,7 +348,7 @@ The entry objects a hook returns are **not** plain JS values. The generated clie
335
348
  | `number` | `number` | plain — not wrapped |
336
349
  | `json` | `Record<string, any>` | plain — not wrapped |
337
350
  | `date` | `any` (raw ISO date string) | not wrapped (`FimoDate` is not emitted — see below) |
338
- | `relation` | `FimoReference` / `FimoReference[]` | reference object(s) with target `documentId`; use `populate` when embedded target rows are needed |
351
+ | `reference` | `FimoReference` / `FimoReference[]` | reference object(s) with target `documentId`; use `populate` when embedded target rows are needed |
339
352
 
340
353
  Rules of thumb:
341
354
 
@@ -9,8 +9,8 @@ This file covers the **CLI workflow** for content. Use it when the user wants to
9
9
  ## Flow
10
10
 
11
11
  1. Write `src/schemas/{Uid}.json` — PascalCase filename (e.g. `BlogPost.json`). For the JSON shape and field types, load `fimo/references/content.md`.
12
- 2. Register the schema with `fimo schemas push {Uid}`.
13
- 3. Pre-build generates `src/schemas/{Uid}.types.ts` and `src/schemas/{Uid}.ts` — type definitions plus typed React Query hooks. App code imports from `@/schemas/{Uid}`, not from `.types.ts` directly. See `fimo/references/content.md` for the hook API.
12
+ 2. Run `fimo schemas push {Uid}`. It generates `src/schemas/{Uid}.types.ts` and `src/schemas/{Uid}.ts`, then registers the schema with Fimo. It does not run whole-project validation.
13
+ 3. App code imports the generated client from `@/schemas/{Uid}`, not from `.types.ts` directly. See `fimo/references/content.md` for the hook API.
14
14
  4. Seed entries via `fimo entries create`. Generate cover images first with `fimo generate <model>` if needed (see `media.md` / `assets.md`).
15
15
  5. Write React pages that import from `@/schemas/{Uid}` and render with `fimo/ui` components.
16
16
 
@@ -29,6 +29,8 @@ The command is `fimo schemas` (plural). There is no singular `fimo schema` alias
29
29
 
30
30
  All commands accept `-C, --cwd <dir>` to point at a different project directory.
31
31
 
32
+ Schema transitions are automatic. Adding fields and changing labels preserve content. Removing a field removes only that field's stored values. One-to-many reference changes wrap existing references in arrays. Field type changes, scalar/reference changes, reference target changes, and many-to-one changes clear only the changed field. Collection-to-singleton removes that type's entries and cleans inbound references; singleton-to-collection preserves its row. There are no reset or data-loss flags: changing the schema is the migration request. Run `fimo validate` after pushing to verify the regenerated client and whole project.
33
+
32
34
  ## Entries CLI commands
33
35
 
34
36
  Entries are NOT JSON-file-driven. Create/update them via CLI.
@@ -58,20 +60,20 @@ fimo entries list BlogPost --fields slug,title,author --populate author
58
60
  ```
59
61
 
60
62
  Use the canonical read query flags: `--search`, `--sort`, `--where`, `--limit`, `--offset`, `--fields`, and `--populate`.
61
- `--where` is a JSON object keyed by schema field name. Simple values mean equality, so booleans, strings, and numbers should stay typed inside the JSON (`{"featured":true}`, `{"category":"react"}`, `{"rank":{"$gte":3}}`). Relation field values are reference objects, but relation filters use the target `documentId` string, for example `{"author":"750e..."}`; for to-many relations that means "contains this target document". Public Fimo-owned metadata lives under `__fimo`; currently `__fimo.contentType` is filterable. Operator objects support `$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte`, `$in`, `$nin`, `$contains`, `$startsWith`, and `$null`, subject to the field type. For to-many relations, `$eq` and `$contains` mean membership, `$in` means any listed document is present, `$ne` and `$nin` mean none are present, and `$null: true` means empty or missing. Relation fields cannot be sorted. Use `--sort field` for ascending and `--sort -field` for descending on scalar fields.
63
+ `--where` is a JSON object keyed by schema field name. Simple values mean equality, so booleans, strings, and numbers should stay typed inside the JSON (`{"featured":true}`, `{"category":"react"}`, `{"rank":{"$gte":3}}`). Reference field values are reference objects, but reference filters use the target `documentId` string, for example `{"author":"750e..."}`; for to-many references that means "contains this target document". Public Fimo-owned metadata lives under `__fimo`; currently `__fimo.contentType` is filterable. Operator objects support `$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte`, `$in`, `$nin`, `$contains`, `$startsWith`, and `$null`, subject to the field type. For to-many references, `$eq` and `$contains` mean membership, `$in` means any listed document is present, `$ne` and `$nin` mean none are present, and `$null: true` means empty or missing. Reference fields cannot be sorted. Use `--sort field` for ascending and `--sort -field` for descending on scalar fields.
62
64
  `--fields` is a comma-separated projection for list payloads. The API always keeps `id`; pass only the fields your view needs, such as `slug,title,excerpt,coverImage`.
63
65
 
64
- `--populate` is a comma-separated list of relation fields to embed. Without `--populate`, relation fields return reference objects like `{ "documentId": "..." }`. With `--populate author,contributors`, those fields return the target entry objects for the requested locale. Only relation fields can be populated; asking to populate a scalar field returns 400.
66
+ `--populate` is a comma-separated list of reference fields to embed. Without `--populate`, reference fields return reference objects like `{ "documentId": "..." }`. With `--populate author,contributors`, those fields return the target entry objects for the requested locale. Only reference fields can be populated; asking to populate a scalar field returns 400. Populated targets missing in the requested locale resolve to null by default; pass `localeFallbacks=true` (query param, or the `localeFallbacks` option in generated hooks) to fall back to the best available locale variant, marked by its `locale` field. Limits: a to-many field accepts at most 200 references per write; one populate request resolves at most 500 distinct target documents per field and returns 400 past that.
65
67
 
66
68
  In generated React code, the equivalent is `populate: ['author', 'contributors'] as const` in the schema hook/query params. Let hooks infer the result when possible; if a named TypeScript shape is needed, use the generated `BlogPostResult<TFields, TPopulate>` type documented in `fimo/references/content.md`. Writes use the separate generated `BlogPostInput` type.
67
69
 
68
- For relation schema JSON and entry value shape, load `fimo/references/content.md` § Relation fields. The short version:
70
+ For reference schema JSON and entry value shape, load `fimo/references/content.md` § Reference fields. The short version:
69
71
 
70
72
  ```json
71
73
  {
72
74
  "fields": {
73
- "author": { "type": "relation", "target": "Author" },
74
- "contributors": { "type": "relation", "target": "Author", "many": true }
75
+ "author": { "type": "reference", "target": "Author" },
76
+ "contributors": { "type": "reference", "target": "Author", "many": true }
75
77
  }
76
78
  }
77
79
  ```
@@ -82,7 +84,7 @@ Entry bodies store reference objects with target `documentId` values:
82
84
  fimo entries create BlogPost --body '{"data":{"slug":"hello","title":"Hello","author":{"documentId":"750e8400-e29b-41d4-a716-446655440000"},"contributors":[]}}'
83
85
  ```
84
86
 
85
- Do not add `localized` or field-level `i18n` to relation fields. The relation value belongs to the entry variant that stores it.
87
+ Do not add `localized` or field-level `i18n` to reference fields. The reference value belongs to the entry variant that stores it.
86
88
 
87
89
  ## Localized and shared content
88
90
 
@@ -90,7 +92,7 @@ Schemas are localized by default. Omit `--locale` for default-locale entries. Pa
90
92
 
91
93
  When creating a localized variant of an existing entry, pass the source entry's `documentId` with `--document-id`. Keep projects single-locale unless the user asks for multilingual or localized output; when multiple locales are configured, create and update every locale explicitly.
92
94
 
93
- For content shared across locales, define `"i18n": { "enabled": false }` at the schema root and create each entry once. Do not pass `--document-id` to manufacture locale variants. Shared entry responses omit `locale`; a read locale is only used to resolve populated relations that target localized schemas. No other `i18n` options and no field-level localization settings are supported.
95
+ For content shared across locales, define `"i18n": { "enabled": false }` at the schema root and create each entry once. Do not pass `--document-id` to manufacture locale variants. Shared entry responses omit `locale`; a read locale is only used to resolve populated references that target localized schemas. No other `i18n` options and no field-level localization settings are supported.
94
96
 
95
97
  Use `"isSingleton": true` for a type with exactly one logical document. Generated singleton clients expose `get`/`update` and their React Query hooks, not collection create/remove APIs.
96
98
 
@@ -107,7 +109,7 @@ Use `"isSingleton": true` for a type with exactly one logical document. Generate
107
109
 
108
110
  ## When to run sync manually
109
111
 
110
- The `fimo/vite` plugin auto-regenerates `src/schemas/*.ts` on `vite dev` / `vite build`. If you need to regen outside vite (e.g. CI, scripting), use:
112
+ `fimo schemas push`, `fimo validate`, and the `fimo/vite` plugin all reuse the same schema generator. `schemas push` does not run route, label, agent, form, or project-wide checks. For a full local validation and codegen pass without updating the remote schema, use:
111
113
 
112
114
  ```bash
113
115
  fimo validate # runs all phases: codegen, lint, route validation, label checks
@@ -97,7 +97,7 @@ node <this-skill>/scripts/write-migration-report.mjs <project-root>
97
97
 
98
98
  - `references/workflow.md` - full decision tree and phase order.
99
99
  - `references/manifest.md` - canonical migration ledger schema.
100
- - `references/content.md` - CMS, entries, relations, and source value rules.
100
+ - `references/content.md` - CMS, entries, references, and source value rules.
101
101
  - `references/media.md` - media inventory, upload, dedupe, and replacement rules.
102
102
  - `references/forms.md` - form classification and Fimo replacement rules.
103
103
  - `references/import-contract.md` - exact import obligations for media, entries,
@@ -73,20 +73,20 @@ For every content type:
73
73
  Do not wire a page to remote Fimo content until the matching schema exists and
74
74
  the page has a seed/empty state for local development.
75
75
 
76
- ## Relations
76
+ ## References
77
77
 
78
78
  Create references only when:
79
79
 
80
- - exports contain stable relation IDs
80
+ - exports contain stable reference IDs
81
81
  - repeated values clearly match another exported collection
82
82
  - the user confirms the relationship
83
83
 
84
84
  Use a two-pass import for references:
85
85
 
86
86
  1. import all referenced entries with source IDs and slugs
87
- 2. resolve relation fields to target IDs/slugs
87
+ 2. resolve reference fields to target IDs/slugs
88
88
 
89
- If relation confidence is low, keep a string/tag field and record an unresolved
89
+ If reference confidence is low, keep a string/tag field and record an unresolved
90
90
  item.
91
91
 
92
92
  ## Skips
@@ -50,7 +50,7 @@ Import requirements:
50
50
  - entries are created or updated in Fimo, not only written as seed JSON
51
51
  - source count, generated count, imported count, skipped count, and unresolved
52
52
  count are recorded per content type
53
- - relations are resolved in a second pass only when stable IDs or confirmed
53
+ - references are resolved in a second pass only when stable IDs or confirmed
54
54
  mappings exist
55
55
 
56
56
  Acceptance:
@@ -80,7 +80,7 @@ than first-party CSV/XML/JSON exports.
80
80
  "coverImage": { "type": "media", "source": "Main Image" },
81
81
  "publishedAt": { "type": "datetime", "source": "Published On" }
82
82
  },
83
- "relations": []
83
+ "references": []
84
84
  }
85
85
  ```
86
86
 
@@ -37,7 +37,7 @@ Use these notes to decide what is source of truth and what must be rebuilt.
37
37
  - Wix/Velo app logic, dynamic pages, search, forms, ecommerce, auth, and member
38
38
  areas need explicit replacement plans.
39
39
  - Large collections may be split across several files. Preserve stable IDs if
40
- present so relations can be reconstructed later.
40
+ present so references can be reconstructed later.
41
41
 
42
42
  ## Carrd and static exports
43
43
 
@@ -85,7 +85,7 @@ Before writing page code:
85
85
  2. Map each CMS export or inferred collection to a Fimo schema or unresolved item.
86
86
  3. Preserve stable source IDs, slugs, dates, SEO, authors, categories, tags,
87
87
  locales, and publish status.
88
- 4. Decide which relations are real. Do not invent references from vague text.
88
+ 4. Decide which references are real. Do not invent references from vague text.
89
89
  5. Build seed/import entries with source IDs and media references preserved.
90
90
  6. Record source count and planned import count for each content type.
91
91
 
@@ -222,7 +222,7 @@ function normalizeSourceContentTypes(contentTypes) {
222
222
  plannedCount: numberOrNull(type.plannedCount),
223
223
  importedCount: numberOrNull(type.importedCount),
224
224
  fields: type.fields ?? {},
225
- relations: type.relations ?? [],
225
+ references: type.references ?? [],
226
226
  }));
227
227
  }
228
228
 
@@ -240,7 +240,7 @@ function normalizeUrlCollections(collections) {
240
240
  plannedCount: asArray(collection.sourceRoutes).length,
241
241
  importedCount: 0,
242
242
  fields: Object.fromEntries(asArray(collection.fields).map((field) => [field, { source: field }])),
243
- relations: [],
243
+ references: [],
244
244
  notes: ['Inferred from crawled route patterns. Review before import.'],
245
245
  }));
246
246
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "bundled": true,
3
3
  "bundler": "esbuild",
4
- "bundledAt": "2026-07-10T13:57:48.782Z",
4
+ "bundledAt": "2026-07-11T15:31:56.826Z",
5
5
  "cliVersion": "0.3.6",
6
6
  "external": [
7
7
  "oxc-parser",
package/dist/cli/index.js CHANGED
@@ -42008,7 +42008,7 @@ var init_common = __esm({
42008
42008
  "boolean",
42009
42009
  "date",
42010
42010
  "json",
42011
- "relation"
42011
+ "reference"
42012
42012
  ]);
42013
42013
  RESERVED_CONTENT_FIELD_NAMES = [
42014
42014
  "id",
@@ -42152,6 +42152,36 @@ var init_realtime = __esm({
42152
42152
  }
42153
42153
  });
42154
42154
 
42155
+ // ../../packages/api-contracts/dist/esm/types/references.js
42156
+ function referenceDocumentId(value) {
42157
+ if (typeof value === "string") {
42158
+ return value;
42159
+ }
42160
+ if (value && typeof value === "object" && "documentId" in value) {
42161
+ const documentId = value.documentId;
42162
+ return typeof documentId === "string" ? documentId : null;
42163
+ }
42164
+ return null;
42165
+ }
42166
+ function toReferenceValue(value) {
42167
+ const documentId = referenceDocumentId(value);
42168
+ return documentId ? {
42169
+ documentId
42170
+ } : null;
42171
+ }
42172
+ var ReferenceValueSchema;
42173
+ var init_references = __esm({
42174
+ "../../packages/api-contracts/dist/esm/types/references.js"() {
42175
+ "use strict";
42176
+ init_zod();
42177
+ __name(referenceDocumentId, "referenceDocumentId");
42178
+ __name(toReferenceValue, "toReferenceValue");
42179
+ ReferenceValueSchema = external_exports.preprocess((value) => toReferenceValue(value) ?? value, external_exports.object({
42180
+ documentId: external_exports.uuid()
42181
+ }));
42182
+ }
42183
+ });
42184
+
42155
42185
  // ../../packages/api-contracts/dist/esm/types/audit-log.js
42156
42186
  var AUDIT_ACTIONS, AuditActionSchema, AUDIT_OUTCOMES, AuditOutcomeSchema, AUDIT_ACTOR_KINDS, AuditActorKindSchema;
42157
42187
  var init_audit_log = __esm({
@@ -45513,6 +45543,7 @@ var init_esm = __esm({
45513
45543
  init_attachments();
45514
45544
  init_env2();
45515
45545
  init_realtime();
45546
+ init_references();
45516
45547
  init_audit_log();
45517
45548
  init_project_dto();
45518
45549
  init_project_env_dto();
@@ -47120,10 +47151,10 @@ function normalizeFields(fields) {
47120
47151
  })
47121
47152
  );
47122
47153
  }
47123
- function relationImportsFor(schema, fields) {
47154
+ function referenceImportsFor(schema, fields) {
47124
47155
  const imports = /* @__PURE__ */ new Map();
47125
47156
  for (const field of Object.values(fields)) {
47126
- if (field.type !== "relation" || !field.target || field.target === schema.uid) {
47157
+ if (field.type !== "reference" || !field.target || field.target === schema.uid) {
47127
47158
  continue;
47128
47159
  }
47129
47160
  imports.set(field.target, { uid: field.target, pascal: pascalCase(field.target) });
@@ -47137,9 +47168,9 @@ function schemaToModel(schema, localizedByUid) {
47137
47168
  pascal: pascalCase(schema.uid),
47138
47169
  isSingleton: schema.isSingleton ?? false,
47139
47170
  localized: schema.i18n?.enabled !== false,
47140
- localizedRelationFields: Object.entries(fields).filter(([, field]) => field.type === "relation" && field.target && localizedByUid.get(field.target) !== false).map(([fieldName]) => fieldName),
47171
+ localizedReferenceFields: Object.entries(fields).filter(([, field]) => field.type === "reference" && field.target && localizedByUid.get(field.target) !== false).map(([fieldName]) => fieldName),
47141
47172
  fields,
47142
- relationImports: relationImportsFor(schema, fields)
47173
+ referenceImports: referenceImportsFor(schema, fields)
47143
47174
  };
47144
47175
  }
47145
47176
  function syncSchemas(options = {}) {
@@ -47246,7 +47277,7 @@ var init_sync_schemas = __esm({
47246
47277
  __name(getSchemaFiles, "getSchemaFiles");
47247
47278
  __name(readSchema, "readSchema");
47248
47279
  __name(normalizeFields, "normalizeFields");
47249
- __name(relationImportsFor, "relationImportsFor");
47280
+ __name(referenceImportsFor, "referenceImportsFor");
47250
47281
  __name(schemaToModel, "schemaToModel");
47251
47282
  __name(syncSchemas, "syncSchemas");
47252
47283
  }
@@ -103088,34 +103119,34 @@ function validateSchemaFile(absolutePath, file2, schemaUids = /* @__PURE__ */ ne
103088
103119
  file: file2
103089
103120
  });
103090
103121
  }
103091
- if (type === "relation") {
103122
+ if (type === "reference") {
103092
103123
  const { target, many } = definition;
103093
103124
  if (typeof target !== "string" || !CONTENT_TYPE_NAME_PATTERN.test(target)) {
103094
103125
  errors2.push({
103095
- rule: "schemas/relation-target",
103096
- message: `Relation field "${fieldName}" must declare a valid "target" content type.`,
103126
+ rule: "schemas/reference-target",
103127
+ message: `Reference field "${fieldName}" must declare a valid "target" content type.`,
103097
103128
  file: file2
103098
103129
  });
103099
103130
  } else if (schemaUids.size > 0 && !schemaUids.has(target)) {
103100
103131
  errors2.push({
103101
- rule: "schemas/relation-target",
103102
- message: `Relation field "${fieldName}" targets unknown schema "${target}".`,
103132
+ rule: "schemas/reference-target",
103133
+ message: `Reference field "${fieldName}" targets unknown schema "${target}".`,
103103
103134
  file: file2
103104
103135
  });
103105
103136
  }
103106
103137
  if (many !== void 0 && typeof many !== "boolean") {
103107
103138
  errors2.push({
103108
- rule: "schemas/relation-many",
103109
- message: `Relation field "${fieldName}" must use a boolean "many" value.`,
103139
+ rule: "schemas/reference-many",
103140
+ message: `Reference field "${fieldName}" must use a boolean "many" value.`,
103110
103141
  file: file2
103111
103142
  });
103112
103143
  }
103113
103144
  } else {
103114
- for (const relationOnlyKey of ["target", "many"]) {
103115
- if (relationOnlyKey in definition) {
103145
+ for (const referenceOnlyKey of ["target", "many"]) {
103146
+ if (referenceOnlyKey in definition) {
103116
103147
  errors2.push({
103117
- rule: "schemas/relation-shape",
103118
- message: `Field "${fieldName}": "${relationOnlyKey}" is only supported on relation fields.`,
103148
+ rule: "schemas/reference-shape",
103149
+ message: `Field "${fieldName}": "${referenceOnlyKey}" is only supported on reference fields.`,
103119
103150
  file: file2
103120
103151
  });
103121
103152
  }
@@ -104893,8 +104924,8 @@ function fieldSignature(field) {
104893
104924
  const type = typeof f.type === "string" ? f.type : "unknown";
104894
104925
  const required2 = f.required === true;
104895
104926
  const parts = [];
104896
- if (type === "relation" && typeof f.target === "string") {
104897
- parts.push(`relation(${f.target})`);
104927
+ if (type === "reference" && typeof f.target === "string") {
104928
+ parts.push(`reference(${f.target})`);
104898
104929
  } else if (type === "enum" && Array.isArray(f.options)) {
104899
104930
  parts.push(`enum(${f.options.map(String).join("|")})`);
104900
104931
  } else if (type === "number") {
@@ -109678,6 +109709,7 @@ __name(deleteCommand, "deleteCommand");
109678
109709
 
109679
109710
  // src/cli/commands/schema.ts
109680
109711
  init_dist();
109712
+ init_sync_schemas();
109681
109713
  import fs42 from "node:fs";
109682
109714
  import path46 from "node:path";
109683
109715
  init_api();
@@ -109732,8 +109764,7 @@ async function schemaPushCommand(typeName, options, cmd) {
109732
109764
  }
109733
109765
  })
109734
109766
  );
109735
- const results = [];
109736
- for (const file2 of files) {
109767
+ const schemas3 = files.map((file2) => {
109737
109768
  const filePath = path46.join(schemasDir, file2);
109738
109769
  if (!fs42.existsSync(filePath)) {
109739
109770
  throw new Error(`Schema file not found: ${filePath}`);
@@ -109742,7 +109773,11 @@ async function schemaPushCommand(typeName, options, cmd) {
109742
109773
  if (issues.length > 0) {
109743
109774
  throw new Error(issues.map((issue2) => `${issue2.file}: ${issue2.message}`).join("\n"));
109744
109775
  }
109745
- const schema = JSON.parse(fs42.readFileSync(filePath, "utf-8"));
109776
+ return JSON.parse(fs42.readFileSync(filePath, "utf-8"));
109777
+ });
109778
+ syncSchemas({ cwd, verbose: false });
109779
+ const results = [];
109780
+ for (const schema of schemas3) {
109746
109781
  const contentType = toApiShape(schema);
109747
109782
  try {
109748
109783
  const result = await api.createSchema(ctx.projectId, contentType);
@@ -110192,7 +110227,7 @@ schemas2.command("get").description("Get a schema by type name").argument("<type
110192
110227
  schemas2.command("push").description("Push schema JSON files to the API (create or update)").argument("[typeName]", "Schema name to push (omit to push all)").option("-C, --cwd <dir>", "Project directory").action(schemaPushCommand);
110193
110228
  schemas2.command("delete").description("Delete a schema by type name").argument("<typeName>", "Content type name").option("-C, --cwd <dir>", "Project directory").action(schemaDeleteCommand);
110194
110229
  var entries = program.command("entries").description("Manage content entries");
110195
- entries.command("list").description("List entries of a content type").argument("<typeName>", "Content type name").option("-C, --cwd <dir>", "Project directory").option("--limit <n>", "Max entries to return").option("--offset <n>", "Offset for pagination").option("--search <query>", "Search query").option("--where <json>", `JSON filter object, e.g. '{"featured":true}'`).option("--sort <field>", "Sort field, prefix with - for descending").option("--fields <fields>", "Comma-separated fields to return, e.g. slug,title,excerpt").option("--populate <fields>", "Comma-separated relation fields to populate, e.g. author,contributors").option("--locale <locale>", "Locale to list (default en)").option("--sort-by <field>", "Sort field").option("--sort-direction <dir>", "Sort direction (asc|desc)").action(entriesListCommand);
110230
+ entries.command("list").description("List entries of a content type").argument("<typeName>", "Content type name").option("-C, --cwd <dir>", "Project directory").option("--limit <n>", "Max entries to return").option("--offset <n>", "Offset for pagination").option("--search <query>", "Search query").option("--where <json>", `JSON filter object, e.g. '{"featured":true}'`).option("--sort <field>", "Sort field, prefix with - for descending").option("--fields <fields>", "Comma-separated fields to return, e.g. slug,title,excerpt").option("--populate <fields>", "Comma-separated reference fields to populate, e.g. author,contributors").option("--locale <locale>", "Locale to list (default en)").option("--sort-by <field>", "Sort field").option("--sort-direction <dir>", "Sort direction (asc|desc)").action(entriesListCommand);
110196
110231
  entries.command("get").description("Get a single entry").argument("<typeName>", "Content type name").argument("<id>", "Entry ID").option("-C, --cwd <dir>", "Project directory").option("--locale <locale>", "Locale to read (default en)").action(entriesGetCommand);
110197
110232
  entries.command("create").description("Create a new entry").argument("<typeName>", "Content type name").option("-C, --cwd <dir>", "Project directory").requiredOption("--body <json>", "Entry JSON body (slug + data)").option("--locale <locale>", "Entry locale (default en)").option("--document-id <id>", "Document ID shared by localized variants").action(entriesCreateCommand);
110198
110233
  entries.command("update").description("Update an existing entry").argument("<typeName>", "Content type name").argument("<id>", "Entry ID").option("-C, --cwd <dir>", "Project directory").requiredOption("--body <json>", "Updated entry JSON body").option("--locale <locale>", "Entry locale").option("--document-id <id>", "Document ID shared by localized variants").action(entriesUpdateCommand);
@@ -7,5 +7,5 @@ export { Boolean } from './components/Boolean.js';
7
7
  export { RichText } from './components/RichText.js';
8
8
  export { useLabels, useTranslations } from './translations.js';
9
9
  export { FimoString, FimoDate, FimoMedia, FimoBoolean, FimoRichText, type FimoReference, type FimoSource, type FimoPart, type FimoRichTextContent, type FimoRichTextNodeProps, type FimoRichTextMarkProps, type FimoRichTextComponents, FIMO_SOURCE, FIMO_PARTS, isFimoString, isFimoDate, hasFimoSource, getFimoSource, getFimoParts, fimo, useFimoLiveValue, } from './lib/index.js';
10
- export type { BoolOps, DateOps, Field, Fields, FilterFor, NullOnlyOps, NumberOps, Populate, Populated, PopulatedResult, Projected, ProjectedWithPopulate, Query, RelationField, RelationMap, ScalarOps, Sort, SortField, StringOps, WithRelations, Where, } from './lib/index.js';
10
+ export type { BoolOps, DateOps, Field, Fields, FilterFor, NullOnlyOps, NumberOps, Populate, Populated, PopulatedResult, Projected, ProjectedWithPopulate, Query, ReferenceField, ReferenceMap, ScalarOps, Sort, SortField, StringOps, WithReferences, Where, } from './lib/index.js';
11
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/runtime/primitives/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAGpD,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAG/D,OAAO,EACL,UAAU,EACV,QAAQ,EACR,SAAS,EACT,WAAW,EACX,YAAY,EACZ,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,WAAW,EACX,UAAU,EACV,YAAY,EACZ,UAAU,EACV,aAAa,EACb,aAAa,EACb,YAAY,EACZ,IAAI,EACJ,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AAExB,YAAY,EACV,OAAO,EACP,OAAO,EACP,KAAK,EACL,MAAM,EACN,SAAS,EACT,WAAW,EACX,SAAS,EACT,QAAQ,EACR,SAAS,EACT,eAAe,EACf,SAAS,EACT,qBAAqB,EACrB,KAAK,EACL,aAAa,EACb,WAAW,EACX,SAAS,EACT,IAAI,EACJ,SAAS,EACT,SAAS,EACT,aAAa,EACb,KAAK,GACN,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/runtime/primitives/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAGpD,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAG/D,OAAO,EACL,UAAU,EACV,QAAQ,EACR,SAAS,EACT,WAAW,EACX,YAAY,EACZ,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,WAAW,EACX,UAAU,EACV,YAAY,EACZ,UAAU,EACV,aAAa,EACb,aAAa,EACb,YAAY,EACZ,IAAI,EACJ,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AAExB,YAAY,EACV,OAAO,EACP,OAAO,EACP,KAAK,EACL,MAAM,EACN,SAAS,EACT,WAAW,EACX,SAAS,EACT,QAAQ,EACR,SAAS,EACT,eAAe,EACf,SAAS,EACT,qBAAqB,EACrB,KAAK,EACL,cAAc,EACd,YAAY,EACZ,SAAS,EACT,IAAI,EACJ,SAAS,EACT,SAAS,EACT,cAAc,EACd,KAAK,GACN,MAAM,gBAAgB,CAAC"}
@@ -1,5 +1,5 @@
1
1
  export { FimoString, FimoDate, FimoMedia, FimoBoolean, FimoRichText, type FimoReference, type FimoSource, type FimoPart, type FimoRichTextContent, type FimoRichTextNodeProps, type FimoRichTextMarkProps, type FimoRichTextComponents, FIMO_SOURCE, FIMO_PARTS, isFimoString, isFimoDate, hasFimoSource, getFimoSource, getFimoParts, } from './primitives.js';
2
2
  export { fimo } from './template.js';
3
3
  export { useFimoLiveValue } from './useFimoLiveValue.js';
4
- export type { BoolOps, DateOps, Field, Fields, FilterFor, NullOnlyOps, NumberOps, Populate, Populated, PopulatedResult, ProjectedWithPopulate, Projected, Query, RelationField, RelationMap, ScalarOps, Sort, SortField, StringOps, Where, WithRelations, } from './query.js';
4
+ export type { BoolOps, DateOps, Field, Fields, FilterFor, NullOnlyOps, NumberOps, Populate, Populated, PopulatedResult, ProjectedWithPopulate, Projected, Query, ReferenceField, ReferenceMap, ScalarOps, Sort, SortField, StringOps, Where, WithReferences, } from './query.js';
5
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/runtime/primitives/lib/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,UAAU,EACV,QAAQ,EACR,SAAS,EACT,WAAW,EACX,YAAY,EACZ,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,WAAW,EACX,UAAU,EACV,YAAY,EACZ,UAAU,EACV,aAAa,EACb,aAAa,EACb,YAAY,GACb,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAErC,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzD,YAAY,EACV,OAAO,EACP,OAAO,EACP,KAAK,EACL,MAAM,EACN,SAAS,EACT,WAAW,EACX,SAAS,EACT,QAAQ,EACR,SAAS,EACT,eAAe,EACf,qBAAqB,EACrB,SAAS,EACT,KAAK,EACL,aAAa,EACb,WAAW,EACX,SAAS,EACT,IAAI,EACJ,SAAS,EACT,SAAS,EACT,KAAK,EACL,aAAa,GACd,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/runtime/primitives/lib/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,UAAU,EACV,QAAQ,EACR,SAAS,EACT,WAAW,EACX,YAAY,EACZ,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,WAAW,EACX,UAAU,EACV,YAAY,EACZ,UAAU,EACV,aAAa,EACb,aAAa,EACb,YAAY,GACb,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAErC,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzD,YAAY,EACV,OAAO,EACP,OAAO,EACP,KAAK,EACL,MAAM,EACN,SAAS,EACT,WAAW,EACX,SAAS,EACT,QAAQ,EACR,SAAS,EACT,eAAe,EACf,qBAAqB,EACrB,SAAS,EACT,KAAK,EACL,cAAc,EACd,YAAY,EACZ,SAAS,EACT,IAAI,EACJ,SAAS,EACT,SAAS,EACT,KAAK,EACL,cAAc,GACf,MAAM,YAAY,CAAC"}
@@ -25,21 +25,22 @@ export type BoolOps = ScalarOps<boolean>;
25
25
  export type NullOnlyOps = {
26
26
  $null?: boolean;
27
27
  };
28
- export type RelationOps = {
28
+ export type ReferenceOps = {
29
29
  $eq?: string | null;
30
30
  $ne?: string | null;
31
31
  $in?: string[];
32
32
  $nin?: string[];
33
- $contains?: string;
34
33
  $null?: boolean;
35
34
  };
36
- export type ManyRelationOps = {
35
+ export type ManyReferenceOps = {
37
36
  $eq?: string;
38
37
  $ne?: string;
38
+ $in?: string[];
39
+ $nin?: string[];
39
40
  $contains?: string;
40
41
  $null?: boolean;
41
42
  };
42
- export type FilterFor<T> = [T] extends [FimoString | string] ? string | StringOps : [T] extends [FimoBoolean | boolean] ? boolean | BoolOps : [T] extends [FimoDate | Date] ? string | DateOps : [T] extends [number] ? number | NumberOps : [T] extends [FimoReference | null] ? string | null | RelationOps : [T] extends [FimoReference[]] ? string | ManyRelationOps : [T] extends [FimoMedia | FimoMedia[]] ? NullOnlyOps : [T] extends [FimoRichText] ? string | StringOps : NullOnlyOps;
43
+ export type FilterFor<T> = [T] extends [FimoString | string] ? string | StringOps : [T] extends [FimoBoolean | boolean] ? boolean | BoolOps : [T] extends [FimoDate | Date] ? string | DateOps : [T] extends [number] ? number | NumberOps : [T] extends [FimoReference | null] ? string | null | ReferenceOps : [T] extends [FimoReference[]] ? string | ManyReferenceOps : [T] extends [FimoMedia | FimoMedia[]] ? NullOnlyOps : [T] extends [FimoRichText] ? string | StringOps : NullOnlyOps;
43
44
  type BaseTopLevelWhere = {
44
45
  id?: string | StringOps;
45
46
  documentId?: string | StringOps;
@@ -68,18 +69,18 @@ type ListItem<TValue> = TValue extends readonly (infer K)[] ? K : TValue;
68
69
  export type Projected<T extends {
69
70
  id: string;
70
71
  }, TFields extends Fields<T> | undefined> = [TFields] extends [undefined] ? T : Pick<T, Extract<'id' | SelectedFields<TFields>, keyof T>>;
71
- export declare const FIMO_RELATIONS: unique symbol;
72
- export interface WithRelations<TRelations> {
73
- readonly [FIMO_RELATIONS]?: TRelations;
72
+ export declare const FIMO_REFERENCES: unique symbol;
73
+ export interface WithReferences<TReferences> {
74
+ readonly [FIMO_REFERENCES]?: TReferences;
74
75
  }
75
- export type RelationMap<TEntry> = TEntry extends WithRelations<infer TRelations> ? TRelations : never;
76
- export type RelationField<TEntry> = Extract<keyof RelationMap<TEntry>, string>;
77
- export type Populate<TEntry> = RelationField<TEntry> | readonly RelationField<TEntry>[];
78
- type PopulatedKeys<TEntry, TPopulate> = Extract<ListItem<TPopulate>, keyof RelationMap<TEntry> & keyof TEntry>;
76
+ export type ReferenceMap<TEntry> = TEntry extends WithReferences<infer TReferences> ? TReferences : never;
77
+ export type ReferenceField<TEntry> = Extract<keyof ReferenceMap<TEntry>, string>;
78
+ export type Populate<TEntry> = ReferenceField<TEntry> | readonly ReferenceField<TEntry>[];
79
+ type PopulatedKeys<TEntry, TPopulate> = Extract<ListItem<TPopulate>, keyof ReferenceMap<TEntry> & keyof TEntry>;
79
80
  export type Populated<TEntry extends {
80
81
  id: string;
81
82
  }, TPopulate extends Populate<TEntry>> = Pick<TEntry, 'id'> & Omit<TEntry, PopulatedKeys<TEntry, TPopulate>> & {
82
- [K in PopulatedKeys<TEntry, TPopulate>]: RelationMap<TEntry>[K];
83
+ [K in PopulatedKeys<TEntry, TPopulate>]: ReferenceMap<TEntry>[K];
83
84
  };
84
85
  export type PopulatedResult<TEntry extends {
85
86
  id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../../src/runtime/primitives/lib/query.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../../src/runtime/primitives/lib/query.ts"],"names":[],"mappings":"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"}
@@ -16,11 +16,11 @@ describe('fimo/ui public type surface', () => {
16
16
  const sharedWhere = { locale: 'en' };
17
17
  expectTypeOf(sharedWhere).toEqualTypeOf();
18
18
  });
19
- it('supports the relation populate pattern emitted into generated schemas', () => {
19
+ it('supports the reference populate pattern emitted into generated schemas', () => {
20
20
  const query = {
21
21
  where: {
22
- sponsor: { $eq: '750e8400-e29b-41d4-a716-446655440000' },
23
- relatedPackages: { $contains: '850e8400-e29b-41d4-a716-446655440000' },
22
+ sponsor: { $in: ['750e8400-e29b-41d4-a716-446655440000'] },
23
+ relatedPackages: { $nin: ['850e8400-e29b-41d4-a716-446655440000'] },
24
24
  },
25
25
  fields: ['name'],
26
26
  populate: ['sponsor'],
@@ -32,11 +32,14 @@ describe('fimo/ui public type surface', () => {
32
32
  expectTypeOf().toEqualTypeOf();
33
33
  expectTypeOf().toEqualTypeOf();
34
34
  expectTypeOf().toEqualTypeOf();
35
- // @ts-expect-error generated relation-aware queries only allow relation fields in populate.
35
+ // @ts-expect-error generated reference-aware queries only allow reference fields in populate.
36
36
  const badQuery = { populate: 'name' };
37
37
  expectTypeOf(badQuery).toEqualTypeOf();
38
- // @ts-expect-error relation filters use target documentId strings, not raw reference objects.
38
+ // @ts-expect-error reference filters use target documentId strings, not raw reference objects.
39
39
  const badWhere = { where: { sponsor: { documentId: '750e8400-e29b-41d4-a716-446655440000' } } };
40
40
  expectTypeOf(badWhere).toEqualTypeOf();
41
+ // @ts-expect-error contains is only valid for to-many reference fields.
42
+ const badToOneContains = { where: { sponsor: { $contains: '750e8400-e29b-41d4-a716-446655440000' } } };
43
+ expectTypeOf(badToOneContains).toEqualTypeOf();
41
44
  });
42
45
  });
@@ -48,10 +48,10 @@ function normalizeFields(fields) {
48
48
  ];
49
49
  }));
50
50
  }
51
- function relationImportsFor(schema, fields) {
51
+ function referenceImportsFor(schema, fields) {
52
52
  const imports = new Map();
53
53
  for (const field of Object.values(fields)) {
54
- if (field.type !== 'relation' || !field.target || field.target === schema.uid) {
54
+ if (field.type !== 'reference' || !field.target || field.target === schema.uid) {
55
55
  continue;
56
56
  }
57
57
  imports.set(field.target, { uid: field.target, pascal: pascalCase(field.target) });
@@ -65,11 +65,11 @@ function schemaToModel(schema, localizedByUid) {
65
65
  pascal: pascalCase(schema.uid),
66
66
  isSingleton: schema.isSingleton ?? false,
67
67
  localized: schema.i18n?.enabled !== false,
68
- localizedRelationFields: Object.entries(fields)
69
- .filter(([, field]) => field.type === 'relation' && field.target && localizedByUid.get(field.target) !== false)
68
+ localizedReferenceFields: Object.entries(fields)
69
+ .filter(([, field]) => field.type === 'reference' && field.target && localizedByUid.get(field.target) !== false)
70
70
  .map(([fieldName]) => fieldName),
71
71
  fields,
72
- relationImports: relationImportsFor(schema, fields),
72
+ referenceImports: referenceImportsFor(schema, fields),
73
73
  };
74
74
  }
75
75
  export function syncSchemas(options = {}) {
@@ -15,7 +15,7 @@ function writeSchema(name, schema) {
15
15
  writeFileSync(join(scratch, 'src/schemas', `${name}.json`), JSON.stringify(schema, null, 2));
16
16
  }
17
17
  describe('syncSchemas', () => {
18
- it('generates raw relation references plus typed populated relation helpers', () => {
18
+ it('generates raw references plus typed populated helpers', () => {
19
19
  writeSchema('Author', {
20
20
  uid: 'Author',
21
21
  name: 'Authors',
@@ -28,9 +28,9 @@ describe('syncSchemas', () => {
28
28
  name: 'Posts',
29
29
  fields: {
30
30
  title: { type: 'string' },
31
- author: { type: 'relation', target: 'Author' },
32
- contributors: { type: 'relation', target: 'Author', many: true },
33
- relatedPosts: { type: 'relation', target: 'Post', many: true },
31
+ author: { type: 'reference', target: 'Author' },
32
+ contributors: { type: 'reference', target: 'Author', many: true },
33
+ relatedPosts: { type: 'reference', target: 'Post', many: true },
34
34
  },
35
35
  });
36
36
  expect(syncSchemas({ cwd: scratch, verbose: false })).toEqual({ created: 4, updated: 0, deleted: 0 });
@@ -40,7 +40,7 @@ describe('syncSchemas', () => {
40
40
  expect(post).toContain('import type { Post, PostInput, PostPopulate, PostResult, PostFields, PostQuery } from "./Post.types";');
41
41
  expect(post).toContain('from "./Post.types";');
42
42
  expect(post).not.toContain('import type { Author } from "./Author";');
43
- expect(post).not.toContain('export interface Post extends WithRelations<PostRelations>');
43
+ expect(post).not.toContain('export interface Post extends WithReferences<PostReferences>');
44
44
  expect(post).not.toContain('type FimoReference');
45
45
  expect(post).toContain('Promise<PostResult<undefined, TPopulate>>');
46
46
  expect(post).toContain('TPopulate extends PostPopulate | undefined');
@@ -49,12 +49,12 @@ describe('syncSchemas', () => {
49
49
  expect(post).toContain('export async function create(payload: PostInput): Promise<Post>');
50
50
  expect(post).toContain('return wrapWithSource(data.data);');
51
51
  expect(postTypes).toContain('import type { Author } from "./Author.types";');
52
- expect(postTypes).toContain('export interface Post extends WithRelations<PostRelations>');
52
+ expect(postTypes).toContain('export interface Post extends WithReferences<PostReferences>');
53
53
  expect(postTypes).toContain('type FimoReference');
54
54
  expect(postTypes).toContain('author: FimoReference | null;');
55
55
  expect(postTypes).toContain('contributors: FimoReference[];');
56
56
  expect(postTypes).toContain('relatedPosts: FimoReference[];');
57
- expect(postTypes).toContain('export interface PostRelations');
57
+ expect(postTypes).toContain('export interface PostReferences');
58
58
  expect(postTypes).toContain('export interface PostInput');
59
59
  expect(postTypes).toContain('author?: FimoReference | null;');
60
60
  expect(postTypes).toContain('author: Author | null;');
@@ -80,8 +80,8 @@ describe('syncSchemas', () => {
80
80
  name: 'Settings',
81
81
  i18n: { enabled: false },
82
82
  fields: {
83
- homepage: { type: 'relation', target: 'Homepage' },
84
- category: { type: 'relation', target: 'Category' },
83
+ homepage: { type: 'reference', target: 'Homepage' },
84
+ category: { type: 'reference', target: 'Category' },
85
85
  },
86
86
  });
87
87
  syncSchemas({ cwd: scratch, verbose: false });
@@ -92,7 +92,7 @@ describe('syncSchemas', () => {
92
92
  expect(settingsTypes).not.toContain('locale?: string;');
93
93
  expect(homepageTypes).toContain('locale: string;');
94
94
  expect(settings).toContain('const LOCALIZED_CONTENT = false;');
95
- expect(settings).toContain('const LOCALIZED_RELATION_FIELDS = new Set<string>(["homepage"]);');
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
98
  expect(settings.match(/readLocale\(params, LOCALIZED_CONTENT, useQueryLocale\(params\.locale\)\)/g)?.length).toBe(2);
@@ -169,36 +169,36 @@ export function validateSchemaFile(absolutePath, file, schemaUids = new Set()) {
169
169
  file,
170
170
  });
171
171
  }
172
- if (type === 'relation') {
172
+ if (type === 'reference') {
173
173
  const { target, many } = definition;
174
174
  if (typeof target !== 'string' || !CONTENT_TYPE_NAME_PATTERN.test(target)) {
175
175
  errors.push({
176
- rule: 'schemas/relation-target',
177
- message: `Relation field "${fieldName}" must declare a valid "target" content type.`,
176
+ rule: 'schemas/reference-target',
177
+ message: `Reference field "${fieldName}" must declare a valid "target" content type.`,
178
178
  file,
179
179
  });
180
180
  }
181
181
  else if (schemaUids.size > 0 && !schemaUids.has(target)) {
182
182
  errors.push({
183
- rule: 'schemas/relation-target',
184
- message: `Relation field "${fieldName}" targets unknown schema "${target}".`,
183
+ rule: 'schemas/reference-target',
184
+ message: `Reference field "${fieldName}" targets unknown schema "${target}".`,
185
185
  file,
186
186
  });
187
187
  }
188
188
  if (many !== undefined && typeof many !== 'boolean') {
189
189
  errors.push({
190
- rule: 'schemas/relation-many',
191
- message: `Relation field "${fieldName}" must use a boolean "many" value.`,
190
+ rule: 'schemas/reference-many',
191
+ message: `Reference field "${fieldName}" must use a boolean "many" value.`,
192
192
  file,
193
193
  });
194
194
  }
195
195
  }
196
196
  else {
197
- for (const relationOnlyKey of ['target', 'many']) {
198
- if (relationOnlyKey in definition) {
197
+ for (const referenceOnlyKey of ['target', 'many']) {
198
+ if (referenceOnlyKey in definition) {
199
199
  errors.push({
200
- rule: 'schemas/relation-shape',
201
- message: `Field "${fieldName}": "${relationOnlyKey}" is only supported on relation fields.`,
200
+ rule: 'schemas/reference-shape',
201
+ message: `Field "${fieldName}": "${referenceOnlyKey}" is only supported on reference fields.`,
202
202
  file,
203
203
  });
204
204
  }
@@ -36,20 +36,20 @@ describe('validateSchemas', () => {
36
36
  body: { type: 'richtext' },
37
37
  coverImage: { type: 'media' },
38
38
  featured: { type: 'boolean' },
39
- author: { type: 'relation', target: 'Author' },
39
+ author: { type: 'reference', target: 'Author' },
40
40
  },
41
41
  },
42
42
  });
43
43
  expect(validateSchemas({ cwd })).toEqual({ errors: [], schemasChecked: 2 });
44
44
  });
45
- it('accepts self-referential relation fields', () => {
45
+ it('accepts self-referential reference fields', () => {
46
46
  const cwd = writeProject({
47
47
  'Item.json': {
48
48
  uid: 'Item',
49
49
  name: 'Items',
50
50
  fields: {
51
51
  title: { type: 'string' },
52
- parent: { type: 'relation', target: 'Item' },
52
+ parent: { type: 'reference', target: 'Item' },
53
53
  },
54
54
  },
55
55
  });
@@ -141,32 +141,32 @@ describe('validateSchemas', () => {
141
141
  expect(errors[0].message).toContain('"decimal"');
142
142
  expect(errors[0].message).toContain('string, richtext, media');
143
143
  });
144
- it('rejects relation fields without a valid target', () => {
144
+ it('rejects reference fields without a valid target', () => {
145
145
  const cwd = writeProject({
146
146
  'Post.json': {
147
147
  uid: 'Post',
148
148
  name: 'Posts',
149
- fields: { author: { type: 'relation' } },
149
+ fields: { author: { type: 'reference' } },
150
150
  },
151
151
  });
152
152
  const { errors } = validateSchemas({ cwd });
153
153
  expect(errors).toHaveLength(1);
154
- expect(errors[0]).toMatchObject({ rule: 'schemas/relation-target' });
154
+ expect(errors[0]).toMatchObject({ rule: 'schemas/reference-target' });
155
155
  });
156
- it('rejects relation fields targeting a missing local schema', () => {
156
+ it('rejects reference fields targeting a missing local schema', () => {
157
157
  const cwd = writeProject({
158
158
  'Post.json': {
159
159
  uid: 'Post',
160
160
  name: 'Posts',
161
- fields: { author: { type: 'relation', target: 'Author' } },
161
+ fields: { author: { type: 'reference', target: 'Author' } },
162
162
  },
163
163
  });
164
164
  const { errors } = validateSchemas({ cwd });
165
165
  expect(errors).toHaveLength(1);
166
- expect(errors[0]).toMatchObject({ rule: 'schemas/relation-target' });
166
+ expect(errors[0]).toMatchObject({ rule: 'schemas/reference-target' });
167
167
  expect(errors[0].message).toContain('unknown schema "Author"');
168
168
  });
169
- it('rejects relation-only properties on scalar fields', () => {
169
+ it('rejects reference-only properties on scalar fields', () => {
170
170
  const cwd = writeProject({
171
171
  'Post.json': {
172
172
  uid: 'Post',
@@ -176,7 +176,7 @@ describe('validateSchemas', () => {
176
176
  });
177
177
  const { errors } = validateSchemas({ cwd });
178
178
  expect(errors).toHaveLength(1);
179
- expect(errors[0]).toMatchObject({ rule: 'schemas/relation-shape' });
179
+ expect(errors[0]).toMatchObject({ rule: 'schemas/reference-shape' });
180
180
  });
181
181
  it('rejects schema uid names that cannot be used as content type identifiers', () => {
182
182
  const cwd = writeProject({
@@ -214,12 +214,12 @@ describe('validateSchemas', () => {
214
214
  expect(errors).toHaveLength(1);
215
215
  expect(errors[0]).toMatchObject({ rule: 'schemas/field-i18n' });
216
216
  });
217
- it('rejects relation-level locale settings', () => {
217
+ it('rejects reference-level locale settings', () => {
218
218
  const cwd = writeProject({
219
219
  'Page.json': {
220
220
  uid: 'Page',
221
221
  name: 'Pages',
222
- fields: { parent: { type: 'relation', target: 'Page', localized: true } },
222
+ fields: { parent: { type: 'reference', target: 'Page', localized: true } },
223
223
  },
224
224
  });
225
225
  const { errors } = validateSchemas({ cwd });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fimo",
3
- "version": "0.4.0-experimental.3",
3
+ "version": "0.4.0-experimental.5",
4
4
  "description": "Fimo CLI - create, deploy, and manage Fimo projects",
5
5
  "bin": {
6
6
  "fimo": "dist/cli/index.js"
@@ -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.0-experimental.3",
24
+ "fimo": "0.4.0-experimental.5",
25
25
  "input-otp": "^1.4.2",
26
26
  "isbot": "^5",
27
27
  "lucide-react": "^0.577.0",