import type { ArtifactDocModel } from '../_components/ArtifactDoc.svelte'; const artifactApis = { auth: [ { title: 'ActiveAuth', body: ['Client reflector from $auth. It talks to server routes and exposes UI state; it is not the security boundary.'], table: [ { name: 'current', purpose: 'Current AuthCurrentView.', notes: 'Session status, actor snapshot, AAL/AMR metadata.' }, { name: 'authenticated', purpose: 'Boolean shortcut.', notes: 'True when current.session.status is authenticated.' }, { name: 'mfaRequired', purpose: 'Boolean shortcut.', notes: 'True when the server asks for MFA completion.' }, { name: 'loading / lastError / disposed', purpose: 'ActiveEngine state.', notes: 'Shared active contract used by client roots.' }, { name: 'loadCurrent()', purpose: 'Reload /current from the server.', notes: 'Use after SSR hydration, focus or explicit refresh.' }, { name: 'signInPassword(input)', purpose: 'Password login through server route.', notes: 'Fetches/sends CSRF before state-changing call.' }, { name: 'signUpPassword(input)', purpose: 'Password signup through server route.', notes: 'Returns the new current view.' }, { name: 'signOut() / signOutGlobal()', purpose: 'End current session or every actor session.', notes: 'Clears active current to anonymous after success.' }, { name: 'requestPasswordReset() / completePasswordReset()', purpose: 'Password recovery flow.', notes: 'Server owns token verification and mutation.' }, { name: 'requestEmailVerification() / completeEmailVerification()', purpose: 'Email verification flow.', notes: 'Server owns expiring flows.' }, { name: 'listDevices() / revokeDevice(input)', purpose: 'Device/session management client calls.', notes: 'Route wiring must expose the device endpoints explicitly.' }, { name: 'snapshot() / onChange() / clearError() / dispose()', purpose: 'Active lifecycle.', notes: 'Snapshot is safe to serialize; dispose stops listeners.' } ] }, { title: 'EngineAuth', body: ['Server authority from $svrs/auth. It owns identity proof, CSRF, flow state, session binding and security events.'], table: [ { name: 'current(input)', purpose: 'Read current auth view for a request.', notes: 'Usually called from server hooks/load functions.' }, { name: 'signUpPassword() / signInPassword()', purpose: 'Password credential flows.', notes: 'Use ports.store, ports.actors, ports.sess and passwordHasher.' }, { name: 'signOut() / signOutGlobal()', purpose: 'Session revocation flows.', notes: 'Also emits events and invalidates cache when wired.' }, { name: 'issueCsrf() / verifyCsrf()', purpose: 'CSRF token lifecycle.', notes: 'Uses configured security.csrf options.' }, { name: 'requestEmailVerification() / completeEmailVerification()', purpose: 'Email verification server flow.', notes: 'Requires mailer for delivery in real apps.' }, { name: 'requestPasswordReset() / completePasswordReset()', purpose: 'Password reset server flow.', notes: 'Tokens are server verified and single-use.' }, { name: 'listDevices() / revokeDevice()', purpose: 'Device management primitives.', notes: 'Engine methods exist even if default route map does not expose all endpoints.' }, { name: 'startOAuth() / completeOAuth()', purpose: 'OAuth/OIDC primitives.', notes: 'Provider adapters decide discovery/profile mapping.' }, { name: 'createMfaChallenge() / verifyMfaChallenge()', purpose: 'MFA primitives.', notes: 'Uses MFA ports where configured.' }, { name: 'handlers / createSvelteKitHandle()', purpose: 'Framework routing helpers.', notes: 'Default handlers cover current, CSRF, password, recovery and sign-out.' }, { name: 'on(name, handler)', purpose: 'Subscribe to auth events.', notes: 'Security/audit hooks receive structured payloads.' } ] }, { title: 'EngineAuthOptions', table: [ { name: 'security', purpose: 'CSRF, cookie and password policy.', notes: 'CSRF signing key is required when CSRF is enabled.' }, { name: 'ports.store / ports.actors', purpose: 'Auth persistence and actor lookup/creation.', notes: 'Memory and DB adapters live under $svrs/auth.' }, { name: 'ports.sess', purpose: 'Session bridge.', notes: 'Auth never owns the session cookie directly.' }, { name: 'ports.logr / ports.timr / ports.crypto', purpose: 'Logging, clock and crypto primitives.', notes: 'No direct Date.now() or random string shortcuts in flows.' }, { name: 'ports.passwordHasher / mailer / http / webauthn', purpose: 'Optional mechanisms.', notes: 'Injected only when the app enables those flows.' }, { name: 'providers', purpose: 'OAuth/OIDC provider adapters.', notes: 'Provider tokens are not persisted unless an adapter does so explicitly.' }, { name: 'hooks.beforeEvent / hooks.afterEvent', purpose: 'Security event interception.', notes: 'Useful for audit, metrics and custom side effects.' } ] } ], sess: [ { title: 'EngineSession / ActiveSession', body: ['ActiveSession is the reactive facade over the same session contract; both expose the same lifecycle methods.'], table: [ { name: 'current', purpose: 'Current session object or null.', notes: 'Contains user, credential and data slots.' }, { name: 'identity', purpose: 'Identity state.', notes: 'none, anonymous or identified.' }, { name: 'generation', purpose: 'Monotonic local change counter.', notes: 'Useful for cache keys and UI invalidation.' }, { name: 'adopt(session)', purpose: 'Adopt a client-provided session after schema validation.', notes: 'Use for client-side session updates.' }, { name: 'adoptServer(session)', purpose: 'Adopt SSR-trusted session.', notes: 'Bypasses client schema distrust because server already validated.' }, { name: 'refresh()', purpose: 'Deduped refresh operation.', notes: 'Keeps current session if refresh throws; clears if refresh returns null.' }, { name: 'revoke(options?)', purpose: 'End local/global session.', notes: 'Calls onRevoke when configured.' }, { name: 'clearLocal(reason?)', purpose: 'Clear local state without server revoke.', notes: 'Use when server already invalidated the session.' }, { name: 'onChange(listener)', purpose: 'Subscribe to lifecycle changes.', notes: 'Used by App connection bridge.' }, { name: 'dispose()', purpose: 'Stop timers/listeners.', notes: 'Called by App.dispose().' } ] }, { title: 'EngineSessionOptions', table: [ { name: 'schemas', purpose: 'Optional Standard Schema validation for user/credential/data.', notes: 'Protects client-provided adoption.' }, { name: 'storage', purpose: 'Storage entry config.', notes: 'Typically App.Storage with local/session/cookie adapter.' }, { name: 'onRefresh', purpose: 'Server refresh callback.', notes: 'Returns next session or null.' }, { name: 'onRevoke', purpose: 'Server revoke callback.', notes: 'Can degrade to local revoke if remote fails.' }, { name: 'logger', purpose: 'Shared Logger contract.', notes: 'Injected by App.' }, { name: 'broadcastChannel', purpose: 'Cross-tab session propagation.', notes: 'Optional browser integration.' } ] } ], cach: [ { title: 'EngineCache / CacheRuntime', body: ['The server engine extends the pure CacheRuntime from $libs/cach and adds disposal/diagnostics.'], table: [ { name: 'query(options)', purpose: 'Read through cache with fetcher.', notes: 'Evaluates freshness, scope, policy, epochs and stale-if-error.' }, { name: 'get(key, options)', purpose: 'Read cached value only.', notes: 'Returns undefined on miss/expired/invalid.' }, { name: 'set(key, value, options)', purpose: 'Write an envelope.', notes: 'Stores scope, tags, policy windows and schemaVersion.' }, { name: 'invalidate(options)', purpose: 'Invalidate by key, keyPrefix or tag.', notes: 'Uses epoch bumping instead of scanning every entry.' }, { name: 'mutate(options)', purpose: 'Run commit plus cache updates/invalidations.', notes: 'Useful after writes.' }, { name: 'explain(key, options)', purpose: 'Debug cache decision.', notes: 'Shows action, reason, scope, timings and epoch comparison.' }, { name: 'stats()', purpose: 'Read event counters.', notes: 'Hit/miss/stale/refresh/error counters.' }, { name: 'on(type, handler)', purpose: 'Subscribe to cache events.', notes: 'Use CACHE_EVENT_ALL for every event.' }, { name: 'clear()', purpose: 'Clear adapter if supported.', notes: 'Falls back according to adapter capability.' }, { name: 'dispose()', purpose: 'Close engine and reject future calls.', notes: 'ActiveCache calls this automatically.' } ] }, { title: 'ActiveCache', table: [ { name: 'lastEvent / eventCount', purpose: 'Reactive event summary.', notes: 'Useful for debug panels.' }, { name: 'loading / lastError / disposed', purpose: 'ActiveEngine state.', notes: 'Tracks active operations.' }, { name: 'entry(options)', purpose: 'Create an ActiveCacheEntry.', notes: 'Entry wraps query/set/invalidate with local status.' }, { name: 'snapshot() / onChange() / clearError()', purpose: 'Active lifecycle.', notes: 'Same convention as other active roots.' } ] }, { title: 'ActiveCacheEntry', table: [ { name: 'data / error / status / loading / updatedAt', purpose: 'Reactive entry state.', notes: 'Status: idle, loading, success, stale, refreshing, degraded or error.' }, { name: 'load()', purpose: 'Initial query.', notes: 'Uses the entry QueryOptions.' }, { name: 'refresh()', purpose: 'Force reload through query.', notes: 'Keeps entry state coordinated.' }, { name: 'set(value, options?)', purpose: 'Write entry value.', notes: 'Scope comes from the entry options.' }, { name: 'invalidate()', purpose: 'Invalidate this key.', notes: 'Keeps tags/prefix policies in the runtime.' }, { name: 'snapshot() / onChange() / dispose()', purpose: 'Entry lifecycle.', notes: 'Dispose removes it from the ActiveCache registry.' } ] } ], stor: [ { title: 'EngineStorage', table: [ { name: 'adapter', purpose: 'Default SyncStorageAdapter.', notes: 'localAdapter, sessionAdapter, cookieAdapter or custom.' }, { name: 'namespace', purpose: 'Optional key prefix.', notes: 'Entry options can override with namespace or false.' }, { name: 'entry(key, defaults, options?)', purpose: 'Create a StorageEntry.', notes: 'Defaults can be a value or factory.' }, { name: 'entries()', purpose: 'List created entries.', notes: 'Engine-owned registry, not adapter scan.' }, { name: 'clear()', purpose: 'Remove known entries.', notes: 'Does not blindly wipe unrelated storage.' }, { name: 'dispose()', purpose: 'Dispose root and entries.', notes: 'Rejects future root operations.' } ] }, { title: 'StorageEntry / ActiveStorageEntry', table: [ { name: 'key / fullKey', purpose: 'Logical and adapter key.', notes: 'fullKey includes namespace unless disabled.' }, { name: 'get()', purpose: 'Read parsed value.', notes: 'Applies envelope, ttl, version, migrate, mergeDefaults and validate.' }, { name: 'set(value)', purpose: 'Serialize and write.', notes: 'Active entry also updates current.' }, { name: 'update(fn)', purpose: 'Read-modify-write.', notes: 'Preferred over deep mutations.' }, { name: 'remove()', purpose: 'Delete adapter value and memory returns to default.', notes: 'Different from reset().' }, { name: 'reset()', purpose: 'Write default value to adapter.', notes: 'Useful for explicit user reset.' }, { name: 'has()', purpose: 'Check whether adapter has a stored value.', notes: 'Independent from current default.' }, { name: 'current', purpose: 'Reactive ActiveStorageEntry value.', notes: 'Not a deep persistence proxy; use set/update.' }, { name: 'onChange(listener)', purpose: 'Subscribe to active value changes.', notes: 'Active entries only.' }, { name: 'dispose()', purpose: 'Detach sync listeners.', notes: 'Important for per-page entries.' } ] }, { title: 'StorageEntryOptions', table: [ { name: 'adapter / namespace', purpose: 'Override root storage per entry.', notes: 'Use cookies for locale/theme, local for drafts, session for wizards.' }, { name: 'serializer', purpose: 'Custom parse/stringify.', notes: 'Auto-selected for primitives, Date, Set, Map and JSON objects.' }, { name: 'version / migrate', purpose: 'Envelope versioning.', notes: 'If version differs, migrate must return the new shape.' }, { name: 'validate', purpose: 'Function or Standard Schema validation.', notes: 'Rejected values fall back safely and report onError.' }, { name: 'mergeDefaults', purpose: 'Evolve object shapes.', notes: 'Boolean shallow merge or custom merge function.' }, { name: 'ttlMs / raw / writeDefaults / syncTabs', purpose: 'Expiry and persistence behavior.', notes: 'raw disables envelope features by design.' } ] } ], http: [ { title: 'EngineHttp', table: [ { name: 'with(options)', purpose: 'Create scoped child client.', notes: 'Use with event.fetch in SvelteKit server loads.' }, { name: 'get(url, options?) / head() / options()', purpose: 'Read-oriented methods.', notes: 'GET can use query, schema, timeout, hooks.' }, { name: 'post() / put() / patch() / delete()', purpose: 'Mutation methods.', notes: 'Body, bodySchema and schema are validated through Standard Schema.' }, { name: 'hooks', purpose: 'Before/after request/result hooks.', notes: 'Session rescue, tracing and auth headers live here.' }, { name: 'retry / timeout / totalTimeout', purpose: 'Resilience controls.', notes: 'Retry respects method/idempotency and Retry-After.' } ] }, { title: 'HttpResult', table: [ { name: '{ ok: true, value, response }', purpose: 'Successful validated response.', notes: 'value is parsed/validated payload.' }, { name: 'http_error', purpose: 'Non-2xx response.', notes: 'Status, headers and parsed body stay available.' }, { name: 'validation_error', purpose: 'Schema rejected payload.', notes: 'Contains validation issues.' }, { name: 'network_error', purpose: 'Fetch threw before response.', notes: 'Original error normalized.' }, { name: 'timeout', purpose: 'Abort by timeout.', notes: 'Per-attempt and total timeouts are separate.' } ] }, { title: 'EngineHttpOptions', table: [ { name: 'baseUrl / headers / fetch', purpose: 'Request defaults.', notes: 'Pass SvelteKit event.fetch on the server.' }, { name: 'timeout / totalTimeout / retry', purpose: 'Failure policy.', notes: 'Avoid per-call magic constants.' }, { name: 'hooks', purpose: 'Composable request/result middleware.', notes: 'No direct coupling to sess/auth/perm.' }, { name: 'logger', purpose: 'Shared Logger contract.', notes: 'Injected by App.' } ] } ], fmts: [ { title: 'EngineFormats / ActiveFormats', table: [ { name: 'numbers', purpose: 'Numbers sub-engine.', notes: 'format, parse, percent, compact and unit helpers.' }, { name: 'currency', purpose: 'Currency sub-engine.', notes: 'Currency resolution, formatting and optional conversion.' }, { name: 'units', purpose: 'Units sub-engine.', notes: 'System defaults, conversion and default-unit formatting.' }, { name: 'dates', purpose: 'Dates sub-engine.', notes: 'Date order, hour cycle and Intl DateTime formatting.' }, { name: 'getLocale() / setLocale(locale)', purpose: 'Shared locale control.', notes: 'Active version usually receives localeSource from App.' }, { name: 'dispose()', purpose: 'Release sub-engines/listeners.', notes: 'Called by App.dispose().' } ] }, { title: 'Numbers', table: [ { name: 'format() / formatPercent() / formatCompact()', purpose: 'Intl number formatting.', notes: 'Uses current locale unless options override.' }, { name: 'formatCurrency() / formatUnit()', purpose: 'Number formatting delegated by currency/units.', notes: 'Useful standalone.' }, { name: 'parse(input)', purpose: 'Locale-aware parse.', notes: 'Uses current separators.' }, { name: 'get/set/clear/isAuto DecimalSeparator', purpose: 'Decimal separator preference.', notes: 'Manual overrides survive locale changes.' }, { name: 'get/set/clear/isAuto GroupSeparator', purpose: 'Group separator preference.', notes: 'Same auto/manual contract.' }, { name: 'get/set/clear/isAuto Grouping', purpose: 'Grouping preference.', notes: 'Same auto/manual contract.' } ] }, { title: 'Currency / Units / Dates', table: [ { name: 'currency.getCurrency() / setCurrency() / clearCurrency()', purpose: 'Currency auto/manual state.', notes: 'Auto derives from locale; manual does not change on locale updates.' }, { name: 'currency.format() / formatAs() / convert() / convertAs()', purpose: 'Money formatting and conversion.', notes: 'Conversion only works when rates are configured.' }, { name: 'units.getSystem() / setSystem() / clearSystem()', purpose: 'Metric/imperial preference.', notes: 'Auto derives from locale.' }, { name: 'units.getDefaultUnit() / formatDefault() / convertToDefault()', purpose: 'Default units per measurement.', notes: 'Defaults are marked by locale/system.' }, { name: 'dates.getDateOrder() / setDateOrder() / clearDateOrder()', purpose: 'Date order preference.', notes: 'Auto derives from locale.' }, { name: 'dates.getHourCycle() / setHourCycle() / clearHourCycle()', purpose: '12/24h preference.', notes: 'Uses default hour cycle resolver unless manual.' }, { name: 'dates.formatDate() / formatTime() / formatDateTime()', purpose: 'Intl DateTime formatting.', notes: 'Uses active locale and options.' } ] } ], fend: [ { title: 'ActiveFrontend', table: [ { name: 'getLocale() / setLocale(locale)', purpose: 'Frontend locale source.', notes: 'Usually bridged from App.Lang.' }, { name: 'getDir() / setDir() / clearDir() / isDirAuto()', purpose: 'Document direction.', notes: 'Auto changes with locale; manual overrides do not.' }, { name: 'getTheme() / setTheme()', purpose: 'Theme token.', notes: 'Applied through Dom attrs.' }, { name: 'getMode() / setMode() / clearMode() / isModeAuto()', purpose: 'Light/dark/system mode.', notes: 'Auto can follow environment preference.' }, { name: 'getReducedMotion() / setReducedMotion() / clearReducedMotion() / isReducedMotionAuto()', purpose: 'Motion preference.', notes: 'Can be auto from media query.' }, { name: 'getReducedSound() / setReducedSound()', purpose: 'Sound preference.', notes: 'Explicit preference.' }, { name: 'getDensity() / setDensity()', purpose: 'UI density.', notes: 'Explicit preference.' }, { name: 'onPreferenceChange(listener)', purpose: 'Subscribe to changes.', notes: 'Used by storage persistence bridge.' }, { name: 'dispose()', purpose: 'Detach DOM/media listeners.', notes: 'Called by App.dispose().' } ] }, { title: 'ActiveFrontendOptions', table: [ { name: 'locale / localeSource', purpose: 'Initial or reactive locale.', notes: 'App passes a source tied to Lang.' }, { name: 'dom / target / applyDom', purpose: 'DOM writer configuration.', notes: 'App passes App.Dom by default.' }, { name: 'dir / theme / mode / density', purpose: 'Initial preferences.', notes: 'auto-capable keys follow the shared clear/isAuto convention.' }, { name: 'reducedMotion / reducedSound', purpose: 'Accessibility preferences.', notes: 'Can be persisted through App frontend.persist.' } ] } ], adom: [ { title: 'ActiveDom', table: [ { name: 'breakpoints', purpose: 'Configured breakpoint map.', notes: 'Default map is available when omitted.' }, { name: 'viewport', purpose: 'Reactive viewport snapshot.', notes: 'No browser tracking during SSR.' }, { name: 'currentBreakpoint', purpose: 'Current named breakpoint.', notes: 'Derived from viewport width.' }, { name: 'resolve(value)', purpose: 'Resolve responsive maps.', notes: 'Accepts scalar or breakpoint object.' }, { name: 'isAtLeast(name)', purpose: 'Breakpoint comparison.', notes: 'Useful for component behavior.' }, { name: 'matches(query)', purpose: 'Media query helper.', notes: 'Browser-only; safe fallback in SSR.' }, { name: 'apply(options)', purpose: 'Apply attrs/classes/styles.', notes: 'Returns a cleanup/remove handle.' }, { name: 'remove(handle)', purpose: 'Remove applied DOM patch.', notes: 'Used by Frontend and test pages.' }, { name: 'dispose()', purpose: 'Detach viewport/listeners.', notes: 'Called by App.dispose().' } ] }, { title: 'DOM Helpers', table: [ { name: 'BodyScrollLock', purpose: 'Reference-counted body scroll lock.', notes: 'Used for modals/drawers.' }, { name: 'DOMContext', purpose: 'Scoped DOM/focus context.', notes: 'Useful for complex components.' }, { name: 'RovingFocusGroup', purpose: 'Keyboard focus coordination.', notes: 'Menus/tabs/toolbars can share it.' } ] } ], sium: [ { title: 'EngineSium', table: [ { name: 'string() / number() / boolean() / literal() / enumOf()', purpose: 'Primitive schema builders.', notes: 'Composable through pipe().' }, { name: 'optional() / nullable() / defaulted()', purpose: 'Value wrappers.', notes: 'Control absence/null/default behavior.' }, { name: 'object() / array() / union() / discriminated() / lazy()', purpose: 'Structured schemas.', notes: 'Nested issues preserve paths.' }, { name: 'pipe() / refine() / transform() / codec()', purpose: 'Validation/effect composition.', notes: 'Use for domain normalization.' }, { name: 'meta()', purpose: 'Attach UI metadata.', notes: 'Form generators can inspect it.' }, { name: 'min() / max() / length() / regex() / email() / url() / integer()', purpose: 'Common constraints.', notes: 'Return structured Sium issues.' }, { name: 'timeValue() / dateValue() / colorValue() and domain helpers', purpose: 'days/color validators.', notes: 'Uses copied libs/days and libs/color primitives.' }, { name: 'resolveIssue() / resolveIssues()', purpose: 'Translate issues.', notes: 'Uses injected Lang first, local resolver as fallback.' }, { name: 'validate() / validateSync()', purpose: 'Run schema validation.', notes: 'Returns ok/value or issues.' }, { name: 'serializeSchema() / walkSchema() / countLeafFields()', purpose: 'Introspection.', notes: 'Useful for UI/form generation.' } ] }, { title: 'EngineSiumOptions', table: [ { name: 'lang', purpose: 'Optional EngineLang/ActiveLang-like resolver.', notes: 'Injected by App.createSiumEngine().' }, { name: 'logger', purpose: 'Shared Logger contract.', notes: 'Debug validation diagnostics when configured.' }, { name: 'locale', purpose: 'Default issue locale.', notes: 'App uses current Lang locale.' } ] } ], logr: [ { title: 'Logger contract from $libs/logr', table: [ { name: 'trace(category, message, input?)', purpose: 'Trace log.', notes: 'Modules depend on this minimal Logger interface.' }, { name: 'debug(category, message, input?)', purpose: 'Debug log.', notes: 'Useful for diagnostics in development.' }, { name: 'info(category, message, input?)', purpose: 'Info log.', notes: 'Normal domain/application information.' }, { name: 'warn(category, message, input?)', purpose: 'Warning log.', notes: 'Recoverable or suspicious conditions.' }, { name: 'error(category, message, input?)', purpose: 'Error log.', notes: 'Failed operations.' }, { name: 'fatal(category, message, input?)', purpose: 'Fatal log.', notes: 'Unrecoverable failures.' } ] }, { title: 'EngineLogger', table: [ { name: 'setLevel(level)', purpose: 'Change enabled level map/runtime threshold.', notes: 'Engine decides whether to emit; transports can filter too.' }, { name: 'getLogs() / clear() / serialize()', purpose: 'In-memory history.', notes: 'Useful in tests and debug pages.' }, { name: 'setMaxLogs(n)', purpose: 'Bound memory history.', notes: 'Prevents unbounded growth.' }, { name: 'setGlobalContext(context)', purpose: 'Attach context to every entry.', notes: 'App version, tenant, runtime, etc.' }, { name: 'addTransport(transport)', purpose: 'Add sink.', notes: 'Console, Sentry, Datadog, custom.' }, { name: 'removeAllTransports()', purpose: 'Clear sinks.', notes: 'Useful in tests.' }, { name: 'subscribe(listener)', purpose: 'Observe entries.', notes: 'Debug panels and tests.' }, { name: 'child(context)', purpose: 'Create contextual logger.', notes: 'Keeps same transports/history policy.' }, { name: 'time(label) / timeEnd(label)', purpose: 'Duration helper.', notes: 'Emits structured timing log.' }, { name: 'flush() / dispose()', purpose: 'Transport lifecycle.', notes: 'Flush buffered transports before shutdown.' } ] }, { title: 'Transport', table: [ { name: 'write(entry)', purpose: 'Required sink method.', notes: 'Called for each accepted entry.' }, { name: 'writeBatch(entries)', purpose: 'Optional batch sink.', notes: 'Used by buffered transports.' }, { name: 'levels / filter', purpose: 'Transport-level filtering.', notes: 'Keeps routing per sink explicit.' }, { name: 'failureThrottleMs', purpose: 'Avoid transport failure storms.', notes: 'Works with deniedFor routing.' }, { name: 'flushIntervalMs / buffer', purpose: 'Buffering config.', notes: 'For remote sinks.' } ] } ], timr: [ { title: 'TimerScheduler / EngineTimers', table: [ { name: 'clock', purpose: 'Injected clock.', notes: 'Tests can use fake clocks.' }, { name: 'size', purpose: 'Number of active timers.', notes: 'Reactive in ActiveTimers snapshots.' }, { name: 'schedule(key, delayMs, task, options?)', purpose: 'One-shot timer.', notes: 'Keyed replacement/cancellation.' }, { name: 'scheduleAt(key, at, task, options?)', purpose: 'Run at absolute timestamp.', notes: 'Uses injected clock.' }, { name: 'interval(key, everyMs, task, options?)', purpose: 'Interval timer.', notes: 'Supports awaitTask and replace.' }, { name: 'cancel(key) / cancelAll(scope?) / has(key)', purpose: 'Timer control.', notes: 'Cancel by key or group.' }, { name: 'keys() / entries() / entry(key)', purpose: 'Snapshot/debug surface.', notes: 'EngineTimers adds these registry methods.' }, { name: 'onChange(listener)', purpose: 'Subscribe to timer changes.', notes: 'ActiveTimers mirrors this into reactive state.' }, { name: 'dispose()', purpose: 'Cancel and close scheduler.', notes: 'Called by App.dispose().' } ] }, { title: 'ActiveTimers', table: [ { name: 'entries()', purpose: 'Reactive timer snapshots.', notes: 'Debug/test pages can render active timers.' }, { name: 'disposed', purpose: 'Lifecycle flag.', notes: 'Shared active convention.' }, { name: 'clearError()', purpose: 'Clear active error state where present.', notes: 'Follows ActiveEngine shape.' } ] }, { title: 'Backoff helpers', table: [ { name: 'computeBackoffDelay(options)', purpose: 'Shared exponential/jitter delay.', notes: 'Used by conn reconnect and other retry loops.' }, { name: 'DEFAULT_BACKOFF_*', purpose: 'Shared constants.', notes: 'Avoids magic retry numbers across modules.' } ] } ], conn: [ { title: 'EngineConnections', table: [ { name: 'createConnection(name, options)', purpose: 'Create/register a named connection.', notes: 'Returns Connection.' }, { name: 'connection(name)', purpose: 'Read registered connection.', notes: 'Undefined when absent.' }, { name: 'has(name) / names()', purpose: 'Registry inspection.', notes: 'Active wrapper exposes derived state too.' }, { name: 'openConnection(name) / closeConnection(name) / reconnectConnection(name)', purpose: 'Single connection lifecycle.', notes: 'close(name) is alias for closeConnection.' }, { name: 'openAll() / closeAll() / reconnectAll()', purpose: 'Registry-wide lifecycle.', notes: 'Useful for app online/offline transitions.' }, { name: 'dispose()', purpose: 'Close and release registry.', notes: 'Also disposes timers/listeners.' } ] }, { title: 'ActiveConnections', table: [ { name: 'size / activeNames / states', purpose: 'Reactive registry snapshots.', notes: 'For debug panels and app indicators.' }, { name: 'connectedNames / connectingNames / reconnectingNames / failedNames / closedNames', purpose: 'State buckets.', notes: 'Derived from every registered connection.' }, { name: 'allConnected / anyConnected / anyConnecting / anyReconnecting / anyFailed', purpose: 'Aggregate booleans.', notes: 'Ready for UI status bars.' } ] }, { title: 'Connection', table: [ { name: 'state / connected / error / generation', purpose: 'Connection state.', notes: 'generation changes on lifecycle transitions.' }, { name: 'connect() / disconnect() / reconnect()', purpose: 'Transport lifecycle.', notes: 'Reconnect uses timr backoff.' }, { name: 'reauthenticate(payload?)', purpose: 'Session/auth bridge operation.', notes: 'Called when session changes if enabled.' }, { name: 'send(type, payload?)', purpose: 'Fire-and-forget frame.', notes: 'Buffer behavior depends on options.' }, { name: 'request(type, payload?, options?)', purpose: 'Request/reply frame.', notes: 'ACK registry handles timeout/reply.' }, { name: 'channel(name, options?) / channels() / hasChannel() / leaveChannel()', purpose: 'Channel management.', notes: 'Channels can rejoin after reconnect.' }, { name: 'onState(listener) / onAny(listener)', purpose: 'Event subscriptions.', notes: 'Use for logs/UI and tests.' }, { name: 'dispose()', purpose: 'Close connection and channels.', notes: 'Registry calls this on dispose.' } ] }, { title: 'ConnectionChannel', table: [ { name: 'state', purpose: 'Channel lifecycle state.', notes: 'join/left/failed-like state machine.' }, { name: 'join() / leave()', purpose: 'Channel lifecycle.', notes: 'Sends protocol frames through parent connection.' }, { name: 'send() / request()', purpose: 'Channel-scoped frames.', notes: 'Payload is tagged with channel name.' }, { name: 'on(type, listener) / onAny(listener)', purpose: 'Channel event subscriptions.', notes: 'Dispose removes listeners.' }, { name: 'dispose()', purpose: 'Leave/cleanup channel.', notes: 'State becomes terminal after disposal.' } ] } ] } satisfies Record>; export const artifactDocs = { auth: { section: 'Identity & Security', title: 'Auth', alias: '$auth', summary: 'Server-authoritative authentication with a reactive client reflector: password flows, CSRF, current session and cache invalidation.', factories: ['createActiveAuth', 'createEngineAuth'], dependsOn: ['$libs/auth', '$http', '$cach', '$svrs/auth'], layer: 'ActiveAuth (client) / EngineAuth (server)', status: { variant: 'tip', title: 'Boundary', body: 'Auth proves identity. Session keeps continuity. Permissions decide access. Storage must not persist secrets.' }, overview: [ 'Auth is split into a shared language package, a server-authoritative engine and an active Svelte client. The server engine owns identity proof, CSRF, password/recovery flows, OAuth/MFA primitives, device methods and security events. The active client currently exposes current, password, recovery, email verification, sign-out and device methods over HTTP.', 'Use Auth when the app needs to sign users in or out, load the current actor, request verification or reset flows, and invalidate identity-derived caches. Do not use Auth to decide permissions or to store long-lived credentials in the browser.', 'In an App composition, Auth receives App.Http for route calls, App.Cache for invalidation and App.Logger for diagnostics.' ], dynamics: [ 'The server creates an EngineAuth with ports. Those ports are the real integration points: store persists auth records, actors maps credentials to actor refs, sess starts or ends sessions, cach clears identity-scoped data, and logr records security events.', 'The browser creates ActiveAuth only as a reflector. It loads /current, sends CSRF-protected commands to server routes, updates current after successful responses and emits local state changes for UI. A protected server action must never trust ActiveAuth state.', 'The normal request path is: server hook resolves current auth, page load serializes a safe AuthCurrentView, ActiveAuth hydrates that snapshot, user triggers sign-in/out, server mutates session, then Auth invalidates cache/permission state.' ], commonMistakes: [ { name: 'checking Auth.authenticated on the server', purpose: 'ActiveAuth is browser state and can be stale or manipulated.', notes: 'Resolve current auth in server hooks/load/actions through $svrs/auth.' }, { name: 'putting permissions inside auth callbacks', purpose: 'It mixes identity proof with authorization and becomes impossible to audit.', notes: 'Auth returns actor/AAL/AMR; $perm decides access.' }, { name: 'persisting tokens in Storage', purpose: 'Storage is intentionally client-readable and not a secret vault.', notes: 'Use opaque HttpOnly session cookies or server-side refresh rotation.' }, { name: 'forgetting CSRF on custom routes', purpose: 'State-changing browser calls become forgeable.', notes: 'Use Auth CSRF helpers or the route handlers that already enforce them.' } ], quickStart: { title: 'Client auth from App', code: `const Auth = App.createActiveAuth({ initial: data.auth }); await Auth.signInPassword({ identifier: 'ada@example.com', password: 'correct horse battery staple' }); if (Auth.authenticated) { console.log(Auth.current.actor?.primaryIdentifier); } await Auth.signOut();` }, factoryRows: [ { name: 'createEngineAuth(options)', purpose: 'Creates the server-side authority for auth flows.', notes: 'Lives in $svrs/auth and receives store, actors, sess, cach, logger, crypto and hasher ports.' }, { name: 'createActiveAuth(options)', purpose: 'Creates the reactive browser client.', notes: 'Normally created through App.createActiveAuth so Http, Cache and Logger are injected.' }, { name: 'App.createActiveAuth(options)', purpose: 'Composition-root factory.', notes: 'Singleton per App; second call throws AappAlreadyCreatedError.' } ], api: artifactApis.auth, sections: [ { title: 'Creation and route wiring', body: [ 'Auth has two creation points. The server creates EngineAuth from $svrs/auth with ports for storage, actors, session, cache, crypto and logging. The browser creates ActiveAuth through App.createActiveAuth(), which talks to the server routes and mirrors the safe AuthCurrentView.', 'Do not create ActiveAuth before the server routes exist. The client cannot prove identity by itself; every sign-in, sign-out, CSRF and recovery operation is a server command.' ], code: { title: 'Server plus client surface', code: `// server const Auth = createEngineAuth({ security, ports: { store, actors, sess, cach, logr, timr, crypto, passwordHasher } }); export const GET = Auth.handlers.current; export const POST = Auth.handlers.signInPassword; // client const AuthClient = App.createActiveAuth({ initial: data.auth, routes: { current: '/api/auth/current' } });` } }, { title: 'State Surface', body: ['ActiveAuth follows the ActiveEngine convention: direct getters, snapshot(), onChange(), clearError() and dispose().'], table: [ { name: 'current', purpose: 'Latest AuthCurrentView.', notes: 'Contains session status and public actor snapshot.' }, { name: 'authenticated', purpose: 'Convenience boolean.', notes: 'Derived from current.session.status.' }, { name: 'loading', purpose: 'True while a client operation is in flight.', notes: 'Shared active-root naming.' }, { name: 'lastError', purpose: 'Safe client error.', notes: 'Secrets and raw backend errors are normalized.' } ] }, { title: 'Server Wiring', body: ['The server engine exposes route handlers and ports instead of importing a specific database, mailer or framework. That keeps auth portable and testable.'], code: { title: 'Server engine shape', code: `const Auth = createEngineAuth({ security, ports: { store, actors, sess, cach, logr: App.Logger, timr: App.Timers, crypto, passwordHasher, mailer } });` } }, { title: 'Flows', bullets: [ 'Password sign-up and sign-in bind a successful identity proof to session state through the session port.', 'CSRF is requested and sent automatically by ActiveAuth for state-changing client calls.', 'Email verification and password reset use expiring server flows; tokens are verified on the server.', 'Device listing and revoke are exposed by the engine/client contracts; route wiring must expose AUTH_ROUTE_PATHS.DEVICES and DEVICE_REVOKE explicitly because the default handler map currently covers current, CSRF, password, recovery and sign-out routes.' ] }, { title: 'Integration Rules', bullets: [ 'Auth invalidates Cache and Permissions after identity changes.', 'Auth never stores refresh tokens, passwords, OTPs or CSRF secrets in Storage.', 'Permissions receives actor context from Auth/Session but remains the authorization authority.', 'Client Auth is UX, not a security boundary.' ] } ], tests: [ { name: 'src/arts/auth/test', purpose: 'Active client behavior.', notes: 'Load current, sign-in/out and cache invalidation.' }, { name: 'src/svrs/auth/test', purpose: 'Server flows.', notes: 'Password, CSRF, recovery, device primitives and handlers.' }, { name: '/test/auth', purpose: 'Interactive auth lab.', notes: 'Password flow, CSRF and event stream.' } ] }, sess: { section: 'Identity & Security', title: 'Session', alias: '$sess', summary: 'Session lifecycle primitive for adopt, revoke, refresh, auto-refresh, SSR adoption and HTTP 401 rescue.', factories: ['createEngineSession', 'createActiveSession'], dependsOn: ['$stor', '$timr', '$http', '$logr (optional)'], layer: 'EngineSession / ActiveSession', overview: [ 'Session is not authentication. It does not verify passwords, OAuth callbacks or permissions. It keeps continuity once another layer has established identity.', 'The session shape has three slots: user, credential and data. User is identity, credential is how the client can refresh or authenticate to the server, and data is session-scoped application state such as tenantId or cartId.', 'The engine is deterministic and testable: refresh can be deduped, storage is pluggable, timers can be injected and all lifecycle changes emit structured events.' ], dynamics: [ 'Session starts from a trusted source: SSR data, an auth success response or a refresh callback. adoptServer() is for data already validated on the server; adopt() validates client-provided values through configured schemas.', 'Refresh is a controlled lifecycle operation. Multiple concurrent refresh calls dedupe into one remote call; a successful refresh replaces current, a null result clears the session, and a thrown refresh keeps the previous value while reporting the error.', 'Revoke is different from clearLocal(). revoke() calls the configured server revoke callback and then clears state. clearLocal() only wipes local memory/storage when the server already invalidated the session.' ], commonMistakes: [ { name: 'using Session as Auth', purpose: 'Session cannot prove identity; it only stores continuity after identity was proven.', notes: 'Use $auth for login/proof, $sess for lifecycle and propagation.' }, { name: 'calling adopt() with SSR data', purpose: 'It treats trusted server data like untrusted browser input and can produce confusing validation paths.', notes: 'Use adoptServer(data.session) for server-validated payloads.' }, { name: 'mutating nested current data directly', purpose: 'Deep mutation can skip persistence and event propagation depending on shape.', notes: 'Adopt a new session object or use the exposed lifecycle methods.' }, { name: 'not clearing caches on revoke', purpose: 'Actor-scoped data can remain visible after logout.', notes: 'Wire revoke/auth events to $cach and $perm invalidation.' } ], quickStart: { title: 'Active session', code: `const Sess = App.createActiveSession({ storage: { adapter: localAdapter, key: 'session' }, onRefresh: async (current) => refreshSession(current), onRevoke: async (current) => revokeSession(current) }); Sess.adoptServer(data.session); if (Sess.identity === 'identified') { console.log(Sess.current?.user); }` }, factoryRows: [ { name: 'createEngineSession(options)', purpose: 'Pure session engine.', notes: 'No Svelte state; useful for services and tests.' }, { name: 'createActiveSession(options)', purpose: 'Reactive Svelte wrapper.', notes: 'State getters are backed by runes.' }, { name: 'App.createActiveSession(options)', purpose: 'App-wired singleton.', notes: 'Injects App.Logger and is bridged into Connections.' } ], api: artifactApis.sess, sections: [ { title: 'Creation and wiring', body: [ 'In an application, create Session through App.createActiveSession() so App can inject Logger and bridge session changes into Connections. Create createEngineSession()/createActiveSession() directly only in tests, isolated services or when you deliberately do not use App.', 'The storage option decides where the local session snapshot lives. The callbacks onRefresh and onRevoke are the only places that should call the server. Session itself does not know your endpoint shape.' ], code: { title: 'App-owned session', code: `const Sess = App.createActiveSession({ storage: { adapter: localAdapter, key: 'session' }, onRefresh: (current) => App.Http.post('/api/session/refresh', current), onRevoke: (current, options) => App.Http.post('/api/session/revoke', { current, options }) }); Sess.adoptServer(data.session);` } }, { title: 'Identity States', table: [ { name: 'none', purpose: 'No local session exists.', notes: 'current is null.' }, { name: 'anonymous', purpose: 'Tracked but unidentified session.', notes: 'Useful for carts or anonymous journeys.' }, { name: 'identified', purpose: 'Session has a user.', notes: 'Authorization can now evaluate an actor.' } ] }, { title: 'Lifecycle', bullets: [ 'adopt() validates client-provided sessions against optional Standard Schema contracts.', 'adoptServer() trusts the server and is the SSR hydration path.', 'refresh() preserves the current session if the refresh call throws, but expires it if refresh returns null.', 'revoke() can be local or global; global degrades to local if the server revocation fails.' ] }, { title: 'HTTP Integration', body: ['The HTTP integration can intercept 401 responses, trigger a deduped refresh and retry once with fresh credentials. A sentinel header prevents infinite retry loops.'], code: { title: '401 rescue', code: `const hook = createBeforeErrorHook(Sess, { applyAuth: (request, session) => { request.headers.set('authorization', 'Bearer ' + session.credential.accessToken); } });` } }, { title: 'Connection Bridge', body: ['When Session is created through App, existing and future App connection registries receive session lifecycle events. A revoke or expire can disconnect opted-in realtime connections.'] } ], tests: [ { name: 'src/arts/sess/test', purpose: 'Lifecycle and integrations.', notes: 'Refresh, revoke, actor metadata, HTTP and auto-refresh.' }, { name: 'src/arts/aapp/test/ecosystem.integration.test.ts', purpose: 'Cross-module session bridge.', notes: 'Session revoke closes opted-in App connections.' }, { name: '/test/sess', purpose: 'Interactive page.', notes: 'Adopt, revoke, refresh, events and permission demo.' } ] }, perm: { section: 'Identity & Security', title: 'Permissions', alias: '$perm', summary: 'Authorization runtime for typed actor + action + resource + context decisions, with server authority and active client reflection.', factories: ['createEnginePermissions', 'createActivePermissions', 'createPermissionHttpHandlers'], dependsOn: ['$libs/perm', '$libs/svrs', '$http', '$logr (optional)'], layer: 'ActivePermissions (client) / EnginePermissions (server)', status: { variant: 'warn', title: 'Security boundary', body: 'The server engine is the authority. ActivePermissions is for UX, cache and rendering helpers only.' }, overview: [ 'Permissions is not a simple RBAC helper. It models authorization as explicit decisions using roles, attributes, relations and context in the same runtime.', 'The server engine evaluates policies and returns rich decisions: allow, deny, not applicable or indeterminate. The client reflector batches remote checks, caches snapshots and exposes a Can component for UI gates.', 'Policies can be explained and list queries can be filtered or compiled into query plans when the provider supports it.' ], quickStart: { title: 'Policy runtime', code: `const schema = definePermSchema({ actors: { user: { attributes: { role: 'string' } } }, resources: { project: { actions: ['update'], attributes: { locked: 'boolean' } } }, context: { risk: { mfa: 'boolean' } } }); const engine = createEnginePermissions({ schema, policies: definePolicies(schema, [ allow('project.update').when( and(attr('actor.role').eq('admin'), attr('context.risk.mfa').eq(true)) ) ]) });` }, factoryRows: [ { name: 'createEnginePermissions(options)', purpose: 'Authoritative policy engine.', notes: 'Use on server routes, services and jobs.' }, { name: 'createPermissionHttpHandlers(engine, resolveActor)', purpose: 'HTTP handlers for check/batch/what/explain.', notes: 'Keeps client thin and server authoritative.' }, { name: 'createActivePermissions(options)', purpose: 'Reactive client reflector.', notes: 'Caches decisions, exposes loading/errors and snapshot.' }, { name: 'App.createActivePermissions(options)', purpose: 'App-wired singleton.', notes: 'Injects App.Http and App.Logger.' } ], sections: [ { title: 'Decision Model', table: [ { name: 'allow', purpose: 'Access granted by a matching policy.', notes: 'May include TTL and explanation.' }, { name: 'deny', purpose: 'Access explicitly denied.', notes: 'Deny overrides allow.' }, { name: 'not_applicable', purpose: 'No policy matched.', notes: 'Fail closed in protected endpoints.' }, { name: 'indeterminate', purpose: 'Provider or evaluation could not decide.', notes: 'Treat as denied for security-sensitive paths.' } ] }, { title: 'Client Usage', code: { title: 'Active permissions', code: `const Permissions = App.createActivePermissions({ endpoint: '/api/permissions', scopeKey: () => App.Sess?.current?.user?.id ?? 'anonymous' }); const decision = await Permissions.check({ action: 'project.update', resource: { type: 'project', id: 'p1', locked: false }, context: { risk: { mfa: true } } });` } }, { title: 'Can Component', body: ['Can is a UI convenience component. It asks ActivePermissions whether content should render, but it never replaces server checks. Protected mutations and reads must still call the server engine.'] } ], tests: [ { name: 'src/svrs/perm/test', purpose: 'Server engine and handlers.', notes: 'Policy evaluation, HTTP handlers, diagnostics.' }, { name: 'src/arts/perm/test', purpose: 'Active client.', notes: 'Cache, remote checks and snapshot behavior.' }, { name: '/test/perm', purpose: 'Interactive authorization lab.', notes: 'Checks, Can, explains and role changes.' } ] }, cach: { section: 'Data', title: 'Cache', alias: '$cach', summary: 'Data coherence layer with deterministic keys, scopes, policies, stale-while-revalidate, tags and active entries.', factories: ['createEngineCache', 'createActiveCache'], dependsOn: ['$libs/cach', '$stor (adapter)', '$logr (optional)'], layer: 'ActiveCache (client) / EngineCache (server)', overview: [ 'Cache answers more than "do I have this value?". It decides freshness, scope safety, invalidation status, stale serving and what to do if the origin fails.', 'The pure runtime lives in libs/cach. Server and active wrappers compose that runtime with adapters, diagnostics and Svelte state.', 'Scopes are part of the key. Private data should be cached under tenant, actor or permission scopes, never as public.' ], dynamics: [ 'A query normalizes its key, resolves scope, reads the adapter, evaluates policy windows and tag epochs, then decides whether to return fresh data, serve stale data while refreshing, serve stale-if-error or call the fetcher.', 'Invalidation does not have to delete every entry immediately. Tag epochs mark entries as stale/invalidated lazily, which keeps distributed adapters cheap while explain() can still tell why a value was rejected.', 'ActiveCache wraps the same runtime with entry state. The UI reads data/status/loading/error from ActiveCacheEntry; the server or service layer uses EngineCache for request handlers, jobs and SSR.' ], commonMistakes: [ { name: 'public scope for private data', purpose: 'Different users or tenants can observe cached values that were not meant for them.', notes: 'Use scope: actor, tenant or permission and provide a scopeResolver.' }, { name: 'using raw strings as keys', purpose: 'Ad-hoc keys collide and cannot be invalidated by structure.', notes: 'Use deterministic array keys such as [\'project\', projectId].' }, { name: 'forgetting tags on queries', purpose: 'Mutations cannot invalidate related reads cleanly.', notes: 'Add stable tags to reads and invalidate those tags after writes.' }, { name: 'hiding cache bugs', purpose: 'Stale data failures are difficult to debug from UI symptoms.', notes: 'Use explain(), stats() and diagnostics when behavior surprises you.' } ], quickStart: { title: 'Query cache', code: `const project = await App.Cache.query({ key: ['project', projectId], scope: 'tenant', policy: 'interactive', tags: [{ type: 'project', id: projectId }], fetcher: () => App.Http.get('/api/projects/' + projectId) });` }, factoryRows: [ { name: '$libs/cach.createCacheRuntime(options)', purpose: 'Pure cache runtime.', notes: 'Not exported by the $cach barrel.' }, { name: 'createEngineCache(options)', purpose: 'Imperative cache surface.', notes: 'Use in server/services/workers.' }, { name: 'createActiveCache(options)', purpose: 'Reactive Svelte wrapper.', notes: 'Adds entry state and active loading/error surface.' }, { name: 'App.Cache', purpose: 'Always-present App cache.', notes: 'Memory adapter by default; configure scopeResolver for private data.' } ], api: artifactApis.cach, sections: [ { title: 'Creation and adapter wiring', body: [ 'App.Cache is always present and defaults to an in-memory adapter. That is safe for first use, tests and local UI state, but production private data should configure scopeResolver and an intentional adapter strategy.', 'Server-side cache roots should be created from $svrs/cach. Client active cache roots are UI helpers and should not become the source of truth for permission-sensitive data.' ], code: { title: 'Scoped cache root', code: `const Cache = createEngineCache({ namespace: 'app', adapter: memoryCacheAdapter({ maxEntries: 5_000 }), defaultPolicy: 'interactive', scopeResolver: () => ({ tenantId: Sess.current?.data.tenantId, actorId: Sess.current?.user?.id, permissionHash: Permissions.snapshot().version }), logger: App.Logger });` } }, { title: 'Policies', table: [ { name: 'interactive', purpose: 'Normal UI data.', notes: 'Short fresh window, stale-while-revalidate.' }, { name: 'catalog', purpose: 'Stable catalog/config data.', notes: 'Longer stale and stale-if-error windows.' }, { name: 'privateSession', purpose: 'Private session data.', notes: 'Short windows and no persistence by default.' }, { name: 'realtime', purpose: 'Almost no cache.', notes: 'Use for data that must always revalidate.' }, { name: 'immutable', purpose: 'Versioned immutable data.', notes: 'Can be cached indefinitely.' } ] }, { title: 'Invalidation', body: ['Entries can be invalidated by exact key, key prefix, tag, scope or predicate. Tags are the recommended high-level contract between mutations and cached reads.'], code: { title: 'Tag invalidation', code: `await App.Cache.invalidate({ tags: [{ type: 'project', id: projectId }] });` } }, { title: 'Explain', body: ['explain() tells why the cache served, missed, refreshed or rejected an entry. This is intentionally part of the public surface because cache bugs are otherwise invisible.'] } ], tests: [ { name: 'src/libs/cach/test', purpose: 'Pure runtime.', notes: 'Keys, policies, query, mutation, invalidation and refresh.' }, { name: 'src/arts/cach/test', purpose: 'Active wrapper.', notes: 'Reactive entries and operation state.' }, { name: '/test/cach', purpose: 'Interactive cache lab.', notes: 'Policies, scopes, tags and active entries.' } ] }, stor: { section: 'Data', title: 'Storage', alias: '$stor', summary: 'Reactive synchronous key/value storage with pluggable adapters, envelopes, versioning, TTL, validation and cross-tab sync.', factories: ['createEngineStorage', 'createActiveStorage'], dependsOn: ['$sium (Standard Schema interop, optional)', '$logr (optional)'], layer: 'EngineStorage / ActiveStorage', overview: [ 'Storage is the framework primitive for client-safe persistence: preferences, drafts, non-secret state and SSR-readable cookies.', 'Each entry has defaults, serializer, optional version/migration, optional validation, TTL and remove/reset semantics. Active entries expose current as reactive state.', 'Storage is intentionally synchronous in v1. Async stores such as IndexedDB can be added later through a separate async contract.', 'Only createEngineStorage() and createActiveStorage() are root factories. Adapters are storage backends: they are passed to a root through options.adapter or overridden per entry.' ], dynamics: [ 'A root owns the adapter default, namespace, entry registry, bus and diagnostics. Every call to entry() creates a handle for one logical key and stores strings through the selected adapter.', 'Reads always pass through serializer, envelope, TTL, migration, mergeDefaults and validation. If a stored value is expired, corrupt or invalid, the entry falls back to its default and reports through diagnostics/onError.', 'Active entries add a reactive current property and an onChange stream. current is not a deep persistence proxy: assigning a nested field does not write to the adapter unless you reassign the value or call update().' ], commonMistakes: [ { name: 'treating adapters as root factories', purpose: 'Adapters only store strings; they do not own entries, lifecycle, namespaces or diagnostics.', notes: 'Create a root with createActiveStorage/createEngineStorage and pass adapters into it.' }, { name: 'storing secrets', purpose: 'localStorage/sessionStorage/cookies used here are not a secret vault.', notes: 'Do not store passwords, OTPs, refresh tokens or private provider tokens in $stor.' }, { name: 'entry.current.foo = value', purpose: 'Nested mutation can skip persistence because current is not a deep proxy.', notes: 'Use entry.update(prev => ({ ...prev, foo: value })) or assign entry.current to a new object.' }, { name: 'raw with version or ttlMs', purpose: 'Raw mode deliberately bypasses the envelope where version/TTL live.', notes: 'Use raw for readable cookies; use envelope mode for migration and TTL.' } ], quickStart: { title: 'Active entry', code: `import { createActiveStorage, localAdapter, cookieAdapter } from '$stor'; // Root created directly. const Storage = createActiveStorage({ adapter: localAdapter, namespace: 'app' }); // In an application root, App.Storage is already an ActiveStorage root. const draft = App.Storage.entry('profile-draft', () => ({ name: '', bio: '' }), { ttlMs: 30 * 60_000, version: 2, mergeDefaults: true }); draft.update((value) => ({ ...value, bio: 'Hello' })); console.log(draft.current.bio); // Adapter override for one entry only. const locale = App.Storage.entry('locale', 'es', { adapter: cookieAdapter({ path: '/', maxAge: 31_536_000 }), namespace: false, raw: true });` }, factoryRows: [ { name: 'createEngineStorage(options)', purpose: 'Root factory: creates an imperative EngineStorage.', notes: 'Pass adapter, namespace, logger and onError here.' }, { name: 'createActiveStorage(options)', purpose: 'Root factory: creates a reactive ActiveStorage.', notes: 'Wraps EngineStorage and adds reactive entries.' } ], api: artifactApis.stor, sections: [ { title: 'Creation Model', body: [ 'Storage has one root and many entries. The root is created with createEngineStorage() or createActiveStorage(); entries are created through Storage.entry(key, defaults, options?).', 'Adapters are not roots. localAdapter, sessionAdapter, cookieAdapter and createMemoryAdapter() implement SyncStorageAdapter. They decide where strings are stored; the root decides namespaces, entry registry, diagnostics and lifecycle.', 'aapp creates App.Storage internally with createActiveStorage(). Feature code should normally use App.Storage.entry(...). Create your own root only in tests, SSR helpers or isolated subsystems.' ], code: { title: 'Root vs adapter', code: `const Storage = createActiveStorage({ adapter: localAdapter, // backend used by default namespace: 'app', // root-level key prefix logger: App.Logger }); const theme = Storage.entry('theme', 'base'); const locale = Storage.entry('locale', 'es', { adapter: cookieAdapter({ path: '/' }), // per-entry backend override namespace: false, raw: true });` } }, { title: 'Adapters', table: [ { name: 'localAdapter', purpose: 'Browser localStorage backend.', notes: 'Singleton adapter; supports cross-tab sync through the storage event.' }, { name: 'sessionAdapter', purpose: 'Browser sessionStorage backend.', notes: 'Session-scoped backend; SSR-safe fallback outside browser.' }, { name: 'cookieAdapter(options)', purpose: 'Browser cookie backend.', notes: 'Factory because cookie attributes are configured per use.' }, { name: 'cookieAdapter.fromCookies(cookies, options)', purpose: 'Server-side cookie backend.', notes: 'Uses a SvelteKit-compatible Cookies object without depending on @sveltejs/kit.' }, { name: 'createMemoryAdapter(seed?)', purpose: 'In-memory backend.', notes: 'Factory because each call gets isolated storage for defaults, tests and SSR.' }, { name: 'custom SyncStorageAdapter', purpose: 'Any synchronous string key/value backend.', notes: 'Must implement getItem, setItem, removeItem and optional onChange.' } ] }, { title: 'Entry Semantics', table: [ { name: 'remove()', purpose: 'Delete adapter value and return memory to default.', notes: 'Storage is clean after remove.' }, { name: 'reset()', purpose: 'Write the default value to the adapter.', notes: 'Useful when default should be persisted.' }, { name: 'writeDefaults', purpose: 'Persist defaults on first read.', notes: 'False by default to avoid contaminating storage.' }, { name: 'raw', purpose: 'Store plain value without envelope.', notes: 'Useful for cookies such as locale/theme.' } ] }, { title: 'Versioning', code: { title: 'Migrate stored shape', code: `const cart = App.Storage.entry('cart', defaults, { version: 3, migrate: (previous, fromVersion) => { if (fromVersion === 2) return migrateCartV2(previous); return defaults; }, validate: CartSchema });` } }, { title: 'Frontend Persistence', body: ['aapp uses Storage to persist Frontend preferences when frontend.persist is enabled. fend owns preference semantics; aapp only bridges them to storage entries.'] } ], tests: [ { name: 'src/arts/stor/test', purpose: 'Entries, adapters and envelopes.', notes: 'TTL, migrate, raw, validation, sync.' }, { name: 'src/arts/aapp/test/storage-integration.test.ts', purpose: 'App integration.', notes: 'Frontend persistence and adapter overrides.' }, { name: '/test/stor', purpose: 'Interactive storage lab.', notes: 'Adapters, versioning, TTL and cross-tab behavior.' } ] }, http: { section: 'Data', title: 'Http', alias: '$http', summary: 'Typed HTTP client with tagged results, schema validation, retries, timeouts, hooks and SvelteKit event.fetch support.', factories: ['createEngineHttp'], dependsOn: ['$libs/http', '$libs/standard-schema', '$logr (optional)'], layer: 'EngineHttp', overview: [ 'Http wraps fetch with a tagged result model. Callers receive ok/value for success and structured non-ok results for HTTP, network, timeout and validation failures.', 'It centralizes shared HTTP constants in libs/http so methods, headers and content types are not magic strings scattered through modules.', 'The engine supports retry, Retry-After, abort signals, request/response hooks, body validation and response validation through Standard Schema.' ], dynamics: [ 'Every call builds a request from root defaults plus per-call options, runs before hooks, serializes query/body, starts timeout control, executes fetch, parses the response, validates it when a schema is supplied and returns a tagged result instead of throwing for normal HTTP failures.', 'Retry is policy-driven. The engine can retry safe/idempotent calls, respect Retry-After and stop on total timeout. Callers still inspect the final tagged result.', 'with(options) creates a scoped child client. This is the preferred way to bind SvelteKit event.fetch, request-specific headers or tenant-specific baseUrl without mutating the root client.' ], commonMistakes: [ { name: 'try/catch around every non-2xx', purpose: 'Http returns tagged results for expected HTTP failures, so catch blocks hide useful status/body metadata.', notes: 'Check response.ok and switch on response.type.' }, { name: 'using global fetch in SvelteKit server code', purpose: 'Cookies and internal routing can be lost.', notes: 'Create a scoped client with Http.with({ fetch: event.fetch }).' }, { name: 'retrying unsafe mutations blindly', purpose: 'POST/PATCH side effects can be duplicated.', notes: 'Configure retry explicitly and only for idempotent operations or idempotency-key protected calls.' }, { name: 'parsing bodies outside Http', purpose: 'Validation and error normalization become inconsistent.', notes: 'Pass schema/bodySchema and consume value from the tagged result.' } ], quickStart: { title: 'GET with schema', code: `const response = await App.Http.get('/api/projects', { schema: ProjectsSchema, query: { page: 1 } }); if (response.ok) { console.log(response.value); } else { App.Logger.warn('http', 'project request failed', { context: response }); }` }, factoryRows: [ { name: 'createEngineHttp(options)', purpose: 'Creates the HTTP engine.', notes: 'No Active wrapper because request state belongs to callers.' }, { name: 'createEngineHttpAuthClient(Http)', purpose: 'Auth adapter.', notes: 'Adapts EngineHttp to ActiveAuth route calls.' }, { name: 'App.Http', purpose: 'App-wired engine.', notes: 'Injects App.Logger and configured fetch/baseUrl.' } ], api: artifactApis.http, sections: [ { title: 'Creation and request scoping', body: [ 'App.Http is the normal browser/client client. On the server, create a scoped child with event.fetch so SvelteKit cookies, internal routes and platform behavior are preserved.', 'Do not mutate a global Http instance with request-specific headers. Use with() to create a child client for one request, tenant or backend integration.' ], code: { title: 'SvelteKit scoped client', code: `export const load = async (event) => { const Http = App.Http.with({ fetch: event.fetch, headers: { 'x-request-id': event.locals.requestId } }); const projects = await Http.get('/api/projects', { schema: ProjectsSchema }); return { projects }; };` } }, { title: 'Result Model', table: [ { name: 'ok', purpose: 'Validated success.', notes: 'value contains parsed payload.' }, { name: 'http_error', purpose: 'Non-2xx response.', notes: 'Status, headers and parsed body are preserved.' }, { name: 'network_error', purpose: 'Fetch threw.', notes: 'Original error is attached.' }, { name: 'timeout', purpose: 'Request exceeded timeout.', notes: 'AbortController is used where available.' }, { name: 'validation_error', purpose: 'Schema rejected body.', notes: 'Issues are returned as data.' } ] }, { title: 'Hooks', body: ['Hooks make session refresh, auth headers, tracing and custom diagnostics composable without hard-coding those concerns into the HTTP engine.'] }, { title: 'SvelteKit', body: ['Pass event.fetch on the server to preserve cookies, platform fetch behavior and internal routing. On the client, default fetch is used.'] } ], tests: [ { name: 'src/arts/http/test', purpose: 'Engine behavior.', notes: 'Retry, timeout, schemas, hooks and tagged errors.' }, { name: 'src/arts/sess/test/http-integration.test.ts', purpose: '401 rescue.', notes: 'Session refresh integration.' }, { name: '/test/http', purpose: 'Interactive HTTP lab.', notes: 'GET/POST, validation, retry and timeout.' } ] }, fmts: { section: 'I18n & Format', title: 'Formats', alias: '$fmts', summary: 'Locale-driven formatting root for numbers, currency, units and dates with a shared auto/manual contract.', factories: [ 'createEngineFormats', 'createActiveFormats', 'createEngineNumbers', 'createEngineCurrency', 'createEngineUnits', 'createEngineDates' ], dependsOn: ['$locale', '$logr (currency diagnostics)'], layer: 'EngineFormats / ActiveFormats', overview: [ 'Formats centralizes everything that depends on locale but is not text translation: numeric separators, currency, units and date/time conventions.', 'It deliberately does not depend on Lang. Both consume the same LocaleSource when composed through App, so changing App locale updates translations and formats from one source of truth.', 'Each submodule can run as an engine or active wrapper. Auto values derive from locale until the user sets an explicit override.' ], dynamics: [ 'ActiveFormats listens to a LocaleSource. When the locale changes, every submodule recomputes values that are still auto: numeric separators, currency, unit system, date order and hour cycle.', 'Manual values are sticky. If a user calls setCurrency(), setSystem(), setDateOrder() or similar setters, later locale changes do not overwrite that choice. clearX() returns that setting to auto mode.', 'Numbers, Currency, Units and Dates can run independently, but the aggregate Formats root is the normal application surface because it keeps one locale and one auto/manual contract across every formatter.' ], commonMistakes: [ { name: 'deriving currency from base language', purpose: 'Locales like es-AR and es-ES do not share currency.', notes: 'Use explicit locale mappings and avoid locale.split("-")[0] currency fallbacks.' }, { name: 'overwriting manual user choices on locale change', purpose: 'A user-selected currency/unit/date preference unexpectedly changes.', notes: 'Respect isAuto/clear/set semantics.' }, { name: 'using Lang for formatting', purpose: 'Translations and Intl formatting have different responsibilities.', notes: 'Use $lang for text, $fmts for numbers/currency/units/dates.' }, { name: 'assuming conversion rates exist', purpose: 'Formatting money is not the same as converting money.', notes: 'Configure rates before using convert()/convertAs().' } ], quickStart: { title: 'App formats', code: `App.setLocale('es-AR'); App.Formats.numbers.format(1234.5); App.Formats.currency.getCurrency(); // ARS App.Formats.units.getSystem(); // metric App.Formats.dates.getDateOrder();` }, factoryRows: [ { name: 'createEngineFormats(options)', purpose: 'Pure aggregate engine.', notes: 'Groups numbers, currency, units and dates.' }, { name: 'createActiveFormats(options)', purpose: 'Reactive aggregate wrapper.', notes: 'Subscribes to LocaleSource.' }, { name: 'createEngineNumbers/Currency/Units/Dates', purpose: 'Standalone sub-engines.', notes: 'Use when only one formatting domain is needed.' } ], api: artifactApis.fmts, sections: [ { title: 'Creation and locale source', body: [ 'When Formats is created through App, it receives a LocaleSource backed by App.Lang. That is the intended wiring: one locale change updates translations, numbers, currency, units, dates and Frontend direction.', 'Create standalone sub-engines only when a non-UI service needs one formatting domain. UI code should prefer App.Formats so auto/manual state stays consistent.' ], code: { title: 'App-owned locale propagation', code: `App.setLocale('es-AR'); App.Lang.t('common.ok'); App.Formats.currency.getCurrency(); // ARS App.Formats.dates.getDateOrder(); App.Frontend.getDir();` } }, { title: 'Submodules', table: [ { name: 'numbers', purpose: 'Number, percent, compact and parse helpers.', notes: 'Backed by Intl.NumberFormat.' }, { name: 'currency', purpose: 'Currency selection, formatting and conversion rates.', notes: 'Currency can be auto from locale or fixed.' }, { name: 'units', purpose: 'Metric/imperial defaults and conversions.', notes: 'Defaults are locale-aware but overridable.' }, { name: 'dates', purpose: 'Date order, hour cycle and date/time formatting.', notes: 'Uses locale conventions with explicit overrides.' } ] }, { title: 'Auto / Manual', body: ['If a setting is auto, locale changes can update it. If the user sets a value explicitly, locale changes do not override it. clearX() returns to auto.'] }, { title: 'Custom Currency', code: { title: 'Fixed currency', code: `const Formats = createActiveFormats({ locale: 'es-ES', currency: { currency: 'USD' } }); Formats.setLocale('fr-FR'); Formats.currency.getCurrency(); // USD, explicit user choice` } } ], tests: [ { name: 'src/arts/fmts/test', purpose: 'Formatting engines.', notes: 'Numbers, currency, units, dates and auto-state.' }, { name: 'src/arts/aapp/test/active-app.test.ts', purpose: 'Locale propagation.', notes: 'App locale updates Formats.' }, { name: '/test/fmts', purpose: 'Interactive formats lab.', notes: 'Locale switching and defaults.' } ] }, fend: { section: 'UI Layer', title: 'Frontend', alias: '$fend', summary: 'Application-level frontend preferences: dir, theme, mode, density, reduced motion and reduced sound, applied through adom.', factories: ['createActiveFrontend'], dependsOn: ['$adom', '$locale'], layer: 'ActiveFrontend', overview: [ 'Frontend is not a component system. It owns global presentation preferences and writes stable attributes to the configured DOM target.', 'dir, mode and reducedMotion can be auto. theme, density and reducedSound are explicit preferences. The same auto/manual dynamic used by Formats applies here.', 'When built through App, Frontend consumes App.Lang as LocaleSource and App.Dom as the DOM writer.' ], dynamics: [ 'Frontend reads locale and environment preferences, resolves auto-capable values, and writes the result to DOM attributes through Dom.apply(). Components then style against those attributes instead of each component recalculating theme, direction or density.', 'The auto/manual dynamic matches Formats. While dir/mode/reducedMotion are auto, locale or media-query changes can update them. Once the user sets a value explicitly, later auto sources stop overriding it until clearX() is called.', 'Persistence is not owned by Frontend. Frontend emits preference changes; aapp can bridge selected keys to Storage when frontend.persist is configured.' ], commonMistakes: [ { name: 'setting document attributes by hand', purpose: 'It bypasses the central writer and can fight Frontend updates.', notes: 'Use Frontend setters or Dom.apply() through the framework.' }, { name: 'expecting manual dir to follow locale', purpose: 'Manual values intentionally survive locale changes.', notes: 'Call clearDir() to return to locale-derived direction.' }, { name: 'using Frontend as a component library', purpose: 'It only owns global presentation state.', notes: 'Use UI components separately; use $fend for app-level preferences.' }, { name: 'persisting every preference blindly', purpose: 'Auto values can become frozen user values unintentionally.', notes: 'Persist explicit keys intentionally and preserve auto/manual metadata.' } ], quickStart: { title: 'Direction and theme', code: `const Frontend = App.Frontend; App.setLocale('ar'); Frontend.getDir(); // rtl while dir is auto Frontend.setDir('ltr'); // manual override App.setLocale('ar-EG'); Frontend.getDir(); // ltr Frontend.clearDir(); Frontend.getDir(); // rtl` }, factoryRows: [ { name: 'createActiveFrontend(options)', purpose: 'Creates the reactive frontend preference root.', notes: 'Can own its own Dom or receive an App Dom.' }, { name: 'App.Frontend', purpose: 'Always-present App root.', notes: 'Wired to App.Lang locale and App.Dom.' } ], api: artifactApis.fend, sections: [ { title: 'Creation and app wiring', body: [ 'Frontend should normally be created by App. App injects Lang as the locale source, Dom as the writer and Storage when persistence is enabled.', 'Create ActiveFrontend directly only for tests or embedded widgets that intentionally own their own DOM target.' ], code: { title: 'App-wired frontend', code: `const App = createActiveApp({ lang: { schema, defaultLocale: 'es' }, frontend: { theme: 'base', mode: 'auto', dir: 'auto', persist: { keys: ['theme', 'mode', 'density'] } } }); App.setLocale('ar'); App.Frontend.getDir(); // rtl while dir remains auto` } }, { title: 'DOM Output', code: { lang: 'html', title: 'Applied attributes', code: `` } }, { title: 'Persisting Preferences', body: ['aapp can persist Frontend preferences through Storage. fend decides how to read user intent; aapp only bridges preferences to storage entries.'], code: { title: 'Persist preferences', code: `const App = createActiveApp({ storage: { adapter: localAdapter, namespace: 'app' }, frontend: { theme: 'base', persist: { keys: ['theme', 'mode', 'density'] } } });` } } ], tests: [ { name: 'src/arts/fend/test', purpose: 'ActiveFrontend behavior.', notes: 'DOM attrs, auto/manual and OS preferences.' }, { name: 'src/arts/aapp/test/storage-integration.test.ts', purpose: 'Persistence bridge.', notes: 'Storage seeding and write-back.' }, { name: '/test/fend', purpose: 'Interactive frontend lab.', notes: 'Theme, mode, dir and density.' } ] }, adom: { section: 'UI Layer', title: 'Dom', alias: '$adom', summary: 'Reactive DOM service for viewport, breakpoints, attribute writes, scroll lock and focus-oriented helpers.', factories: ['createActiveDom'], dependsOn: ['$libs/dom', '$reactive'], layer: 'ActiveDom', overview: [ 'Dom is the runtime DOM layer. It keeps browser-specific behavior out of formatting, frontend preferences and feature modules.', 'It can resolve responsive values, track viewport, apply attributes declaratively and coordinate scroll lock. In SSR, browser tracking is inert.', 'Frontend uses Dom.apply() to update application-level attrs, so theme/dir changes are centralized.' ], dynamics: [ 'In the browser, ActiveDom installs viewport/media listeners and updates a reactive viewport snapshot. During SSR those listeners are inert so imports stay safe.', 'Responsive values are resolved from the current breakpoint. Consumers can pass a scalar or a breakpoint map and receive the best value for the active viewport.', 'DOM writes are centralized through apply(). Frontend uses this to update html/body attributes, while feature modules can use the same writer for controlled attribute/class/style updates.' ], commonMistakes: [ { name: 'reading window directly in modules', purpose: 'SSR breaks and tests become non-deterministic.', notes: 'Use App.Dom viewport/responsive helpers and keep browser access inside $adom.' }, { name: 'multiple scroll locks without ownership', purpose: 'One modal can unlock scroll while another is still open.', notes: 'Use the scroll lock API so locks are reference-counted/coordinated.' }, { name: 'duplicating breakpoint logic in components', purpose: 'Responsive behavior drifts across the app.', notes: 'Resolve responsive maps through Dom.resolve().' }, { name: 'manual attrs fighting Frontend', purpose: 'Theme/dir/mode can be overwritten by the next preference update.', notes: 'Let Frontend write app-level attrs through Dom.' } ], quickStart: { title: 'Responsive value and attrs', code: `const Dom = App.Dom; const size = Dom.resolve({ base: 'compact', md: 'comfortable' }); Dom.apply({ target: document.documentElement, attrs: { dir: App.Frontend.getDir(), 'data-theme': App.Frontend.getTheme() } });` }, factoryRows: [ { name: 'createActiveDom(options)', purpose: 'Creates the active DOM service.', notes: 'Used directly or through App.Dom.' }, { name: 'App.Dom', purpose: 'Always-present App root.', notes: 'Shared by Frontend and consumers.' } ], api: artifactApis.adom, sections: [ { title: 'Creation and target ownership', body: [ 'App.Dom is the shared DOM service for the application. It should own viewport tracking, responsive resolution and global writes that other artifacts depend on.', 'When using createActiveDom() directly, decide the target boundary explicitly: document-level app shell, an embedded widget root, or a test DOM. Do not let unrelated feature modules write global attributes independently.' ], code: { title: 'Isolated DOM root', code: `const Dom = createActiveDom({ target: document.documentElement, breakpoints: { base: 0, sm: 480, md: 768, lg: 1024 } }); const layout = Dom.resolve({ base: 'stack', md: 'split' });` } }, { title: 'Features', table: [ { name: 'viewport', purpose: 'Reactive viewport snapshot.', notes: 'Browser only; inert in SSR.' }, { name: 'resolve()', purpose: 'Resolve breakpoint maps.', notes: 'Useful for responsive component logic.' }, { name: 'apply()', purpose: 'Apply attrs/styles/classes declaratively.', notes: 'Used by Frontend.' }, { name: 'scroll lock', purpose: 'Coordinate body scroll locks.', notes: 'Useful for modals/drawers.' } ] }, { title: 'SSR', body: ['Dom is safe to import during SSR. Viewport tracking and browser APIs activate only when the runtime has document/window.'] } ], tests: [ { name: 'src/arts/adom/test', purpose: 'DOM primitives.', notes: 'Viewport, attrs, scroll and responsive resolution.' }, { name: 'src/arts/fend/test', purpose: 'Frontend integration.', notes: 'Frontend applies attrs through Dom.' }, { name: '/test/adom', purpose: 'Interactive DOM lab.', notes: 'Responsive, scroll lock and focus examples.' } ] }, sium: { section: 'Validation', title: 'Sium', alias: '$sium', summary: 'Validation engine with schemas, issues, metadata, Standard Schema interop and optional Lang/Logger injection.', factories: ['createEngineSium'], dependsOn: ['$lang (optional)', '$logr (optional)', '$libs/days', '$libs/color'], layer: 'EngineSium', overview: [ 'Sium is page-scoped by design. Forms live in pages and features, so App exposes createSiumEngine() instead of keeping a global validator alive for every route.', 'The engine creates schemas, validates values, returns structured issues and carries metadata for UI generation. It supports Standard Schema interoperability.', 'When created from App, Sium receives App.Lang and App.Logger. If Lang is not provided, its local resolver is only a fallback.' ], dynamics: [ 'Create a Sium engine close to the form or feature that needs it. Define schemas once, then call validate() for submitted values or Standard Schema consumers such as HTTP body validation.', 'Validation returns structured issues with paths. UI code should render issues by path instead of flattening everything into one string, otherwise nested forms become hard to explain.', 'Translations flow through the injected Lang engine when present. Sium should not copy Lang behavior; its local resolver exists only so validation still has fallback messages without i18n.' ], commonMistakes: [ { name: 'creating one global validator for every page', purpose: 'Forms are page/feature scoped and global validators load unnecessary schemas.', notes: 'Use App.createSiumEngine() where the form lives.' }, { name: 'throwing on normal validation failure', purpose: 'Invalid user input is data, not an exception path.', notes: 'Return/inspect result.ok and render result.issues.' }, { name: 'hard-coded issue messages', purpose: 'Messages bypass Lang and cannot localize consistently.', notes: 'Use injected Lang and constants for message keys/fallbacks.' }, { name: 'discarding issue paths', purpose: 'The UI cannot attach messages to fields.', notes: 'Keep structured issues and group/render by path.' } ], quickStart: { title: 'Page validator', code: `const Sium = App.createSiumEngine(); const ProfileSchema = Sium.object({ name: Sium.pipe(Sium.string(), Sium.min(2)), email: Sium.pipe(Sium.string(), Sium.email()) }); const result = await Sium.validate(ProfileSchema, formData); if (!result.ok) { console.log(result.issues); }` }, factoryRows: [ { name: 'createEngineSium(options)', purpose: 'Creates a validation engine.', notes: 'Accepts lang, logger and locale.' }, { name: 'App.createSiumEngine()', purpose: 'App-wired page factory.', notes: 'Injects App.Lang and App.Logger.' } ], api: artifactApis.sium, sections: [ { title: 'Creation and schema ownership', body: [ 'Sium is intentionally not an always-on App root. Create it where the form, HTTP body or feature validator lives, usually through App.createSiumEngine() so Lang and Logger are injected.', 'Schemas should be owned by the feature that validates the data. Share schemas only when multiple boundaries validate the exact same shape, for example a form and an HTTP endpoint.' ], code: { title: 'Feature-owned validator', code: `const Sium = App.createSiumEngine(); export const ProfileSchema = Sium.object({ name: Sium.pipe(Sium.string(), Sium.min(2)), email: Sium.pipe(Sium.string(), Sium.email()) }); const result = await Sium.validate(ProfileSchema, formValue);` } }, { title: 'Schema Model', table: [ { name: 'primitive schemas', purpose: 'string, number, boolean, date and similar checks.', notes: 'Composable through pipe.' }, { name: 'object/array schemas', purpose: 'Structured validation.', notes: 'Nested issues keep paths.' }, { name: 'meta()', purpose: 'Attach UI metadata.', notes: 'Useful for form generation.' }, { name: 'Standard Schema', purpose: 'Interop contract.', notes: 'Can validate external consumers and HTTP bodies.' } ] }, { title: 'Translations', body: ['Issue messages go through the injected Lang engine when available. The local resolver exists as fallback, not as a parallel copy of Lang behavior.'] } ], tests: [ { name: 'src/arts/sium/test', purpose: 'Core schemas and engine.', notes: 'Validation, pipes, metadata and translations.' }, { name: 'src/arts/aapp/test/create-sium-engine.test.ts', purpose: 'App injection.', notes: 'Lang and Logger are wired into Sium.' }, { name: '/test/sium', purpose: 'Interactive validation lab.', notes: 'Forms, translated issues and schemas.' } ] }, logr: { section: 'Infrastructure', title: 'Logger', alias: '$logr', summary: 'Structured logger with shared Logger contract, EngineLogger runtime, transports, filters, failure routing and diagnostics support.', factories: ['createEngineLogger'], dependsOn: ['$libs/logr'], layer: 'EngineLogger / Logger contract', overview: [ 'The minimal Logger interface lives in libs/logr and is what all modules receive. EngineLogger lives in arts/logr and extends that contract with transports, history, child loggers, timers and lifecycle.', 'Diagnostics are a cataloged layer above Logger. They map internal framework events to normal logger calls without forcing every log to become an event.', 'Transports can be filtered per level, buffered, throttled on failure and adapted to Sentry, Datadog, Loki, Logtail or OpenTelemetry.' ], dynamics: [ 'Application code and modules receive the small Logger contract from $libs/logr: trace, debug, info, warn, error and fatal. They do not need to know whether the concrete logger is EngineLogger, Sentry, a test spy or a custom adapter.', 'EngineLogger is the runtime implementation. It normalizes entries, applies level enablement, routes to transports, handles transport failures and emits synthetic failure entries to the remaining transports without cascading into the failed one.', 'Diagnostics sit above Logger. A module can define a catalog of internal events and map each one to a normal logger call. Free-form info/debug logs still go directly through Logger.' ], commonMistakes: [ { name: 'defining local Logger interfaces', purpose: 'Every module drifts and integrations become incompatible.', notes: 'Import Logger and LogFn from $libs/logr.' }, { name: 'hard-coded categories/messages', purpose: 'Search, routing and audits become unreliable.', notes: 'Keep categories, diagnostic names and standard messages in consts.ts.' }, { name: 'turning every log into a diagnostic event', purpose: 'Normal info/debug logging becomes boilerplate.', notes: 'Use diagnostics for cataloged framework events; use Logger directly for normal logs.' }, { name: 'letting a transport log its own failure', purpose: 'Failure cascades can loop indefinitely.', notes: 'Use denied/deniedFor routing and transport failure throttling.' } ], quickStart: { title: 'Create logger', code: `import { createEngineLogger, consoleTransport, levelsAtLeast, LogLevel } from '$logr'; import { sentryTransport } from '$logr/adapters/sentry'; const Logger = createEngineLogger({ level: LogLevel.INFO, transports: [ consoleTransport(), sentryTransport(Sentry, { levels: levelsAtLeast(LogLevel.ERROR) }) ], globalContext: { appVersion: '1.0.0' } }); Logger.info('checkout', 'payment completed', { context: { orderId }, traceId });` }, factoryRows: [ { name: 'createEngineLogger(options)', purpose: 'Creates the full logger runtime.', notes: 'Use directly or through App.Logger.' }, { name: '$libs/logr.createCatalogDiagnostics(options)', purpose: 'Maps typed diagnostic events to logger calls.', notes: 'Shared helper, not an EngineLogger factory.' }, { name: '$libs/logr.createLoggerDiagnostics(options)', purpose: 'Lower-level diagnostic resolver.', notes: 'Shared helper, not an EngineLogger factory.' } ], api: artifactApis.logr, sections: [ { title: 'Creation and injection', body: [ 'Create one EngineLogger at the application root and inject its Logger contract into other artifacts. Modules should depend on $libs/logr.Logger, not on EngineLogger internals.', 'Use child/context helpers for module scopes if needed, but keep category names and standard messages in module consts.ts.' ], code: { title: 'Shared logger contract', code: `import type { Logger } from '$libs/logr'; export function createFeature(options: { logger?: Logger }) { const logger = options.logger ?? App.Logger; logger.info('feature.started', { context: { source: 'profile' } }); }` } }, { title: 'Levels', body: ['The framework uses an explicit per-level enablement map for routing, not module-specific severity systems. EngineLogger still performs final filtering and transport dispatch.'] }, { title: 'Failure Routing', body: ['If a transport fails, EngineLogger emits a synthetic failure entry to the remaining transports and marks deniedFor so the failing sink does not receive its own failure. Throttling prevents cascades.'] }, { title: 'Diagnostics', code: { title: 'Diagnostic catalog from $libs/logr', code: `import { createCatalogDiagnostics, LogLevel } from '$libs/logr'; const diagnostics = createCatalogDiagnostics({ logger, defaultCategory: 'conn', catalog: { reconnect_exhausted: { level: LogLevel.WARN, message: 'reconnect attempts exhausted' } } }); diagnostics.emit({ artifact: 'conn', type: 'reconnect_exhausted', meta: { attempts: 5 } });` } } ], tests: [ { name: 'src/arts/logr/test', purpose: 'Engine logger.', notes: 'Levels, transports, failures, buffers and adapters.' }, { name: 'src/libs/logr/test', purpose: 'Shared diagnostics.', notes: 'Catalog routing and error normalization.' }, { name: '/test/logr', purpose: 'Interactive logger lab.', notes: 'Levels, transports and web vitals.' } ] }, timr: { section: 'Infrastructure', title: 'Timers', alias: '$timr', summary: 'Deterministic timer scheduler with injectable clock, one-shots, intervals, cancellation, snapshots and backoff helpers.', factories: ['createEngineTimers', 'createActiveTimers'], dependsOn: ['$libs/timers', '$logr (optional)'], layer: 'EngineTimers / ActiveTimers', overview: [ 'Timers centralizes scheduling so modules do not scatter setTimeout and setInterval logic. Reconnect, heartbeat, auto-refresh and cache-like workflows can all share a deterministic scheduler.', 'The engine supports keyed timers, replacement, cancellation by scope, intervals, snapshots and injected clocks for deterministic tests.', 'ActiveTimers exposes reactive snapshots for debug panels and test pages.' ], dynamics: [ 'Every timer has a stable key and optional scope. Scheduling with replace cancels the previous entry for that key before creating the next one, which prevents duplicate refresh/reconnect loops.', 'The engine uses the injected clock for timestamps and backoff calculations, so tests can advance time deterministically instead of waiting for real time.', 'ActiveTimers wraps the engine and exposes entries() as reactive snapshots. That is for observability and test pages; business logic should keep using schedule(), interval(), cancel() and cancelScope().' ], commonMistakes: [ { name: 'using setTimeout directly in modules', purpose: 'Timers become impossible to cancel, inspect or fake in tests.', notes: 'Use $timr for reconnect, refresh, heartbeat and delayed jobs.' }, { name: 'anonymous timer keys', purpose: 'Duplicate timers accumulate and cause repeated work.', notes: 'Use stable keys and replace when the latest task should win.' }, { name: 'not canceling by scope on dispose', purpose: 'Page/module timers can run after their owner is gone.', notes: 'Use scopes and cancelScope() or dispose the owner root.' }, { name: 'using Date.now() beside fake timers', purpose: 'Tests advance scheduler time but metadata remains real-time.', notes: 'Use the injected clock or TimerClock for related timestamps.' } ], quickStart: { title: 'Schedule work', code: `const Timers = App.Timers; Timers.schedule('profile:refresh', 5_000, async () => { await refreshProfile(); }); Timers.interval('sync', 30_000, syncInBackground, { replace: true, awaitTask: false });` }, factoryRows: [ { name: 'createEngineTimers(options)', purpose: 'Pure scheduler.', notes: 'Use in services, tests and non-Svelte contexts.' }, { name: 'createActiveTimers(options)', purpose: 'Reactive scheduler.', notes: 'Exposes entries() snapshots through Svelte state.' }, { name: 'App.Timers', purpose: 'Always-present App root.', notes: 'Injected into Connections and available to consumers.' } ], api: artifactApis.timr, sections: [ { title: 'Creation and ownership', body: [ 'App.Timers is the shared scheduler for browser-side artifacts. Connections, auto-refresh and debug panels should use this root instead of creating their own timer islands.', 'Create EngineTimers directly for deterministic unit tests, workers or server utilities that need an injected clock and do not need Svelte state.' ], code: { title: 'Scoped scheduling', code: `Timers.schedule('profile:refresh', 5_000, refreshProfile, { scope: 'profile', replace: true }); Timers.interval('conn:heartbeat', 30_000, heartbeat, { scope: 'conn', awaitTask: false }); Timers.cancelScope('profile');` } }, { title: 'Timer Entries', table: [ { name: 'key', purpose: 'Stable identity for timer operations.', notes: 'Used for replace/cancel/snapshot.' }, { name: 'scope', purpose: 'Optional group.', notes: 'Cancel a whole feature at once.' }, { name: 'run count', purpose: 'How many times the task ran.', notes: 'Useful for intervals and debug UI.' }, { name: 'nextRunAt', purpose: 'Scheduled timestamp.', notes: 'Uses injected clock.' } ] }, { title: 'Diagnostics', body: ['Timers logs task failures, listener failures and schedules in the past through the shared diagnostics layer when a logger is provided.'] } ], tests: [ { name: 'src/arts/timr/test', purpose: 'Scheduler behavior.', notes: 'One-shots, intervals, cancellation, backoff and fake clocks.' }, { name: 'src/arts/conn/test', purpose: 'Consumer integration.', notes: 'Reconnect, heartbeat and ACK timeouts.' }, { name: '/test/timr', purpose: 'Interactive timer lab.', notes: 'Snapshots, intervals and cancellation.' } ] }, conn: { section: 'Infrastructure', title: 'Connections', alias: '$conn', summary: 'Realtime connection registry with transports, reconnect, heartbeat, request/reply, channels, buffering and session bridge.', factories: ['createEngineConnections', 'createActiveConnections', 'createWebSocketTransport'], dependsOn: ['$timr', '$logr (optional)', '$sess bridge (optional)'], layer: 'EngineConnections / ActiveConnections', overview: [ 'Connections is a registry of named realtime connections. A connection is not the engine: EngineConnections owns all connection state; each Connection owns transport, channels, heartbeat, reconnect and request/reply.', 'The transport contract is pluggable. Browser WebSocket is one transport; tests and demos can use mock transports without changing the connection runtime.', 'When composed through App, Connections receives App.Timers, App.Logger and the App session bridge.' ], dynamics: [ 'Create one registry, then create named connections inside it. The registry tracks all names and aggregate state; each connection owns its transport lifecycle, channel collection, send buffer, heartbeat timers and reconnect strategy.', 'Transports emit open/message/close/failure signals. The connection translates those into framework states, schedules heartbeat and reconnect through Timers, and routes logs through the shared Logger/diagnostic constants.', 'Channels are scoped streams over a connection. They can join, leave, send and request. After reconnect, auto-join channels rejoin so feature code does not rebuild subscriptions manually.' ], commonMistakes: [ { name: 'treating Connection as the root', purpose: 'You lose registry-level lifecycle, aggregate state and disposal.', notes: 'Create EngineConnections/ActiveConnections first, then createConnection(name, options).' }, { name: 'using mock transports for real demos', purpose: 'It hides network ordering, close and reconnect behavior.', notes: 'Use createWebSocketTransport for demos intended to validate realtime behavior.' }, { name: 'logging through ad-hoc callbacks', purpose: 'Reconnect/heartbeat/channel logs bypass the framework logger pipeline.', notes: 'Use the injected Logger and module diagnostics/constants.' }, { name: 'forgetting session bridge behavior', purpose: 'Connections can keep old identity after login/logout/refresh.', notes: 'Enable session integration or explicitly reconnect/disconnect on session changes.' } ], quickStart: { title: 'WebSocket connection', code: `const Connections = App.createActiveConnections(); const Updates = Connections.createConnection('updates', { transport: createWebSocketTransport({ url: '/ws' }), heartbeat: { enabled: true }, reconnect: { enabled: true }, session: { enabled: true } }); await Updates.connect(); await Updates.send('project.updated', { id: projectId });` }, factoryRows: [ { name: 'createEngineConnections(options)', purpose: 'Connection registry engine.', notes: 'Owns all named connections.' }, { name: 'createActiveConnections(options)', purpose: 'Reactive registry wrapper.', notes: 'Exposes activeNames, states and aggregate booleans.' }, { name: 'createWebSocketTransport(options)', purpose: 'Browser WebSocket transport.', notes: 'Real network transport for production and demos.' }, { name: 'createMockTransport(options)', purpose: 'Test transport.', notes: 'No network; useful for deterministic tests.' } ], api: artifactApis.conn, sections: [ { title: 'Creation and registry ownership', body: [ 'Create a Connections registry first. The registry is the root; individual connections are children owned by that registry. App.createActiveConnections() injects App.Timers, App.Logger and the session bridge.', 'Use one registry for related realtime connections so aggregate state, disposal and session reactions stay coordinated.' ], code: { title: 'Registry first', code: `const Connections = App.createActiveConnections(); const Chat = Connections.createConnection('chat', { transport: createWebSocketTransport({ url: '/ws/chat' }), reconnect: { enabled: true }, heartbeat: { enabled: true }, session: { enabled: true } }); const room = Chat.channel('room:general', { autoJoin: true }); await Chat.connect();` } }, { title: 'Connection Features', table: [ { name: 'heartbeat', purpose: 'Ping/pong liveness.', notes: 'Closes transport on timeout.' }, { name: 'reconnect', purpose: 'Backoff-based reconnect.', notes: 'Can reconnect on online/visible browser events.' }, { name: 'request()', purpose: 'Request/reply over frames.', notes: 'ACK registry handles timeouts and replies.' }, { name: 'channels', purpose: 'Topic-like scoped streams.', notes: 'Can auto-join and rejoin after reconnect.' }, { name: 'buffer', purpose: 'Buffer/drop/fail sends while closed.', notes: 'Configurable max messages and bytes.' } ] }, { title: 'Session Bridge', body: ['If session integration is enabled, refreshed sessions trigger reauthentication and revoked/expired sessions disconnect the connection unless disabled per connection.'] }, { title: 'Active State', body: ['ActiveConnections tracks names and states reactively: connectedNames, reconnectingNames, failedNames, allConnected and anyConnected are ready for UI panels.'] } ], tests: [ { name: 'src/arts/conn/test', purpose: 'Connection runtime.', notes: 'States, channels, websocket transport, session bridge.' }, { name: 'src/arts/aapp/test/ecosystem.integration.test.ts', purpose: 'App integration.', notes: 'Connections with App session bridge.' }, { name: '/test/conn', purpose: 'Interactive realtime demo.', notes: 'WebSocket chat and connection/channel lifecycle.' } ] } } satisfies Record;