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.
216 lines
6.5 KiB
216 lines
6.5 KiB
<script lang="ts">
|
|
import CodeBlock from '../../_components/CodeBlock.svelte';
|
|
import Callout from '../../_components/Callout.svelte';
|
|
import PageNav from '../../_components/PageNav.svelte';
|
|
</script>
|
|
|
|
<svelte:head>
|
|
<title>Installation — Active</title>
|
|
</svelte:head>
|
|
|
|
<article class="article">
|
|
<header class="breadcrumbs">
|
|
<a href="/active">Get Started</a>
|
|
<span aria-hidden="true">/</span>
|
|
<span>Installation</span>
|
|
</header>
|
|
|
|
<h1>Installation</h1>
|
|
<p class="lead">
|
|
Active is a runtime that lives inside your SvelteKit project. It ships as a set of
|
|
path-aliased folders under <code>src/arts/*</code> rather than a published npm package, so
|
|
"installation" is really wiring the aliases and using the factories.
|
|
</p>
|
|
|
|
<h2>Prerequisites</h2>
|
|
<ul>
|
|
<li>SvelteKit <code>2.x</code> with Svelte <code>5.x</code> (runes mode).</li>
|
|
<li>TypeScript <code>5.4+</code> recommended for full inference.</li>
|
|
<li>A <code>Node 22+</code> runtime if you plan to use the server-authoritative engines (<code>$svrs/auth</code>, <code>$svrs/perm</code>, <code>$svrs/cache</code>).</li>
|
|
</ul>
|
|
|
|
<h2>Path aliases</h2>
|
|
<p>
|
|
The framework expects a fixed set of aliases, declared in <code>svelte.config.js</code>.
|
|
These match the folder layout under <code>src/</code> and let every artifact import its
|
|
peers without relative paths.
|
|
</p>
|
|
|
|
<CodeBlock
|
|
title="svelte.config.js"
|
|
lang="js"
|
|
code={`alias: {
|
|
'$active-app/services': 'src/arts/active-app/service-factories',
|
|
'$active-app/presets': 'src/arts/active-app/presets',
|
|
'$active-app': 'src/arts/active-app',
|
|
$adom: 'src/arts/adom',
|
|
$auth: 'src/arts/auth',
|
|
$bus: 'src/arts/bus',
|
|
$cache: 'src/arts/cache',
|
|
$connection: 'src/arts/connection',
|
|
$format: 'src/arts/format',
|
|
$http: 'src/arts/http',
|
|
$langs: 'src/arts/langs',
|
|
$logger: 'src/arts/logger',
|
|
$orca: 'src/arts/orca',
|
|
$perm: 'src/arts/perm',
|
|
$prefs: 'src/arts/prefs',
|
|
$session: 'src/arts/session',
|
|
$sium: 'src/arts/sium',
|
|
$storage: 'src/arts/storage',
|
|
$svrs: 'src/svrs',
|
|
$timer: 'src/arts/timer',
|
|
$libs: 'src/libs',
|
|
$locale: 'src/libs/locale',
|
|
$reactive: 'src/libs/reactive'
|
|
}`}
|
|
/>
|
|
|
|
<Callout variant="info" title="Why aliases?">
|
|
<p>
|
|
Aliases make every artifact import its peers as <code>$langs</code> /
|
|
<code>$logger</code>, regardless of where it lives. This keeps the boundary between layers
|
|
explicit and prevents accidental cross-imports.
|
|
</p>
|
|
</Callout>
|
|
|
|
<h2>Bundle policy</h2>
|
|
<p>
|
|
Active is designed to tree-shake. <code>package.json</code> declares only CSS and Svelte
|
|
files as side-effectful, and every barrel uses <strong>named re-exports</strong> instead of
|
|
<code>export *</code>:
|
|
</p>
|
|
|
|
<CodeBlock
|
|
title="package.json"
|
|
lang="json"
|
|
code={`{
|
|
"sideEffects": ["**/*.css", "**/*.svelte"]
|
|
}`}
|
|
/>
|
|
|
|
<p>
|
|
As a result, the minimum <code>createActiveApp({})</code> import has a measured budget
|
|
instead of a guess. <code>npm run test:bundle</code> builds a virtual Vite entry with OXC,
|
|
gzips the emitted JavaScript and fails above the configured budget.
|
|
</p>
|
|
|
|
<CodeBlock
|
|
title="Bundle smoke"
|
|
lang="sh"
|
|
code={`npm run test:bundle
|
|
# active bundle smoke: ~65 KB gzip by current baseline
|
|
|
|
ACTIVE_BUNDLE_GZIP_LIMIT_KB=70 npm run test:bundle`}
|
|
/>
|
|
|
|
<p>
|
|
The default budget is <code>70 KB gzip</code>. That is intentionally a smoke gate, not a
|
|
micro-benchmark: it protects the root runtime from accidental graph explosions while still
|
|
leaving room for the fixed App core.
|
|
</p>
|
|
|
|
<h2>First app</h2>
|
|
<p>
|
|
Create an <code>App</code> instance once at the root of your client tree. The most minimal
|
|
setup:
|
|
</p>
|
|
|
|
<CodeBlock
|
|
lang="ts"
|
|
title="src/lib/app.ts"
|
|
code={`import { createActiveApp } from '$active-app';
|
|
|
|
export const App = createActiveApp();
|
|
|
|
App.logger.info('app.boot', 'Active app ready');`}
|
|
/>
|
|
|
|
<p>
|
|
Even with no options, the fixed core is present: <code>App.logger</code>,
|
|
<code>App.bus</code>, <code>App.timers</code>, <code>App.orca</code> and
|
|
<code>App.prefs</code>. Feature modules are added explicitly under <code>services</code>.
|
|
</p>
|
|
|
|
<h2>Realistic setup</h2>
|
|
<p>For typed i18n, preferences, formatting, DOM projection and storage:</p>
|
|
|
|
<CodeBlock
|
|
lang="ts"
|
|
title="src/lib/app.ts"
|
|
code={`import { createActiveApp } from '$active-app';
|
|
import {
|
|
defineActiveLangs,
|
|
defineActiveFormat,
|
|
defineActiveDom,
|
|
defineActiveStorage
|
|
} from '$active-app/services';
|
|
import { createActivePrefsDomProjection } from '$prefs';
|
|
import { LogLevel, consoleTransport } from '$logger';
|
|
import { localAdapter } from '$storage';
|
|
import { schema } from './i18n/schema';
|
|
|
|
export const App = createActiveApp({
|
|
logger: { level: LogLevel.INFO, transports: [consoleTransport()] },
|
|
prefs: {
|
|
capabilities,
|
|
environment,
|
|
intent: { language: 'es', locale: 'es-ES' }
|
|
},
|
|
services: {
|
|
dom: defineActiveDom(),
|
|
langs: defineActiveLangs({ schema, defaultLocale: 'es', fallbackChain: ['en'] }),
|
|
format: defineActiveFormat(),
|
|
storage: defineActiveStorage({ adapter: localAdapter })
|
|
}
|
|
});
|
|
|
|
export const prefsProjection = createActivePrefsDomProjection({
|
|
prefs: App.prefs,
|
|
dom: App.dom
|
|
});`}
|
|
/>
|
|
|
|
<Callout variant="tip" title="Single-instance services">
|
|
<p>
|
|
<code>session</code>, <code>auth</code> and <code>perm</code> are declared as service
|
|
slots in <code>createActiveApp({`{ services: { session, auth, perm } }`})</code> via
|
|
<code>defineActiveSession()</code>, <code>defineActiveAuth()</code> and
|
|
<code>defineActivePerm()</code>. A duplicate slot is a JavaScript object-literal error,
|
|
so the schema makes "factory called twice" structurally impossible.
|
|
</p>
|
|
</Callout>
|
|
|
|
<h2>Verify</h2>
|
|
<p>
|
|
Open the playground at <a href="/test">/test</a> for an interactive page per artifact, or
|
|
jump straight to <a href="/test/ecosystem">/test/ecosystem</a> for the full integration
|
|
demo. After <code>npm run build</code>, use <code>npm run test:static</code> to verify that
|
|
the generated static docs, ecosystem test page and app assets exist.
|
|
</p>
|
|
|
|
<h2>Next</h2>
|
|
<p>
|
|
Read <a href="/active/get-started/ecosystem">Ecosystem</a> for the full layer map and
|
|
<a href="/active/get-started/composition">Composition</a> to understand how the artifacts
|
|
plug into each other, then drill into a module from the sidebar.
|
|
</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>
|