Boot without the compiler
The scan reads the convention directories. What it writes from them is a fronds.ts — the list of
frond() calls an author would write — and that file is what every host boots from. Writing it
yourself is how an app runs with no @fougere/compiler installed.
A class says most of what it is: Repository<Post> its five operations, Present<Post> its fields. What
TypeScript erases — the parameters of a method you wrote, and what it answers — is stated, and nothing
is guessed:
// fronds.ts
import { frond } from '@fougere/core';
import Post from './fronds/blog/entities/Post.ts';
import User from './fronds/user/entities/User.ts';
import PostHandler from './fronds/blog/handlers/PostHandler.ts';
import CurrentUserCollector from './fronds/blog/services/CurrentUserCollector.ts';
import { slug } from './fronds/blog/vocabulary/slug.ts';
export const vocabulary = [slug];
export default [
frond('blog', {
entities: [Post],
collectors: [CurrentUserCollector],
handlers: [{
ctor: PostHandler,
operations: {
publish: { params: { id: [Post, 'id'], user: { type: User, optional: true } }, output: Post },
mine: { params: { user: { type: User, optional: true } }, output: [Post] },
},
}],
}),
frond('user', { entities: [User] }),
];
[Post, 'id'] is a field of an entity, user is what the collector answers, [Post] answers several, and whether an operation is a query or
a command is told by its name: only read verbs make a query. What the file does not list is not served — the boot
cannot tell a method left out from one kept private, since TypeScript erases the keyword.
fougere check reads the sources and names a public method the file does not list.
What an operation refuses is yours to state too: errors: [ErrorCode.CONFLICT] beside output. The scan finds
that list by following the throws, and a list you write is taken as it is, so nothing checks it against the code.
Every generated file is optional
The scan writes in .fougere/, which git ignores and you can delete at any time. No command has to run:
a host writes these files when it starts, and fougere build writes them for a CI. Each one saves you
some writing, and none is required:
| Generated | Without it | By hand |
|---|---|---|
fronds.generated.ts | the scan runs at boot, or the boot refuses if the compiler is not installed | a fronds.ts, above |
facade.generated.ts | a page's @fronds/facade import does not resolve | one line per handler, below |
names.generated.d.ts | a wrong name in the config is refused at boot, not in the editor | nothing to write |
build/ | fougere serve refuses; fougere dev runs the sources | nothing to write |
A facade is the handler as a type, and an address as a value. Written in a page, it brings no server code to the browser:
import type { FacadeName } from '@fougere/core/contract';
import type PostHandler from '@fronds/blog/handlers/PostHandler.js';
const post: FacadeName<PostHandler, 'post'> = { address: 'post' };
Two things are lost. The address is yours to keep right, since a typo only fails when the call runs. And
a client reads every error code, because what an operation refuses is what the scan finds by following
its throws.