fimo 2.2.0-staging.8 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/assets/agent-templates/_default/config.yaml +6 -0
  2. package/assets/agent-templates/broken-link-sweeper/config.yaml +1 -0
  3. package/assets/content-templates/shared/types.eta +65 -6
  4. package/assets/skills/fimo-cli/SKILL.md +5 -3
  5. package/assets/skills/fimo-cli/references/agents.md +29 -4
  6. package/assets/skills/fimo-cli/references/assets.md +42 -3
  7. package/assets/skills/fimo-cli/references/branches.md +9 -8
  8. package/assets/skills/fimo-cli/references/content.md +4 -4
  9. package/assets/skills/fimo-cli/references/credits.md +12 -19
  10. package/assets/skills/fimo-cli/references/deploy.md +8 -8
  11. package/assets/skills/fimo-cli/references/forms.md +3 -1
  12. package/assets/skills/fimo-cli/references/history.md +3 -2
  13. package/assets/skills/fimo-cli/references/inbox.md +86 -0
  14. package/assets/skills/fimo-cli/references/init.md +7 -6
  15. package/assets/skills/fimo-cli/references/invite.md +11 -9
  16. package/assets/skills/fimo-cli/references/labels.md +3 -11
  17. package/assets/skills/fimo-cli/references/locales.md +5 -3
  18. package/assets/skills/fimo-cli/references/preview.md +1 -2
  19. package/assets/skills/fimo-cli/references/projects.md +8 -8
  20. package/assets/skills/fimo-cli/references/schedules.md +6 -4
  21. package/assets/skills/fimo-cli/references/webhooks.md +6 -4
  22. package/assets/skills/fimo-code/references/assets.md +22 -11
  23. package/assets/skills/fimo-code/references/content/clients.md +15 -1
  24. package/assets/skills/fimo-code/references/content/schema.md +1 -0
  25. package/assets/skills/fimo-code/references/frameworks/astro.md +1 -1
  26. package/assets/skills/fimo-code/references/frameworks/next/app-router.md +12 -1
  27. package/assets/skills/fimo-code/references/frameworks/next/locales.md +2 -2
  28. package/assets/skills/fimo-code/references/frameworks/next.md +1 -1
  29. package/assets/skills/fimo-code/references/locales.md +4 -2
  30. package/assets/skills/fimo-migration/SKILL.md +54 -16
  31. package/assets/skills/fimo-migration/references/frameworks/nextjs.md +8 -0
  32. package/assets/skills/fimo-migration/references/media.md +11 -0
  33. package/assets/skills/fimo-migration/references/qa.md +30 -4
  34. package/assets/skills/fimo-migration/references/questions-and-improvements.md +188 -0
  35. package/assets/skills/fimo-migration/references/workers/content-planner.md +8 -3
  36. package/assets/skills/fimo-migration/references/workers/validator.md +20 -6
  37. package/assets/skills/fimo-migration/references/workers.md +17 -6
  38. package/assets/skills/fimo-migration/references/workflow.md +36 -6
  39. package/assets/skills/fimo-studio/SKILL.md +3 -1
  40. package/assets/skills/fimo-studio/references/agents.md +25 -0
  41. package/assets/skills/fimo-studio/references/ai-chat.md +1 -2
  42. package/assets/skills/fimo-studio/references/billing-and-credits.md +14 -7
  43. package/assets/skills/fimo-studio/references/visual-editing.md +9 -1
  44. package/assets/skills/fimo-studio/references/webhooks.md +1 -1
  45. package/dist/cli/bundle.json +2 -2
  46. package/dist/cli/index.js +210 -179
  47. package/dist/runtime/content/client/collection-core.js +3 -2
  48. package/dist/runtime/content/client/core-types.d.ts +4 -3
  49. package/dist/runtime/content/client/internal.d.ts +2 -0
  50. package/dist/runtime/content/client/internal.js +54 -25
  51. package/dist/runtime/content/client/singleton-core.js +2 -1
  52. package/dist/runtime/content/index.d.ts +1 -1
  53. package/dist/runtime/content/index.js +1 -1
  54. package/dist/runtime/content/serialization.d.ts +4 -3
  55. package/dist/runtime/content/serialization.js +4 -3
  56. package/dist/runtime/primitives/lib/index.d.ts +1 -1
  57. package/dist/runtime/primitives/lib/index.js +1 -1
  58. package/dist/runtime/primitives/lib/primitives.d.ts +39 -12
  59. package/dist/runtime/primitives/lib/primitives.js +83 -17
  60. package/dist/runtime/primitives/lib/template.js +2 -2
  61. package/dist/runtime/shared/local-runtime-env.d.ts +2 -2
  62. package/dist/runtime/shared/local-runtime-env.js +14 -9
  63. package/dist/runtime/templates.d.ts +9 -2
  64. package/dist/runtime/templates.js +36 -7
  65. package/dist/scripts/validate-schemas.js +10 -15
  66. package/package.json +10 -8
  67. package/release.json +2 -2
  68. package/templates/astro/AGENTS.md +1 -1
  69. package/templates/astro/package.json +2 -2
  70. package/templates/nextjs/AGENTS.md +2 -2
  71. package/templates/nextjs/package.json +3 -3
  72. package/templates/nextjs/src/app/layout.tsx +4 -1
  73. package/templates/react-router/AGENTS.md +2 -2
  74. package/templates/react-router/package.json +4 -4
  75. package/templates/sveltekit/AGENTS.md +1 -1
  76. package/templates/sveltekit/package.json +2 -2
  77. package/templates/vite/AGENTS.md +5 -1
  78. package/templates/vite/package.json +4 -4
  79. package/templates/welcome/manifest.json +655 -0
  80. package/templates/welcome/media/analytics-overview.avif +0 -0
  81. package/templates/welcome/media/autonomous-loop.avif +0 -0
  82. package/templates/welcome/media/editable-preview.avif +0 -0
  83. package/templates/welcome/media/media-library.avif +0 -0
  84. package/templates/welcome/media/slack-teams.avif +0 -0
  85. package/templates/welcome/template/.fimo/agents/welcome-guide/GOAL.md +12 -0
  86. package/templates/welcome/template/.fimo/agents/welcome-guide/capabilities.yaml +3 -0
  87. package/templates/welcome/template/.fimo/agents/welcome-guide/config.yaml +8 -0
  88. package/templates/welcome/template/.fimo/config.json +53 -0
  89. package/templates/welcome/template/src/font.css +60 -0
  90. package/templates/welcome/template/src/pages/ArticleDetail.tsx +104 -0
  91. package/templates/welcome/template/src/pages/Index.tsx +36 -0
  92. package/templates/welcome/template/src/pages/SiteContent.tsx +674 -0
  93. package/templates/welcome/template/src/pages/fallback-content.ts +152 -0
  94. package/templates/welcome/template/src/routes.ts +9 -0
  95. package/templates/welcome/template/src/schemas/Article.json +34 -0
  96. package/templates/welcome/template/src/schemas/Article.ts +54 -0
  97. package/templates/welcome/template/src/schemas/Article.types.ts +93 -0
  98. package/templates/welcome/template/src/schemas/Author.json +17 -0
  99. package/templates/welcome/template/src/schemas/Author.ts +51 -0
  100. package/templates/welcome/template/src/schemas/Author.types.ts +54 -0
  101. package/templates/welcome/template/src/schemas/Category.json +17 -0
  102. package/templates/welcome/template/src/schemas/Category.ts +51 -0
  103. package/templates/welcome/template/src/schemas/Category.types.ts +54 -0
  104. package/templates/welcome/template/src/schemas/HomePage.json +110 -0
  105. package/templates/welcome/template/src/schemas/HomePage.ts +22 -0
  106. package/templates/welcome/template/src/schemas/HomePage.types.ts +116 -0
@@ -11,11 +11,12 @@ export function createCollectionContentCore(config, runtime) {
11
11
  const meta = { uid: info.uid, pascal: info.pascal, localized: info.localized };
12
12
  const self = { wrapWithSource, meta };
13
13
  const resolve = (uid) => uid === info.uid ? self : resolveClientHandle(references[uid]);
14
- function wrapWithSource(entry) {
14
+ function wrapWithSource(entry, context) {
15
15
  return wrapEntry(info, entry, {
16
16
  alwaysIncludeLocale: true,
17
17
  defaultLocale: requestDefaultLocale ?? 'en',
18
18
  resolve,
19
+ context,
19
20
  });
20
21
  }
21
22
  function readPath(url, options) {
@@ -88,7 +89,7 @@ export function createCollectionContentCore(config, runtime) {
88
89
  throw new Error(await response.text());
89
90
  }
90
91
  const body = parseCollectionResponse(await response.json());
91
- const items = body.data.map(wrapWithSource);
92
+ const items = body.data.map((entry) => wrapWithSource(entry));
92
93
  const pageInfo = {
93
94
  limit: body.meta.limit,
94
95
  offset: body.meta.offset,
@@ -1,3 +1,4 @@
1
+ import type { FimoSourceContext } from '../../primitives/lib/primitives.js';
1
2
  import type { Fields, Populate, PopulatedResult, ProjectedWithPopulate, Query } from '../../primitives/lib/query.js';
2
3
  export type ContentResult<T extends {
3
4
  id: string;
@@ -43,7 +44,7 @@ export interface ContentModuleMeta {
43
44
  readonly localized: boolean;
44
45
  }
45
46
  export interface ContentClientHandle {
46
- wrapWithSource: (entry: Record<string, any>) => unknown;
47
+ wrapWithSource: (entry: Record<string, any>, context?: FimoSourceContext) => unknown;
47
48
  meta: ContentModuleMeta;
48
49
  }
49
50
  export type ContentModuleRef = ContentClientHandle | {
@@ -73,7 +74,7 @@ export interface CollectionContentCore<T extends {
73
74
  id: string;
74
75
  }, TInput extends object> {
75
76
  readonly meta: ContentModuleMeta;
76
- wrapWithSource(entry: Record<string, any>): T;
77
+ wrapWithSource(entry: Record<string, any>, context?: FimoSourceContext): T;
77
78
  getById<TPopulate extends Populate<T> | undefined = undefined>(id: string, options?: ContentReadOptions<TPopulate>): Promise<ContentResult<T, undefined, TPopulate>>;
78
79
  getBySlug<TPopulate extends Populate<T> | undefined = undefined>(slug: string, options?: ContentReadOptions<TPopulate>): Promise<ContentResult<T, undefined, TPopulate> | null>;
79
80
  getByField<TPopulate extends Populate<T> | undefined = undefined>(fieldName: string, value: string, options?: ContentReadOptions<TPopulate>): Promise<ContentResult<T, undefined, TPopulate> | null>;
@@ -89,7 +90,7 @@ export interface SingletonContentCore<T extends {
89
90
  id: string;
90
91
  }, TInput extends object> {
91
92
  readonly meta: ContentModuleMeta;
92
- wrapWithSource(entry: Record<string, any>): T;
93
+ wrapWithSource(entry: Record<string, any>, context?: FimoSourceContext): T;
93
94
  get<TPopulate extends Populate<T> | undefined = undefined>(options?: ContentReadOptions<TPopulate>): Promise<ContentResult<T, undefined, TPopulate>>;
94
95
  update(payload: Partial<TInput>): Promise<T>;
95
96
  }
@@ -1,3 +1,4 @@
1
+ import { type FimoSourceContext } from '../../primitives/lib/primitives.js';
1
2
  import type { ContentClientHandle, ContentModuleRef, ContentSchema, ContentSchemaField } from './core-types.js';
2
3
  export interface InterpretedSchema {
3
4
  uid: string;
@@ -37,6 +38,7 @@ export declare function wrapEntry(info: InterpretedSchema, entry: Record<string,
37
38
  alwaysIncludeLocale: boolean;
38
39
  defaultLocale: string;
39
40
  resolve: ReferenceResolver;
41
+ context?: FimoSourceContext;
40
42
  }): Record<string, unknown>;
41
43
  export {};
42
44
  //# sourceMappingURL=internal.d.ts.map
@@ -1,5 +1,5 @@
1
1
  import { pascalCase } from 'change-case';
2
- import { FIMO_SOURCE, FimoBoolean, FimoMedia, FimoRichText, FimoString } from '../../primitives/lib/primitives.js';
2
+ import { FIMO_SOURCE, FIMO_SOURCE_CONTEXT, FimoBoolean, FimoMedia, FimoRichText, FimoString, } from '../../primitives/lib/primitives.js';
3
3
  export function interpretSchema(schema) {
4
4
  if (!schema || typeof schema.uid !== 'string' || schema.uid.length === 0) {
5
5
  throw new Error('Content schema is missing a "uid".');
@@ -94,6 +94,19 @@ function isPopulatedEntry(value) {
94
94
  typeof fimo === 'object' &&
95
95
  typeof fimo.contentType === 'string');
96
96
  }
97
+ function appendSourceContext(context, item) {
98
+ return [...(context ?? []), item];
99
+ }
100
+ function tagWithSourceContext(value, context) {
101
+ if (context && context.length > 0) {
102
+ Object.defineProperty(value, FIMO_SOURCE_CONTEXT, {
103
+ value: Object.freeze([...context]),
104
+ enumerable: false,
105
+ configurable: true,
106
+ });
107
+ }
108
+ return value;
109
+ }
97
110
  function asClientHandle(candidate) {
98
111
  if (candidate == null || typeof candidate !== 'object') {
99
112
  return undefined;
@@ -133,7 +146,7 @@ function unresolvableReference(uid) {
133
146
  * union narrows in a switch. Raw (unpopulated) values and types outside the
134
147
  * field's declared targets pass through untouched.
135
148
  */
136
- function wrapPolymorphic(value, targetUids, resolve) {
149
+ function wrapPolymorphic(value, targetUids, source, resolve, context) {
137
150
  if (!isPopulatedEntry(value)) {
138
151
  return value;
139
152
  }
@@ -145,11 +158,11 @@ function wrapPolymorphic(value, targetUids, resolve) {
145
158
  if (!target) {
146
159
  throw unresolvableReference(contentType);
147
160
  }
148
- const wrapped = target.wrapWithSource(value);
161
+ const wrapped = target.wrapWithSource(value, appendSourceContext(context, { kind: 'relation', source }));
149
162
  wrapped.$type = contentType;
150
- return wrapped;
163
+ return tagWithSource(wrapped, source, context);
151
164
  }
152
- function wrapReference(value, uid, resolve) {
165
+ function wrapReference(value, uid, source, resolve, context) {
153
166
  if (!isPopulatedEntry(value)) {
154
167
  return value;
155
168
  }
@@ -157,7 +170,7 @@ function wrapReference(value, uid, resolve) {
157
170
  if (!target) {
158
171
  throw unresolvableReference(uid);
159
172
  }
160
- return target.wrapWithSource(value);
173
+ return tagWithSource(target.wrapWithSource(value, appendSourceContext(context, { kind: 'relation', source })), source, context);
161
174
  }
162
175
  /**
163
176
  * Blocks values have no wrapper class of their own — the array structure stays
@@ -174,7 +187,7 @@ function blockItemFields(field, item) {
174
187
  }
175
188
  return schema.fields;
176
189
  }
177
- function wrapBlockItem(field, item, source, resolve) {
190
+ function wrapBlockItem(field, item, listSource, itemSource, resolve, context) {
178
191
  if (item == null || typeof item !== 'object' || Array.isArray(item)) {
179
192
  return item;
180
193
  }
@@ -183,61 +196,77 @@ function wrapBlockItem(field, item, source, resolve) {
183
196
  if (!fields) {
184
197
  return item;
185
198
  }
199
+ const itemContext = typeof record._key === 'string' && record._key.length > 0
200
+ ? appendSourceContext(context, {
201
+ kind: 'component',
202
+ source: listSource,
203
+ _key: record._key,
204
+ ...(typeof record._type === 'string' && record._type.length > 0 ? { _type: record._type } : {}),
205
+ })
206
+ : context;
186
207
  const wrapped = { ...record };
187
208
  for (const [key, childField] of Object.entries(fields)) {
188
- wrapped[key] = wrapField(childField, record[key], `${source}.${key}`, resolve);
209
+ wrapped[key] = wrapField(childField, record[key], `${itemSource}.${key}`, resolve, itemContext);
189
210
  }
190
211
  return wrapped;
191
212
  }
192
213
  /**
193
214
  * A JSON field's declared shape is a plain object/array — tagging it in place
194
215
  * (rather than boxing it, like the scalar wrappers below) keeps that shape
195
- * intact so consumers that read it as ordinary JSON see no difference.
216
+ * intact so consumers that read it as ordinary JSON see no difference. A
217
+ * populated reference is tagged the same way, with the source of the field
218
+ * that HOLDS it (`Post.id:p1:en.author`), so the toolbar can select and
219
+ * repoint the reference itself; the target's own fields keep the sources the
220
+ * target client gave them.
196
221
  */
197
- function tagWithSource(value, source) {
222
+ function tagWithSource(value, source, context) {
198
223
  Object.defineProperty(value, FIMO_SOURCE, { value: source, enumerable: false, configurable: true });
199
- return value;
224
+ return tagWithSourceContext(value, context);
200
225
  }
201
- function wrapField(field, value, source, resolve) {
226
+ function wrapField(field, value, source, resolve, context) {
202
227
  switch (field.type) {
203
228
  case 'string':
204
229
  case 'text':
205
230
  case 'date':
206
231
  case 'datetime':
207
- return value != null ? new FimoString(String(value), source) : value;
232
+ return value != null ? new FimoString(String(value), source, context) : value;
208
233
  case 'richtext':
209
- return value != null ? new FimoRichText(value, source) : value;
234
+ return value != null ? new FimoRichText(value, source, context) : value;
210
235
  case 'boolean':
211
- return value != null ? new FimoBoolean(value, source) : value;
236
+ return value != null ? new FimoBoolean(value, source, context) : value;
212
237
  case 'json':
213
238
  return value != null && typeof value === 'object'
214
- ? tagWithSource(Array.isArray(value) ? [...value] : { ...value }, source)
239
+ ? tagWithSource(Array.isArray(value) ? [...value] : { ...value }, source, context)
215
240
  : value;
216
241
  case 'media':
217
242
  if (field.multiple) {
218
- return (value?.map((m, i) => new FimoMedia(m, `${source}[${i}]`)) ?? value);
243
+ return (value?.map((m, i) => new FimoMedia(m, `${source}[${i}]`, context)) ?? value);
219
244
  }
220
- return value != null ? new FimoMedia(value, source) : value;
245
+ return value != null ? new FimoMedia(value, source, context) : value;
221
246
  case 'reference': {
222
247
  const polymorphic = polymorphicTargetUids(field);
223
248
  if (polymorphic) {
224
249
  if (field.many) {
225
- return Array.isArray(value) ? value.map((item) => wrapPolymorphic(item, polymorphic, resolve)) : value;
250
+ return Array.isArray(value)
251
+ ? value.map((item, i) => wrapPolymorphic(item, polymorphic, `${source}[${i}]`, resolve, context))
252
+ : value;
226
253
  }
227
- return wrapPolymorphic(value, polymorphic, resolve);
254
+ return wrapPolymorphic(value, polymorphic, source, resolve, context);
228
255
  }
229
256
  const uid = field.target;
230
257
  if (!uid) {
231
258
  return value;
232
259
  }
233
260
  if (field.many) {
234
- return Array.isArray(value) ? value.map((item) => wrapReference(item, uid, resolve)) : value;
261
+ return Array.isArray(value)
262
+ ? value.map((item, i) => wrapReference(item, uid, `${source}[${i}]`, resolve, context))
263
+ : value;
235
264
  }
236
- return wrapReference(value, uid, resolve);
265
+ return wrapReference(value, uid, source, resolve, context);
237
266
  }
238
267
  case 'blocks':
239
268
  return Array.isArray(value)
240
- ? value.map((item, i) => wrapBlockItem(field, item, `${source}[${i}]`, resolve))
269
+ ? value.map((item, i) => wrapBlockItem(field, item, source, `${source}[${i}]`, resolve, context))
241
270
  : value;
242
271
  // 'number' deliberately falls through here — this is a decision, not a
243
272
  // gap. React renders a boxed String directly (`<span>{new FimoString(...)}
@@ -267,7 +296,7 @@ export function wrapEntry(info, entry, options) {
267
296
  id,
268
297
  entryId,
269
298
  documentId,
270
- slug: entry.slug != null ? new FimoString(entry.slug, `${sourcePrefix}.slug`) : entry.slug,
299
+ slug: entry.slug != null ? new FimoString(entry.slug, `${sourcePrefix}.slug`, options.context) : entry.slug,
271
300
  ...(options.alwaysIncludeLocale || entry.locale ? { locale } : {}),
272
301
  createdAt: entry.createdAt,
273
302
  updatedAt: entry.updatedAt,
@@ -276,7 +305,7 @@ export function wrapEntry(info, entry, options) {
276
305
  },
277
306
  };
278
307
  for (const [key, field] of info.fields) {
279
- wrapped[key] = wrapField(field, entry[key], `${sourcePrefix}.${key}`, options.resolve);
308
+ wrapped[key] = wrapField(field, entry[key], `${sourcePrefix}.${key}`, options.resolve, options.context);
280
309
  }
281
310
  return wrapped;
282
311
  }
@@ -28,11 +28,12 @@ export function createSingletonContentCore(config, runtime) {
28
28
  localizedReferenceFields = fields;
29
29
  return fields;
30
30
  }
31
- function wrapWithSource(entry) {
31
+ function wrapWithSource(entry, context) {
32
32
  return wrapEntry(info, entry, {
33
33
  alwaysIncludeLocale: info.localized,
34
34
  defaultLocale: requestDefaultLocale ?? 'en',
35
35
  resolve,
36
+ context,
36
37
  });
37
38
  }
38
39
  function localize(options, fallbackLocale = requestDefaultLocale) {
@@ -16,6 +16,6 @@ export type { SingletonContentCore } from './client/singleton-core.js';
16
16
  export { serializeContent, type SerializedContent, type SerializedMedia } from './serialization.js';
17
17
  export { parseCollectionResponse, type CollectionResponse } from './parseCollectionResponse.js';
18
18
  export type { CollectionPage, CollectionPageInfo, ContentClientConfig, ContentClientHandle, ContentModuleMeta, ContentModuleRef, ContentReadOptions, ContentResult, ContentSchema, ContentSchemaField, } from './client/core-types.js';
19
- export { FIMO_PARTS, FIMO_SOURCE, FimoBoolean, FimoDate, FimoMedia, FimoRichText, FimoString, getFimoParts, getFimoSource, hasFimoSource, isFimoDate, isFimoString, type FimoPart, type FimoPolymorphicReference, type FimoReference, type FimoRichTextContent, type FimoSource, } from '../primitives/lib/primitives.js';
19
+ export { FIMO_PARTS, FIMO_SOURCE, FIMO_SOURCE_CONTEXT, FimoBoolean, FimoDate, FimoMedia, FimoRichText, FimoString, getFimoParts, getFimoSource, getFimoSourceContext, hasFimoSource, isFimoDate, isFimoString, type FimoPart, type FimoPolymorphicReference, type FimoReference, type FimoRichTextContent, type FimoSourceContext, type FimoSourceContextItem, type FimoSource, } from '../primitives/lib/primitives.js';
20
20
  export type { BoolOps, DateOps, Field, Fields, FilterFor, NullOnlyOps, NumberOps, Populate, Populated, PopulatedResult, Projected, ProjectedWithPopulate, Query, ReferenceField, ReferenceMap, ScalarOps, Sort, SortField, StringOps, Where, WithReferences, } from '../primitives/lib/query.js';
21
21
  //# sourceMappingURL=index.d.ts.map
@@ -13,4 +13,4 @@ export { readCollectionLocale, splitWritePayload, withMutationLocale } from './c
13
13
  export { requireContentApiUrl } from './client/runtime.js';
14
14
  export { serializeContent } from './serialization.js';
15
15
  export { parseCollectionResponse } from './parseCollectionResponse.js';
16
- export { FIMO_PARTS, FIMO_SOURCE, FimoBoolean, FimoDate, FimoMedia, FimoRichText, FimoString, getFimoParts, getFimoSource, hasFimoSource, isFimoDate, isFimoString, } from '../primitives/lib/primitives.js';
16
+ export { FIMO_PARTS, FIMO_SOURCE, FIMO_SOURCE_CONTEXT, FimoBoolean, FimoDate, FimoMedia, FimoRichText, FimoString, getFimoParts, getFimoSource, getFimoSourceContext, hasFimoSource, isFimoDate, isFimoString, } from '../primitives/lib/primitives.js';
@@ -1,9 +1,10 @@
1
1
  import { FimoBoolean, FimoDate, FimoMedia, FimoRichText, FimoString, type FimoRichTextContent } from '../primitives/lib/primitives.js';
2
2
  export interface SerializedMedia {
3
+ id?: string;
3
4
  url: string;
4
- alt: string;
5
- name: string;
6
- mime: string;
5
+ alt?: string;
6
+ name?: string;
7
+ mime?: string;
7
8
  width?: number;
8
9
  height?: number;
9
10
  }
@@ -18,10 +18,11 @@ export function serializeContent(value) {
18
18
  }
19
19
  if (value instanceof FimoMedia) {
20
20
  const media = {
21
+ ...(value.id === undefined ? {} : { id: value.id }),
21
22
  url: value.url,
22
- alt: value.alt,
23
- name: value.name,
24
- mime: value.mime,
23
+ ...(value.alt === undefined ? {} : { alt: value.alt }),
24
+ ...(value.name === undefined ? {} : { name: value.name }),
25
+ ...(value.mime === undefined ? {} : { mime: value.mime }),
25
26
  ...(value.width === undefined ? {} : { width: value.width }),
26
27
  ...(value.height === undefined ? {} : { height: value.height }),
27
28
  };
@@ -1,4 +1,4 @@
1
- export { FimoString, FimoDate, FimoMedia, FimoBoolean, FimoRichText, type FimoPolymorphicReference, type FimoReference, type FimoSource, type FimoPart, type FimoRichTextContent, FIMO_SOURCE, FIMO_PARTS, isFimoString, isFimoDate, hasFimoSource, getFimoSource, getFimoParts, } from './primitives.js';
1
+ export { FimoString, FimoDate, FimoMedia, FimoBoolean, FimoRichText, type FimoPolymorphicReference, type FimoReference, type FimoSource, type FimoPart, type FimoRichTextContent, type FimoSourceContext, type FimoSourceContextItem, FIMO_SOURCE, FIMO_SOURCE_CONTEXT, FIMO_PARTS, isFimoString, isFimoDate, hasFimoSource, getFimoSource, getFimoSourceContext, getFimoParts, } from './primitives.js';
2
2
  export { fimo } from './template.js';
3
3
  export { renderRichTextHtml, renderRichTextHtml as renderRichText } from './render-rich-text.js';
4
4
  export { parseImageHostSuffixes, resolveImagePresentation, type ImageFit, type ImageObjectFit, type ImagePresentation, type ImagePresentationOptions, } from './image-optimization.js';
@@ -1,5 +1,5 @@
1
1
  // Fimo - Source tracking for inline editing
2
- export { FimoString, FimoDate, FimoMedia, FimoBoolean, FimoRichText, FIMO_SOURCE, FIMO_PARTS, isFimoString, isFimoDate, hasFimoSource, getFimoSource, getFimoParts, } from './primitives.js';
2
+ export { FimoString, FimoDate, FimoMedia, FimoBoolean, FimoRichText, FIMO_SOURCE, FIMO_SOURCE_CONTEXT, FIMO_PARTS, isFimoString, isFimoDate, hasFimoSource, getFimoSource, getFimoSourceContext, getFimoParts, } from './primitives.js';
3
3
  export { fimo } from './template.js';
4
4
  export { renderRichTextHtml, renderRichTextHtml as renderRichText } from './render-rich-text.js';
5
5
  export { parseImageHostSuffixes, resolveImagePresentation, } from './image-optimization.js';
@@ -16,13 +16,25 @@ export interface FimoPolymorphicReference {
16
16
  id: string;
17
17
  }
18
18
  export declare const FIMO_SOURCE: unique symbol;
19
+ export declare const FIMO_SOURCE_CONTEXT: unique symbol;
19
20
  export declare const FIMO_PARTS: unique symbol;
21
+ export type FimoSourceContextItem = {
22
+ kind: 'relation';
23
+ source: string;
24
+ } | {
25
+ kind: 'component';
26
+ source: string;
27
+ _key: string;
28
+ _type?: string;
29
+ };
30
+ export type FimoSourceContext = readonly FimoSourceContextItem[];
20
31
  /**
21
32
  * A piece of a composed FimoString — maps text to its source
22
33
  */
23
34
  export type FimoPart = {
24
35
  text: string;
25
36
  source?: string;
37
+ context?: FimoSourceContext;
26
38
  };
27
39
  /**
28
40
  * FimoString - A string with source tracking
@@ -32,8 +44,9 @@ export type FimoPart = {
32
44
  */
33
45
  export declare class FimoString extends String {
34
46
  readonly [FIMO_SOURCE]: string | null;
47
+ readonly [FIMO_SOURCE_CONTEXT]?: FimoSourceContext;
35
48
  readonly [FIMO_PARTS]?: FimoPart[];
36
- constructor(value: string, source: string | null);
49
+ constructor(value: string, source: string | null, context?: FimoSourceContext);
37
50
  /**
38
51
  * Compose multiple tracked values into one FimoString.
39
52
  * Parts carry individual sources; the main FIMO_SOURCE is null.
@@ -50,28 +63,38 @@ export declare class FimoString extends String {
50
63
  */
51
64
  export declare class FimoDate extends Date {
52
65
  readonly [FIMO_SOURCE]: string;
53
- constructor(value: Date | string | number, source: string);
66
+ readonly [FIMO_SOURCE_CONTEXT]?: FimoSourceContext;
67
+ constructor(value: Date | string | number, source: string, context?: FimoSourceContext);
54
68
  get __fimo(): FimoSource;
55
69
  }
56
70
  /**
57
- * FimoMedia - Media object with source tracking
71
+ * FimoMedia - a media value with source tracking.
72
+ *
73
+ * A stored media value is a reference: the media-library `id`, the `url` that
74
+ * still resolves if the asset is deleted, and the `alt` the entry owns. The
75
+ * facts the asset owns, such as `name`, `mime`, and intrinsic `width`/`height`,
76
+ * are optional because a read only carries them when it named the field in
77
+ * `populate`. Renderers must treat them as enrichment.
58
78
  */
59
79
  export declare class FimoMedia {
80
+ readonly id?: string;
60
81
  readonly url: string;
61
- readonly alt: string;
62
- readonly name: string;
63
- readonly mime: string;
82
+ readonly alt?: string;
83
+ readonly name?: string;
84
+ readonly mime?: string;
64
85
  readonly width?: number;
65
86
  readonly height?: number;
66
87
  readonly [FIMO_SOURCE]: string;
88
+ readonly [FIMO_SOURCE_CONTEXT]?: FimoSourceContext;
67
89
  constructor(data: {
90
+ id?: string;
68
91
  url: string;
69
- alt: string;
70
- name: string;
71
- mime: string;
92
+ alt?: string;
93
+ name?: string;
94
+ mime?: string;
72
95
  width?: number;
73
96
  height?: number;
74
- }, source: string);
97
+ }, source: string, context?: FimoSourceContext);
75
98
  get __fimo(): FimoSource;
76
99
  }
77
100
  /**
@@ -80,7 +103,8 @@ export declare class FimoMedia {
80
103
  export declare class FimoBoolean {
81
104
  readonly value: boolean;
82
105
  readonly [FIMO_SOURCE]: string;
83
- constructor(value: boolean, source: string);
106
+ readonly [FIMO_SOURCE_CONTEXT]?: FimoSourceContext;
107
+ constructor(value: boolean, source: string, context?: FimoSourceContext);
84
108
  get __fimo(): FimoSource;
85
109
  valueOf(): boolean;
86
110
  }
@@ -105,7 +129,8 @@ export interface FimoRichTextContent {
105
129
  export declare class FimoRichText {
106
130
  readonly content: FimoRichTextContent;
107
131
  readonly [FIMO_SOURCE]: string;
108
- constructor(content: FimoRichTextContent, source: string);
132
+ readonly [FIMO_SOURCE_CONTEXT]?: FimoSourceContext;
133
+ constructor(content: FimoRichTextContent, source: string, context?: FimoSourceContext);
109
134
  get __fimo(): FimoSource;
110
135
  }
111
136
  /**
@@ -126,6 +151,8 @@ export declare function hasFimoSource(value: unknown): value is {
126
151
  * Get the source from a fimo value, or undefined if not tracked
127
152
  */
128
153
  export declare function getFimoSource(value: unknown): string | undefined;
154
+ /** Get the hidden relation/component ancestry from a wrapped value. */
155
+ export declare function getFimoSourceContext(value: unknown): FimoSourceContext | undefined;
129
156
  /**
130
157
  * Get the decomposed parts from a composed FimoString, or undefined
131
158
  */