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.
324 lines
14 KiB
324 lines
14 KiB
<script lang="ts">
|
|
import Callout from '../../_components/Callout.svelte';
|
|
import CodeBlock from '../../_components/CodeBlock.svelte';
|
|
import PageNav from '../../_components/PageNav.svelte';
|
|
|
|
const layerMap = `src/libs/* shared contracts, constants and pure helpers
|
|
src/svrs/* server-authoritative engines and backend adapters
|
|
src/arts/* client/runtime artifacts, active wrappers and browser ergonomics
|
|
src/web/routes documentation, test pages and app routes
|
|
|
|
$active-app client composition root: Logger, Bus, Timers, Orca, Prefs core + typed services`;
|
|
|
|
const appFlow = `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'] }),
|
|
storage: defineActiveStorage({ adapter: localAdapter, namespace: 'app' }),
|
|
http: defineEngineHttp({ baseUrl: '/api' }),
|
|
cache: defineActiveCache({ defaultPolicy: 'interactive' }),
|
|
sium: defineEngineSium({}),
|
|
session: defineActiveSession({ storage, onRefresh, onRevoke }),
|
|
auth: defineActiveAuth({ initial: data.auth }),
|
|
perm: defineActivePerm({ endpoint: '/api/perm' }),
|
|
connections: defineActiveConnections({})
|
|
}
|
|
});
|
|
|
|
const prefsProjection = createActivePrefsDomProjection({
|
|
prefs: App.prefs,
|
|
dom: App.dom
|
|
});
|
|
|
|
applyStandardOrca(App); // wires cache, perm and connections reactions through App.orca`;
|
|
|
|
const serverFlow = `const Auth = createEngineAuth({
|
|
security,
|
|
ports: { store, actors, sess, cache, logger, timer, crypto, passwordHasher, mailer }
|
|
});
|
|
|
|
const Perms = createEnginePerms({
|
|
schema,
|
|
policies,
|
|
providers,
|
|
compilers,
|
|
logger
|
|
});
|
|
|
|
const Cache = createEngineCache({
|
|
namespace: 'api',
|
|
adapter,
|
|
scopeResolver,
|
|
policies,
|
|
logger
|
|
});`;
|
|
|
|
const requestFlow = `Browser
|
|
→ ActiveAuth / ActivePerms / App.http / ActiveCache
|
|
→ SvelteKit endpoint or action
|
|
→ $svrs/auth reads Auth current and session binding
|
|
→ $svrs/perm asserts actor can perform action
|
|
→ $svrs/cache serves or fetches scoped data
|
|
→ App.http receives typed result
|
|
→ Active roots update snapshots and UI`;
|
|
|
|
const identityFlow = `signInPassword()
|
|
→ ActiveAuth obtains CSRF and posts to server
|
|
→ EngineAuth validates credential and binds session through the session port
|
|
→ Session state changes
|
|
→ Perms snapshot/cache must be invalidated for the new actor
|
|
→ Cache private scopes change actorId / permissionHash
|
|
→ Connections can reauthenticate or disconnect through the App session bridge`;
|
|
|
|
const localeFlow = `App.prefs.language.set('ar')
|
|
App.prefs.locale.set('ar-EG')
|
|
-> prefs resolves effective language and regional locale
|
|
-> App.langs follows App.prefs.language.get()
|
|
-> App.format follows App.prefs.locale.get()
|
|
-> prefs projection writes dir="rtl" and cross-modal attrs through App.dom
|
|
-> ActiveEidos owns data-theme, data-mode and data-density when UIX is mounted`;
|
|
</script>
|
|
|
|
<svelte:head>
|
|
<title>Ecosystem — Active</title>
|
|
</svelte:head>
|
|
|
|
<article class="article">
|
|
<header class="breadcrumbs">
|
|
<a href="/active">Get Started</a>
|
|
<span aria-hidden="true">/</span>
|
|
<span>Ecosystem</span>
|
|
</header>
|
|
|
|
<h1>Ecosystem</h1>
|
|
<p class="lead">
|
|
Active is not a bag of utilities. It is a runtime ecosystem: a small language of
|
|
artifacts, factories, contracts and layer boundaries that lets a SvelteKit app avoid
|
|
reinventing infrastructure for every feature.
|
|
</p>
|
|
|
|
<h2>Layer map</h2>
|
|
<p>
|
|
The first thing to understand is where a module is allowed to live. The same concept can
|
|
have shared contracts, a server authority and an active client reflector, but those are not
|
|
the same layer.
|
|
</p>
|
|
<CodeBlock code={layerMap} lang="text" />
|
|
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Layer</th>
|
|
<th>What belongs there</th>
|
|
<th>What must not happen there</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><code>libs</code></td>
|
|
<td>Types, constants, pure algorithms, shared contracts.</td>
|
|
<td>No Svelte state, no browser APIs, no database or framework dependency.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>svrs</code></td>
|
|
<td>Server engines, secrets, backend adapters, request security.</td>
|
|
<td>No UI state and no trust in client-provided identity or permissions.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>arts</code></td>
|
|
<td>Public runtime artifacts, active wrappers, browser/client ergonomics.</td>
|
|
<td>No server secrets, no authoritative authorization, no password/token persistence.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>$active-app</code></td>
|
|
<td>Client composition root with fixed core plus typed services.</td>
|
|
<td>No replacement for server engines; it wires runtime modules, not backend authority.</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<Callout variant="warn" title="Server authority">
|
|
<p>
|
|
<code>auth</code>, <code>perm</code> and <code>cache</code> have server-level modules
|
|
because they can affect identity, access or private data coherence. Their active
|
|
counterparts are reflections for UX, not the source of truth.
|
|
</p>
|
|
</Callout>
|
|
|
|
<h2>Domains</h2>
|
|
<p>
|
|
The modules are grouped by problem, not by technical trick. This makes the dependency
|
|
direction easier to reason about.
|
|
</p>
|
|
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Domain</th>
|
|
<th>Modules</th>
|
|
<th>Responsibility</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr><td>Composition</td><td><code>$active-app</code></td><td>Single client root, fixed Logger/Bus/Timers/Orca/Prefs core, typed service schema and disposal order.</td></tr>
|
|
<tr><td>Identity</td><td><code>$auth</code>, <code>$session</code>, <code>$perm</code></td><td>Prove identity, keep session continuity, decide access.</td></tr>
|
|
<tr><td>Data</td><td><code>$http</code>, <code>$cache</code>, <code>$storage</code></td><td>Remote calls, coherent cached data, safe local persistence.</td></tr>
|
|
<tr><td>Preferences</td><td><code>$prefs</code></td><td>User intent, environment defaults and effective locale/timezone/perception values.</td></tr>
|
|
<tr><td>I18n and formats</td><td><code>$langs</code>, <code>$format</code></td><td>Text translation plus preference-driven numbers, currency, units and dates.</td></tr>
|
|
<tr><td>DOM and UI runtime</td><td><code>$adom</code>, <code>$prefs</code>, <code>$uix/eidos</code></td><td>DOM writes, global preference attrs and visual theme/mode/density.</td></tr>
|
|
<tr><td>Validation</td><td><code>$sium</code></td><td>Page-scoped schemas, issues, metadata and translated validation messages.</td></tr>
|
|
<tr><td>Infrastructure</td><td><code>$bus</code>, <code>$logger</code>, <code>$timer</code>, <code>$orca</code>, <code>$connection</code></td><td>Events, structured logs, deterministic timers, orchestration and realtime connections.</td></tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<h2>Factories</h2>
|
|
<p>
|
|
The factory naming is the backbone of the framework. If a module owns pure behavior, it has
|
|
an <code>Engine</code>. If it owns reactive state for Svelte, it has an <code>Active</code>.
|
|
If it must be authoritative on the server, the engine lives in <code>$svrs</code>.
|
|
</p>
|
|
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Factory</th>
|
|
<th>Use it when</th>
|
|
<th>Examples</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr><td><code>createEngineXxx()</code></td><td>You need deterministic, non-runes runtime logic.</td><td><code>createEngineHttp</code>, <code>createEngineTimers</code>, <code>createEngineSium</code></td></tr>
|
|
<tr><td><code>createActiveXxx()</code></td><td>You need Svelte 5 reactive state and lifecycle.</td><td><code>createActiveStorage</code>, <code>createActiveConnections</code></td></tr>
|
|
<tr><td><code>$svrs/createEngineXxx()</code></td><td>The result must be server-authoritative.</td><td><code>createEngineAuth</code>, <code>createEnginePerms</code>, <code>createEngineCache</code></td></tr>
|
|
<tr><td><code>App.logger / App.bus / App.timers / App.orca / App.prefs</code></td><td>You need the fixed App core.</td><td>Always present; configured from root options, never declared as services.</td></tr>
|
|
<tr><td><code>defineActiveXxx() / defineEngineXxx()</code></td><td>You want App to declare a service in its schema and inject core deps automatically.</td><td><code>defineEngineSium({`{}`})</code>, <code>defineActiveAuth({`{...}`})</code></td></tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<h2>App composition</h2>
|
|
<p>
|
|
<code>createActiveApp()</code> gives the browser/client side a stable surface. The fixed
|
|
core is always present: <code>Logger</code>, <code>Bus</code>, <code>Timers</code>,
|
|
<code>Orca</code> and <code>Prefs</code>. Feature modules are declared explicitly in
|
|
<code>services</code>, so pages only pay for what the app composes.
|
|
</p>
|
|
<CodeBlock code={appFlow} lang="ts" title="Client composition" />
|
|
|
|
<h2>Server composition</h2>
|
|
<p>
|
|
Server engines are explicit. They receive ports instead of importing infrastructure
|
|
directly, which keeps tests deterministic and adapters replaceable.
|
|
</p>
|
|
<CodeBlock code={serverFlow} lang="ts" title="Server composition" />
|
|
|
|
<h2>Request flow</h2>
|
|
<p>
|
|
A full request crosses several modules, but each one has one job. That separation is the
|
|
main value of the ecosystem.
|
|
</p>
|
|
<CodeBlock code={requestFlow} lang="text" />
|
|
|
|
<h2>Identity flow</h2>
|
|
<p>
|
|
Identity-sensitive work follows a stricter chain. <code>auth</code> proves identity,
|
|
<code>$session</code> keeps continuity, <code>$perm</code> decides access, and
|
|
<code>cache</code> must scope or invalidate private data.
|
|
</p>
|
|
<CodeBlock code={identityFlow} lang="text" />
|
|
|
|
<h2>Locale flow</h2>
|
|
<p>
|
|
Preferences are the ecosystem-wide source of user intent. Translation language, regional
|
|
format locale and document direction can be related, but they are not the same value.
|
|
</p>
|
|
<CodeBlock code={localeFlow} lang="text" />
|
|
|
|
<h2>Choosing a module</h2>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>If you need...</th>
|
|
<th>Use</th>
|
|
<th>Do not use</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr><td>Translate labels, messages or fallbacks.</td><td><code>$langs</code></td><td><code>$format</code> or ad-hoc dictionaries.</td></tr>
|
|
<tr><td>Format numbers, dates, currency or units.</td><td><code>$format</code></td><td><code>$langs</code>.</td></tr>
|
|
<tr><td>Persist non-secret preferences or drafts.</td><td><code>$storage</code></td><td><code>$session</code>, localStorage calls spread through pages.</td></tr>
|
|
<tr><td>Keep logged-in continuity.</td><td><code>$session</code></td><td><code>$auth</code> alone.</td></tr>
|
|
<tr><td>Prove identity or run login/recovery flows.</td><td><code>$svrs/auth</code> plus <code>$auth</code></td><td><code>perm</code> or client-only checks.</td></tr>
|
|
<tr><td>Decide if an actor can do something.</td><td><code>$svrs/perm</code> plus <code>$perm</code></td><td><code>auth</code>, roles hard-coded in UI.</td></tr>
|
|
<tr><td>Cache data with scopes and invalidation.</td><td><code>$svrs/cache</code> or <code>$cache</code></td><td><code>$storage</code> as a query cache.</td></tr>
|
|
<tr><td>Schedule retries, refreshes or timeouts.</td><td><code>timer</code></td><td>raw <code>setTimeout</code> scattered across modules.</td></tr>
|
|
<tr><td>Open realtime sockets and channels.</td><td><code>$connection</code></td><td>custom WebSocket state in components.</td></tr>
|
|
<tr><td>Validate forms and generate issues.</td><td><code>sium</code></td><td><code>perm</code> or manual string errors.</td></tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<h2>Integration rules</h2>
|
|
<ul>
|
|
<li>Every public string used for logs, events, methods, categories or protocol names belongs in constants.</li>
|
|
<li>Modules receive the shared <code>Logger</code> contract; they do not invent local logger interfaces.</li>
|
|
<li>Diagnostics are allowed as a catalog layer, but they emit through <code>Logger</code>.</li>
|
|
<li>Server engines never trust actor, permission or private scope values sent by the browser.</li>
|
|
<li>Active clients can cache for UX, but protected data and mutations must be checked server-side.</li>
|
|
<li>Auto/manual preferences behave consistently: auto follows source changes; manual stays fixed until <code>clearX()</code>.</li>
|
|
<li><code>dispose()</code> must be real, idempotent and should reject future work where the module owns resources.</li>
|
|
</ul>
|
|
|
|
<Callout variant="tip" title="Working with AI agents">
|
|
<p>
|
|
If an AI is going to modify Active, send it first to
|
|
<a href="/active/get-started/ai-agents">AI Agents</a>. That page defines the operating
|
|
rules: read order, layer boundaries, logger contract, constants, docs truthfulness and
|
|
the completion checklist.
|
|
</p>
|
|
</Callout>
|
|
|
|
<h2>Test strategy</h2>
|
|
<p>
|
|
The ecosystem needs three levels of tests. A module can be correct in isolation and still
|
|
fail when identity, permissions, cache and realtime state interact.
|
|
</p>
|
|
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Level</th>
|
|
<th>Targets</th>
|
|
<th>What it proves</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr><td>Unit</td><td><code>src/arts/*/test</code>, <code>src/libs/*/test</code>, <code>src/svrs/*/test</code></td><td>Each artifact obeys its own contract.</td></tr>
|
|
<tr><td>Integration</td><td><code>src/arts/active-app/test</code></td><td>App wiring, prefs propagation, orca presets, session bridge, disposal order.</td></tr>
|
|
<tr><td>Scenario</td><td><code>/test/ecosystem</code></td><td>A realistic app story with auth, session, perm, cache, http, sium, connection and UI state together.</td></tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<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>
|