Stone.jsDocs
Paradigm

Foundations

Lifecycle & the kernel

The kernel is the initialization dimension: the part that takes a normalised intention and applies your domain to it, once per event, inside a fresh container. It knows nothing of any platform, and it is where the request's short life plays out.

#What the kernel does

The principle

Between "a request arrived" and "a response left" there is a fixed sequence: build a scope, run the middleware, invoke the handler, handle errors, produce a response. Naming that sequence, and keeping it platform-agnostic, is what lets one flow serve every runtime.

In Stone.js

The kernel receives an IncomingEvent from an adapter, creates the per-event container, sends the event through the middleware pipeline to the resolved handler, maps the result (or an error) to a response, and returns it to the adapter. Every context reuses this exact kernel.

The adapter says what happened. The kernel decides what to do about it. Your domain says what it means.

#The application lifecycle

There are two timelines. The first runs once: the app is built, collapses to one context at run time, starts, serves events, and terminates. The long-lived adapter is the only thing that persists across events.

  1. Setup · once

    Build the Blueprint

    Discovered decorators and define* modules compose the single manifest, once, before any event. Then it freezes.

    onPreparingBlueprintonBlueprintPrepared
  2. Initialization

    Initialize

    The container comes up and providers register; the app is wired but not yet listening.

    onInit
  3. Integrationrun time

    Collapse to one context

    StoneFactory.run() resolves a single adapter from the stack: the one whose alias matches the platform, else the default, else the only one. This is the contextual collapse.

  4. Integration

    Start & listen

    The resolved adapter (the only long-lived thing) starts and begins accepting causes.

    onStart
  5. Functional · per event

    Handle events

    For every cause, the per-event cycle runs (below), then repeats for the life of the process.

  6. Shutdown · once

    Terminate

    On shutdown the app drains and closes; the adapter stops.

    onTerminate
Startup to shutdown, once per process.

#The per-event lifecycle

The second timeline runs for every cause: from the raw platform event to the native effect, inside a fresh container that is created and discarded per event. The pills mark the hooks that fire at each step.

  1. Integration · adapter

    Capture the raw cause

    The long-lived adapter receives the platform’s raw event: an HTTP request, a message, an argv, an agent call.

  2. Integration · adapter

    Adapter middleware

    Runs on the raw cause and response, around the kernel call, for boundary concerns.

    onProcessingAdapterMiddlewareonAdapterMiddlewareProcessed
  3. Integration · adapter

    Normalise to an intention

    The adapter builds an IncomingEvent: transport-agnostic, the form the domain reads.

    onBuildingIncomingEvent
  4. Initialization · kernel

    Fresh ephemeral container

    The kernel creates a container for this one event and resolves handlers and services into it.

    onHandlingEvent
  5. Initialization · kernel

    Kernel middleware pipeline

    The event flows through the middleware chain, where validation, auth and authorization attach.

    onProcessingKernelMiddlewareonKernelMiddlewareProcessed
  6. Functional · your domain

    Run the handler

    The resolved handler runs and returns a value (or throws). This is your code.

    onExecutingEventHandleronEventHandled
  7. Initialization · kernel

    On error, map it

    A thrown error is routed to the matching error handler and mapped to a response.

    onExecutingErrorHandleronHandlingAdapterError
  8. Initialization · kernel

    Prepare the response

    The result (or mapped error) becomes an OutgoingResponse; terminating middleware runs.

    onPreparingResponseonResponsePrepared
  9. Integration · adapter

    Emit the native effect

    The adapter turns the response into the platform’s native effect; the ephemeral container is discarded.

    onBuildingRawResponse
One intention, from cause to effect.

#Lifecycle hooks

Hooks let you run code at the precise moments above, application startup, shutdown, and around each event, without threading that code through your handlers. Mark a method with @Hook(name) and the kernel calls it at the right time.

app/Application.tsdeclarativeimperative
import { Hook, StoneApp } from '@stone-js/core'

@StoneApp({ name: 'tasks' })
export class Application {
  @Hook('onStart')
  async warmUp () { /* open pools, prime caches, once at startup */ }

  @Hook('onTerminate')
  async drain () { /* flush and close, once at shutdown */ }
}
import { defineHookListener } from '@stone-js/core'

// The same two moments, registered imperatively as a list of hook listeners.
export const hooks = [
  defineHookListener('onStart', () => { /* open pools, prime caches, once at startup */ }),
  defineHookListener('onTerminate', () => { /* flush and close, once at shutdown */ })
]

#Two scopes, dimension-bound

Hooks are scoped to the dimension they observe, which sorts them into two lifetimes:

  • Global hooks fire once over the app's lifetime (onInit, onStart, onTerminate), plus the setup pair around the Blueprint (onPreparingBlueprint, onBlueprintPrepared). They live with the long-lived adapter.
  • Per-intent hooks fire for every event, inside its ephemeral container (onHandlingEvent, onProcessingKernelMiddleware, onExecutingEventHandler, onEventHandled, onExecutingErrorHandler, onPreparingResponse), from the moment the context is created to when the response is sent and it is torn down.
Hooks exist to observe the lifecycle, not to alter it. To change what happens to an event, use middleware.

Stone.js

Your app exists in every runtime. Until you run it.

An open-source project by Stone Foundation
Created by Mr. Stone (Evens Pierre)