fimo 0.8.0 → 0.9.0-experimental.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/assets/content-templates/hooks-template.eta +4 -2
  2. package/assets/content-templates/types-template.eta +66 -1
  3. package/assets/skills/fimo/SKILL.md +1 -1
  4. package/assets/skills/fimo/references/content.md +86 -15
  5. package/assets/skills/fimo-cli/SKILL.md +6 -2
  6. package/assets/skills/fimo-cli/references/content.md +61 -2
  7. package/assets/skills/fimo-cli/references/integrations/firecrawl.md +20 -4
  8. package/assets/skills/fimo-cli/references/integrations.md +5 -1
  9. package/assets/skills/fimo-cli/references/webhooks.md +162 -0
  10. package/assets/skills/fimo-migration/SKILL.md +22 -2
  11. package/assets/skills/fimo-migration/references/content.md +24 -0
  12. package/assets/skills/fimo-migration/references/manifest.md +35 -0
  13. package/assets/skills/fimo-migration/references/qa.md +24 -2
  14. package/assets/skills/fimo-migration/references/workflow.md +12 -1
  15. package/assets/skills/fimo-migration/references/wrap.md +617 -0
  16. package/assets/skills/fimo-studio/SKILL.md +5 -3
  17. package/assets/skills/fimo-studio/references/cms.md +9 -1
  18. package/assets/skills/fimo-studio/references/visual-editing.md +5 -3
  19. package/assets/skills/fimo-studio/references/webhooks.md +42 -0
  20. package/dist/build/vite/plugins/translations.d.ts +2 -8
  21. package/dist/build/vite/plugins/translations.js +3 -121
  22. package/dist/cli/bundle.json +1 -1
  23. package/dist/cli/index.js +1051 -144
  24. package/dist/runtime/app/FimoProviders.d.ts +1 -11
  25. package/dist/runtime/app/FimoProviders.js +2 -23
  26. package/dist/runtime/astro/index.d.ts +2 -0
  27. package/dist/runtime/astro/index.js +3 -0
  28. package/dist/runtime/astro/server.d.ts +3 -0
  29. package/dist/runtime/astro/server.js +6 -0
  30. package/dist/runtime/content/client/internal.js +1 -0
  31. package/dist/runtime/content/client/types.d.ts +16 -0
  32. package/dist/runtime/i18n/FimoLocaleContext.js +1 -0
  33. package/dist/runtime/next/FimoNextProviders.d.ts +24 -0
  34. package/dist/runtime/next/FimoNextProviders.js +20 -0
  35. package/dist/runtime/next/FimoNextScripts.d.ts +10 -0
  36. package/dist/runtime/next/FimoNextScripts.js +19 -0
  37. package/dist/runtime/next/index.d.ts +14 -0
  38. package/dist/runtime/next/index.js +14 -0
  39. package/dist/runtime/next/server.d.ts +2 -0
  40. package/dist/runtime/next/server.js +4 -0
  41. package/dist/runtime/nuxt/composables.d.ts +9 -0
  42. package/dist/runtime/nuxt/composables.js +57 -0
  43. package/dist/runtime/nuxt/index.d.ts +3 -0
  44. package/dist/runtime/nuxt/index.js +6 -0
  45. package/dist/runtime/nuxt/server.d.ts +3 -0
  46. package/dist/runtime/nuxt/server.js +6 -0
  47. package/dist/runtime/primitives/labels-hook.d.ts +33 -0
  48. package/dist/runtime/primitives/labels-hook.js +41 -0
  49. package/dist/runtime/primitives/labels-store.d.ts +12 -0
  50. package/dist/runtime/primitives/labels-store.js +60 -0
  51. package/dist/runtime/primitives/lib/useFimoLiveValue.d.ts +5 -3
  52. package/dist/runtime/primitives/lib/useFimoLiveValue.js +11 -4
  53. package/dist/runtime/primitives/translations.d.ts +1 -27
  54. package/dist/runtime/primitives/translations.js +7 -85
  55. package/dist/runtime/react/FimoSpaProviders.d.ts +36 -0
  56. package/dist/runtime/react/FimoSpaProviders.js +63 -0
  57. package/dist/runtime/react/index.d.ts +33 -0
  58. package/dist/runtime/react/index.js +39 -0
  59. package/dist/runtime/remix/FimoRemixProviders.d.ts +16 -0
  60. package/dist/runtime/remix/FimoRemixProviders.js +12 -0
  61. package/dist/runtime/remix/FimoRemixScripts.d.ts +10 -0
  62. package/dist/runtime/remix/FimoRemixScripts.js +19 -0
  63. package/dist/runtime/remix/index.d.ts +12 -0
  64. package/dist/runtime/remix/index.js +11 -0
  65. package/dist/runtime/remix/server.d.ts +2 -0
  66. package/dist/runtime/remix/server.js +2 -0
  67. package/dist/runtime/shared/display-routes.d.ts +14 -0
  68. package/dist/runtime/shared/display-routes.js +25 -0
  69. package/dist/runtime/shared/fimo-config.server.js +1 -0
  70. package/dist/runtime/shared/fimo-config.types.d.ts +2 -0
  71. package/dist/runtime/shared/i18n-messages.d.ts +29 -0
  72. package/dist/runtime/shared/i18n-messages.js +79 -0
  73. package/dist/runtime/shared/labels-fetch.pure.d.ts +17 -0
  74. package/dist/runtime/shared/labels-fetch.pure.js +71 -0
  75. package/dist/runtime/shared/labels-fetch.server.d.ts +7 -0
  76. package/dist/runtime/shared/labels-fetch.server.js +97 -0
  77. package/dist/runtime/shared/labels-t.pure.d.ts +2 -0
  78. package/dist/runtime/shared/labels-t.pure.js +10 -0
  79. package/dist/runtime/strapi/client.d.ts +30 -0
  80. package/dist/runtime/strapi/client.js +89 -0
  81. package/dist/runtime/strapi/index.d.ts +5 -0
  82. package/dist/runtime/strapi/index.js +6 -0
  83. package/dist/runtime/strapi/reshape.d.ts +46 -0
  84. package/dist/runtime/strapi/reshape.js +114 -0
  85. package/dist/runtime/svelte/index.d.ts +3 -0
  86. package/dist/runtime/svelte/index.js +7 -0
  87. package/dist/runtime/svelte/server.d.ts +3 -0
  88. package/dist/runtime/svelte/server.js +6 -0
  89. package/dist/runtime/vue/index.d.ts +3 -0
  90. package/dist/runtime/vue/index.js +5 -0
  91. package/dist/runtime/vue/server.d.ts +3 -0
  92. package/dist/runtime/vue/server.js +6 -0
  93. package/dist/runtime/wrapped/FimoFileLink.d.ts +17 -0
  94. package/dist/runtime/wrapped/FimoFileLink.js +19 -0
  95. package/dist/runtime/wrapped/FimoMediaImage.d.ts +14 -0
  96. package/dist/runtime/wrapped/FimoMediaImage.js +20 -0
  97. package/dist/runtime/wrapped/FimoMediaVideo.d.ts +14 -0
  98. package/dist/runtime/wrapped/FimoMediaVideo.js +20 -0
  99. package/dist/runtime/wrapped/FimoRichTextBlock.d.ts +23 -0
  100. package/dist/runtime/wrapped/FimoRichTextBlock.js +22 -0
  101. package/dist/runtime/wrapped/FimoText.d.ts +22 -0
  102. package/dist/runtime/wrapped/FimoText.js +20 -0
  103. package/dist/runtime/wrapped/WrappedContentContext.d.ts +28 -0
  104. package/dist/runtime/wrapped/WrappedContentContext.js +30 -0
  105. package/dist/runtime/wrapped/WrappedProviders.d.ts +33 -0
  106. package/dist/runtime/wrapped/WrappedProviders.js +101 -0
  107. package/dist/runtime/wrapped/dom.d.ts +4 -0
  108. package/dist/runtime/wrapped/dom.js +23 -0
  109. package/dist/runtime/wrapped/server.d.ts +77 -0
  110. package/dist/runtime/wrapped/server.js +237 -0
  111. package/dist/scripts/apply-wrap-templates.d.ts +61 -0
  112. package/dist/scripts/apply-wrap-templates.js +132 -0
  113. package/dist/scripts/apply-wrap.d.ts +46 -0
  114. package/dist/scripts/apply-wrap.js +278 -0
  115. package/dist/scripts/cms/discover.d.ts +2 -0
  116. package/dist/scripts/cms/discover.js +17 -0
  117. package/dist/scripts/cms/example-content.d.ts +33 -0
  118. package/dist/scripts/cms/example-content.js +163 -0
  119. package/dist/scripts/cms/strapi/detect.d.ts +2 -0
  120. package/dist/scripts/cms/strapi/detect.js +186 -0
  121. package/dist/scripts/cms/strapi/index.d.ts +3 -0
  122. package/dist/scripts/cms/strapi/index.js +25 -0
  123. package/dist/scripts/cms/strapi/list-source.d.ts +17 -0
  124. package/dist/scripts/cms/strapi/list-source.js +90 -0
  125. package/dist/scripts/cms/strapi/schema-map.d.ts +9 -0
  126. package/dist/scripts/cms/strapi/schema-map.js +279 -0
  127. package/dist/scripts/cms/strapi/shapes.d.ts +23 -0
  128. package/dist/scripts/cms/strapi/shapes.js +38 -0
  129. package/dist/scripts/cms/strapi/touchpoints.d.ts +31 -0
  130. package/dist/scripts/cms/strapi/touchpoints.js +145 -0
  131. package/dist/scripts/cms/strapi/wire-client.d.ts +15 -0
  132. package/dist/scripts/cms/strapi/wire-client.js +107 -0
  133. package/dist/scripts/cms/strapi/wiring.d.ts +17 -0
  134. package/dist/scripts/cms/strapi/wiring.js +46 -0
  135. package/dist/scripts/cms/types.d.ts +145 -0
  136. package/dist/scripts/cms/types.js +11 -0
  137. package/dist/scripts/detect-framework.d.ts +39 -0
  138. package/dist/scripts/detect-framework.js +133 -0
  139. package/dist/scripts/draft-rows.d.ts +19 -0
  140. package/dist/scripts/draft-rows.js +135 -0
  141. package/dist/scripts/draft-schemas.d.ts +27 -0
  142. package/dist/scripts/draft-schemas.js +188 -0
  143. package/dist/scripts/extract-translations.js +16 -11
  144. package/dist/scripts/frameworks/astro/routes.d.ts +3 -0
  145. package/dist/scripts/frameworks/astro/routes.js +55 -0
  146. package/dist/scripts/frameworks/astro/template-style.d.ts +7 -0
  147. package/dist/scripts/frameworks/astro/template-style.js +19 -0
  148. package/dist/scripts/frameworks/discover.d.ts +70 -0
  149. package/dist/scripts/frameworks/discover.js +178 -0
  150. package/dist/scripts/frameworks/next/rewire.d.ts +30 -0
  151. package/dist/scripts/frameworks/next/rewire.js +258 -0
  152. package/dist/scripts/frameworks/next/routes.d.ts +17 -0
  153. package/dist/scripts/frameworks/next/routes.js +134 -0
  154. package/dist/scripts/frameworks/next/wiring.d.ts +33 -0
  155. package/dist/scripts/frameworks/next/wiring.js +186 -0
  156. package/dist/scripts/frameworks/nuxt/routes.d.ts +3 -0
  157. package/dist/scripts/frameworks/nuxt/routes.js +54 -0
  158. package/dist/scripts/frameworks/nuxt/template-style.d.ts +9 -0
  159. package/dist/scripts/frameworks/nuxt/template-style.js +27 -0
  160. package/dist/scripts/frameworks/remix/routes.d.ts +3 -0
  161. package/dist/scripts/frameworks/remix/routes.js +50 -0
  162. package/dist/scripts/frameworks/support.d.ts +75 -0
  163. package/dist/scripts/frameworks/support.js +165 -0
  164. package/dist/scripts/frameworks/sveltekit/routes.d.ts +3 -0
  165. package/dist/scripts/frameworks/sveltekit/routes.js +50 -0
  166. package/dist/scripts/frameworks/sveltekit/template-style.d.ts +9 -0
  167. package/dist/scripts/frameworks/sveltekit/template-style.js +54 -0
  168. package/dist/scripts/frameworks/vite/routes.d.ts +15 -0
  169. package/dist/scripts/frameworks/vite/routes.js +93 -0
  170. package/dist/scripts/frameworks/vite/wiring.d.ts +10 -0
  171. package/dist/scripts/frameworks/vite/wiring.js +99 -0
  172. package/dist/scripts/i18n-namespace.d.ts +27 -0
  173. package/dist/scripts/i18n-namespace.js +155 -0
  174. package/dist/scripts/lib/validation-report.d.ts +1 -0
  175. package/dist/scripts/lib/validation-report.js +3 -0
  176. package/dist/scripts/render-parity/capture.d.ts +56 -0
  177. package/dist/scripts/render-parity/capture.js +366 -0
  178. package/dist/scripts/render-parity/compare.d.ts +59 -0
  179. package/dist/scripts/render-parity/compare.js +0 -0
  180. package/dist/scripts/render-parity/normalize-html.d.ts +13 -0
  181. package/dist/scripts/render-parity/normalize-html.js +223 -0
  182. package/dist/scripts/render-parity/types.d.ts +41 -0
  183. package/dist/scripts/render-parity/types.js +1 -0
  184. package/dist/scripts/scan-wrappable.d.ts +196 -0
  185. package/dist/scripts/scan-wrappable.js +1232 -0
  186. package/dist/scripts/validate-route-metadata.js +11 -0
  187. package/dist/scripts/validate-schemas.js +42 -1
  188. package/package.json +84 -4
  189. package/release.json +2 -2
  190. package/templates/react-router/package.json +1 -1
@@ -0,0 +1,617 @@
1
+ # Wrap Mode
2
+
3
+ Wrap mode is the default migration for a **source-backed, working JS codebase**.
4
+ The supported-framework list lives in the CLI, not in this document: `fimo init`
5
+ detects the framework and says whether it can adopt the site (and offers the
6
+ rebuild path when it cannot). The contract below is framework-neutral;
7
+ framework-specific notes live in the "Framework notes" section at the end.
8
+ The user's stack stays. Nothing is rebuilt. The migration is a mechanical
9
+ wrapping pass that makes existing content editable from Fimo, plus a mandatory
10
+ content-seeding pass so Fimo becomes the source of truth.
11
+
12
+ **Never rewrite routing, styling, structure, or component logic.** The final
13
+ diff must contain only: the one-time wiring, wrapper components, label calls,
14
+ config files, and lockfile/dependency changes. If a change does not make
15
+ content editable, it does not belong in a wrap migration.
16
+
17
+ ## When wrap does NOT apply
18
+
19
+ - URL-only migrations (no code) → rebuild path (`fimo migrate <url>`).
20
+ - Builder exports / static HTML dumps → rebuild path.
21
+ - Codebases that do not build/run as-is → fix-first is the owner's call;
22
+ wrap assumes a working site.
23
+ - Non-JS backends (PHP/WordPress/Drupal) → not supported yet.
24
+
25
+ ## Phases
26
+
27
+ ### W0. Agent-driven run (default)
28
+
29
+ When YOU (the coding agent) are asked to migrate an adopted site, you own the
30
+ whole loop — the human's only remaining jobs are `fimo init` (done before you)
31
+ and the final `fimo deploy` (never yours). The deterministic machine does the
32
+ heavy lifting; you run it, relay the user's decisions, handle the judgment
33
+ items, and loop until the gates say done:
34
+
35
+ `fimo init` writes a starter `.fimo/migration/wrap/agent-prompt.md` and
36
+ `fimo migrate` regenerates it with the full work order — that file is the
37
+ source of truth at every step; the user's entry prompt just points to it.
38
+
39
+ 1. Preflight: `npx fimo whoami`. If it fails, STOP and ask the user to run
40
+ `fimo login` — do not investigate credentials or probe backends.
41
+ 2. Run `npx fimo migrate`. Non-interactive shells are the normal agent case:
42
+ it scans, wires, captures the render baseline, applies the safe transforms
43
+ (codemod), seeds staged labels, imports env keys, and detects collections
44
+ and external data sources. Recover mechanical failures yourself (missing
45
+ install, pnpm `minimumReleaseAgeExclude`, broken dev script) and re-run.
46
+ 3. Read the output facts and `.fimo/migration/manifest.json` →
47
+ `wrap.dataSources`: entries marked `decidedBy: "default"` are USER
48
+ decisions that could not be asked in your shell. Ask the user in your chat
49
+ NOW — before any wrapping work (one question per host: keep live, or
50
+ ingest into Fimo?), update the manifest (`decision`, `decidedBy: "user"`),
51
+ and re-run `npx fimo migrate` if anything changed (idempotent).
52
+ 4. Execute `.fimo/migration/wrap/agent-prompt.md`: the judgment items
53
+ (attributes, next/image, formatted text), the detected collection
54
+ candidates, and any user-approved ingestion. Collection candidates are
55
+ CLASSIFIED before anything is created: navigation links, style-bearing
56
+ lists, page furniture, and decorative imagery are NOT collections — ask
57
+ the user one batched question (candidate list + your recommendation),
58
+ keep-in-code candidates get their strings wrapped with `t()` (the array
59
+ stays), and only user-confirmed content lists become Fimo collections.
60
+ Candidates the scan annotated with REFERENCES (field values pointing at
61
+ another candidate's slugs/names) migrate with `reference` fields —
62
+ `{ "type": "reference", "target": "<Uid>" }`, `"many": true` for arrays,
63
+ `targets: [...]` for genuinely mixed slots — never as plain strings, and
64
+ their entries are created in dependency order (targets first, then
65
+ referencing entries carrying the recorded documentIds).
66
+ NAMING STANDARD (this is the judgment the whole flow exists for — hold
67
+ the bar): collection Uids are SINGULAR PascalCase DOMAIN terms — what the
68
+ site's owner would call one item — never the source variable or file name
69
+ when a better domain word exists (a `posts.json` of episode notes is
70
+ `Episode`, not `Post`; `items` rendered as staff cards is `TeamMember`).
71
+ Field names stay camelCase and faithful to the source unless the source
72
+ name is meaningless. Machine drafts under
73
+ `.fimo/migration/schema-drafts/` carry PROVISIONAL names precisely so you
74
+ rename them. Adopt them in ONE command:
75
+ `npx fimo schemas adopt-drafts --rename "Prov=Final,…" --drop "Junk,…"`
76
+ (copies kept drafts to src/schemas, rewrites reference targets through
77
+ the renames, renames the staged rows files in step, and pushes in
78
+ dependency order). Each draft carries `source`, `rowCount`, and a
79
+ `sample` row — classify and name from the drafts ALONE, without opening
80
+ the source data files. SEEDING is then ONE command for everything:
81
+ `npx fimo migrate seed`
82
+ — it seeds every `.fimo/migration/rows/*.rows.json` in
83
+ reference-dependency order (targets first, automatically, chunked at
84
+ 1000 rows/request); the staged rows are already transformed (media
85
+ envelopes, richtext docs, dates) and the SERVER resolves reference
86
+ source values to documentIds, reporting per-field resolved/dangling
87
+ counts. Do not write mapping scripts, do not seed staged types
88
+ one-by-one, do not re-transform from the source files. PAGE REWIRING is
89
+ also mechanical where the shape is safe (Next app router): `fimo migrate`
90
+ switches pages rendering a staged collection to
91
+ `wrapCollection('<Uid>', sourceRows)` — a Fimo overlay onto the retained
92
+ source rows (per-field type-matched fallback, source order), in-code
93
+ arrays extracted to `src/data/*.json` first. `adopt-drafts` renames the
94
+ call-site uids in step. Only pages listed with a bail reason in the
95
+ migrate warnings need hand wiring — reuse the same `wrapCollection`
96
+ pattern there. Your FINAL REPORT must end with the naming table: every
97
+ collection Uid (with the source it came from) and every reference field
98
+ with its target type(s) and cardinality (single / many / polymorphic).
99
+ DANGLING references (source values pointing at targets that do not
100
+ exist) are handled at TWO layers: in the DATABASE store them null/empty
101
+ and document them in the manifest (`wrap.unresolvedReferences`) — never
102
+ invent placeholder targets; at RENDER time fall back per-field to the
103
+ raw source value (the retained fallback array's slug/string) whenever
104
+ the Fimo reference is null, so the page shows exactly what the source
105
+ site showed and render parity holds. Honest data AND honest rendering
106
+ are not in tension.
107
+ This deliberately differs from `content.md`'s rebuild-path rule ("prefer
108
+ modeling as a collection"): in wrap mode the user's code stays primary —
109
+ the rebuild rule does NOT apply here.
110
+ On simple sites the machine may leave NOTHING to do: the regenerated
111
+ prompt then says so explicitly ("already done by the machine") — your job
112
+ collapses to running the check and reporting. Never invent work the
113
+ prompt does not list.
114
+ When you start the dev server yourself, pin it to a free port (PORT env
115
+ or --port flag) — never the Fimo API's own port (apiUrl in
116
+ .fimo.settings.json; localhost:3000 in local dev). Next and Nuxt default
117
+ to 3000 and silently shadow the API for the whole machine.
118
+ 5. Loop `npx fimo migrate wrap --check` after every batch of fixes until it
119
+ exits 0. Make the loop cheap: start the dev server once in the background
120
+ at the beginning of your work and pass it to every check with
121
+ `--server http://localhost:<port>` — the warm server recompiles
122
+ incrementally, so each check costs seconds instead of a cold boot. Never
123
+ recapture the baseline, never add a convenience exemption, never bypass a
124
+ gate. If the same gate fails twice with the same error, stop and report
125
+ honestly.
126
+ 6. Report the summary and hand back: deploying is the user's call
127
+ (`npx fimo deploy` — never run it yourself).
128
+
129
+ ### W1. Adopt
130
+
131
+ Connect the existing site to a Fimo project. From the site directory:
132
+
133
+ ```bash
134
+ fimo init # interactive: detects the framework, confirms the adopt
135
+ fimo init . -y # non-interactive (agents/CI): explicit dir = consent
136
+ ```
137
+
138
+ Adoption creates the project, writes `fimo-config.json` (`framework` +
139
+ scanned `routes` for the Studio page switcher), `.fimo.settings.json`, the
140
+ framework env flavor (`NEXT_PUBLIC_FIMO_API_URL`,
141
+ `NEXT_PUBLIC_FIMO_PREVIEW_SCRIPT_URL` for Next), wires the `fimo` git remote
142
+ into the existing repo, and pushes the site as-is. No template is copied.
143
+
144
+ If the user's config sets CSP headers with `frame-ancestors`, the Fimo Studio
145
+ origins must be permitted or the preview iframe will be blocked. Framework
146
+ quirks (preview origin allowlists, dev-server flags) are listed under
147
+ "Framework notes" at the end of this document.
148
+
149
+ Sites whose pages fetch external APIs server-side (GitHub, CMSes, analytics)
150
+ hang in the sandbox when their API keys are absent — the page never answers
151
+ and the preview proxy returns 502/504 while the dev server logs
152
+ `Request timed out … Retrying`. `fimo migrate` imports the keys declared in
153
+ the project's `.env`/`.env.local` into the Fimo project automatically
154
+ (sensitive-looking keys as secrets, the rest as variables; existing values
155
+ are never overwritten; Fimo-owned keys are skipped). If that step was
156
+ unavailable (not signed in), push them manually with
157
+ `fimo secrets set <KEY> <value>` / `fimo vars set`, then redeploy.
158
+
159
+ ### W2. Scan + automatic wiring
160
+
161
+ ```bash
162
+ fimo migrate # inside an adopted JS codebase this IS the wrap flow
163
+ fimo migrate --apply # + apply the provably-safe transforms automatically
164
+ ```
165
+
166
+ `--apply` performs the deterministic wraps itself in seconds — plain JSX
167
+ text → `FimoText` (whitespace preserved), `<img src="literal">` →
168
+ `FimoMediaImage`, file links → `FimoFileLink` — with deterministic key
169
+ naming and the generated labels staged AND seeded. The render-parity
170
+ baseline is always captured before any transform. Everything needing
171
+ judgment (attributes, formatted text, `next/image`, collections) is refused
172
+ with a reason and stays in the plan for the agent.
173
+
174
+ (`fimo migrate wrap` is the explicit form; `fimo migrate <url>` remains the
175
+ crawl-and-rebuild path for URL-only migrations.)
176
+
177
+ Writes `.fimo/migration/plan.json` (every unwrapped item with
178
+ `kind`/`file`/`line`/`preview`) and `.fimo/migration/wrap/agent-prompt.md`.
179
+ The scan finds hardcoded JSX text, `img`/`Image`/`video` sources, file links
180
+ (pdf/zip/docx/…), and user-visible attribute strings (`placeholder`, `title`,
181
+ `aria-label`). Content already inside Fimo wrappers or `t()` calls counts as
182
+ covered.
183
+
184
+ The command also performs the deterministic wiring itself: it adds the `fimo`
185
+ dependency (platform-pinned) and, for Next.js, inserts `allowedDevOrigins`
186
+ into the next config when missing. Run the project's package manager install
187
+ afterwards.
188
+
189
+ Known friction: pnpm ≥ 11 ships a `minimumReleaseAge` supply-chain policy
190
+ that refuses packages younger than the cutoff — experimental `fimo` builds
191
+ are minutes old by nature. If install fails with
192
+ `ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION`, propose adding to the project's
193
+ `pnpm-workspace.yaml`:
194
+
195
+ ```yaml
196
+ minimumReleaseAgeExclude:
197
+ - fimo
198
+ ```
199
+
200
+ ### W3. Wire the layout (one-time)
201
+
202
+ - Root layout (Next.js app router):
203
+
204
+ ```tsx
205
+ import { FimoNextProviders, FimoNextScripts } from 'fimo/next';
206
+ import { loadFimoConfig, loadFimoLabels } from 'fimo/next/server';
207
+
208
+ const config = loadFimoConfig();
209
+ const labels = await loadFimoLabels();
210
+ // <head><FimoNextScripts /></head>
211
+ // <body><FimoNextProviders config={config} labels={labels}>{children}</FimoNextProviders></body>
212
+ ```
213
+
214
+ - React (framework-agnostic) uses `fimo/react`; React Router projects keep
215
+ `fimo/react-router`.
216
+
217
+ RSC rule: the wrapper primitives are client components. Server components may
218
+ render them directly as JSX children; do not call hooks like `useLabels()`
219
+ outside client components (`'use client'`).
220
+
221
+ ### W4. Wrap
222
+
223
+ Work through `plan.json` item by item:
224
+
225
+ | Item kind | Wrap with |
226
+ | ----------------------------------- | -------------------------------------------------------------------------------------- |
227
+ | text (plain) | `<FimoText id="…">original text</FimoText>` |
228
+ | text (formatted: strong/em/u/links) | `<FimoRichTextBlock id="…">…original markup…</FimoRichTextBlock>` — marks must survive |
229
+ | attribute | `const { t } = useLabels()` then `placeholder={String(t('key', 'original value'))}` |
230
+ | image | `<FimoMediaImage id="…" src="/original.png" …/>` |
231
+ | video | `<FimoMediaVideo id="…" src="/original.mp4" …/>` |
232
+ | file-link | `<FimoFileLink id="…" href="/original.pdf">…</FimoFileLink>` |
233
+
234
+ Rules:
235
+
236
+ - The original value always stays inline as the fallback (child or prop). Wrap
237
+ fallbacks are deliberate: they keep the site rendering without Fimo
238
+ connectivity. This differs from Fimo-template projects, where inline
239
+ fallbacks are forbidden by lint — do not "fix" wrap fallbacks to match.
240
+ - Key naming: `<route>.<section>.<n>` — `home.hero.title`,
241
+ `pricing.plans.starter.description`. Stable, human-readable, no indexes
242
+ unless the content is genuinely a list.
243
+ - Repeated hardcoded arrays of 3+ structurally identical items (cards, tiles)
244
+ are still collections: model a schema + entries as in
245
+ `references/content.md`, render from `fetchEntries` (`fimo/next/server`).
246
+ Wrapping each array literal string individually is acceptable only as a
247
+ first pass and must be recorded as an unresolved modeling item.
248
+
249
+ ### W5. Seed (MANDATORY)
250
+
251
+ Fallbacks are resilience, not storage. Every wrapped item is seeded into Fimo:
252
+
253
+ - Labels: every `FimoText`/`t()` key with its original string via
254
+ `fimo labels set-many --file <json>`.
255
+ - Media/files: `fimo assets upload <path>` per item, then a `WrappedMedia`
256
+ entry per wrapper id (`key` = wrapper id, `image` = the asset) via
257
+ `fimo schemas push` + `fimo entries create` (or, for anything beyond ~20
258
+ rows, `fimo entries create-many <Uid> --file <rows.json>` — a JSON array of
259
+ bare data objects, chunked through the batch endpoint with per-row
260
+ results; never loop single creates over a large collection).
261
+ - Collections with machine-staged rows: `fimo migrate seed` — one command
262
+ seeds every `.fimo/migration/rows/*.rows.json` in reference-dependency
263
+ order at 1000 rows/request. Each file's `references` descriptor has the
264
+ server resolve reference source values (slugs) to documentIds;
265
+ self-references within one file resolve only against rows already
266
+ inserted (earlier chunks), so a fully intra-file cycle surfaces as
267
+ dangling counts — record those in the manifest like any dangling
268
+ reference. (`fimo entries create-many <Uid> --file <rows.json>` remains
269
+ the per-type primitive for rows you build yourself.)
270
+ - Rich text: `WrappedRichText` entries (`key`, `body` = TipTap JSON converted
271
+ from the original markup; bold/italic/underline/link marks preserved).
272
+
273
+ A wrap migration whose labels/entries were never created in Fimo is
274
+ `draft-only`, exactly like a rebuild migration with un-imported seed JSON.
275
+
276
+ ### W6. Verify — four machine-enforced gates
277
+
278
+ `fimo migrate wrap --check` is the definition of done. It fails until all
279
+ four gates pass:
280
+
281
+ 1. **Coverage** — zero unwrapped items. Deliberate exceptions are recorded in
282
+ the ledger and the gate honors them: add to
283
+ `.fimo/migration/manifest.json` → `wrap.exemptions[]` an entry
284
+ `{ file, kind, preview, reason, status }` where `(file, kind, preview)`
285
+ matches the plan item exactly (a content change invalidates the exemption
286
+ so the item resurfaces).
287
+ 2. **Render parity** — the check boots the project's dev server and compares
288
+ every route's rendered text, inline marks, media srcs, and link hrefs
289
+ against the pre-wrap baseline that `fimo migrate` captured
290
+ (`.fimo/migration/parity-baseline.json`). Drift is reported per route and
291
+ block. Dynamic routes are captured by substituting the project's default
292
+ locale for `[locale]`-style params (so an all-`[locale]` i18n site is
293
+ verified, not skipped); routes with content params (`[slug]`, `[id]`) that
294
+ have no safe default still skip — call those out as UNVERIFIED in the
295
+ handoff. In a monorepo, the baseline boot builds the app's workspace deps
296
+ first (root `build` / `turbo run build`) so `next dev` does not die on
297
+ `Cannot find module '@repo/…'`. Run the check right after wrapping (strict,
298
+ pre-seed) and again after seeding. If the baseline is missing, capture one
299
+ from the pre-wrap git state: `git stash && npx fimo migrate snapshot && git stash pop`.
300
+ Text the site renders dynamically BY DESIGN (live clock, visitor counter)
301
+ drifts on every capture: record `wrap.parityIgnores[]`
302
+ `{ pattern, reason }` in the manifest — a full-match regex both sides must
303
+ match to compare equal. A route rendering a keep-live external source
304
+ (`wrap.dataSources` decision `live`) drifts wholesale: record
305
+ `{ route, reason }` instead (counted as unverified, never silently
306
+ clean). Never use either form to hide drift caused by your edit.
307
+ 3. **Seeding** — every wrapped key exists in the Fimo database (labels and
308
+ WrappedMedia/WrappedRichText entries), verified via the API. Fallbacks are
309
+ not storage. Labels-decision data files (below) are verified too via
310
+ their recorded `labelPrefix`.
311
+ 4. **Wiring** — the root providers are mounted (framework marker file, e.g.
312
+ `FimoNextProviders` in the app-router layout, `FimoSpaProviders` in the
313
+ SPA entry, `FimoRemixProviders` in `app/root`, `fimoLabels` in the
314
+ SvelteKit root `+layout.server`). Without them every wrapped value renders
315
+ its fallback forever and no other gate notices — coverage counts wraps,
316
+ parity sees identical fallbacks, seeding is database-side. `fimo migrate`
317
+ applies this wiring itself for safe shapes (Next.js app router, Vite React
318
+ SPA); the gate enforces the rest. Enforced only once items are wrapped;
319
+ Next pages-router-only projects get a warning, not a failure.
320
+
321
+ **Config-driven sites** (all copy in a data file, templates render only
322
+ expressions): the scanner lists copy-bearing `.json`/`.yml` files and
323
+ markdown groups in the plan (`dataFiles`) and the coverage gate fails until
324
+ each has a decision in the manifest: `wrap.dataFiles[]`
325
+ `{ file, decision: "labels" | "entries" | "ignore", reason, labelPrefix? }`.
326
+ LOCAL CONTENT ALWAYS MIGRATES INTO FIMO — do not ask the user whether to
327
+ migrate local files (only external live sources are a user question).
328
+ `labels` = flat copy documents: seed each prose string as
329
+ `<labelPrefix><json.path>` (arrays by index) and deep-merge label values over
330
+ the parsed file at its server-side loader, keeping the file as fallback;
331
+ `entries` = repeated documents/structures, INCLUDING markdown blog posts:
332
+ the machine stages these as schema drafts + rows (a same-shaped group of ≥2
333
+ md/mdx docs → frontmatter parsed into typed fields, `body` kept verbatim as a
334
+ string so the site's own renderer still works, `slug` from the filename), so
335
+ you adopt them with `adopt-drafts` + `migrate seed` like any collection — no
336
+ hand-authoring, no re-parsing. Record the `entries` decision and rewire the
337
+ reading route to `fetchEntries()` with the files as fallback; `ignore` =
338
+ strictly non-content data (fixtures, geo payloads) — never a deferral.
339
+ GeoJSON/TopoJSON are auto-skipped.
340
+
341
+ Never bypass a failing gate (`--no-parity` / `--skip-seeding` are for the
342
+ owner to decide, not the agent). Additional manual checks:
343
+
344
+ - The site builds and runs **with its own toolchain** (`next build`,
345
+ `npm run dev`, …) — never assume Vite.
346
+ - Diff audit: the migration diff contains only wiring, wrappers, label calls,
347
+ config, and dependency changes.
348
+ - Then `fimo deploy` (allowed in wrap mode: it pushes the preview env and
349
+ boots the sandbox preview — it does not publish). Verify in Fimo Studio that
350
+ the preview renders and Edit mode can select and edit wrapped content, and
351
+ that an edited label round-trips (`fimo labels set <key> "new"` → reload →
352
+ rendered). `fimo publish` / `fimo deploy --publish` remain owner-only.
353
+
354
+ ### W7. Unwire the previous CMS — the cutover (when scaffolded)
355
+
356
+ Injecting Fimo is only **half** a migration. If a CMS was scaffolded (W2/W5),
357
+ the site now READS example content from Fimo but is still **wired to the old
358
+ CMS**: the generated Fimo adapter falls back to it, the SDK/client is still
359
+ imported, and the CMS env is still present. The migration is not done until a
360
+ plain `pnpm dev` runs **purely on Fimo, with zero requests to the old CMS**.
361
+ That cutover is the "wow" — deliver it.
362
+
363
+ This phase is the agent's: every codebase talks to its CMS differently (bare
364
+ `fetch`, an SDK client, axios, GraphQL, RSC loaders, image hosts), so the
365
+ replacement needs judgment. The machine hands you the worklist; you do the work.
366
+
367
+ 1. **Read the worklist.** `npx fimo migrate cutover` lists every remaining
368
+ coupling — SDK/client/type deps, source lines that reach the CMS (env /
369
+ content-type IDs / SDK import), and leftover env keys. The generated agent
370
+ prompt embeds the same list under **Unwire &lt;CMS&gt;**.
371
+ 2. **Replace the data-access layer.** Prefer rewriting the CMS **client module**
372
+ (the one file that instantiates the SDK / holds the fetch helpers) into a
373
+ Fimo-backed adapter exposing the **same interface**, so every caller is fixed
374
+ by a single change. Read from Fimo with `fetchEntries` (`fimo/<framework>/server`).
375
+ 3. **Sever the fallthrough.** The generated Fimo adapter falls back to the CMS
376
+ for unmapped reads — a scaffold crutch. Once the client is Fimo-backed, drop
377
+ the fallthrough so an unmapped read fails loudly, never silently hits the CMS.
378
+ 4. **Remove the coupling.** Delete the CMS SDK/client/type dependencies and the
379
+ base-URL / token env, and fix any standalone routing the CMS handled (e.g. a
380
+ `/` → `/[locale]` redirect its middleware provided).
381
+ 5. **Verify — the cutover gate.** `npx fimo migrate cutover --check` must report
382
+ a **clean** cutover (0 couplings). Then `pnpm dev` and confirm the network
383
+ tab shows **zero** requests to the old CMS. Record the cutover in the manifest.
384
+
385
+ Detection keys on actual CMS references, not filenames — a CMS client module you
386
+ rewrote to call Fimo reads as clean. Keep-live sites (the user chose NOT to
387
+ scaffold) skip this phase: the CMS stays the source of truth.
388
+
389
+ ## Manifest
390
+
391
+ Record wrap mode in the canonical ledger (`references/manifest.md`): set
392
+ `mode: "wrap"` and fill the `wrap` block (framework, coverage counts, seeded
393
+ counts, wiring status). Routes/media/forms sections apply as usual — wrap mode
394
+ changes how content becomes editable, not the accounting obligations.
395
+
396
+ ## Framework notes
397
+
398
+ One short block per wrappable framework. Add a block when the CLI gains a
399
+ framework — nothing above this section should name a specific framework.
400
+
401
+ ### Next.js
402
+
403
+ - `fimo migrate` inserts `allowedDevOrigins: ['*.preview.fimo.ai']` into the
404
+ next config (named `nextConfig` bindings, `export default {` literals, and
405
+ `module.exports = {`); without it the sandbox preview serves a black iframe
406
+ with failing HMR websockets.
407
+ - `fimo migrate` also wires the app-router ROOT LAYOUT itself for safe shapes
408
+ (one default-exported function returning `<html>…<body>…{children}`):
409
+ imports, async, loader calls, `<FimoNextScripts />` in head, `{children}`
410
+ wrapped in `<FimoNextProviders>`. When it bails (unusual layout), the agent
411
+ prompt's section 0 carries the recipe and the bail reason; pages-router
412
+ projects (`pages/_app`) are always manual and only WARN in the wiring gate.
413
+ - Env flavor: `NEXT_PUBLIC_FIMO_API_URL`, `NEXT_PUBLIC_FIMO_PREVIEW_SCRIPT_URL`.
414
+ - Server components cannot call `useLabels()`: use `loadFimoT()` from
415
+ `fimo/next/server` for attribute strings.
416
+ - `next dev` rejects an unknown `--host` flag (fimopm only passes it to
417
+ Vite-family dev servers).
418
+
419
+ ### Remix (v2)
420
+
421
+ - New Remix apps no longer exist (`create-remix` redirects to
422
+ create-react-router): this adapter targets the EXISTING remix-vite v2
423
+ fleet. Classic-compiler (`remix dev`) apps predate vite and are not
424
+ supported by the parity capture.
425
+ - The JSX codemod runs here (React guaranteed): `--apply` wraps plain text,
426
+ imgs and file links with `fimo/remix` imports automatically.
427
+ - Root wiring: a `loader` in `app/root.tsx` returns
428
+ `{ fimoConfig: loadFimoConfig(), fimoLabels: await loadFimoLabels() }`
429
+ (from `fimo/remix/server`); the `Layout` reads it with
430
+ `useRouteLoaderData<typeof loader>("root")` — GUARD the null case (error
431
+ boundaries render Layout without loader data) — and mounts
432
+ `<FimoRemixScripts />` in head plus
433
+ `<FimoRemixProviders config={…} labels={…}>{children}</FimoRemixProviders>`.
434
+
435
+ - React: the full wrapper set applies (`fimo/remix` re-exports it) plus
436
+ `FimoRemixProviders`/`FimoRemixScripts` for `app/root.tsx`.
437
+ - Env flavor: `FIMO_API_URL`, `FIMO_PREVIEW_SCRIPT_URL` (server-read).
438
+ - Routes: flat files under `app/routes` (dots are slashes, `$param` dynamic).
439
+ - Modern Remix dev runs on Vite (`--host` handled); classic `remix dev` honors HOST.
440
+
441
+ ### Astro
442
+
443
+ - Server-loader + DOM-attrs based (no React needed): in frontmatter
444
+ `const t = await loadFimoT()` (from `fimo/astro/server`), render
445
+ `{t('key', 'fallback')}` with `{...fimoLabelAttrs('key')}` on the element.
446
+ - **Islands are NOT React by default** (Solid, Svelte, Preact all ship as
447
+ `.tsx`/`.svelte`): never import `fimo/next` wrappers there — the JSX
448
+ runtime differs and the build breaks. The `--apply` codemod skips JSX in
449
+ Astro projects for this reason. Recipe for a mounted island: the parent
450
+ `.astro` loads `t` server-side and passes label values as props; the
451
+ island renders the prop with a literal
452
+ `data-fimo-source="__labels.<locale>.<key>.value"` attribute (no import
453
+ needed — it is just a DOM attribute).
454
+ - Mixed text nodes (`Hi, I'm <b>Name</b>, a developer…`) wrap per text node:
455
+ each segment gets its own `<span {...fimoLabelAttrs(...)}>{t(...)}</span>`
456
+ (or the attrs go on the existing `<b>`/`<a>`), punctuation stays in the
457
+ segment it belongs to.
458
+ - Playground/demo components not referenced by any route: exempt them
459
+ (`wrap.exemptions`, reason "dead template code") instead of fabricating
460
+ editability for UI that never renders.
461
+ - Config-driven Astro themes (all copy in `src/data/*.json`) surface as
462
+ `dataFiles` in the plan — follow the data-file triage in W6.
463
+ - Env flavor: `PUBLIC_FIMO_API_URL`, `PUBLIC_FIMO_PREVIEW_SCRIPT_URL`.
464
+ - Routes: file-based under `src/pages` (`.astro`/`.md`/`.mdx`).
465
+ - `astro dev` needs `--host` (fimopm passes it); verify the preview origin
466
+ allowlist on the first real dogfood.
467
+ - Scanner coverage: `.astro` templates are text-scanned — frontmatter,
468
+ `<script>`/`<style>` bodies, `<title>` head metadata, and nested
469
+ `{expressions}` are excluded; what remains is real copy.
470
+ - `--apply` auto-wraps PLAIN TEXT NODES in `.astro` templates (injects the
471
+ `loadFimoT` head into the frontmatter, wraps `{t('key', 'fallback')}`,
472
+ stages + seeds the labels). Files that already bind their own `t` (i18n
473
+ libraries) are left whole. Attributes, media, mixed markup, and
474
+ collections stay agent work via the recipe above.
475
+ - pnpm >= 11 hard-fails installs on unapproved build scripts; adopt writes
476
+ `allowBuilds: { fimo: true }` into `pnpm-workspace.yaml` (the project's
477
+ own blocked deps — esbuild, sharp — are the owner's existing approvals).
478
+
479
+ ### Nuxt (3+)
480
+
481
+ - Composable-based (SFCs are UNIVERSAL — they hydrate client-side, so never
482
+ import `fimo/nuxt/server` in a component; the nuxtship dogfood proved it
483
+ breaks `nuxt build` three different ways). In `<script setup>`:
484
+
485
+ ```vue
486
+ <script setup>
487
+ import { useFimoT, fimoLabelAttrs } from 'fimo/nuxt';
488
+ const t = await useFimoT();
489
+ </script>
490
+ <template>
491
+ <h1 v-bind="fimoLabelAttrs('hero.title')">{{ t('hero.title', 'Fallback') }}</h1>
492
+ </template>
493
+ ```
494
+
495
+ `useFimoT` fetches on the server only and hands the dictionary to the
496
+ client through the Nuxt payload (hydration-safe, one fetch per page).
497
+ `fimo/nuxt/server` remains for nitro server routes only.
498
+
499
+ - In-component arrays (pricing tiers, feature lists): keep the array, wrap
500
+ its copy strings with `t('key', 'fallback')` inside the array literal —
501
+ `t` is available above them in the same `<script setup>`.
502
+ - Env flavor: `NUXT_PUBLIC_FIMO_API_URL`, `NUXT_PUBLIC_FIMO_PREVIEW_SCRIPT_URL`.
503
+ - Routes: file-based under `pages/`. `nuxt dev` needs `--host`.
504
+ - Scanner coverage: `.vue` templates are text-scanned (template region only,
505
+ inline-svg `<title>` excluded); `<script setup>` arrays are
506
+ collection-scanned (nested copy like `button.text` counts).
507
+ - `--apply` auto-wraps PLAIN TEXT NODES in `.vue` templates (injects the
508
+ `useFimoT` head into `<script setup>`, wraps `{{ t('key', 'fallback') }}`,
509
+ stages + seeds the labels). Options-API files and files that bind their
510
+ own `t` (vue-i18n) are left whole for the agent; attributes, media, and
511
+ collections stay agent work.
512
+ - Parity capture: nuxi answers 503 while Vite builds — the boot probe waits
513
+ for a sub-500 response, so slow first compiles are fine.
514
+
515
+ ### SvelteKit
516
+
517
+ - Load labels ONCE in `src/routes/+layout.server.js` (SvelteKit's own
518
+ server-only convention — no client-bundle risk):
519
+
520
+ ```js
521
+ import { loadFimoLabels } from 'fimo/svelte/server';
522
+ export const load = async () => {
523
+ const bundle = await loadFimoLabels();
524
+ return { fimoLabels: bundle.dictionaries[bundle.defaultLocale] ?? {} };
525
+ };
526
+ ```
527
+
528
+ Components build `t` locally from client-safe `fimo/svelte`:
529
+ pages/layouts via the data prop (`let { data } = $props()` in Svelte 5),
530
+ deep components via the page store (`$page.data.fimoLabels` /
531
+ `page.data` from `$app/state`):
532
+
533
+ ```svelte
534
+ <script>
535
+ import { makeFimoT, fimoLabelAttrs } from 'fimo/svelte';
536
+ let { data } = $props();
537
+ const t = makeFimoT(data.fimoLabels);
538
+ </script>
539
+ <h1 {...fimoLabelAttrs('hero.title')}>{t('hero.title', 'Fallback')}</h1>
540
+ ```
541
+
542
+ Never import `fimo/svelte/server` in a `.svelte` file (components
543
+ hydrate; the server barrel reads fs).
544
+ Build `t` with the mode-agnostic form
545
+ `const t = makeFimoT($page.data.fimoLabels);` — a plain store read,
546
+ valid in BOTH legacy and runes components (labels are fixed per render,
547
+ no reactivity needed). Do NOT use `$derived(...)` (a single rune flips
548
+ the whole component to runes mode and any existing `$:` in it 500s) and
549
+ do NOT use `$: t = ...` (reactive statements run after init, so arrays
550
+ built at init see `t` undefined). In files with a
551
+ `<script lang="ts" module>` block, the import goes in the INSTANCE
552
+ script, never the module script (`$page` is illegal there).
553
+
554
+ - mdsvex blogs: `.md` post copy is markdown-ingestion territory (Tier 1b),
555
+ not wrap items; `+page.md` routes still discover and parity-capture.
556
+ - Env flavor: `PUBLIC_FIMO_API_URL` (SvelteKit's public prefix).
557
+ - Routes: `src/routes/**/+page.svelte` (+ `+page.md`); `(groups)` are
558
+ layout-only.
559
+ - Vite dev server (`--host` handled). `.svelte` templates are text-scanned.
560
+ - `--apply` auto-wraps PLAIN TEXT NODES in `.svelte` files (injects the
561
+ mode-agnostic head above into the instance script, wraps
562
+ `{t('key', 'fallback')}`, stages + seeds the labels) and writes
563
+ `src/routes/+layout.server.js` when the project has none. An EXISTING
564
+ root layout load is not touched — merging `fimoLabels` into it is your
565
+ work. Files binding their own `t` are left whole; attributes, media, and
566
+ collections stay agent work.
567
+ - Repo pitfalls seen in the wild: a checked-in FOREIGN lockfile (pnpm-lock
568
+ in an npm project) makes the capture spawn the wrong package manager —
569
+ align on one lockfile; a `master` default branch maps to a nonexistent
570
+ Fimo env — rename to `main` (or create the env) before data commands.
571
+
572
+ ### Vite + React (SPA)
573
+
574
+ - Detection: a `vite` dependency plus `react` (no meta-framework). The react
575
+ dependency pins the JSX runtime, so the full JSX codemod runs here —
576
+ `--apply` wraps plain text, imgs, and file links with `fimo/react` imports.
577
+ Bare Vite without react (Solid, Preact, lit) stays agent work.
578
+ - One-time wiring: `fimo migrate` wraps the root render itself for the
579
+ mainstream entry shape (`createRoot(…).render(<JSX/>)` in `src/main.*` /
580
+ `src/index.*`). When it bails, mount the SPA providers yourself once around
581
+ the app root — there is no server pass; labels are fetched in the browser
582
+ and everything renders inline fallbacks until they resolve:
583
+
584
+ ```tsx
585
+ import { FimoSpaProviders } from 'fimo/react';
586
+ import config from '../fimo-config.json';
587
+
588
+ <FimoSpaProviders
589
+ config={config}
590
+ apiUrl={import.meta.env.VITE_API_URL}
591
+ previewScriptUrl={import.meta.env.DEV ? import.meta.env.VITE_FIMO_PREVIEW_SCRIPT_URL : undefined}
592
+ >
593
+ <App />
594
+ </FimoSpaProviders>;
595
+ ```
596
+
597
+ - Routes: no file convention — the scan reads LITERAL ABSOLUTE paths from
598
+ react-router (`<Route path="/x">` and `createBrowserRouter([{ path: '/x' }])`)
599
+ into the fimo-config route registry. Relative child paths, `index` routes,
600
+ and `*` wildcards are not resolved: add those to `fimo-config.json` routes
601
+ yourself (scanned routes only ever ADD, never overwrite).
602
+ - Parity caveat: a CSR dev server answers every route with the same HTML
603
+ shell, so the parity gate compares shells and passes vacuously. Coverage
604
+ and seeding carry the real signal; verify rendering in the browser.
605
+ - Env flavor: `VITE_API_URL`, `VITE_FIMO_PREVIEW_SCRIPT_URL` (Vite inlines
606
+ only `VITE_`-prefixed vars into client code).
607
+
608
+ ### Vue (Vite SPA)
609
+
610
+ - v1: DOM-attrs + client-side label hydration; routes come from fimo-config
611
+ (no file convention to discover).
612
+
613
+ ### Vite / React Router (native template)
614
+
615
+ - The template pipeline (virtual modules, `t()` extraction) predates wrap mode
616
+ and stays the canonical path for Fimo-created projects; wrap mode is for
617
+ adopted codebases.
@@ -6,9 +6,9 @@ description: >-
6
6
  Triggers: "in Fimo Studio", "in the dashboard", "in the web app",
7
7
  "how do I X in the UI", or screenshots of the dashboard. Topics
8
8
  covered: dashboard content and CMS, publishing from the dashboard,
9
- dashboard SEO/domain settings, visual editing, version history UI,
10
- dashboard sharing/billing, and dashboard chat. Never describe UI
11
- locations, buttons, or panels from memory — always read the relevant
9
+ dashboard SEO/domain settings, outbound webhooks, visual editing, version
10
+ history UI, dashboard sharing/billing, and dashboard chat. Never describe
11
+ UI locations, buttons, or panels from memory — always read the relevant
12
12
  reference file first.
13
13
  ---
14
14
 
@@ -37,6 +37,7 @@ Read the relevant reference file when the user's question matches a topic.
37
37
  | AI Chat | references/ai-chat.md | "What can the AI do?", "How do I give context?" |
38
38
  | Code Editor | references/code-editor.md | "How do I edit code?", "Where are my files?" |
39
39
  | SEO Settings | references/seo-settings.md | "How do I set up SEO?", "Favicon, title, share image" |
40
+ | Webhooks | references/webhooks.md | "React to forms", "Send events to another service" |
40
41
  | Preview & Responsive | references/preview-and-responsive.md | "How does it look on mobile?", "How do I test sizes?" |
41
42
  | Visual Editing | references/visual-editing.md | "Can I edit visually?", "How do I change colors?" |
42
43
  | Version History | references/version-history.md | "How do I undo?", "How do I go back?" |
@@ -53,6 +54,7 @@ Read the relevant reference file when the user's question matches a topic.
53
54
  | Custom domains | Publishing (must publish first), Billing (premium required) |
54
55
  | Adding images | Media Library, AI Chat, Edit Mode |
55
56
  | Content/CMS | AI Chat, Media Library |
57
+ | Webhooks | Project secrets, Forms, Publishing, Agents |
56
58
  | Visual changes | Edit Mode (quick tweaks), AI Chat (bigger changes), Code Editor (full control) |
57
59
  | Something went wrong | Version History, AI Chat |
58
60
  | Working with others | Sharing, Collaboration |