fimo 2.2.0-staging.12 → 2.2.0-staging.13

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.
@@ -24,6 +24,27 @@ import type { <%= referenceImport.pascal %> } from "./<%= referenceImport.pascal
24
24
  /* ──────────── 📄 Type definitions ──────────── */
25
25
  /*───────────────────────────────────────────────────────────────*/
26
26
 
27
+ <% if (Object.values(model.fields || {}).some(holdsMedia)) { -%>
28
+ /**
29
+ * What a media field stores: a reference to a media-library asset. Point at the
30
+ * asset by `id`, or by the `url` an upload returned and Fimo resolves the `id`
31
+ * for you. `alt` belongs to the entry, so it survives replacing the asset. The
32
+ * facts the asset owns are never copied in, so they cannot go stale.
33
+ */
34
+ export type FimoMediaRef = ({ id: string; url?: string } | { id?: string; url: string }) & {
35
+ alt?: string;
36
+ source?: "local" | "external";
37
+ };
38
+
39
+ /**
40
+ * What a read returns for a media field it named in `populate`: the reference
41
+ * plus the facts the asset owns as of that read. `name`, `mime`, `width`, and
42
+ * `height` remain optional when the asset is external, missing, or incomplete.
43
+ * A read that did not name the field returns `FimoMedia` with those facts absent.
44
+ */
45
+ export type FimoPopulatedMedia = FimoMedia & { name?: string; mime?: string };
46
+
47
+ <% } -%>
27
48
  /**
28
49
  * <%= model.pascal %> entity interface
29
50
  */
@@ -131,10 +152,8 @@ function getInputTypeForField(field) {
131
152
  return 'string';
132
153
  case 'json':
133
154
  return 'Record<string, unknown>';
134
- case 'media': {
135
- const media = 'Pick<FimoMedia, "url" | "alt" | "name" | "mime" | "width" | "height">';
136
- return field.multiple ? `${media}[]` : media;
137
- }
155
+ case 'media':
156
+ return field.multiple ? 'FimoMediaRef[]' : 'FimoMediaRef';
138
157
  case 'reference': {
139
158
  const raw = Array.isArray(field.targets) && field.targets.length > 0 ? 'FimoPolymorphicReference' : 'FimoReference';
140
159
  return field.many ? `${raw}[]` : `${raw} | null`;
@@ -176,6 +195,32 @@ function getBlocksType(field, input) {
176
195
  return `${item}[]`;
177
196
  }
178
197
 
198
+ /** A media field, or a blocks field with a media value in some variant. */
199
+ function holdsMedia(field) {
200
+ if (field.type === 'media') {
201
+ return true;
202
+ }
203
+ if (field.type !== 'blocks') {
204
+ return false;
205
+ }
206
+ const schema = field.schema || {};
207
+ const variants = Array.isArray(schema.oneOf) ? schema.oneOf : [schema];
208
+ return variants.some((variant) => Object.values(variant.fields || {}).some(holdsMedia));
209
+ }
210
+
211
+ /**
212
+ * What the field becomes when `populate` names it. A media field gains the
213
+ * asset facts; a blocks field keeps its declared item types — the media inside
214
+ * it is populated at runtime, but naming which variant carries which value is
215
+ * beyond what this flat contract can say, so the type stays conservative.
216
+ */
217
+ function getPopulatedTypeForField(field) {
218
+ if (field.type === 'media') {
219
+ return field.multiple ? 'FimoPopulatedMedia[]' : 'FimoPopulatedMedia';
220
+ }
221
+ return getBlocksType(field, false);
222
+ }
223
+
179
224
  function getStringLiteralUnion(field) {
180
225
  if (!Array.isArray(field.options) || field.options.length === 0) {
181
226
  return null;
@@ -202,17 +247,26 @@ function isConditionalField(field) {
202
247
  return field.showIf !== undefined || field.showIfAny !== undefined;
203
248
  }
204
249
 
205
- const populatedReferenceFields = Object.entries(model.fields || {}).filter(([, field]) => {
250
+ const modelFields = Object.entries(model.fields || {});
251
+ const populatedReferenceFields = modelFields.filter(([, field]) => {
206
252
  if (field.type !== 'reference') {
207
253
  return false;
208
254
  }
209
255
  return (Array.isArray(field.targetRefs) && field.targetRefs.length > 0) || typeof field.targetPascal === 'string';
210
256
  });
257
+ const populatedMediaFields = modelFields.filter(([, field]) => holdsMedia(field));
258
+ const populatableFields = [...populatedReferenceFields, ...populatedMediaFields];
211
259
  -%>
212
260
 
213
- <% if (populatedReferenceFields.length === 0) { -%>
261
+ <% if (populatableFields.length === 0) { -%>
214
262
  export type <%= model.pascal %>References = Record<never, never>;
215
263
  <% } else { -%>
264
+ /**
265
+ * What each populatable field returns when a read names it in `populate`: a
266
+ * reference field resolves to its target entry, a media field to the asset's
267
+ * current facts, a blocks field to the same items with every media value
268
+ * inside them filled. Fields absent here are returned as stored.
269
+ */
216
270
  export interface <%= model.pascal %>References {
217
271
  <%
218
272
  populatedReferenceFields.forEach(([key, field]) => {
@@ -235,6 +289,11 @@ populatedReferenceFields.forEach(([key, field]) => {
235
289
  <%= key %>: <%= type %>;
236
290
  <%
237
291
  });
292
+ populatedMediaFields.forEach(([key, field]) => {
293
+ -%>
294
+ <%= key %>: <%= getPopulatedTypeForField(field) %>;
295
+ <%
296
+ });
238
297
  -%>
239
298
  }
240
299
  <% } -%>
@@ -1,12 +1,12 @@
1
- # Assets — package surface
1
+ # Assets in code
2
2
 
3
3
  > For the CLI commands (`fimo assets upload`, `fimo image generate`, folder management) and the "when to generate AI images" workflow rules, see **`fimo-cli/references/assets.md`**.
4
4
 
5
- This file covers the package surface for working with assets in code: the `media` field value shape, rendering primitives, and URL format.
5
+ This file covers media fields and the Fimo image components.
6
6
 
7
- ## `media` field value (stored/wire shape)
7
+ ## Media field values
8
8
 
9
- Asset values returned from `fimo assets upload` or Fimo media tasks, and stored in `media` content-type fields:
9
+ A media field stores a reference to an asset, not a copy of the asset metadata. Write the value returned by upload or generation:
10
10
 
11
11
  ```json
12
12
  {
@@ -16,11 +16,22 @@ Asset values returned from `fimo assets upload` or Fimo media tasks, and stored
16
16
  }
17
17
  ```
18
18
 
19
- - `source`: `"local"` for uploaded/generated assets in the Fimo media library. `"external"` for hard-coded third-party URLs (avoid when possible — editors can't manage these).
20
- - `url`: stable HTTPS URL, no auth required, safe to embed.
21
- - `alt`: optional alt text.
19
+ - `source`: `"local"` for a Fimo media-library asset, or `"external"` for a third-party URL.
20
+ - `url`: the public asset URL.
21
+ - `alt`: optional text owned by the entry.
22
22
 
23
- Generated content reads wrap this stored value in `FimoMedia`, which exposes `url`, `alt`, `name`, `mime`, and optional `width`/`height`. It does not expose `source`; Fimo tracks that internally. Pass the whole entry value to `<Image>` / `<Video>`.
23
+ For a local asset, Fimo stores its ID and keeps the URL as a fallback. Do not copy `name`, `mime`, `width`, or `height` into the value. Those facts belong to the asset and are removed from writes.
24
+
25
+ By default, reads return the stored reference without an asset lookup. Name a media or blocks field in `populate` when the page needs the asset's current URL, name, MIME type, or intrinsic size:
26
+
27
+ ```ts
28
+ const post = await Post.getBySlug('hello');
29
+ const withAsset = await Post.getBySlug('hello', { populate: ['cover'] });
30
+ ```
31
+
32
+ `populate` takes top-level field names. A blocks field populates every media value inside it. One lookup covers all selected media in the response. Media inside a populated reference target stays a reference. Use `fimo assets open <id> --download` to inspect the source file.
33
+
34
+ Generated reads wrap media in `FimoMedia`. Its `id`, `alt`, `name`, `mime`, `width`, and `height` can be absent. A populated value has the current facts when the asset resolves; missing or external assets stay unchanged. `source` is internal. Pass the whole value to `<Image>` or `<Video>`.
24
35
 
25
36
  ## Rendering in application code
26
37
 
@@ -41,11 +52,11 @@ import { Image, StaticImage, Video } from 'fimo-next';
41
52
  <StaticImage src="https://assets.fimo.ai/<project>/<id>.jpg" alt="Hero" width={1200} height={630} />
42
53
  ```
43
54
 
44
- Fimo's `<Image>` is not Next's `next/image` component. Do not pass Next-only props such as `priority`, `fill`, `quality`, or `loader`; use only the props documented for the Fimo primitive. Preserve `next/image` for non-Fimo images when the application needs those Next-specific controls.
55
+ Fimo's `<Image>` is not Next's `next/image`. Use the Fimo props documented here. Keep `next/image` for non-Fimo images that need its controls.
45
56
 
46
- **Provide `width` and `height` when known** so Fimo can optimize the asset and reserve layout space (prevents CLS).
57
+ Choose dimensions, CSS, `sizes`, and `fit` from the page design. Do not copy the source file's intrinsic dimensions into layout props by default. When a read includes intrinsic size, Fimo uses it for aspect-ratio reservation and responsive candidates.
47
58
 
48
- **Never hand-construct a `FimoMedia` object** to pass into `<Image>` — that bypasses inline-editing metadata. If you have a raw URL, use `<StaticImage>` instead.
59
+ **Never hand-construct a `FimoMedia` object** to pass into `<Image>`. That bypasses inline-editing metadata. If you have a raw URL, use `<StaticImage>` instead.
49
60
 
50
61
  See `references/ui.md` for the full component table and rules.
51
62
 
@@ -125,7 +125,21 @@ const page = await Article.list({
125
125
  });
126
126
  ```
127
127
 
128
- Fimo always keeps `id`, `entryId`, `documentId`, and `locale`. Without `populate`, references are `{ id }`; with it, they become the target entry or `null`. Populate is depth-one and only accepts reference field names.
128
+ Fimo always keeps `id`, `entryId`, `documentId`, and `locale`, plus every field named in `populate`.
129
+
130
+ `populate` is depth-one and takes top-level field names of two kinds:
131
+
132
+ | Field named | Without `populate` | With it |
133
+ | -------------------- | ------------------------ | ------------------------------------------------------------------------------------------- |
134
+ | reference | `{ id }` | the target entry, or `null` |
135
+ | media | the stored reference | `FimoPopulatedMedia` with current asset facts when it resolves; asset facts remain optional |
136
+ | blocks holding media | stored references inside | every media value inside it populated; item types unchanged |
137
+
138
+ Any other field name is a 400. A populated reference target carries its own media as stored references. Population does not recurse, and a missing or external asset keeps its stored media value.
139
+
140
+ Even when media is populated, `id`, `name`, `mime`, `width`, and `height` can be absent for external, missing, or incomplete assets. The stored `url` remains the fallback, and `alt` is optional entry-owned text.
141
+
142
+ Generated schema modules keep `FimoMediaRef` and `FimoPopulatedMedia` local. Their shapes depend on the schema, while the installed runtime package may be an older release.
129
143
 
130
144
  When a named type is needed, use the generated result type rather than typing populated data as the base entry:
131
145
 
@@ -248,9 +248,13 @@ ID supplied by the owner — without one, stop with an unresolved owner action
248
248
  instead of inventing an ID:
249
249
 
250
250
  - Link an owner-supplied (empty) project:
251
- `fimo migrate init <target-dir> --project-id <projectId> --framework <framework>`
251
+ `fimo migrate init <target-dir> --project-id <projectId> --framework <framework> --source-platform <platform>`
252
252
  - Create a new project in an owner-supplied organization:
253
- `fimo migrate init <target-dir> --org <organizationId> --framework <framework>`
253
+ `fimo migrate init <target-dir> --org <organizationId> --framework <framework> --source-platform <platform>`
254
+
255
+ `--source-platform` names what the site is migrated from (`wordpress`, `nextjs`,
256
+ `astro`, `react-router`, `webflow`, `framer`, `squarespace`, `wix`, `carrd`,
257
+ `other`); pass what the source checkout proves, `unknown` only when nothing does.
254
258
 
255
259
  After setup, check `.fimo/config.json#runtime.framework` and the scaffold's
256
260
  framework files before starting workers. A Next.js target must still have its
@@ -286,7 +290,10 @@ Commands that perform actual data writes, all requiring the linked target:
286
290
  - `fimo schemas push`, `fimo assets upload`, `fimo entries create|update`,
287
291
  `fimo singles update`, `fimo labels set|set-many`, `fimo forms push`.
288
292
  - `fimo migrate init` (source-backed form): the project link/creation and
289
- initial commit push described above.
293
+ initial commit push described above. Pass `--source-platform <platform>`
294
+ (webflow, framer, squarespace, wix, carrd, wordpress, nextjs, astro,
295
+ react-router, other) when the source is known; URL crawls detect it.
296
+
290
297
  - `fimo link`: project creation or selection plus the in-place adoption of a
291
298
  copied source target.
292
299
  - `fimo deploy` / `fimo publish` — owner-approved only.
@@ -86,6 +86,17 @@ the asset owns the uploaded bytes and their metadata, and the entry field holds
86
86
  the media value `{ source, url, alt }` that points at it. Keep both carrying the
87
87
  same alt text, and do not duplicate alt into a second schema field.
88
88
 
89
+ Do not copy upload metadata into media values yourself. Writing `{ source, url,
90
+ alt }` is enough: the write path resolves and persists the media-library ID, and
91
+ a read that names the field in `populate` returns the asset's current URL, name,
92
+ MIME type, and intrinsic dimensions from the library. A read that does not name
93
+ it returns the stored reference. The ID lets you inspect the asset later with
94
+ `fimo assets open <id> --download`.
95
+
96
+ Treat intrinsic dimensions as source facts, not layout instructions. Choose the
97
+ displayed size, crop, `sizes`, loading behavior, and fit from the migrated page's
98
+ design and responsive behavior.
99
+
89
100
  ## Failure Handling
90
101
 
91
102
  If a media asset cannot be downloaded or uploaded:
@@ -21,6 +21,9 @@ ask a generic optimization question.
21
21
  - URL migration: `fimo migrate init <source-url> <target-dir> --framework <framework>`
22
22
  - link an existing project: `fimo migrate init <target-dir> --project-id <projectId> --framework <framework>`
23
23
  - create a project: `fimo migrate init <target-dir> --org <organizationId> --framework <framework>`
24
+ - add `--source-platform <platform>` when the source platform is known
25
+ (webflow, framer, squarespace, wix, carrd, wordpress, nextjs, astro,
26
+ react-router, other); URL crawls detect it themselves.
24
27
  Use the detected source framework for source-backed migrations. A framework
25
28
  conversion requires an explicit user choice; URL-only and rebuild
26
29
  migrations ask the user which framework to use. Never choose a framework by
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "bundled": true,
3
3
  "bundler": "esbuild",
4
- "bundledAt": "2026-08-28T14:46:45.101Z",
4
+ "bundledAt": "2026-08-28T21:37:20.437Z",
5
5
  "cliVersion": "2.1.0",
6
6
  "external": [
7
7
  "oxc-parser",