# arts — runtime artifacts `src/arts/` contains the runtime building blocks of the application. Each artifact is independent, has its own README, and follows two consistent naming conventions: - **`Engine*`** — public methods over private state (or no state at all). Pure factory; the locale, logger or any volatile input is passed as argument on every call. When an artifact has a true server-authoritative counterpart (`perm`, `cache`), the engine lives under `src/svrs/`. - **`Active*`** — an `Engine*` that exposes public reactive state. Lives in a `.svelte.ts` file because it owns `$state`. Imports must target the file directly, not the barrel, to keep the rest of the artifact runes-free. ## ActiveEngine Contract Root active artifacts implement the shared `ActiveEngine` contract from `$libs/active`: ```ts interface ActiveEngine { readonly loading: boolean; readonly lastError: TError | null; readonly disposed: boolean; snapshot(): TSnapshot; clearError(): void; onChange(listener: (snapshot: TSnapshot) => void): () => void; dispose(): void; } ``` Conventions: - Use direct getters (`Auth.current`, `Cache.loading`, `Perms.lastError`), not a module-specific `.state` object. - Use `loading`, never `pending`, for in-flight work. - Use `onChange()` for snapshot subscriptions. Lower-level clients may expose their own event buses, but active roots keep this name. - `dispose()` is idempotent, clears owned listeners/entries/resources, and subsequent public operations throw the artifact's `XxxDisposedError`. - Root active artifacts that create entries (`ActiveCache.entry()`, `ActiveConnections.connection()`, etc.) own those entries and dispose them when the root is disposed. ## Logger And Diagnostics Contract Every artifact that emits runtime information follows the same two-layer contract: ```ts import type { DiagnosticEvent, Diagnostics, Logger } from '$libs/logger'; ``` - Public options use `logger?: Logger`. Do not create artifact-local logger interfaces or narrowed aliases for individual modules. - The root logger implementation is `EngineLogger` from `$logger`; it extends the shared `Logger` contract from `$libs/logger`. - Artifact code defines `Diagnostics` in a dedicated `diagnostics.ts` file — the canonical home for the `DiagnosticCatalog`, `createDiagnostics(logger?)` and `emitDiagnostic(...)`. Every artifact that emits diagnostics ships this file. - Diagnostic event names live in the artifact `consts.ts` as `*_DIAGNOSTIC_EVENTS`. Message strings are **named constants** in `consts.ts` or `errors.ts` (or co-located in `diagnostics.ts` when only the catalog reads them) — never inline string literals in the catalog or runtime logic. - `Diagnostics` always exposes `{ logger, emit(event) }`. The `logger` property is the common `Logger`, so modules that need an ad-hoc `info` or `error` still have the full logger without inventing a second interface. - Level routing is controlled by the logger/transports via the existing per-level enablement map, not by module-specific severity systems. Typical shape: ```ts export const HTTP_DIAGNOSTIC_EVENTS = { REQUEST: 'http.request', NETWORK_ERROR: 'http.network_error' } as const; export function createHttpDiagnostics(logger?: Logger): HttpDiagnostics { return createCatalogDiagnostics({ logger, defaultCategory: LOGGER_CATEGORY, catalog: HTTP_DIAGNOSTIC_LOGS }); } ``` This gives every module the same path to Sentry, Loki, Datadog, console, test-capture transports or any future sink: inject one `Logger`, emit typed diagnostic events, let `logger` route. ## Error Contract Errors follow the same rule: strings are centralized, and public programmer errors are typed. - Error messages and error names live in `errors.ts` or `consts.ts`. - Runtime code must not throw inline string/template errors outside tests or vendored code. - Public programmer errors use artifact-specific classes and guards: `SessionDisposedError`, `ConnInvalidNameError`, `UnitsUnknownUnitError`, etc. - Expected runtime failures should be returned as tagged data/results when the artifact already has such a contract (`http`, `connection`, `perm`, `cache`). - Validation failures are data (`SiumValidationError.issues`) and diagnostics are emitted separately when a logger is injected. ## Map | Artifact | Layer(s) | Purpose | Depends on | | -------------------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | | [`langs`](./langs/README.md) | `EngineLangs`, `ActiveLangs`, `ActiveMonoLangs` | i18n: type-safe translations, BCP 47 resolution, plurals, refs, JSON round-trip | — | | [`logger`](./logger/README.md) | `EngineLogger` | Structured logger: levels, transports, filters, vitals, dispose | — | | [`timer`](./timer/README.md) | `EngineTimers`, `ActiveTimers` | Deterministic timer scheduler: clock injection, one-shots, intervals, cancellation, snapshots, backoff | `$libs/timers`, `$logger` (optional) | | [`format`](./format/README.md) | `EngineFormat`, `ActiveFormat` | Localized formatting: numbers, currency, units, dates | `$logger` (currency) | | [`adom`](./adom/README.md) | `ActiveDom` | Reactive DOM service: viewport, breakpoints, attribute writes, scroll lock, post-layout read scheduling (`measure`) | `$libs/dom`, `$reactive` | | [`motion`](./motion/README.md) | `EngineMotion` | Animation runtime: registers + runs `--state` presets (CSS settle / JS drivers — spring / waapi / rect FLIP); the bridge BOTH UIX layers consume via `uix.motion` | `MotionDom` port (injected; `adom` satisfies it) | | [`clipboard`](./clipboard/README.md) | `ActiveClipboard` | Clipboard write capability with injectable writer and explicit unavailable errors | browser `navigator.clipboard` or injected writer | | [`sium`](./sium/README.md) | `EngineSium` | Validation contracts: schemas, issues, introspection, Standard Schema interop | `$langs` (optional), `$logger` (optional), `$libs/days`, `$libs/color` | | [`storage`](./storage/README.md) | `EngineStorage`, `ActiveStorage` | Reactive sync key/value: pluggable adapters, version+migrate, TTL, validation, intra-tab + cross-tab sync, reactive keys | `$sium` (Standard Schema interop, optional) | | [`http`](./http/README.md) | `EngineHttp` | HTTP client: tagged `HttpResult`, Standard Schema validation, retry, timeouts, hooks, SvelteKit `event.fetch` integration | `$libs/http`, `$libs/standard-schema` (type-only), `$logger` | | [`session`](./session/README.md) | `EngineSession`, `ActiveSession` | Session lifecycle: adopt/revoke/refresh, auto-refresh, 401-rescue hook, SvelteKit SSR via `adoptServer` + cookie reader | `$storage`, `$timer`, `$http`, `$logger` (optional) | | [`connection`](./connection/README.md) | `EngineConnections`, `ActiveConnections` | Realtime connection registry: transports, reconnect, heartbeat, request/reply, channels, session bridge | `$timer`, `$logger` (optional), `$session` bridge (optional) | | [`auth`](./auth/README.md) | `ActiveAuth` (`EngineAuth` in `$svrs/auth`) | Authentication: password flows, CSRF, current session reflector, devices, logout, server-authoritative auth handlers | `$libs/auth`, `$http`, `$cache`, `$svrs/auth` | | [`perm`](./perm/README.md) | `ActivePerms` (`EnginePerms` in `$svrs/perm`) | Authorization: policy runtime adapter, HTTP client/handlers, cache snapshot, `` guard | `$libs/perm`, `$libs/svrs`, `$http`, `$logger` (optional) | | [`cache`](./cache/README.md) | `ActiveCache` (`EngineCache` in `$svrs/cache`) | Data cache: deterministic keys, policies, scopes, stale/revalidate, tags, memory/storage adapters | `$libs/cache`, `$storage` (adapter), `$logger` (optional) | | [`color`](./color/README.md) | `$color` namespace (`uix.color`) | Isomorphic colour math: OKLCH↔sRGB, APCA, scale/scheme generation, alpha. Pure + stateless — `Engine`-grade, no class | — (zero-dep; consumed by eidos at build + runtime) | | [`perf`](./perf/README.md) | `ActivePerf` (`uix.perf`) | Dev forced-reflow detector: Long Animation Frames → attributed `forcedStyleAndLayoutDuration` reports; opt-in, inert in prod | platform LoAF API (Chromium) — zero-dep | | [`active-app`](./active-app/README.md) | `ActiveApp` | App composition: core Logger + Bus + Timers + Orca + Prefs, plus declared services via factories | every artifact above | ## Composition Most apps consume the artifacts through `active-app`: ```ts import { createActiveApp } from '$active-app'; import { defineActiveClipboard, defineActiveDom, defineActiveFormat, defineActiveLangs } from '$active-app/service-factories'; const App = createActiveApp({ logger: { level: LogLevel.INFO, transports: [consoleTransport()] }, services: { langs: defineActiveLangs({ schema, defaultLocale: 'es', fallbackChain: ['en'] }), clipboard: defineActiveClipboard(), format: defineActiveFormat(), dom: defineActiveDom() } }); App.langs.t('common.ok'); App.format.currency.format(99.5); App.prefs.language.set('es-MX'); // propagates to langs when wired by the factory ``` `App` always exposes the fixed core (`logger`, `bus`, `timers`, `orca`, `prefs`). Feature services exist only when the application declares their slot. For translations that means `App.langs` exists when `services.langs` is declared with `defineActiveLangs(...)`; otherwise the property is not part of the typed surface. `Sium`, `Session`, `Connections`, `Auth` and `Perms` are exposed as **factories** because they are feature/page-scoped: App injects shared services, but construction is explicit at the call site. ```ts const App = createActiveApp({ services: { sium: defineEngineSium({}), connections: defineActiveConnections({}), auth: defineActiveAuth({ initial: data.auth }), perm: defineActivePerm({ endpoint: '/perm' }) } }); applyStandardOrca(App); ``` See `active-app/README.md` for the full composition contract. ## Cross-artifact dependencies ``` langs logger \ / | \ \ / | \ format http timer \ | /|\ adom ──────────────────\ | / | connection \ \ | / | \ \ \ | / auth perm ───────────── active-app ─ storage ─ cache : sium ``` - `langs` and `logger` are the dependency-free roots (`zero-dep` libraries). - `format` consumes `logger` only inside `currency` (rate fetcher diagnostics). - `sium` accepts `langs` and `logger` via injection; without them it falls back to local message interpolation. - `timer` is the deterministic scheduler consumed by `session` and `connection`. - `http` accepts `logger` via injection (auto-wired through `active-app`) and keeps shared HTTP literals/types in `$libs/http`. - `session` consumes `storage` for persistence, `timer` for auto-refresh, and `http` for 401-rescue integration. - `connection` consumes `timer` for reconnect/heartbeat/ack timeouts and accepts the App session bridge when composed through `active-app`. - `auth` splits cleanly: `$svrs/auth` owns identity proof, CSRF and server handlers; `$auth` owns the active client reflector. It feeds `session`, `perm` and `cache` through ports rather than owning their state. - `perm` splits cleanly: `$svrs/perm` owns the authoritative engine/HTTP handlers, while `$perm` owns the active UI reflector and ``. - `cache` splits cleanly: `$svrs/cache` owns the imperative engine, while `$cache` owns the active Svelte wrapper and can consume `storage` through its storage adapter. - `adom` depends only on the pure helpers in `libs/dom` and on `libs/reactive`. - `motion` is the animation engine consumed by BOTH UIX layers via `uix.motion` (soma's `Presence` + eidos wrappers), which dissolves the would-be soma→eidos coupling. It imports no other art — the DOM dependency arrives injected via the structural `MotionDom` port, which `adom` satisfies. - `active-app` composes always-present roots and exposes factories for scoped artifacts (`sium`, `session`, `connection`, `auth`, `perm`). ## Shared types | Module | Type | Used by | | --------- | ------------------------------------------------------- | ---------------------------------------------- | | `$locale` | `LocaleSource` | `format.localeSource`, `active-app` wiring | | `$langs` | `SupportedLocale` (`LangBase \| ${LangBase}-${string}`) | `langs`, consumers that want type-safe locales | ## Aliases ```js // svelte.config.js alias: { $active-app: 'src/arts/active-app', $adom: 'src/arts/adom', $auth: 'src/arts/auth', $cache: 'src/arts/cache', $clipboard: 'src/arts/clipboard', $color: 'src/arts/color', $connection: 'src/arts/connection', $format: 'src/arts/format', $http: 'src/arts/http', $langs: 'src/arts/langs', $logger: 'src/arts/logger', $motion: 'src/arts/motion', $perf: 'src/arts/perf', $perm: 'src/arts/perm', $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' } ``` ## Bundle policy Every artifact is designed to tree-shake cleanly: - `package.json` declares `"sideEffects": ["**/*.css", "**/*.svelte"]`, so any `.ts` / `.svelte.ts` module that the bundler does not statically reach is dropped from the production bundle. - All barrels use **named re-exports** (`export { a, b } from './x'`) instead of `export *`. This lets the bundler prove which symbols are reached from a given import and drop the rest of the source module. - `.svelte.ts` files defer module-level state (`viewport.svelte.ts`, `body-scroll-lock.svelte.ts`) so importing the barrel does not allocate Svelte runes runtime for unused features. - External adapters (`$logger/adapters/*`) and dev helpers (`$active-app/testing`, `$sium/_examples/`) live outside the main barrel. A consumer that does not reference them never pays for them. A consumer that builds `createActiveApp({ services: { langs: defineActiveLangs({ schema }) } })` and only calls `App.langs.t(...)` should land roughly in the 40–50 KB minified range. A consumer that wires every artifact (Sium + Storage cookies + DOM runtime + Web Vitals) lands in the ~120 KB range. The difference is the per-feature surface, paid only when reached. ## Test pages Interactive docs now live under `web/routes/active` and `web/routes/uix`. Older `/test/*` pages may still exist in local branches, but the canonical artifact names are the directory names listed in the map above: - `active-app` — full composition end-to-end - `ecosystem` — total integration demo: auth, session, perm, cache, http, storage, sium, format, adom, timer, connection, langs and logger in one app flow - `langs` — i18n with reactive locale switching, plurals, BCP 47 - `logger` — log levels, transports, vitals, Sentry integration - `format` — numbers / currency / units / dates with shared locale - `adom` — viewport, breakpoints, scroll lock, roving focus - `sium` — login / signup / profile schemas with translated issues - `storage` — adapters (memory / local / session / cookie), envelope versioning + migrate, raw mode, TTL, mergeDefaults, cross-tab sync - `http` — GET/POST with Sium validation, retry + Retry-After, timeout, cancellation, lifecycle hooks, tagged `HttpResult` - `session` — session lifecycle: adopt/revoke/refresh with generation guard + dedup, auto-refresh, tagged `RevokeResult`, permission checks, event stream - `timer` — scheduler snapshots, intervals, cancellation and deterministic clocks - `connection` — websocket chat and connection/channel lifecycle - `auth` — server-authoritative auth surface: password flow, CSRF, devices, routes and security events - `perm` — authorization checks, ``, HTTP handlers and client cache - `cache` — cache policies, scopes, tags and active entries Use `/active` for the current application/runtime docs and `/uix` for the UIX component system docs.