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.
232 lines
8.7 KiB
232 lines
8.7 KiB
<script lang="ts">
|
|
import CodeBlock from '../../_components/CodeBlock.svelte';
|
|
import Callout from '../../_components/Callout.svelte';
|
|
import PageNav from '../../_components/PageNav.svelte';
|
|
|
|
const dependencyDiagram = `createActiveApp()
|
|
core, always present:
|
|
Logger -> Bus
|
|
Logger -> Timers
|
|
Logger + Bus + Timers -> Orca
|
|
Prefs
|
|
|
|
services, opt-in:
|
|
core Prefs -> lang
|
|
core Prefs -> format
|
|
dom + prefs projector -> global preference attrs
|
|
storage + http + timers -> session
|
|
session -> auth / perm / connections
|
|
lang -> sium
|
|
|
|
orchestration:
|
|
session events -> App.bus -> App.orca actions -> cache / perm / connections`;
|
|
|
|
const appExample = `import { createActiveApp } from '$active-app';
|
|
import {
|
|
defineActiveLangs,
|
|
defineActiveFormat,
|
|
defineActiveDom,
|
|
defineActiveStorage,
|
|
defineEngineHttp,
|
|
defineActiveSession,
|
|
defineActiveCache
|
|
} from '$active-app/services';
|
|
import { createActivePrefsDomProjection } from '$prefs';
|
|
import { applyStandardOrca } from '$active-app/presets';
|
|
import { LogLevel, consoleTransport } from '$logger';
|
|
|
|
export const App = createActiveApp({
|
|
logger: { level: LogLevel.INFO, transports: [consoleTransport()] },
|
|
orca: { maxDepth: 24 },
|
|
prefs: {
|
|
capabilities,
|
|
environment,
|
|
intent: { language: 'es', locale: 'es-ES' }
|
|
},
|
|
services: {
|
|
dom: defineActiveDom(),
|
|
langs: defineActiveLangs({ schema, defaultLocale: 'es', fallbackChain: ['en'] }),
|
|
format: defineActiveFormat(),
|
|
storage: defineActiveStorage(),
|
|
http: defineEngineHttp({ baseUrl: '/api' }),
|
|
session: defineActiveSession({ schemas, storage, onRefresh, onRevoke }),
|
|
cache: defineActiveCache()
|
|
}
|
|
});
|
|
|
|
export const prefsProjection = createActivePrefsDomProjection({
|
|
prefs: App.prefs,
|
|
dom: App.dom
|
|
});
|
|
|
|
applyStandardOrca(App);`;
|
|
|
|
const orcaExample = `import {
|
|
applyCacheClearOnIdentityChange,
|
|
applyPermInvalidateOnIdentityChange,
|
|
applyConnectionsReauthOnIdentityChange
|
|
} from '$active-app/presets';
|
|
|
|
applyCacheClearOnIdentityChange(App);
|
|
applyPermInvalidateOnIdentityChange(App);
|
|
applyConnectionsReauthOnIdentityChange(App);`;
|
|
|
|
const prefsExample = `App.prefs.language.set('es');
|
|
App.prefs.locale.set('es-MX');
|
|
App.prefs.motion.set('reduce');
|
|
|
|
// App.prefs drives downstream services when they are declared:
|
|
// - App.langs follows App.prefs.language.get()
|
|
// - App.format follows App.prefs.locale.get()
|
|
// - prefs projection can write direction, motion, sound and haptic attrs`;
|
|
</script>
|
|
|
|
<svelte:head>
|
|
<title>Composition - Active</title>
|
|
</svelte:head>
|
|
|
|
<article class="article">
|
|
<header class="breadcrumbs">
|
|
<a href="/active">Get Started</a>
|
|
<span aria-hidden="true">/</span>
|
|
<span>Composition</span>
|
|
</header>
|
|
|
|
<h1>Composition</h1>
|
|
<p class="lead">
|
|
Active is wired through <code>$active-app</code>. The current model is a fixed runtime
|
|
core plus a typed service schema: <code>Logger</code>, <code>Bus</code>,
|
|
<code>Timers</code>, <code>Orca</code> and <code>Prefs</code> always exist; the rest of
|
|
the ecosystem is declared explicitly under <code>services</code>.
|
|
</p>
|
|
|
|
<h2>Factories</h2>
|
|
<ul>
|
|
<li>
|
|
<strong><code>createEngineXxx(options)</code></strong> - pure factory. It has no
|
|
Svelte runes and is safe for server code, tests and adapters.
|
|
</li>
|
|
<li>
|
|
<strong><code>createActiveXxx(options)</code></strong> - reactive runtime wrapper. It
|
|
lives in a <code>.svelte.ts</code> file when it owns <code>$state</code>.
|
|
</li>
|
|
<li>
|
|
<strong><code>defineActiveXxx()</code> / <code>defineEngineXxx()</code></strong> -
|
|
service-schema adapters used only by <code>$active-app/services</code>.
|
|
</li>
|
|
</ul>
|
|
|
|
<Callout variant="info" title="Core is not a service">
|
|
<p>
|
|
The App core is configured with root options on <code>createActiveApp()</code>. Do not
|
|
declare <code>logger</code>, <code>bus</code>, <code>timers</code>,
|
|
<code>orca</code> or <code>prefs</code> inside <code>services</code>.
|
|
</p>
|
|
</Callout>
|
|
|
|
<h2>Fixed core</h2>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Member</th>
|
|
<th>Created by</th>
|
|
<th>Purpose</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr><td><code>App.logger</code></td><td><code>createEngineLogger()</code></td><td>Structured logs and transports.</td></tr>
|
|
<tr><td><code>App.bus</code></td><td><code>createSvelteEngineBus()</code></td><td>Typed application event bus.</td></tr>
|
|
<tr><td><code>App.timers</code></td><td><code>createActiveTimers()</code></td><td>Deterministic scheduler and shared clock.</td></tr>
|
|
<tr><td><code>App.orca</code></td><td><code>createEngineOrca()</code></td><td>Cross-module orchestration. It is inert until actions or presets are registered.</td></tr>
|
|
<tr><td><code>App.prefs</code></td><td><code>createActivePrefs()</code></td><td>Core preference engine. Uses root <code>prefs</code> options or neutral defaults.</td></tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<h2>Typed services</h2>
|
|
<p>
|
|
Every declared service becomes a typed lowercase property on <code>App</code>. Services can
|
|
be lazy or immediate, can request a subset of the core, and can depend on other declared
|
|
services. Undeclared services do not exist on the App type.
|
|
</p>
|
|
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Service</th>
|
|
<th>Factory</th>
|
|
<th>Important wiring</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr><td><code>lang</code></td><td><code>defineActiveLangs()</code></td><td>Always follows <code>App.prefs.language.get()</code>.</td></tr>
|
|
<tr><td><code>format</code></td><td><code>defineActiveFormat()</code></td><td>Always reads its locale source from <code>App.prefs</code> (or an explicit <code>localeSource</code> override).</td></tr>
|
|
<tr><td><code>dom</code></td><td><code>defineActiveDom()</code></td><td>Browser document adapter; inert on the server.</td></tr>
|
|
<tr><td><code>storage</code></td><td><code>defineActiveStorage()</code></td><td>Runtime storage adapter, memory-backed by default.</td></tr>
|
|
<tr><td><code>http</code></td><td><code>defineEngineHttp()</code></td><td>Engine service for API calls and request diagnostics.</td></tr>
|
|
<tr><td><code>cache</code></td><td><code>defineActiveCache()</code></td><td>Passive cache. Identity reactions belong to Orca presets.</td></tr>
|
|
<tr><td><code>sium</code></td><td><code>defineEngineSium()</code></td><td>Validation engine; consumes <code>lang</code> if declared.</td></tr>
|
|
<tr><td><code>session</code></td><td><code>defineActiveSession()</code></td><td>Publishes session events on <code>App.bus</code>.</td></tr>
|
|
<tr><td><code>auth</code></td><td><code>defineActiveAuth()</code></td><td>Client reflector for server-authoritative auth flows.</td></tr>
|
|
<tr><td><code>perm</code></td><td><code>defineActivePerm()</code></td><td>Permission reflector. Invalidation is opt-in through Orca.</td></tr>
|
|
<tr><td><code>connections</code></td><td><code>defineActiveConnections()</code></td><td>Realtime registry; reauth/close reactions are Orca presets.</td></tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<h2>Dependency graph</h2>
|
|
<CodeBlock lang="text" code={dependencyDiagram} title="Current composition map" />
|
|
|
|
<h2>App composition</h2>
|
|
<CodeBlock lang="ts" code={appExample} title="src/lib/app.ts" />
|
|
|
|
<h2>Orca reactions</h2>
|
|
<p>
|
|
Module events are public typed contracts on <code>App.bus</code>. The bus does not run
|
|
destructive behavior by itself; <code>App.orca</code> owns those cross-module reactions.
|
|
Use <code>applyStandardOrca(App)</code> for the default set, or cherry-pick individual
|
|
presets when the application needs a narrower policy.
|
|
</p>
|
|
<CodeBlock lang="ts" code={orcaExample} />
|
|
|
|
<h2>Preferences propagation</h2>
|
|
<p>
|
|
<code>App.prefs</code> is the ecosystem-wide source of user intent. Locale is no longer a
|
|
Lang-only concern: language drives translations, locale drives regional formats, and
|
|
shared preferences such as direction, motion, sound and haptic can be projected through
|
|
the explicit prefs DOM projector.
|
|
</p>
|
|
<CodeBlock lang="ts" code={prefsExample} />
|
|
|
|
<h2>Disposal</h2>
|
|
<p>
|
|
<code>App.dispose()</code> publishes the dispose-starting event, disposes constructed
|
|
services in reverse construction order, tears down the prefs storage bridge, then disposes
|
|
<code>Prefs</code>, <code>Orca</code>, <code>Bus</code>, <code>Timers</code> and finally
|
|
<code>Logger</code>. The operation is idempotent.
|
|
</p>
|
|
|
|
<CodeBlock lang="ts" code={`onDestroy(() => App.dispose());`} />
|
|
|
|
<h2>Server boundary</h2>
|
|
<p>
|
|
<code>$active-app</code> is the client composition root. Server authority stays in
|
|
<code>$svrs</code> and pure engines. Shared contracts belong in <code>$libs</code>.
|
|
</p>
|
|
|
|
<PageNav />
|
|
</article>
|
|
|
|
<style>
|
|
.breadcrumbs {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.4rem;
|
|
font-size: 0.8125rem;
|
|
color: var(--c-fg-subtle);
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.breadcrumbs a {
|
|
color: var(--c-fg-muted);
|
|
}
|
|
</style>
|