The boot, step by step

A boot rises in steps, and a release goes down them in reverse. An extension states what it does to an app and what it undoes:

const app = await createApp({
  root,
  createContainer,
  extensions: [observability()],
});

up runs before createApp returns, in declaration order; down runs inside dispose(), in reverse. It is also the one point in the boot that may await, which is where a provider that has to open something belongs — a constructor cannot.

Two members are the framework's own, and they are ordinary members:

namewhat it does
schemareads the database against the entities and refuses the boot when it is behind, naming each missing table and column. It writes nothing: that is fougere migrate --apply. A process whose database is born with it replaces the member with migrating(storage.migrate), which writes instead
seedsplants what seeds/ declares, in dependency order

A name already declared is replaced, not refused. That is how a host says the seeding, but mine:

// what @fougere/nuxt generates: a bundler needs its seed modules as static imports
extensions: [
  { name: 'seeds', up: (app) => runSeeds(app, [/* imported above */]) },
]

// a test, whose `:memory:` database is born with it and has nobody else to migrate it
extensions: [migrating(storage.migrate)]

The ascent and the descent refuse in opposite ways, on purpose:

  • up stops at the first refusal. A seed must not run against a database the schema check just refused, and a half-started app must not be handed out.
  • down releases every member even when one throws, and the refusals leave together in an AggregateError — a release that gives up on the first failure leaks everything after it.

An extension is not a frond and cannot become one: a frond has entities and may move behind fronds:, while an extension belongs to the process that hosts them. observability() is the case that proves it — moved, it would report the observer instead of the observed.

Built with Fougere and Nuxt, hosted on GitHub Pages.