Qu'est-ce que Fougere
Fougere est un framework TypeScript construit autour de deux idées.
Single-schema. Une classe d'entité déclare vos données une fois — et juge elle-même
ses entrées : le même validate() tourne dans le navigateur et à la façade. Le juge est
lui-même une projection — dérivée de l'axe shape — mais une projection normative,
embarquée avec la classe : toutes les autres doivent lui obéir, et elle ne peut pas
dériver seule. Les tables
SQLite, les types GraphQL, les contrats de formulaire et les surfaces d'API sont des
projections de cette déclaration — rien n'est écrit deux fois.
import { entity, primary, text, created, oneOf, date, readOnly, optional } from '@fougere/schema';
export default class Post extends entity({
id: primary(),
title: text({ min: 1, max: 160 }),
body: optional(text()),
createdAt: created(),
status: readOnly(oneOf('draft', 'published', { default: 'draft' })),
publishedAt: readOnly(optional(date())),
}) {}
Le schéma — déclaré une fois
class Post extends entity({
id: primary(),
title: text({ min: 1 }),
status: readOnly(oneOf(
'draft', 'published')),
}) {}Post.validate(input)dérivée du shape · embarquée avec la classe
Surface d'API
post.list · post.publish
Table de base de données
auto-DDL → SQLite
Type TypeScript
function render(p: Post)
Contrat de formulaire
useFormFor(Post)
Type GraphQL
type Post { … }
Désignation & DI
useQuery(Post, 'list')
Un noyau, six projections — changez la déclaration, chaque projection suit.
Cette seule classe est à la fois :
- le type TypeScript d'une ligne (
function render(p: Post)— pas d'Infer<typeof …>), - le validateur des entrées client (
Post.validate(input)), - la metadata que chaque adapter lit (
Post.getFields()), - la désignation que les pages utilisent pour appeler les opérations (
useQuery(Post, 'list')), - le nom nominal que l'injection de dépendances matche dans les signatures (
user: User | null).
Le gradient. La logique métier vit dans des Fronds — des modules autonomes d'entités, handlers, collectors et seeds. Une Frond tourne in-process aujourd'hui et dans son propre process demain, derrière JSON-RPC 2.0, avec un code utilisateur identique. L'énoncé de topologie entier tient en une ligne de config :
// fougere.config.ts
remotes: { blog: 'http://127.0.0.1:4100' }
Pas de RPC sans voyage : un appel est une valeur (entity, operation, invocation) ; le
runner l'exécute directement en mémoire quand la Frond est locale et la met sur le fil
quand elle est distante. Les transports déplacent la valeur — ils ne la remodèlent jamais.
Donc le split coûte le saut et le JSON qui voyage avec, et rien d'autre : aucune sérialisation que le chemin local éviterait, aucun impôt du framework par-dessus le réseau.
Ordre de lecture
Concepts — Philosophie · La Frond
Côté serveur — Démarrer · App Nuxt existante · La CLI · Entités · Vues · Standard Schema · Handlers · Presenters · Collectors · Erreurs · Seeds · Les faits
Côté client — Queries & commands · Formulaires · Session · invoke
Topologie — Le gradient · Surfaces · Déploiement
Statut. Fougere est en alpha : les paquets
@fougere/*sont sur npm sous le tagalpha, et cette documentation décrit l'API telle qu'elle existe dans le dépôt aujourd'hui. Ce site tourne dessus.