fimo 2.4.0-staging.2 → 2.4.0-staging.3

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 (45) hide show
  1. package/assets/skills/fimo-cli/SKILL.md +1 -1
  2. package/assets/skills/fimo-cli/references/branches.md +4 -4
  3. package/assets/skills/fimo-cli/references/deploy.md +2 -5
  4. package/assets/skills/fimo-cli/references/domains.md +5 -5
  5. package/assets/skills/fimo-cli/references/hosting.md +5 -4
  6. package/assets/skills/fimo-cli/references/preview.md +1 -1
  7. package/assets/skills/fimo-cli/references/projects.md +5 -5
  8. package/assets/skills/fimo-code/SKILL.md +1 -0
  9. package/assets/skills/fimo-code/references/assets.md +4 -2
  10. package/assets/skills/fimo-code/references/best-practices/next.md +0 -2
  11. package/assets/skills/fimo-code/references/content/schema.md +4 -1
  12. package/assets/skills/fimo-code/references/design.md +2 -2
  13. package/assets/skills/fimo-code/references/frameworks/astro.md +37 -1
  14. package/assets/skills/fimo-code/references/frameworks/hosting.md +23 -2
  15. package/assets/skills/fimo-code/references/frameworks/seo-head.md +78 -0
  16. package/assets/skills/fimo-code/references/frameworks/sveltekit.md +57 -4
  17. package/assets/skills/fimo-migration/SKILL.md +4 -0
  18. package/assets/skills/fimo-migration/references/qa.md +6 -0
  19. package/assets/skills/fimo-studio/references/billing-and-credits.md +7 -4
  20. package/assets/skills/fimo-studio/references/seo-settings.md +2 -0
  21. package/dist/cli/bundle.json +1 -1
  22. package/dist/cli/index.js +10 -9
  23. package/dist/runtime/seo/head.d.ts +31 -0
  24. package/dist/runtime/seo/head.js +47 -0
  25. package/dist/runtime/seo/index.d.ts +1 -0
  26. package/dist/runtime/seo/index.js +1 -0
  27. package/dist/runtime/seo/resolver.d.ts +16 -2
  28. package/package.json +8 -8
  29. package/templates/astro/.fimo/config.json +1 -0
  30. package/templates/astro/AGENTS.md +1 -1
  31. package/templates/astro/astro.config.mjs +1 -0
  32. package/templates/astro/package.json +2 -2
  33. package/templates/astro/src/layouts/Base.astro +20 -4
  34. package/templates/astro/src/pages/[locale]/index.astro +1 -1
  35. package/templates/astro/src/pages/[locale]/projects/[slug].astro +4 -1
  36. package/templates/astro/src/pages/index.astro +1 -1
  37. package/templates/astro/src/pages/projects/[slug].astro +4 -1
  38. package/templates/nextjs/package.json +3 -3
  39. package/templates/react-router/package.json +4 -4
  40. package/templates/sveltekit/AGENTS.md +1 -1
  41. package/templates/sveltekit/package.json +2 -2
  42. package/templates/sveltekit/src/routes/+layout.server.ts +4 -0
  43. package/templates/sveltekit/src/routes/+layout.svelte +12 -0
  44. package/templates/sveltekit/src/routes/[[locale=locale]]/+page.svelte +0 -5
  45. package/templates/vite/package.json +4 -4
@@ -145,7 +145,7 @@ These fire at specific moments in the project lifecycle. **Surface them proactiv
145
145
  - **Using `fimo deploy --publish` casually** — `--publish` makes the site live to the public. Plain `fimo deploy` (preview) is the default. Use `--publish` only when the user says "ship it" / "go live" / "publish".
146
146
  - **Bare `fimo init` in a non-interactive shell** — will hang. Always pass `[dir]`, `--no-install` (or `--install`), `--no-skills` (or `--skills`), and `--org`/`--agents` as needed explicitly when scripted.
147
147
  - **Hardcoding content / strings / images in code** — that's what schemas, `t()`, and the media library are for. See the `fimo-code` skill's anti-patterns.
148
- - **Adding another deploy tool (Vercel, Netlify, Cloudflare Pages)** — Fimo handles deploys; don't bolt another platform on.
148
+ - **Adding another deploy tool**. Fimo handles deploys; don't bolt another platform on.
149
149
  - **Committing secrets** — hidden values use `fimo secrets`; public browser-safe values use `fimo vars`. Local env files are ignored and must not become deploy inputs.
150
150
  - **Hardcoding tokens in flags** — use the credentials written by `fimo login` (one file per backend at `~/.config/fimo/credentials/<host>-<hash>.json`).
151
151
  - **Treating the daily allowance as extra credits** — the allowance section appears only for legacy Premium organizations, and `fimo credits` already includes its spendable credits in "Available now"; it is accounting context, not an amount to add.
@@ -36,7 +36,7 @@ fimo --branch <name> <command> # peek at another Branch for one command (does n
36
36
  | `fimo diff [from] [to]` | `git diff` | Two-Branch diff across all surfaces. Defaults `from` = current Branch, `to` = `main`. `--surface <name>` limits to one; `--json`; `--no-color`. |
37
37
  | `fimo status` | `git status` | Multi-surface status for the current Branch vs its parent. See below. |
38
38
  | `fimo merge <from>` | `git merge` | Merge another Branch into the current one (multi-surface, atomic, interactive conflict resolver). See below. |
39
- | `fimo publish` | — | Publish the synced Production source revision with a stable Fimo publication ID. See "Publishing" below. |
39
+ | `fimo publish` | — | Publish the revision already on the Fimo remote (no push) with a stable Fimo publication ID. See "Publishing" below. |
40
40
 
41
41
  All of these accept the global `--branch` override. `diff`, `status`, `merge`, and `publish` additionally accept `-C, --cwd <dir>` to point at a project directory.
42
42
 
@@ -98,10 +98,10 @@ Syncs the current Branch's data plane from its parent (or `--from <ancestor>`).
98
98
 
99
99
  Two different "go live" mechanisms — don't confuse them:
100
100
 
101
- - **`fimo publish [--label <text>] [--notes <md>]`** — publishes the synced Production source revision. Fimo assigns the immutable publication ID; the label and notes are optional annotations. `--skip-validate` is an operator override.
102
- - **`fimo deploy --publish`** — the project-publish path. Pushes the current Production source code and flips the project live in one shot. See `references/deploy.md`.
101
+ - **`fimo publish [--label <text>] [--notes <md>]`** — publishes the revision already on the Fimo remote without pushing. Fimo assigns the immutable publication ID; labels and notes are optional annotations. `--skip-validate` is an operator override.
102
+ - **`fimo deploy --publish`** — pushes the current Production source Branch and flips the project live. See `references/deploy.md`.
103
103
 
104
- Use `fimo publish` when the Production source Branch is already synced; use `fimo deploy --publish` when the local revision still needs to be pushed first. Both only run from the configured Production source Branch. Existing projects use `main`.
104
+ Choose `fimo deploy --publish` when local changes need to go live; choose `fimo publish` when the remote revision is the intended release. Both only run from the configured Production source Branch. Existing projects use `main`.
105
105
 
106
106
  ## Anti-patterns
107
107
 
@@ -123,12 +123,9 @@ There's nothing special about this — it's the same `fimo deploy` you'd run lat
123
123
 
124
124
  ## Publishing: `fimo deploy --publish` vs `fimo publish`
125
125
 
126
- Two different "go live" mechanisms — pick deliberately:
126
+ Use `fimo deploy --publish` to push and publish local changes. Use `fimo publish` to publish the revision already on the Fimo remote without pushing. Both target the configured Production source Branch.
127
127
 
128
- - **`fimo deploy --publish`** — push what's on the Production source Branch now and flip the project live in one shot. The simple "ship it" path. Runs from that Branch only.
129
- - **`fimo publish [--label <text>] [--notes <md>]`** — publish the synced Production source revision without pushing. Fimo assigns the immutable publication ID; a label and notes are optional human annotations. See `references/branches.md`.
130
-
131
- When unsure, use `fimo deploy --publish` so the local revision is pushed before it goes live. Use `fimo publish` when the Production source Branch is already synced.
128
+ Check the reported source commit and warnings before reporting completion. If local work is excluded or the built commit cannot be confirmed, follow the CLI's hint.
132
129
 
133
130
  ## Markdown for AI agents
134
131
 
@@ -51,8 +51,8 @@ Global flags: `--project <id>` / `-C, --cwd <dir>` for project resolution, `--or
51
51
  When a user says "I want to change my website's URL," ask which case fits:
52
52
 
53
53
  - **"It's fine as-is, just nicer"** → `rename-default` or `suggest-default`. Free, instant, no DNS.
54
- - **"I own example.com already"** → `add example.com`. Premium. They configure CNAME at their registrar.
55
- - **"I want to buy a domain"** → `search` → `pricing` → `buy`. Premium + the registered-domains feature flag has to be on (it's not enabled in every environment).
54
+ - **"I own example.com already"** → `add example.com`. Pay as you go or higher. They configure CNAME at their registrar.
55
+ - **"I want to buy a domain"** → `search` → `pricing` → `buy`. The registered-domains feature flag and an eligible plan must be on (the flag is not enabled in every environment).
56
56
 
57
57
  If they aren't sure, run `fimo domains suggest-default` first — it's free, takes one step, and a good fallback if they don't want to commit to a domain purchase.
58
58
 
@@ -60,7 +60,7 @@ If they aren't sure, run `fimo domains suggest-default` first — it's free, tak
60
60
 
61
61
  After the automatic first-preview deploy on a newly created project, offer a cleaner free `*.fimo.site` URL before the first public publish when there is no custom domain and the current default slug still looks generated or was never explicitly chosen. Choosing it now lets the first public launch use the clean URL. Skip the offer if the user already chose the default slug, attached a custom domain, or declined.
62
62
 
63
- The default URL is free and needs no DNS. A custom domain is the separate Premium workflow above. A default slug can be reserved before publication, but it returns "Project Not Found" until the site is published. Renaming changes the address that will become public, so never silently claim a suggestion:
63
+ The default URL is free and needs no DNS. A custom domain uses the Pay as you go (or higher) workflow above. A default slug can be reserved before publication, but it returns "Project Not Found" until the site is published. Renaming changes the address that will become public, so never silently claim a suggestion:
64
64
 
65
65
  1. Run `fimo domains suggest-default --base "<brand>" --json` to get candidates.
66
66
  2. Show the candidates and ask the user to choose one explicitly.
@@ -72,7 +72,7 @@ Choosing the slug does not publish the site. The first successful `fimo deploy -
72
72
 
73
73
  The CLI never charges directly. Two distinct money paths:
74
74
 
75
- 1. **Premium subscription** — gates `add`, `buy`, `owned`, `refresh`, `remove` for custom domains. The user upgrades through the Fimo web app (Hyperline-hosted checkout). When a free user runs a gated command they get a 403 with the upgrade URL — surface it as-is to the user.
75
+ 1. **Pay as you go or higher** — gates custom-domain operations. A valid card activates Pay as you go through the Fimo web app (Hyperline-hosted checkout). When a Free user runs a gated command they get a 403 with the billing URL; surface it as-is to the user.
76
76
  2. **Domain registration** — `fimo domains buy` creates a Hyperline invoice with `payment_method_strategy: 'external'` and returns a `checkoutUrl`. The user opens that URL in a browser to pay. **No money moves until they pay the invoice.** That's why `--yes` on `buy` is safe: it queues an invoice; the human still has to consent at checkout.
77
77
 
78
78
  Domain prices come from name.com via the registrar reseller. They vary by TLD and can change. Pricing is in USD (cents in the API, formatted as dollars in CLI output).
@@ -121,7 +121,7 @@ Propagation: typically 15–30 minutes, up to 48 hours rarely.
121
121
  ## Common errors
122
122
 
123
123
  - **"Project Not Found" on a new URL or custom domain.** The route exists but the site was never published — run `fimo deploy --publish`.
124
- - **"Custom domains require a Premium plan."** — the operation is paid; the message includes the org billing URL. Free users can still run `list`, `rename-default`, `suggest-default`, `search`, and `pricing`.
124
+ - **"Custom domains require Pay as you go or higher."** — add a card at `/app/billing`, then retry. Free users can still run `list`, `rename-default`, `suggest-default`, `search`, and `pricing`.
125
125
  - **"Domain purchase is not enabled on this account."** — the registered-domains feature flag is off on this API instance. Contact support.
126
126
  - **"This URL is not available."** — slug or hostname collides with another project or is reserved.
127
127
  - **"That's a Fimo-owned domain."** — user tried `add <something>.fimo.site`. Use `rename-default` instead.
@@ -57,9 +57,9 @@ them are present.
57
57
  {
58
58
  "redirects": [
59
59
  {
60
- "source": "/(.*)",
61
- "has": [{ "type": "header", "key": "x-vercel-ip-country", "value": "GB" }],
62
- "destination": "/uk/:path*"
60
+ "source": "/offers",
61
+ "has": [{ "type": "header", "key": "x-promo", "value": "summer" }],
62
+ "destination": "/summer-sale"
63
63
  }
64
64
  ],
65
65
  "rewrites": [
@@ -74,12 +74,13 @@ them are present.
74
74
 
75
75
  | `type` | Matches on |
76
76
  | -------- | ---------------------------------------------------- |
77
- | `header` | a request header, including CDN-injected geo headers |
77
+ | `header` | a request header |
78
78
  | `cookie` | a cookie |
79
79
  | `query` | a query parameter |
80
80
  | `host` | the hostname the request arrived on — takes no `key` |
81
81
 
82
82
  Omit `value` to match on presence alone, as the `consent` cookie above does.
83
+ The redirect example requires the request to include `x-promo: summer`.
83
84
 
84
85
  **Prefer doing this in the application when it can.** Middleware, a loader, or a
85
86
  server component can be run and tested locally; a condition here only takes
@@ -24,7 +24,7 @@ JSON output contains only the Branch identity and hosted URL:
24
24
  "project_id": "prj_abc",
25
25
  "branch": "main",
26
26
  "env": "main",
27
- "preview_url": "https://fimo-preview-example.vercel.app"
27
+ "preview_url": "https://preview.example.com"
28
28
  }
29
29
  }
30
30
  ```
@@ -43,10 +43,10 @@ fimo rename "<Site Name>" # local edit: set seo.siteName in the project c
43
43
 
44
44
  ## Publish commands
45
45
 
46
- | Command | Effect |
47
- | ----------------------- | -------------------------------------------------------------------------- |
48
- | `fimo deploy --publish` | Push the current Production source + flip the project live in one shot. |
49
- | `fimo projects publish` | Flip the project live (no git push). |
50
- | `fimo publish` | Flip the synced Production source revision live; optional label and notes. |
46
+ | Command | Effect |
47
+ | ----------------------- | ----------------------------------------------------------------------- |
48
+ | `fimo deploy --publish` | Push the current Production source + flip the project live in one shot. |
49
+ | `fimo projects publish` | Flip the project live (no git push). |
50
+ | `fimo publish` | Flip the revision already on the Fimo remote live (no git push). |
51
51
 
52
52
  See `references/deploy.md` and `references/branches.md` for the full picture.
@@ -72,6 +72,7 @@ Each reference defines one package contract. Fetch it when the task reaches that
72
72
  - Locale ownership and framework handoff → `references/locales.md`
73
73
  - Upgrading a React Router app that imports `fimo/react-router/routes` → `references/migrations/react-router-route-ownership.md`
74
74
  - Astro connector and output/adapter contract → `references/frameworks/astro.md`
75
+ - Astro/SvelteKit SEO or a new content route → the framework reference, then `references/frameworks/seo-head.md` for value ownership and edit-and-render acceptance
75
76
  - Framework detection, production routing, and hosting adapters → `references/frameworks/hosting.md`
76
77
  - Major visual work → `references/design.md`
77
78
 
@@ -6,7 +6,9 @@ This file covers media fields and the Fimo image components.
6
6
 
7
7
  ## Media field values
8
8
 
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:
9
+ For single-image and gallery schemas, see `references/content/schema.md`.
10
+
11
+ Each media object stores a reference to an asset, not a copy of the asset metadata. Use the value returned by upload or generation:
10
12
 
11
13
  ```json
12
14
  {
@@ -31,7 +33,7 @@ const withAsset = await Post.getBySlug('hello', { populate: ['cover'] });
31
33
 
32
34
  `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
35
 
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>`.
36
+ Generated reads wrap each media object 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 one `FimoMedia` value, not the array, to `<Image>` or `<Video>`.
35
37
 
36
38
  ## Rendering in application code
37
39
 
@@ -38,5 +38,3 @@ Use `instant = true` to find blocking code, then verify with `next build` and `n
38
38
  - Avoid duplicate content reads between `generateMetadata` and the page.
39
39
 
40
40
  Verify valid and missing slugs, canonical and initial HTML, shipped JavaScript, duplicate reads, image sizing, instant shell rendering, streaming, freshness, and bounded prefetching.
41
-
42
- Sources: [Instant Navigations](https://nextjs.org/blog/next-16-3-instant-navigations), [`partialPrefetching`](https://github.com/vercel/next.js/blob/canary/docs/01-app/03-api-reference/05-config/01-next-config-js/partialPrefetching.mdx), and [`instant`](https://github.com/vercel/next.js/blob/canary/docs/01-app/03-api-reference/03-file-conventions/02-route-segment-config/instant.mdx).
@@ -14,6 +14,7 @@ Write one schema per `src/schemas/<Uid>.json`. The PascalCase filename must matc
14
14
  "publishedAt": { "type": "date" },
15
15
  "featured": { "type": "boolean" },
16
16
  "cover": { "type": "media" },
17
+ "gallery": { "type": "media", "multiple": true },
17
18
  "metadata": { "type": "json" },
18
19
  "author": { "type": "reference", "target": "Author" },
19
20
  "contributors": { "type": "reference", "target": "Author", "many": true }
@@ -80,11 +81,13 @@ Never declare `id`, `entryId`, `documentId`, `slug`, `locale`, `createdAt`, `upd
80
81
  | `boolean` | boolean | flags |
81
82
  | `date` | ISO date string | calendar days; loose about the form |
82
83
  | `datetime` | ISO instant with offset | exact moments (`2026-08-13T09:30:00Z`) |
83
- | `media` | media object or array | images, video, files |
84
+ | `media` | media object by default | images, video, files |
84
85
  | `json` | JSON | structured metadata |
85
86
  | `reference` | `{ "id": "..." }` or array | links to another schema |
86
87
  | `blocks` | ordered object array | nested/repeatable sections |
87
88
 
89
+ Set `multiple: true` on a media field for an array, even with one image. See `references/assets.md` for the object shape.
90
+
88
91
  A `datetime` value must carry `Z` or `±HH:MM`; a bare day or a naive local time is rejected. Render it with `<DateTime>`, which keeps the whole instant in the `<time datetime>` attribute, and a `date` with `<Date>`. `unique` is not supported on `datetime`.
89
92
 
90
93
  An enum string uses `options`; its optional `default` must be one of them:
@@ -44,10 +44,10 @@ Reference sites to anchor the aesthetic direction. Pull any well-known site, bra
44
44
  | Category | Example Anchors | Quality Bar |
45
45
  | ------------------------ | --------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
46
46
  | **Creative / Agency** | Awwwards, FWA, CSS Design Awards, Pentagram, Sagmeister & Walsh, Active Theory, Fantasy Interactive, Resn | Would it win Awwwards SOTD? Would Pentagram put this in their portfolio? |
47
- | **SaaS / Product** | Linear, Vercel, Stripe, Notion, Figma, Raycast, Arc Browser, Supabase, Mercury, Clerk, Loom, Pitch | Is it Linear-polished? Would a developer tweet "their site is so clean"? |
47
+ | **SaaS / Product** | Linear, Stripe, Notion, Figma, Raycast, Arc Browser, Supabase, Mercury, Clerk, Loom, Pitch | Is it Linear-polished? Would a developer tweet "their site is so clean"? |
48
48
  | **Editorial / Blog** | Medium, Substack, The Verge, NYT, The Atlantic, Monocle, It's Nice That, Eye on Design, Wired | Would The Verge's design team respect this? Is it magazine-worthy? |
49
49
  | **E-commerce** | Apple Store, Shopify Dawn, Gumroad, Everlane, Glossier, SSENSE, Mr Porter, Mejuri, Allbirds | Does it feel Apple Store premium? Would you trust buying from this? |
50
- | **Documentation** | Stripe Docs, Tailwind Docs, Vercel Docs, Supabase Docs, Prisma Docs, Algolia Docs, Clerk Docs | Is it Stripe Docs clear? Can a developer find what they need in 3 seconds? |
50
+ | **Documentation** | Stripe Docs, Tailwind Docs, Supabase Docs, Prisma Docs, Algolia Docs, Clerk Docs | Is it Stripe Docs clear? Can a developer find what they need in 3 seconds? |
51
51
  | **Personal / Portfolio** | Read.cv, Paco Coursey, Josh Comeau, Rauno Freiberg, Brian Lovin, Lee Robinson, Linus Rogge | Would Read.cv feature this? Is it memorable and distinctly personal? |
52
52
  | **Corporate** | Apple, Stripe, Airbnb, Slack, Square, Intercom, Dropbox, Zendesk | Does it feel Airbnb-trustworthy? Premium but not flashy? |
53
53
  | **Dashboard** | Linear, Figma, Raycast, Retool, Rows, Height, Airtable, Coda | Is it Linear-efficient? Dense but never cluttered? |
@@ -1,6 +1,6 @@
1
1
  # Astro integration
2
2
 
3
- Use this reference for an existing Astro application. The application keeps its route files, output mode, adapter, locale system, links, `<html lang/dir>`, and SEO. Fimo connects content, labels, native rendering, preview, and hosting.
3
+ Use this reference for an existing Astro application. The application keeps its route files, output mode, adapter, locale system, links, `<html lang/dir>`, and per-page SEO. Fimo connects content, labels, native rendering, preview, hosting, and the site-wide SEO defaults from Studio's Settings → SEO.
4
4
 
5
5
  ## Setup
6
6
 
@@ -90,6 +90,42 @@ browser values before submitting. Do not import the server form module into a
90
90
  client island; add a project endpoint if the form is intentionally browser-
91
91
  submitted.
92
92
 
93
+ ## SEO defaults
94
+
95
+ Resolve the current route, apply page values, then convert to native attributes.
96
+ New projects have this in `src/layouts/Base.astro`. Existing apps should extend
97
+ their current head component, preserving unrelated metadata.
98
+
99
+ ```astro
100
+ ---
101
+ import { resolveSeoForPath, toSeoHead } from 'fimo/seo';
102
+ import config from '../../.fimo/config.json';
103
+
104
+ const { seo = resolveSeoForPath(Astro.url.pathname, config) } = Astro.props;
105
+ const canonicalUrl = seo?.url ? new URL(Astro.url.pathname, seo.url).href : undefined;
106
+ const head = toSeoHead(seo, { canonicalUrl });
107
+ ---
108
+ <head>
109
+ <title>{head.title ?? 'My site'}</title>
110
+ {head.meta.map((attributes) => <meta {...attributes} />)}
111
+ {head.links.map((attributes) => <link {...attributes} />)}
112
+ </head>
113
+ ```
114
+
115
+ For a content page, resolve after loading the entry and pass the final values
116
+ to the layout: `const seo = resolveSeoForPath(Astro.url.pathname, config, { article })`,
117
+ then `<Base lang={locale} seo={seo}>`. Route templates can read
118
+ `{{article.title}}`. Apply app-owned overrides before conversion so the title,
119
+ Open Graph and Twitter tags agree.
120
+
121
+ The example canonical policy fits a root-hosted site. Preserve the app's own
122
+ production domain, base path and trailing-slash policy. Omitting the converter's
123
+ `canonicalUrl` omits canonical and `og:url`; `seo.url` alone is only a site base.
124
+ Imported config changes need sync and rebuild/redeploy. Content freshness
125
+ follows the route's rendering and caching policy.
126
+
127
+ Full field and composition contract: read `references/frameworks/seo-head.md` when changing SEO.
128
+
93
129
  ## Static and on-demand rendering
94
130
 
95
131
  Keep the application's per-route rendering choice:
@@ -64,6 +64,28 @@ local environment alias by hand.
64
64
  projects. Do not add it to new code, env files, setup instructions, or migration
65
65
  plans.
66
66
 
67
+ ## `hosting`: how the deployed site is served
68
+
69
+ Declare hosting rules only in `.fimo/config.json#hosting`, not provider
70
+ deployment files. Run `fimo validate` to catch unsupported keys and malformed rules.
71
+
72
+ | Key | Shape |
73
+ | --------------- | ---------------------------------------------------------------- |
74
+ | `rewrites` | `{ source, destination }`, URL unchanged |
75
+ | `redirects` | `{ source, destination, permanent?, statusCode? }` |
76
+ | `headers` | `{ source, headers: [{ key, value }] }`, response headers |
77
+ | `cleanUrls` | Serve `/about` for `/about.html` and redirect the extension away |
78
+ | `trailingSlash` | `true` requires a trailing slash, `false` forbids one |
79
+
80
+ Rewrites, redirects, and header rules accept `has` / `missing` conditions:
81
+ `header`, `cookie`, and `query` require `key`; `host` requires only `value`.
82
+
83
+ Redirects default to temporary. Use `permanent: true` only for lasting moves;
84
+ browsers cache it. `statusCode`, one of 301, 302, 307, 308, overrides `permanent`.
85
+
86
+ Verify rules on a hosted Preview or published site, not a local dev server.
87
+ Legacy static hosting ignores them; contact support if deployed rules have no effect.
88
+
67
89
  ## React Router framework mode
68
90
 
69
91
  For the supported client-rendered setup, keep `ssr: false` and add Fimo's
@@ -111,5 +133,4 @@ Astro has a dedicated connector — load `references/frameworks/astro.md` before
111
133
  editing an Astro config.
112
134
 
113
135
  `.fimo/config.json#routes` is the Studio page catalog, not deployment routing.
114
- Never generate hosting rewrites from it. If a project already has a
115
- provider-specific deployment file, preserve it; that file remains authoritative.
136
+ Never generate hosting rewrites from it.
@@ -0,0 +1,78 @@
1
+ # Astro and SvelteKit SEO
2
+
3
+ Resolve route data, apply the app's policy, then convert. Both imports are pure
4
+ and bundle-safe. Neither reads files, fetches content, discovers a route, nor
5
+ sets framework caching. Use the installed package's exports.
6
+
7
+ ```ts
8
+ import { resolveSeoForPath, toSeoHead, type FimoSEO } from 'fimo/seo';
9
+ import config from '../../.fimo/config.json'; // adjust to the actual file
10
+
11
+ const resolved = resolveSeoForPath(pathname, config, { article });
12
+ const seo: FimoSEO = { ...resolved, title: String(article.title) };
13
+ const head = toSeoHead(seo, { canonicalUrl });
14
+ ```
15
+
16
+ `resolveSeoForPath` combines global defaults and the matching catalog route's
17
+ `seo`. Supply the actual pathname, including the app's locale/base path, and
18
+ the already-loaded data named by templates such as `{{article.title}}`.
19
+ Unresolved tokens fall back for the entire field. The route catalog is not a
20
+ router. For domain/custom locale routing, the app selects locale-specific
21
+ values explicitly. No global defaults are required to use route SEO.
22
+
23
+ All `FimoSEO` fields are optional strings:
24
+
25
+ | Fields | Output |
26
+ | ---------------------- | --------------------------------------------------------------------------------- |
27
+ | `title`, `description` | Native title/description and matching Open Graph/Twitter values |
28
+ | `image` | Open Graph and Twitter share image |
29
+ | `favicon` | Icon and apple-touch-icon links |
30
+ | `siteName`, `locale` | Open Graph site name and locale |
31
+ | `twitterSite` | Twitter site account |
32
+ | `robots`, `themeColor` | Native meta tags |
33
+ | `url` | Site base only. The app computes `canonicalUrl`; it emits canonical and `og:url`. |
34
+
35
+ `toSeoHead` returns `{ title?, meta, links }`. No configured values produces
36
+ empty arrays. Absent/empty fields emit no corresponding tags. The default
37
+ social type/card are `website`/`summary_large_image`; use the existing head
38
+ component for article tags, alternate languages, JSON-LD, and other app-owned
39
+ metadata. Filter a converter attribute before replacing that same tag.
40
+
41
+ Preserve editor-owned settings through resolution; do not copy their initial
42
+ values into app constants that overwrite config. Derive content-owned title
43
+ and description from the same loaded entry or singleton as the visible page,
44
+ including Home/About. Pass that data to resolution or compose its current
45
+ values before conversion. A correct first render does not prove editability.
46
+
47
+ App values win only where intentionally required. Keep overrides together and
48
+ record their reason in the existing code or handoff, not a new ownership
49
+ registry. Set `image: undefined` to remove a
50
+ global image, or `robots: 'noindex'` for an editorial page before conversion. An
51
+ explicit `undefined` in a spread removes a field; use conditional spreads to
52
+ retain defaults for missing optional overrides. Resolve once, then compose;
53
+ do not resolve again after applying policy.
54
+
55
+ Pass a final absolute canonical URL from the app's production URL policy.
56
+ `new URL(pathname, seo.url).href` fits a root-hosted starter; preserve existing
57
+ base paths, domains, trailing slashes and query policy in other apps. Never use
58
+ the preview/request origin as a production canonical. Omit `canonicalUrl` to
59
+ emit neither canonical nor `og:url`.
60
+
61
+ For existing apps, inventory the current head owner and extend it in place.
62
+ Do not add duplicate tags or overwrite unrelated metadata. Framework-escape
63
+ attributes; never render a raw HTML string. Each new route must use the app's
64
+ SEO path, with route data where needed. Verify title, description, canonical,
65
+ robots and social values in built HTML, not only the browser tab title.
66
+
67
+ Imported JSON is a build snapshot. Studio config changes need sync and
68
+ rebuild/redeploy. Content freshness follows the app's SSR/prerender/cache
69
+ policy; changing a helper does not change that policy.
70
+
71
+ For SEO integration acceptance, use an authorized isolated project to change
72
+ one inherited setting and one content-owned value after the initial render.
73
+ Follow the app's sync/build/cache policy, then compare built HTML and the
74
+ hydrated head; verify intentional overrides remain unchanged. Restore the
75
+ test values. Include client navigation and Back where supported. Never mutate
76
+ customer production data for this check. If edits or rendering are unavailable,
77
+ report freshness unproven. `fimo validate` checks declared contracts, not rendered
78
+ head propagation; a green result or helper import is not this acceptance proof.
@@ -2,7 +2,8 @@
2
2
 
3
3
  Use this reference when `fimo describe --json` reports
4
4
  `framework.slug: "sveltekit"`. SvelteKit keeps owning routes, load functions,
5
- locale resolution, links, document metadata, and SEO.
5
+ locale resolution, links, document metadata, and per-page SEO; Fimo supplies the
6
+ site-wide SEO defaults from Studio's Settings → SEO.
6
7
 
7
8
  ## Compatibility
8
9
 
@@ -12,7 +13,7 @@ SvelteKit 1 or Svelte 4 projects are compatible.
12
13
 
13
14
  ## Install both packages
14
15
 
15
- Every SvelteKit surface lives in `fimo-sveltekit`, installed at the same version
16
+ SvelteKit-specific APIs live in `fimo-sveltekit`, installed at the same version
16
17
  as `fimo`:
17
18
 
18
19
  ```bash
@@ -95,9 +96,10 @@ export async function load(event) {
95
96
  <script lang="ts">
96
97
  import { afterNavigate } from '$app/navigation';
97
98
  import { FimoProvider } from 'fimo-sveltekit/components';
98
- import type { LayoutData } from './$types';
99
99
  import type { Snippet } from 'svelte';
100
100
 
101
+ import type { LayoutData } from './$types';
102
+
101
103
  let { data, children }: { data: LayoutData; children: Snippet } = $props();
102
104
 
103
105
  afterNavigate(() => {
@@ -110,6 +112,57 @@ export async function load(event) {
110
112
  </FimoProvider>
111
113
  ```
112
114
 
115
+ ## SEO defaults
116
+
117
+ Resolve SEO in the existing server load and return `seoHead`. Preserve the
118
+ load's other data, including labels. A content page resolves after its read:
119
+ `resolveSeoForPath(event.url.pathname, config, { article })`, applies app
120
+ overrides, and returns its complete `seoHead` using `toSeoHead`.
121
+
122
+ ```ts
123
+ // src/routes/+layout.server.ts, or a content page's +page.server.ts
124
+ import { resolveSeoForPath, toSeoHead } from 'fimo/seo';
125
+ import config from '../../.fimo/config.json';
126
+
127
+ export function load({ url }) {
128
+ const seo = resolveSeoForPath(url.pathname, config);
129
+ return {
130
+ seoHead: toSeoHead(seo, {
131
+ canonicalUrl: seo?.url ? new URL(url.pathname, seo.url).href : undefined,
132
+ }),
133
+ };
134
+ }
135
+ ```
136
+
137
+ Render the final merged route data from one head owner. A leaf load's
138
+ `seoHead` replaces the default head as a whole.
139
+
140
+ ```svelte
141
+ <!-- Inside the existing root layout, alongside its provider and children -->
142
+ <script lang="ts">
143
+ import { page } from '$app/state';
144
+ const head = $derived(page.data.seoHead ?? { meta: [], links: [] });
145
+ </script>
146
+
147
+ <svelte:head>
148
+ <title>{head.title ?? 'My site'}</title>
149
+ {#each head.meta as attributes}<meta {...attributes} />{/each}
150
+ {#each head.links as attributes}<link {...attributes} />{/each}
151
+ </svelte:head>
152
+ ```
153
+
154
+ Do not add a second page title beside root social tags. Use the same final
155
+ values for all tags and check client navigation, including returning to a page
156
+ without overrides. Existing apps can keep their current head component and
157
+ extend it in place.
158
+
159
+ The example canonical policy fits a root-hosted site; preserve existing
160
+ production domains, base paths and trailing slashes. Imported config needs
161
+ sync and rebuild/redeploy. Content freshness follows the app's load,
162
+ prerender and cache policy.
163
+
164
+ Read `references/frameworks/seo-head.md` for the field and composition contract.
165
+
113
166
  Generated values are serialized at a `load` boundary and hydrated in the
114
167
  component that renders them. Use `Text`, `RichText`, `Image`, `Video`, `Date`,
115
168
  `DateTime`, `Boolean`, `Json`, and `Number` from
@@ -142,7 +195,7 @@ component, read Fimo preview environment variables, or inject Fimo scripts.
142
195
 
143
196
  ## Boundaries
144
197
 
145
- - SvelteKit owns routing, history, 404s, locale URLs, and SEO.
198
+ - SvelteKit owns routing, history, 404s, locale URLs, and per-page SEO; the root layout renders Fimo's site-wide SEO defaults.
146
199
  - `fimo-sveltekit/vite` owns dev source tagging only.
147
200
  - `FimoProvider` owns labels and the optional Studio preview bridge.
148
201
  - Do not add React, React Query, or a Vite SPA fallback to a SvelteKit project.
@@ -224,6 +224,10 @@ report the exact failed command and setup requirement.
224
224
  - Content data/code workers -> `references/workers/content-execution.md`.
225
225
  - Independent validator -> `references/workers/validator.md`.
226
226
  - Next.js source -> `references/frameworks/nextjs.md`.
227
+ - Astro/SvelteKit SEO -> the target's `fimo-code` framework reference and
228
+ `frameworks/seo-head`. Preserve the existing head owner and URL policy;
229
+ follow its value-ownership and edit-and-render contract, preserving
230
+ intentional source overrides rather than freezing editor-owned values.
227
231
  - React Router/Vite source -> load the target project's `fimo-code`
228
232
  `frameworks/react-router`, `frameworks/vite-react`, and
229
233
  `frameworks/hosting` references as applicable.
@@ -67,6 +67,12 @@ items.
67
67
  categories, tags, redirects, and locales are preserved when present.
68
68
  - Existing SEO, OG, and social behavior - including dynamic OG image routes -
69
69
  is preserved or explicitly reported as a gap.
70
+ - Editor-owned SEO remains connected to its setting, entry, or singleton.
71
+ Inspect the implementation lane's before/after edit-and-render receipt from
72
+ an authorized isolated target, including preserved intentional overrides and
73
+ required rebuild/cache steps. If absent, report freshness unproven and hand
74
+ back a bounded test; this read-only validator does not mutate content.
75
+ `fimo validate` and matching initial tags do not prove later edits propagate.
70
76
  - When the source has no low-risk SEO or social value, safe defaults derived
71
77
  from verified page/site data are present or the missing behavior is reported;
72
78
  the migration must not invent marketing claims.
@@ -19,13 +19,13 @@ From a linked project directory, `fimo credits` shows the same organization bala
19
19
 
20
20
  ## Buying More Credits
21
21
 
22
- Buying credits is available on paid plans, since it charges the payment method already on file.
22
+ Buying credits is available while a paid plan is effective, including after scheduling its cancellation. Cancellation stops automatic spending immediately but keeps the current plan until renewal. A deliberate credit purchase still charges the payment method on file.
23
23
 
24
24
  1. Click the credit display near the chat input.
25
25
  2. Click **Buy credits**.
26
26
  3. Enter a dollar amount (up to $10,000) or pick one of the presets: **$25**, **$50**, **$100**, or **$200**.
27
- 4. You get **1,000 credits per $1** spent.
28
- 5. Confirm the purchase. Your card on file is charged and the credits are added to your account immediately -- there is no separate checkout page to complete.
27
+ 4. Check the rate shown in the dialog. It comes from your current plan, so the same dollar amount can buy a different number of credits on Pay as you go than on Pro.
28
+ 5. Confirm the purchase. Your card on file is charged and the credits are added after confirmation; there is no separate checkout page to complete. If confirmation stays pending, retry the same purchase or contact support. Do not start another purchase to replace an unresolved one.
29
29
 
30
30
  ## Refilling Credits Automatically
31
31
 
@@ -33,7 +33,7 @@ Organization billing admins on a paid plan can configure auto-refill from **Sett
33
33
 
34
34
  1. Choose the balance threshold that should trigger a refill.
35
35
  2. Choose the dollar amount to purchase when the balance crosses that threshold.
36
- 3. Save the configuration. Hyperline stores the setting and charges the payment method on file when the threshold is crossed.
36
+ 3. Check the plan rate shown in the dialog, then save. Fimo charges the payment method on file and buys credits at that rate when the balance crosses the threshold.
37
37
 
38
38
  Turn auto-refill off from the same control. Fimo does not keep a separate copy of this configuration; the Billing page always shows the provider's current setting. Leaving a paid plan clears auto-refill, and billing admins retain the off-switch if provider cleanup is delayed.
39
39
 
@@ -54,6 +54,9 @@ From there:
54
54
  ## Changing or leaving a paid plan
55
55
 
56
56
  - Free to Pay as you go or Pro, and Pay as you go to Pro, use checkout and take effect after payment succeeds.
57
+ - Cancelling Pay as you go or Pro stops auto-refill and paid bandwidth immediately. The current plan, remaining allowances, credits, and reset date stay unchanged until Free takes over at renewal. Manual credit purchases remain available while the paid plan is active.
58
+ - Automatic spending cannot be enabled while cancellation is scheduled. Keep your plan first; then you can immediately enable auto-refill and paid bandwidth independently. Keeping the plan itself leaves both off.
59
+ - Usage before cancellation remains payable. Enabling paid bandwidth again does not make usage from the off-period billable.
57
60
  - Pro to Pay as you go or Free takes effect at the end of the current billing period. Billing shows the destination and effective date until then.
58
61
  - Choosing the current plan again before that date cancels the scheduled change.
59
62
  - Pay as you go requires an active payment method. Removing its only valid card returns the organization to Free immediately.
@@ -24,6 +24,8 @@ This is the image that shows up when someone shares your link on social media (F
24
24
 
25
25
  All SEO changes auto-save as you make them. However, a small amber reminder appears in the top-right corner: "Publish your website to apply your changes." Click Publish to push the changes to your live site.
26
26
 
27
+ Current templates include SEO wiring. For older Next.js, Astro or SvelteKit sites, ask the coding agent to update Fimo and connect these values to the app's existing SEO flow. Route-specific content must reach that flow too. Load the matching recipe: `fimo skills get fimo-code --ref frameworks/next`, `fimo skills get fimo-code --ref frameworks/astro`, or `fimo skills get fimo-code --ref frameworks/sveltekit`.
28
+
27
29
  ## Why SEO Matters
28
30
 
29
31
  - **Favicon** -- Makes your site look professional and easy to find among open tabs.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "bundled": true,
3
3
  "bundler": "esbuild",
4
- "bundledAt": "2026-09-05T10:27:58.561Z",
4
+ "bundledAt": "2026-09-05T15:57:16.360Z",
5
5
  "cliVersion": "2.3.2",
6
6
  "external": [
7
7
  "oxc-parser",