fimo 2.2.0-staging.11 → 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.
- package/assets/content-templates/shared/types.eta +65 -6
- package/assets/skills/fimo-cli/references/content.md +1 -1
- package/assets/skills/fimo-code/references/assets.md +22 -11
- package/assets/skills/fimo-code/references/content/clients.md +15 -1
- package/assets/skills/fimo-code/references/content/schema.md +1 -0
- package/assets/skills/fimo-migration/SKILL.md +10 -3
- package/assets/skills/fimo-migration/references/media.md +11 -0
- package/assets/skills/fimo-migration/references/workflow.md +3 -0
- package/dist/cli/bundle.json +1 -1
- package/dist/cli/index.js +40 -26
- package/dist/runtime/content/serialization.d.ts +4 -3
- package/dist/runtime/content/serialization.js +4 -3
- package/dist/runtime/primitives/lib/primitives.d.ts +15 -7
- package/dist/runtime/primitives/lib/primitives.js +14 -1
- package/dist/scripts/validate-schemas.js +10 -15
- package/package.json +7 -7
- package/templates/astro/package.json +2 -2
- package/templates/nextjs/package.json +3 -3
- package/templates/react-router/package.json +4 -4
- package/templates/sveltekit/package.json +2 -2
- package/templates/vite/package.json +4 -4
|
@@ -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
|
-
|
|
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
|
|
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 (
|
|
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
|
<% } -%>
|
|
@@ -103,7 +103,7 @@ Reference filters take the target `id` string (`{"author":"750e..."}`). On a to-
|
|
|
103
103
|
|
|
104
104
|
`--populate` is a comma-separated list of reference fields to embed. Without it, reference fields return reference objects like `{ "id": "..." }`; with `--populate author,contributors`, those fields return the target entry objects for the requested locale. Only reference fields can be populated (scalar fields 400), and populate never selects which target fields come back. A populated target missing in the requested locale resolves to null unless you pass `localeFallbacks=true`, which falls back to the best available locale variant, marked by its `locale` field. Limits: 200 references per to-many write, 500 distinct populated documents per field per request (400 past either).
|
|
105
105
|
|
|
106
|
-
Polymorphic references declare `targets: ["Post", "Product"]` instead of `target` (never both) and their values carry both parts: `{ "contentType": "Post", "id": "..." }`. Populate returns mixed full entries, and generated hooks expose `$type` on each populated value to branch on. `--where` on polymorphic fields is not supported yet and fails with a clear error.
|
|
106
|
+
Polymorphic references declare `targets: ["Post", "Product"]` instead of `target` (never both) and their values carry both parts: `{ "contentType": "Post", "id": "..." }`. Populate returns mixed full entries, and generated hooks expose `$type` on each populated value to branch on. `--where` on polymorphic fields is not supported yet and fails with a clear error. Fixed and polymorphic references also work inside blocks with these same write shapes (`id`, `documentId`, or `slug` selectors), but nested fields cannot be populated or filtered.
|
|
107
107
|
|
|
108
108
|
In generated application code, the equivalent is `populate: ['author', 'contributors'] as const` in query params. Let the generated client infer the result when possible; if a named TypeScript shape is needed, use `BlogPostResult<TFields, TPopulate>` as documented by `fimo skills get fimo-code --ref content/clients`. Writes use `BlogPostInput`.
|
|
109
109
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
# Assets
|
|
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
|
|
5
|
+
This file covers media fields and the Fimo image components.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Media field values
|
|
8
8
|
|
|
9
|
-
|
|
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
|
|
20
|
-
- `url`:
|
|
21
|
-
- `alt`: optional
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
|
@@ -140,6 +140,7 @@ Use `schema.fields` for one repeated shape and `schema.oneOf` for selectable var
|
|
|
140
140
|
- Fimo adds stable `_key` values; `oneOf` items also receive `_type`.
|
|
141
141
|
- Nest blocks up to seven levels. One `oneOf` supports up to 40 variants and one blocks field up to 200 items.
|
|
142
142
|
- Nested scalar values use the same source-tracked value type as top-level fields.
|
|
143
|
+
- Fixed and polymorphic references work inside blocks with the same value shapes as top-level reference fields.
|
|
143
144
|
- Nested reference population and filtering are not supported.
|
|
144
145
|
|
|
145
146
|
## References
|
|
@@ -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
|