Chapter 04Get started
Anatomy of a project
Every file, its role, and above all: which ones you will touch daily, which ones never. Plus the full list of commands.
A site project
This is what npm create inline@latest produces, and what you version in the
client's repository.
my-site/
├── astro.config.mjs ★ integration, languages, static output
├── package.json ★ site scripts
├── wrangler.toml function configuration for the host
├── tsconfig.json
├── .env.example variable template — the only versioned one
├── .dev.vars local variables — ignored by Git
├── .gitignore
│
├── functions/
│ └── api/
│ ├── auth.ts ┐
│ ├── content.ts │ four 5-line adapters,
│ ├── save.ts │ never a rule
│ └── upload.ts ┘
│
├── scripts/
│ ├── check-html.mjs content present in raw HTML
│ ├── check-locales.mjs key parity across languages
│ ├── check-logs.mjs no secret in a console.*
│ ├── check-secrets.mjs no secret in dist/
│ ├── make-key.mjs a key and its hash — rotation
│ └── mock-git-api.mjs fake local repository
│
├── src/
│ ├── content/
│ │ ├── config.ts collection declaration (schema from the package)
│ │ ├── site.json ★ navigation, contact details, footer
│ │ ├── site.ts site.json validation at build time
│ │ └── pages/
│ │ ├── fr/home.json ★ THE content — one page, one language, one file
│ │ └── en/home.json ★
│ │
│ ├── components/ ★ your components — static by default
│ │ └── Testimonial.astro
│ ├── layouts/
│ │ └── Base.astro ★ metadata, navigation, footer
│ ├── lib/
│ │ └── locales.ts ★ the languages of THIS site
│ ├── media/ ★ the images — in src/, not in public/
│ │ └── library.ts created by the integration, leave as is
│ ├── pages/
│ │ └── [lang]/[...slug].astro ★ the page template
│ └── styles/
│ └── theme.css ★ THE theme: palette, scale, rhythm
│
└── .github/workflows/ci.yml automated checks★ = files you edit for a given site.
Every day
Content and theme
src/content/pages/**, src/styles/theme.css, src/components/, the page template.
Once per site
Configuration
astro.config.mjs, src/lib/locales.ts, src/content/site.json, the environment variables.
Never
Adapters and checks
functions/api/*.ts and scripts/check-*.mjs: they ship correct. Adding a rule there is a symptom, not a solution.
The shared package
inline-core is not in the site repository: it is a dependency installed in
node_modules. Its structure tells you where to read when hunting for a
behaviour.
inline-core/
├── astro/index.ts the integration: /admin, /aide, overlay, bootstrap
├── components/
│ ├── Editable.astro a text or richtext field
│ ├── Media.astro an image or a video
│ └── Collection.astro a list + its item template
├── pages/
│ ├── admin.astro the key entry page
│ └── aide.astro the client help page, ready to use
├── styles/tokens.css one class per schema value
└── src/
├── schema.ts THE content model (Zod) — build AND server
├── style-tokens.ts the allowed variants, single source
├── safe-href.ts what a safe link is, on both sides
├── video.ts reading a YouTube / Vimeo address
├── translate.ts translation fallback (not the language list)
├── editor/ the overlay — TypeScript, no framework
│ ├── index.ts the loop: a path, a pointer, a mutation
│ ├── bar.ts floating bar and banners
│ ├── toolbar.ts variants and richtext toolbars
│ ├── media.ts image / video panel
│ ├── collection.ts add, duplicate, move, delete
│ ├── draft.ts local draft
│ ├── sanitize.ts browser-side sanitising
│ ├── heic.ts iPhone photo decoding, on demand
│ └── api.ts the calls to the four routes
└── server/
├── auth.ts verifyAuth / createSession — sole judge of identity
├── guard.ts rate, caps, allowed paths
├── rate-limit.ts call counting, interchangeable storage
├── git-provider.ts the Git provider interface
├── github.ts GitHub implementation (version = blob SHA)
├── gitlab.ts signature + notes — not implemented
├── image.ts format recognition from the bytes
├── sanitize.ts server-side sanitising (no DOM)
└── routes/ the four routes, as configurable factoriesWhat is imported, and under which name
| Import | What it is | Where it is used |
|---|---|---|
inline-core/astro | The integration | astro.config.mjs |
inline-core/schema | Zod schemas and types | src/content/config.ts, components |
inline-core/style-tokens | Style enums and styleClasses() | Components, tooling |
inline-core/components/Editable.astro | Text / richtext field | Pages |
inline-core/components/Media.astro | Image or video | Pages |
inline-core/components/Collection.astro | List + item template | Pages |
inline-core/styles/tokens.css | Token classes | The layout |
inline-core/server | The four route factories | functions/api/*.ts |
inline-core/translate | mergeWithDefault, localePath | src/lib/locales.ts |
inline-core/video | parseVideoUrl, embedUrl | Components, tooling |
Commands
In a site
npm run dev # Astro server alone — layout, without editing
npm run build # production build (fails if content is invalid)
npm run serve:functions # site + functions: this is where you edit
npm run mock:git # fake local Git repository
npm run make:key # a key and its hash — rotation
npm run check # raw HTML + language parity + logs + secretsIn the reference repository
npm run dev # Astro server alone
npm run build # production build
npm run serve:functions # site + functions on http://127.0.0.1:8788
npm run check # the four checks
npm test # the whole test suite
npm run create:site # prepares a site's credentials (key, hash, variables)
npm run make:key # a key and its hash — rotation
npm run bootstrap # extracts content from an annotated HTML page
npm run mock:git # fake local Git repository
npm run test:scaffold # creates a site from scratch, installs, builds, checks it
npm run test:pack # same, from the archives a publication would produce
npm run pack:core # builds the inline-core archive
npm run release:core # publishes inline-core to the registry
npm test and npm run check — exactly the ones CI runs, and they
block the branch. A check that does not block gets bypassed, then disabled, then removed.
Where does a change belong?
The question comes up with every evolution, and one wrong answer is enough to make ten sites diverge. The criterion is simple: would this be true for every client?
| You want to… | Where |
|---|---|
| change a colour, a heading size, a font | the site's src/styles/theme.css |
| add a page, a block, a section | src/content/pages/… + the page template |
| add a language | astro.config.mjs, src/lib/locales.ts, scripts/check-locales.mjs |
| add a field type (a table, say) | inline-core/src/schema.ts — major version |
| change a size cap or a rate budget | inline-core/src/server/guard.ts |
| fix a message shown to the client | inline-core/src/editor/* |
| support a new Git provider | inline-core/src/server/ + GIT_PROVIDER |
| add an identity check | inline-core/src/server/auth.ts, and nowhere else |
If you are about to write a rule in functions/api/*.ts, stop. Those files hold
three lines of wiring. A rule appearing there is a rule you will have to find again in every
client repository the day it changes.
What the site must give the package
Three things, and only three:
- its configuration — the languages, passed to the integration and the routes;
- its content —
src/content/pages/{language}/{page}.json; - its appearance — the CSS variables
tokens.cssexpects, its components, its layout.
If that list grows, it is a sign a package decision has leaked into the sites.