You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

122 lines
4.8 KiB

/**
* Service-schema contract for `arts/active-app`.
*
* `aapp` is built on top of two layers:
*
* - **Core** — fixed runtime infrastructure that always exists. Today
* that is `logger`, `bus`, `timers` and `orca`. Configurable via the
* options object root, never declared as a service.
*
* - **Services** — opt-in runtime pieces that the application declares
* in `services: { … }`. If a service is not declared, it does not
* exist on the App, and TypeScript reports an error when the
* consumer tries to access it.
*
* Each service is built from an `AppServiceFactory` produced by a
* `defineActive*` / `defineEngine*` helper that lives in
* `arts/active-app/service-factories/`. Arts themselves stay pure — they
* do not know they are wired into a service.
*/
import type { EngineBus } from '$bus';
import type { EngineLogger } from '$logger';
import type { EngineOrca } from '$orca';
import type { ActiveTimers } from '$timer';
// ── Core ────────────────────────────────────────────────────────────────
/**
* The four pieces of the core. Always built before any service. A factory
* may declare a subset of these as `coreDependencies`; the builder
* supplies only the declared keys to `create()`.
*/
export interface CoreServices {
readonly logger: EngineLogger;
readonly bus: EngineBus;
readonly timers: ActiveTimers;
readonly orca: EngineOrca;
}
export type CoreServiceKey = keyof CoreServices;
// ── Service lifecycle ───────────────────────────────────────────────────
/**
* Construction policy for a service.
*
* `lazy` (default) — built on first access via `App.<name>`. Suitable for
* services that may never be used in some flows.
*
* `immediate` — built during `createActiveApp()` after the core is up.
* Suitable for services with construction-time side effects (subscribing
* to BroadcastChannel, hydrating from storage on boot, etc.).
*/
export type ServiceInitMode = 'immediate' | 'lazy';
/**
* Observable state of a service. The builder exposes a snapshot of
* `{ [name]: ServiceStatus }` via `App.services`. Useful for devtools
* and tests; the application itself rarely reads this.
*/
export type ServiceStatus = 'absent' | 'present' | 'failed';
// ── Factory ─────────────────────────────────────────────────────────────
/**
* Factory contract for a service. Each art that participates in App is
* adapted to this contract by a `defineActive*` / `defineEngine*` helper
* in `arts/active-app/service-factories/`.
*
* Generics:
* - `TName` — string literal name, must match the schema key.
* - `TCoreDeps` — subset of `CoreServiceKey` the service consumes.
* - `TServiceDeps` — keys of OTHER services the service depends on.
* Resolved against the schema; if a declared dependency is not in the
* schema, the slot is `undefined` at `create()` time. The factory
* decides whether to error or degrade.
* - `TInstance` — type of the constructed instance.
*/
export interface AppServiceFactory<
TName extends string = string,
TCoreDeps extends readonly CoreServiceKey[] = readonly CoreServiceKey[],
TServiceDeps extends readonly string[] = readonly string[],
TInstance = unknown
> {
readonly name: TName;
readonly coreDependencies: TCoreDeps;
readonly serviceDependencies?: TServiceDeps;
readonly initMode?: ServiceInitMode;
create(deps: {
readonly core: Pick<CoreServices, TCoreDeps[number]>;
readonly services: Partial<Record<TServiceDeps[number], unknown>>;
}): TInstance;
dispose?(instance: TInstance): void;
}
// ── Schema ──────────────────────────────────────────────────────────────
/**
* A schema is a record `{ [name]: AppServiceFactory }`. The key MUST equal
* `factory.name`; the builder validates this at construction time.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type AppServiceSchema = Record<string, AppServiceFactory<string, any, any, unknown>>;
/**
* Resolves the instance shape from a schema. Used by `ActiveApp<TSchema>`
* so `App.cache` is typed as `ActiveCache` when `services.cache` is
* declared, and `never` (i.e. compile error on access) when it is not.
*/
export type ResolveServiceInstances<TSchema extends AppServiceSchema> = {
readonly [K in keyof TSchema]: TSchema[K] extends AppServiceFactory<
string,
readonly CoreServiceKey[],
readonly string[],
infer I
>
? I
: never;
};

Powered by TurnKey Linux.