N2 — codex audit blockers #1, #8: route migration + arts/cache layer fix

Closes the remaining items from segunda_auditoria-codex.md the user
explicitly called out as still pending after N1.

Layer fix (audit blocker #8):
`createEngineCache`, `EngineCache`, `EngineCacheOptions`, the cache
diagnostics catalog and the disposed-engine error infrastructure move
from `svrs/cache/*` into `libs/cache/*` (renamed `engine-*` to
distinguish from the existing pure-runtime files). Nothing in those
files is server-specific — only `Logger` integration and a dispose
guard, both universal. `arts/cache` now imports from `$libs/cache`
directly; `svrs/cache/index.ts` becomes a transparent re-export of
the same names so historical `$svrs/cache` consumers keep working.
The arts → svrs layer inversion the audit flagged is now
structurally impossible for the cache module.

Route migration (audit blocker #1):
The `/test/{aapp,cach,conn,perm,http}` pages are migrated from the
pre-`createActiveApp({ services })` API surface to the current
service-schema shape:
- `App.createSiumEngine()` → `defineEngineSium()` in services + `App.sium`.
- `App.createActiveSession(...)` → `defineActiveSession(...)` + `App.session`.
- `App.createActivePerms(...)` → `defineActivePerm(...)` + `App.perm`.
- `App.createActiveConnections<...>()` → `defineActiveConnections(...)` + `App.connections`.
- Top-level option blocks (`lang: {...}`, `http: {...}`, `cache: {...}`,
  `frontend: {...}`, `storage: {...}`, `permissions: {...}`,
  `connections: {...}`) now wrap their factories under `services: {...}`.
- `App.setLocale` / `App.getLocale` migrate to `App.lang.*`.
- `density: 'normal'` → `'comfortable'` (post-L1 vocabulary).
- Each page's `+page.ts` prerender opt-out is removed; they now build
  statically and are reachable from the test index.

`/test/ecosystem` is the one outlier: its 1387-line scenario harness
threads through Auth + Session + Perms + Cache + Connections in ways
that need API-port stubbing (AuthClientHttpPort, an EcosystemConnections
generic shape that no longer exists, etc.). The factory wiring is
already migrated in the file (services schema, App.sium / App.auth /
App.session / App.perm / App.connections / cache scope locale via a
synced `currentLocale` cell), but the consumer code still uses
`App.format` / `App.dom` / `App.createActiveConnections<...>()`
shapes that don't typecheck against the new schema. Re-excluded from
typecheck + prerender with a follow-up note; the page still loads in
dev. Migration of the remaining call sites is its own commit.

Suite: 1695 / 1695 passing. check / build / bundle / aliases all green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
master
dev 5 months ago
parent ca01324f67
commit 843c7087e7

@ -1,5 +1,5 @@
import { CACHE_EVENT_ALL, type CacheClock, type CacheEvent } from '$libs/cache'; import { CACHE_EVENT_ALL, type CacheClock, type CacheEvent } from '$libs/cache';
import { createEngineCache } from '$svrs/cache'; import { createEngineCache } from '$libs/cache';
import { import {
CACHE_ACTIVE_DIAGNOSTIC_EVENTS, CACHE_ACTIVE_DIAGNOSTIC_EVENTS,
CACHE_METHOD_CLEAR, CACHE_METHOD_CLEAR,
@ -13,7 +13,7 @@ import {
CACHE_METHOD_SET, CACHE_METHOD_SET,
CACHE_METHOD_STATS CACHE_METHOD_STATS
} from './consts.ts'; } from './consts.ts';
import { disposedCacheMessage } from '$svrs/cache'; import { disposedCacheMessage } from '$libs/cache';
import { createActiveCacheEntry } from './active-cache-entry.svelte.ts'; import { createActiveCacheEntry } from './active-cache-entry.svelte.ts';
import { normalizeActiveCacheError } from './active-cache-helpers.ts'; import { normalizeActiveCacheError } from './active-cache-helpers.ts';
import { createActiveCacheDiagnostics, emitActiveCacheDiagnostic } from './diagnostics.ts'; import { createActiveCacheDiagnostics, emitActiveCacheDiagnostic } from './diagnostics.ts';

@ -25,7 +25,7 @@ export {
CACHE_METHOD_SET, CACHE_METHOD_SET,
CACHE_METHOD_STATS, CACHE_METHOD_STATS,
CACHE_MODULE CACHE_MODULE
} from '$svrs/cache'; } from '$libs/cache';
export const CACHE_ACTIVE_STATUS_IDLE = 'idle'; export const CACHE_ACTIVE_STATUS_IDLE = 'idle';
export const CACHE_ACTIVE_STATUS_LOADING = 'loading'; export const CACHE_ACTIVE_STATUS_LOADING = 'loading';

@ -1,8 +1,8 @@
import { CodeError } from '$libs/errs'; import { CodeError } from '$libs/errs';
import { CACHE_ERR_ACTIVE_ENTRY_DISPOSED } from '$libs/cache'; import { CACHE_ERR_ACTIVE_ENTRY_DISPOSED } from '$libs/cache';
import { CACHE_MODULE } from '$svrs/cache'; import { CACHE_MODULE } from '$libs/cache';
export { CACHE_ERROR_MSG_DISPOSED_SUFFIX, CacheDisposedError, isCacheDisposedError } from '$svrs/cache'; export { CACHE_ERROR_MSG_DISPOSED_SUFFIX, CacheDisposedError, isCacheDisposedError } from '$libs/cache';
export const CACHE_ERROR_MSG_ACTIVE_ENTRY_DISPOSED = export const CACHE_ERROR_MSG_ACTIVE_ENTRY_DISPOSED =
`[${CACHE_MODULE}] ActiveCacheEntry used after dispose().`; `[${CACHE_MODULE}] ActiveCacheEntry used after dispose().`;

@ -53,7 +53,7 @@ export {
isCacheActiveEntryDisposedError, isCacheActiveEntryDisposedError,
isCacheDisposedError isCacheDisposedError
} from './errors.ts'; } from './errors.ts';
export { disposedCacheMessage } from '$svrs/cache'; export { disposedCacheMessage } from '$libs/cache';
export type { export type {
ActiveCacheDiagnosticEvent, ActiveCacheDiagnosticEvent,
ActiveCacheDiagnosticMeta, ActiveCacheDiagnosticMeta,
@ -145,7 +145,7 @@ export {
stableHash, stableHash,
stableStringify, stableStringify,
storageCacheAdapter storageCacheAdapter
} from '$svrs/cache'; } from '$libs/cache';
export type { export type {
CacheAdapter, CacheAdapter,
@ -187,4 +187,4 @@ export type {
SetOptions, SetOptions,
StorageCacheAdapterOptions, StorageCacheAdapterOptions,
StorageLike StorageLike
} from '$svrs/cache'; } from '$libs/cache';

@ -1,9 +1,9 @@
import type { CACHE_ACTIVE_ENTRY_EVENTS, CACHE_ACTIVE_STATUSES } from './consts.ts'; import type { CACHE_ACTIVE_ENTRY_EVENTS, CACHE_ACTIVE_STATUSES } from './consts.ts';
import type { CacheClock, CacheEvent, CacheKey, QueryOptions, SetOptions } from '$libs/cache'; import type { CacheClock, CacheEvent, CacheKey, QueryOptions, SetOptions } from '$libs/cache';
import type { EngineCache, EngineCacheOptions } from '$svrs/cache'; import type { EngineCache, EngineCacheOptions } from '$libs/cache';
import type { ActiveChangeListener, ActiveEngine } from '$libs/active'; import type { ActiveChangeListener, ActiveEngine } from '$libs/active';
export type { EngineCache, EngineCacheOptions } from '$svrs/cache'; export type { EngineCache, EngineCacheOptions } from '$libs/cache';
export type ActiveCacheEntryStatus = (typeof CACHE_ACTIVE_STATUSES)[number]; export type ActiveCacheEntryStatus = (typeof CACHE_ACTIVE_STATUSES)[number];

@ -10,10 +10,10 @@ import {
CACHE_EVENT_ADAPTER_ERROR, CACHE_EVENT_ADAPTER_ERROR,
CACHE_EVENT_REFRESH_ERROR, CACHE_EVENT_REFRESH_ERROR,
CACHE_EVENT_STALE_IF_ERROR, CACHE_EVENT_STALE_IF_ERROR,
type CacheEvent, CACHE_MODULE
type CacheEventType } from './consts.ts';
} from '$libs/cache'; import type { CacheEvent, CacheEventType } from './types.ts';
import { CACHE_LOG_MESSAGE_BY_EVENT, CACHE_MODULE } from './consts.ts'; import { CACHE_LOG_MESSAGE_BY_EVENT } from './engine-consts.ts';
export type CacheDiagnosticEvent = DiagnosticEvent<CacheEventType, CacheEvent>; export type CacheDiagnosticEvent = DiagnosticEvent<CacheEventType, CacheEvent>;
export type CacheDiagnostics = Diagnostics<CacheDiagnosticEvent>; export type CacheDiagnostics = Diagnostics<CacheDiagnosticEvent>;

@ -1,4 +1,5 @@
import { createCacheRuntime, memoryCacheAdapter } from '$libs/cache'; import { createCacheRuntime } from './engine.ts';
import { memoryCacheAdapter } from './adapters/memory.ts';
import { import {
CACHE_METHOD_CLEAR, CACHE_METHOD_CLEAR,
CACHE_METHOD_EXPLAIN, CACHE_METHOD_EXPLAIN,
@ -9,11 +10,11 @@ import {
CACHE_METHOD_QUERY, CACHE_METHOD_QUERY,
CACHE_METHOD_SET, CACHE_METHOD_SET,
CACHE_METHOD_STATS CACHE_METHOD_STATS
} from './consts.ts'; } from './engine-consts.ts';
import { createCacheDiagnostics, emitCacheDiagnostic } from './diagnostics.ts'; import { createCacheDiagnostics, emitCacheDiagnostic } from './diagnostics.ts';
import { CacheDisposedError } from './errors.ts'; import { CacheDisposedError } from './engine-errors.ts';
import { disposedCacheMessage } from './helpers.ts'; import { disposedCacheMessage } from './engine-helpers.ts';
import type { EngineCache, EngineCacheOptions } from './types.ts'; import type { EngineCache, EngineCacheOptions } from './engine-types.ts';
export function createEngineCache(options: EngineCacheOptions = {}): EngineCache { export function createEngineCache(options: EngineCacheOptions = {}): EngineCache {
const diagnostics = createCacheDiagnostics(options.logger); const diagnostics = createCacheDiagnostics(options.logger);

@ -13,15 +13,9 @@ import {
CACHE_EVENT_SET, CACHE_EVENT_SET,
CACHE_EVENT_SINGLEFLIGHT_JOIN, CACHE_EVENT_SINGLEFLIGHT_JOIN,
CACHE_EVENT_STALE_HIT, CACHE_EVENT_STALE_HIT,
CACHE_EVENT_STALE_IF_ERROR, CACHE_EVENT_STALE_IF_ERROR
CACHE_MODULE, } from './consts.ts';
type CacheEventType import type { CacheEventType } from './types.ts';
} from '$libs/cache';
// `CACHE_MODULE` is owned by `libs/cache` (the pure layer needs it for
// diagnostics) and re-exported from this server consts module so
// existing `$svrs/cache` consumers keep their import paths.
export { CACHE_MODULE };
export const CACHE_LOG_MESSAGE_HIT = 'cache hit'; export const CACHE_LOG_MESSAGE_HIT = 'cache hit';
export const CACHE_LOG_MESSAGE_MISS = 'cache miss'; export const CACHE_LOG_MESSAGE_MISS = 'cache miss';

@ -1,5 +1,5 @@
import { CodeError } from '$libs/errs'; import { CodeError } from '$libs/errs';
import { CACHE_ERR_DISPOSED } from '$libs/cache'; import { CACHE_ERR_DISPOSED } from './errors.ts';
export const CACHE_ERROR_MSG_DISPOSED_SUFFIX = '() called on a disposed cache engine'; export const CACHE_ERROR_MSG_DISPOSED_SUFFIX = '() called on a disposed cache engine';

@ -1,4 +1,4 @@
import { CACHE_ERROR_MSG_DISPOSED_SUFFIX } from './errors.ts'; import { CACHE_ERROR_MSG_DISPOSED_SUFFIX } from './engine-errors.ts';
export function disposedCacheMessage(method: string): string { export function disposedCacheMessage(method: string): string {
return `${method}${CACHE_ERROR_MSG_DISPOSED_SUFFIX}`; return `${method}${CACHE_ERROR_MSG_DISPOSED_SUFFIX}`;

@ -2,9 +2,9 @@ import type {
CacheAdapter, CacheAdapter,
CacheEventHandler, CacheEventHandler,
CacheRuntime, CacheRuntime,
CacheRuntimeConfig, CacheRuntimeConfig
MemoryCacheAdapterOptions } from './types.ts';
} from '$libs/cache'; import type { MemoryCacheAdapterOptions } from './adapters/memory.ts';
import type { Logger } from '$libs/logger'; import type { Logger } from '$libs/logger';
export interface EngineCacheOptions extends Omit<CacheRuntimeConfig, 'adapter' | 'onEvent'> { export interface EngineCacheOptions extends Omit<CacheRuntimeConfig, 'adapter' | 'onEvent'> {

@ -68,3 +68,48 @@ export type {
export type { MemoryCacheAdapter, MemoryCacheAdapterOptions } from './adapters/memory.ts'; export type { MemoryCacheAdapter, MemoryCacheAdapterOptions } from './adapters/memory.ts';
export type { StorageCacheAdapterOptions, StorageLike } from './adapters/storage.ts'; export type { StorageCacheAdapterOptions, StorageLike } from './adapters/storage.ts';
// ── Engine surface (formerly in `svrs/cache`) ──────────────────────────
//
// The engine factory is the runtime wrapper consumed by both `arts/cache`
// (reactive) and `svrs/cache` (server compositions). It lives here in the
// pure layer because nothing about it is server-specific — only `Logger`
// integration and a dispose guard. Closes the codex audit blocker #8
// (arts → svrs layer inversion).
export { createEngineCache } from './engine-cache.ts';
export {
CACHE_METHOD_CLEAR,
CACHE_METHOD_EXPLAIN,
CACHE_METHOD_GET,
CACHE_METHOD_INVALIDATE,
CACHE_METHOD_MUTATE,
CACHE_METHOD_ON,
CACHE_METHOD_QUERY,
CACHE_METHOD_SET,
CACHE_METHOD_STATS,
CACHE_LOG_MESSAGE_BY_EVENT,
CACHE_LOG_MESSAGE_HIT,
CACHE_LOG_MESSAGE_MISS,
CACHE_LOG_MESSAGE_STALE_HIT,
CACHE_LOG_MESSAGE_REFRESH_START,
CACHE_LOG_MESSAGE_REFRESH_SUCCESS,
CACHE_LOG_MESSAGE_REFRESH_ERROR,
CACHE_LOG_MESSAGE_STALE_IF_ERROR,
CACHE_LOG_MESSAGE_INVALIDATE,
CACHE_LOG_MESSAGE_SCHEMA_MISMATCH,
CACHE_LOG_MESSAGE_SCOPE_ERROR,
CACHE_LOG_MESSAGE_SINGLEFLIGHT_JOIN,
CACHE_LOG_MESSAGE_EVICTION,
CACHE_LOG_MESSAGE_ADAPTER_ERROR,
CACHE_LOG_MESSAGE_SET,
CACHE_LOG_MESSAGE_DELETE
} from './engine-consts.ts';
export {
CACHE_ERROR_MSG_DISPOSED_SUFFIX,
CacheDisposedError,
isCacheDisposedError
} from './engine-errors.ts';
export { disposedCacheMessage } from './engine-helpers.ts';
export { createCacheDiagnostics, emitCacheDiagnostic } from './diagnostics.ts';
export type { EngineCache, EngineCacheOptions } from './engine-types.ts';
export type { CacheDiagnosticEvent, CacheDiagnostics } from './diagnostics.ts';

@ -1,155 +1,10 @@
export { createEngineCache } from './engine-cache.ts'; /**
export { createCacheDiagnostics, emitCacheDiagnostic } from './diagnostics.ts'; * Server-side surface for `cache`. After the codex audit blocker #8 fix,
export { CACHE_ERR_DISPOSED } from '$libs/cache'; * the engine factory + diagnostics + types live in `$libs/cache` (the
export { * pure layer) so `arts/cache` can consume them without inverting layers.
CACHE_LOG_MESSAGE_ADAPTER_ERROR, *
CACHE_LOG_MESSAGE_BY_EVENT, * This module re-exports the same names from `$libs/cache` so historical
CACHE_LOG_MESSAGE_DELETE, * `import { ... } from '$svrs/cache'` callers keep working — production
CACHE_LOG_MESSAGE_EVICTION, * compositions, READMEs, docs and ecosystem tests all flow through here.
CACHE_LOG_MESSAGE_HIT, */
CACHE_LOG_MESSAGE_INVALIDATE, export * from '$libs/cache';
CACHE_LOG_MESSAGE_MISS,
CACHE_LOG_MESSAGE_REFRESH_ERROR,
CACHE_LOG_MESSAGE_REFRESH_START,
CACHE_LOG_MESSAGE_REFRESH_SUCCESS,
CACHE_LOG_MESSAGE_SCHEMA_MISMATCH,
CACHE_LOG_MESSAGE_SCOPE_ERROR,
CACHE_LOG_MESSAGE_SET,
CACHE_LOG_MESSAGE_SINGLEFLIGHT_JOIN,
CACHE_LOG_MESSAGE_STALE_HIT,
CACHE_LOG_MESSAGE_STALE_IF_ERROR,
CACHE_METHOD_CLEAR,
CACHE_METHOD_EXPLAIN,
CACHE_METHOD_GET,
CACHE_METHOD_INVALIDATE,
CACHE_METHOD_MUTATE,
CACHE_METHOD_ON,
CACHE_METHOD_QUERY,
CACHE_METHOD_SET,
CACHE_METHOD_STATS,
CACHE_MODULE
} from './consts.ts';
export {
CACHE_ERROR_MSG_DISPOSED_SUFFIX,
CacheDisposedError,
isCacheDisposedError
} from './errors.ts';
export { disposedCacheMessage } from './helpers.ts';
export type { EngineCache, EngineCacheOptions } from './types.ts';
export type { CacheDiagnosticEvent, CacheDiagnostics } from './diagnostics.ts';
export {
CACHE_ADAPTER_MEMORY,
CACHE_ADAPTER_STORAGE,
CACHE_DECISION_ACTION_DELETE_AND_FETCH,
CACHE_DECISION_ACTION_FETCH,
CACHE_DECISION_ACTION_SERVE,
CACHE_DECISION_ACTION_SERVE_AND_REFRESH,
CACHE_DECISION_ACTION_SKIP_CACHE,
CACHE_DECISION_REASON_BYPASS_CACHE,
CACHE_DECISION_REASON_CACHE_MISS,
CACHE_DECISION_REASON_EXPIRED,
CACHE_DECISION_REASON_FRESH,
CACHE_DECISION_REASON_NO_STORE,
CACHE_DECISION_REASON_PREFIX_EPOCH_CHANGED,
CACHE_DECISION_REASON_SCHEMA_VERSION_MISMATCH,
CACHE_DECISION_REASON_SCOPE_MISMATCH,
CACHE_DECISION_REASON_STALE_WINDOW_VALID,
CACHE_DECISION_REASON_TAG_EPOCH_CHANGED,
CACHE_ENVELOPE_STATE_DEGRADED,
CACHE_ENVELOPE_STATE_EXPIRED,
CACHE_ENVELOPE_STATE_FRESH,
CACHE_ENVELOPE_STATE_INVALIDATED,
CACHE_ENVELOPE_STATE_STALE,
CACHE_EVENT_ADAPTER_ERROR,
CACHE_EVENT_ALL,
CACHE_EVENT_DELETE,
CACHE_EVENT_EVICTION,
CACHE_EVENT_HIT,
CACHE_EVENT_INVALIDATE,
CACHE_EVENT_MISS,
CACHE_EVENT_REFRESH_ERROR,
CACHE_EVENT_REFRESH_START,
CACHE_EVENT_REFRESH_SUCCESS,
CACHE_EVENT_SCHEMA_MISMATCH,
CACHE_EVENT_SCOPE_ERROR,
CACHE_EVENT_SET,
CACHE_EVENT_SINGLEFLIGHT_JOIN,
CACHE_EVENT_STALE_HIT,
CACHE_EVENT_STALE_IF_ERROR,
CACHE_POLICY_CATALOG,
CACHE_POLICY_IMMUTABLE,
CACHE_POLICY_INTERACTIVE,
CACHE_POLICY_PRIVATE_SESSION,
CACHE_POLICY_REALTIME,
CACHE_READ_MODE_BYPASS_CACHE,
CACHE_READ_MODE_CACHE_FIRST,
CACHE_READ_MODE_MUST_REVALIDATE,
CACHE_READ_MODE_NO_STORE,
CACHE_READ_MODE_STALE_WHILE_REVALIDATE,
CACHE_SCOPE_ACTOR,
CACHE_SCOPE_CUSTOM,
CACHE_SCOPE_PERMISSION,
CACHE_SCOPE_PUBLIC,
CACHE_SCOPE_TENANT,
CacheKeyError,
CachePolicyError,
CacheScopeError,
createMapStorage,
defaultCachePolicies,
durationToMs,
isCacheKeyError,
isCachePolicyError,
isCacheScopeError,
memoryCacheAdapter,
normalizeKey,
normalizeKeyPrefixes,
normalizeTag,
normalizeTags,
resolveScope,
stableHash,
stableStringify,
storageCacheAdapter
} from '$libs/cache';
export type {
CacheAdapter,
CacheAdapterSetOptions,
CacheClock,
CacheDecision,
CacheDecisionAction,
CacheDecisionReason,
CacheEnvelope,
CacheEnvelopeState,
CacheEvent,
CacheEventHandler,
CacheEventSelector,
CacheEventType,
CacheExplain,
CacheKey,
CachePolicy,
CacheReadMode,
CacheRuntime,
CacheRuntimeConfig,
CacheScopeInput,
CacheScopeMode,
CacheStats,
CacheTagLike,
CacheTagObject,
CustomCacheScope,
ExplainOptions,
GetOptions,
InvalidateOptions,
MemoryCacheAdapter,
MemoryCacheAdapterOptions,
MemoryCacheEvictReason,
MutateOptions,
QueryOptions,
ResolvedCachePolicy,
ResolvedCacheScope,
ResolvedScopeValues,
ScopeResolver,
SetOptions,
StorageCacheAdapterOptions,
StorageLike
} from '$libs/cache';

@ -1,7 +1,15 @@
<script lang="ts"> <script lang="ts">
import { onDestroy } from 'svelte'; import { onDestroy } from 'svelte';
import { createActiveApp } from '$active-app'; import { createActiveApp } from '$active-app';
import { cookieAdapter, localAdapter } from '$storage'; import {
defineActiveCache,
defineActiveFormat,
defineActiveFrontend,
defineActiveLang,
defineActiveStorage,
defineEngineSium
} from '$active-app/services';
import { localAdapter } from '$storage';
import type { SupportedLocale } from '$lang'; import type { SupportedLocale } from '$lang';
import { siumLangs, type Issue } from '$sium'; import { siumLangs, type Issue } from '$sium';
import { LogLevel, type LogEntry, type Transport } from '$logger'; import { LogLevel, type LogEntry, type Transport } from '$logger';
@ -25,39 +33,36 @@
let appRoot = $state<HTMLElement | null>(null); let appRoot = $state<HTMLElement | null>(null);
const App = createActiveApp({ const App = createActiveApp({
lang: { schema: langSchema, defaultLocale: 'es', fallbackChain: ['en'] },
logger: { logger: {
level: LogLevel.TRACE, level: LogLevel.TRACE,
globalContext: { surface: '/test/aapp' }, globalContext: { surface: '/test/aapp' },
transports: [captureTransport] transports: [captureTransport]
}, },
storage: { adapter: localAdapter, namespace: 'test-aapp' }, services: {
cache: { lang: defineActiveLang({
defaultMemoryAdapter: { suppressProductionWarning: true } schema: langSchema,
}, defaultLocale: 'es',
frontend: { fallbackChain: ['en']
theme: 'base', }),
mode: 'auto', storage: defineActiveStorage({ adapter: localAdapter, namespace: 'test-aapp' }),
density: 'normal', format: defineActiveFormat(),
target: () => appRoot, cache: defineActiveCache({
persist: { defaultMemoryAdapter: { suppressProductionWarning: true }
// Theme via cookie (server-readable for SSR demos), the rest in }),
// localStorage with the App's namespace. frontend: defineActiveFrontend({
overrides: { theme: 'base',
theme: { mode: 'auto',
adapter: cookieAdapter({ path: '/', sameSite: 'lax', maxAge: 31_536_000 }), density: 'comfortable',
namespace: false, target: () => appRoot
raw: true }),
} sium: defineEngineSium()
}
}
} }
}); });
// Sium is page-scoped — `App.createSiumEngine()` wires App.lang + // Sium is now an App service — `App.sium` is the validation engine,
// App.Logger so issues translate live and validation diagnostics flow // wired to App.lang for i18n issue messages and to App.Logger for
// through the same bus. // diagnostics through the same bus.
const sium = App.createSiumEngine(); const sium = App.sium;
onDestroy(() => App.dispose()); onDestroy(() => App.dispose());
@ -76,7 +81,7 @@
]; ];
function setLocale(locale: SupportedLocale): void { function setLocale(locale: SupportedLocale): void {
App.setLocale(locale); App.lang.setLocale(locale);
App.Logger.info('locale', `Locale switched to ${locale}`, { App.Logger.info('locale', `Locale switched to ${locale}`, {
context: { locale } context: { locale }
}); });
@ -121,7 +126,7 @@
loginIssues.map((entry) => ({ loginIssues.map((entry) => ({
path: entry.path.length === 0 ? '<root>' : entry.path.join('.'), path: entry.path.length === 0 ? '<root>' : entry.path.join('.'),
code: entry.code, code: entry.code,
message: sium.resolveIssue(entry, App.getLocale()) message: sium.resolveIssue(entry, App.lang.getLocale())
})) }))
); );
@ -199,7 +204,7 @@
<section class="locale" dir="ltr"> <section class="locale" dir="ltr">
<h2>1. Locale (single source of truth)</h2> <h2>1. Locale (single source of truth)</h2>
<p class="hint"> <p class="hint">
<code>App.setLocale(...)</code> propagates to <strong>Lang</strong>, <code>App.lang.setLocale(...)</code> propagates to <strong>Lang</strong>,
<strong>Format</strong> and <strong>Frontend</strong> through a shared <strong>Format</strong> and <strong>Frontend</strong> through a shared
<code>LocaleSource</code>. BCP 47 resolution runs per record on every <code>LocaleSource</code>. BCP 47 resolution runs per record on every
<code>Lang.t()</code> call. <code>Lang.t()</code> call.
@ -208,7 +213,7 @@
{#each LOCALES as { code, label, flag } (code)} {#each LOCALES as { code, label, flag } (code)}
<button <button
type="button" type="button"
class:active={App.getLocale() === code} class:active={App.lang.getLocale() === code}
onclick={() => setLocale(code)} onclick={() => setLocale(code)}
> >
<span aria-hidden="true">{flag}</span> <span aria-hidden="true">{flag}</span>
@ -218,7 +223,7 @@
{/each} {/each}
</div> </div>
<p class="active-line"> <p class="active-line">
<strong>Active:</strong> <code>{App.getLocale()}</code> <strong>Active:</strong> <code>{App.lang.getLocale()}</code>
</p> </p>
</section> </section>

@ -1,8 +0,0 @@
/**
* Skip static prerender for the legacy aapp demo. The page still
* relies on pre-service-schema App methods that the current runtime
* no longer exposes; until it is migrated to
* `createActiveApp({ services })`, render it on-demand only so the
* static gate stays green and the page remains reachable in dev.
*/
export const prerender = false;

@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
import { createActiveApp } from '$active-app'; import { createActiveApp } from '$active-app';
import { defineActiveCache } from '$active-app/services';
import { import {
CACHE_EVENT_ALL, CACHE_EVENT_ALL,
CACHE_POLICY_INTERACTIVE, CACHE_POLICY_INTERACTIVE,
@ -54,32 +55,34 @@
let logSeq = 0; let logSeq = 0;
const App = createActiveApp({ const App = createActiveApp({
cache: { services: {
defaultMemoryAdapter: { suppressProductionWarning: true }, cache: defineActiveCache({
scopeResolver: (): ResolvedScopeValues => ({ defaultMemoryAdapter: { suppressProductionWarning: true },
tenantId, scopeResolver: (): ResolvedScopeValues => ({
actorId, tenantId,
permissionHash, actorId,
locale permissionHash,
}), locale
policies: { }),
[CACHE_POLICY_INTERACTIVE]: { policies: {
freshFor: 2_000, [CACHE_POLICY_INTERACTIVE]: {
staleFor: 30_000, freshFor: 2_000,
staleIfErrorFor: 60_000, staleFor: 30_000,
gcAfter: 120_000, staleIfErrorFor: 60_000,
mode: CACHE_READ_MODE_STALE_WHILE_REVALIDATE, gcAfter: 120_000,
persist: true mode: CACHE_READ_MODE_STALE_WHILE_REVALIDATE,
}, persist: true
[CACHE_POLICY_PRIVATE_SESSION]: { },
freshFor: 1_000, [CACHE_POLICY_PRIVATE_SESSION]: {
staleFor: 3_000, freshFor: 1_000,
staleIfErrorFor: 0, staleFor: 3_000,
gcAfter: 15_000, staleIfErrorFor: 0,
mode: CACHE_READ_MODE_STALE_WHILE_REVALIDATE, gcAfter: 15_000,
persist: false mode: CACHE_READ_MODE_STALE_WHILE_REVALIDATE,
persist: false
}
} }
} })
} }
}); });

@ -1,2 +0,0 @@
/** Skip static prerender — uses pre-service-schema App API. See /test/aapp. */
export const prerender = false;

@ -2,6 +2,11 @@
import { onDestroy } from 'svelte'; import { onDestroy } from 'svelte';
import { resolve } from '$app/paths'; import { resolve } from '$app/paths';
import { createActiveApp } from '$active-app'; import { createActiveApp } from '$active-app';
import {
defineActiveCache,
defineActiveConnections,
defineActiveSession
} from '$active-app/services';
import { LogLevel } from '$logger'; import { LogLevel } from '$logger';
import { import {
CONNECTION_BUFFER_POLICY_BUFFER, CONNECTION_BUFFER_POLICY_BUFFER,
@ -88,24 +93,44 @@
at: string; at: string;
}; };
// Declared up here so the session factory's `onRefresh` closure can
// capture them. They are mutated by UI actions later in the file.
let refreshMode: 'success' | 'expired' = 'success';
let tokenSerial = 1;
const App = createActiveApp({ const App = createActiveApp({
logger: { level: LogLevel.NONE, transports: [] }, logger: { level: LogLevel.NONE, transports: [] },
cache: { services: {
defaultMemoryAdapter: { suppressProductionWarning: true } cache: defineActiveCache({
}, defaultMemoryAdapter: { suppressProductionWarning: true }
connections: { }),
defaults: { session: defineActiveSession<ChatUser, ChatCredential>({
heartbeat: false, schemas: {},
buffer: { onRefresh: async (current) => {
policy: CONNECTION_BUFFER_POLICY_BUFFER, if (refreshMode === 'expired') return null;
maxMessages: 12 tokenSerial += 1;
}, return {
reconnect: { ...current,
minDelayMs: 650, credential: { token: `chat-token-${tokenSerial}` },
maxDelayMs: 650, issuedAt: Date.now(),
jitterMs: 0 expiresAt: Date.now() + 120_000
};
} }
} }),
connections: defineActiveConnections({
defaults: {
heartbeat: false,
buffer: {
policy: CONNECTION_BUFFER_POLICY_BUFFER,
maxMessages: 12
},
reconnect: {
minDelayMs: 650,
maxDelayMs: 650,
jitterMs: 0
}
}
})
} }
}); });
@ -114,8 +139,6 @@
let draft = $state('Hola, esto va por WebSocket real'); let draft = $state('Hola, esto va por WebSocket real');
let pendingQuestion = $state(false); let pendingQuestion = $state(false);
let serverTyping = $state(false); let serverTyping = $state(false);
let refreshMode: 'success' | 'expired' = 'success';
let tokenSerial = 1;
let messages = $state<ChatMessage[]>([]); let messages = $state<ChatMessage[]>([]);
let states = $state<ConnectionStateChange[]>([]); let states = $state<ConnectionStateChange[]>([]);
let frames = $state<FrameLog[]>([]); let frames = $state<FrameLog[]>([]);
@ -166,21 +189,9 @@
} }
}; };
const Sess = App.createActiveSession<ChatUser, ChatCredential>({ const Sess = App.session;
schemas: {},
onRefresh: async (current) => {
if (refreshMode === 'expired') return null;
tokenSerial += 1;
return {
...current,
credential: { token: `chat-token-${tokenSerial}` },
issuedAt: Date.now(),
expiresAt: Date.now() + 120_000
};
}
});
const Connections = App.createActiveConnections<AppConnections>(); const Connections = App.connections;
const Chat = Connections.createConnection(CONNECTION_NAME, { const Chat = Connections.createConnection(CONNECTION_NAME, {
transport: () => transport: () =>
createWebSocketTransport({ createWebSocketTransport({

@ -1,2 +0,0 @@
/** Skip static prerender — uses pre-service-schema App API. See /test/aapp. */
export const prerender = false;

@ -2,6 +2,18 @@
import { onDestroy, untrack } from 'svelte'; import { onDestroy, untrack } from 'svelte';
import { resolve } from '$app/paths'; import { resolve } from '$app/paths';
import { createActiveApp } from '$active-app'; import { createActiveApp } from '$active-app';
import {
defineActiveAuth,
defineActiveCache,
defineActiveConnections,
defineActiveFrontend,
defineActiveLang,
defineActivePerm,
defineActiveSession,
defineActiveStorage,
defineEngineHttp,
defineEngineSium
} from '$active-app/services';
import { import {
CACHE_EVENT_ALL, CACHE_EVENT_ALL,
CACHE_POLICY_INTERACTIVE, CACHE_POLICY_INTERACTIVE,
@ -347,60 +359,80 @@
return jsonResponse({ error: 'not-found', path }, 404); return jsonResponse({ error: 'not-found', path }, 404);
}) as typeof fetch; }) as typeof fetch;
// `currentLocale` mirrors `App.lang.getLocale()` so the cache scope
// resolver can read it without referencing the App handle (which
// doesn't exist yet at the moment the resolver is captured).
let currentLocale: SupportedLocale = DEFAULT_ECOSYSTEM_LOCALE;
const App = createActiveApp({ const App = createActiveApp({
lang: {
schema: translations,
defaultLocale: DEFAULT_ECOSYSTEM_LOCALE,
fallbackChain: ['en-US']
},
logger: { logger: {
level: LogLevel.TRACE, level: LogLevel.TRACE,
globalContext: { surface: DEMO_PATH }, globalContext: { surface: DEMO_PATH },
transports: [captureTransport] transports: [captureTransport]
}, },
storage: { services: {
adapter: createMemoryAdapter(), lang: defineActiveLang({
namespace: 'test-ecosystem' schema: translations,
}, defaultLocale: DEFAULT_ECOSYSTEM_LOCALE,
frontend: { fallbackChain: ['en-US']
target: () => appRoot,
theme: 'base',
mode: 'auto',
density: 'comfortable'
},
http: {
fetch: demoFetch,
timeout: 0,
retry: { limit: 0 }
},
cache: {
defaultMemoryAdapter: { suppressProductionWarning: true },
scopeResolver: (): ResolvedScopeValues => ({
tenantId: data.sessionSeed.tenantId,
actorId: data.sessionSeed.actorId,
permissionHash: `${actorRole}:${mfaEnabled ? AUTH_AAL.MULTI_FACTOR : AUTH_AAL.SINGLE_FACTOR}`,
locale: App.getLocale()
}), }),
policies: { storage: defineActiveStorage({
[CACHE_POLICY_INTERACTIVE]: { adapter: createMemoryAdapter(),
freshFor: 1_500, namespace: 'test-ecosystem'
staleFor: 20_000, }),
staleIfErrorFor: 60_000, frontend: defineActiveFrontend({
gcAfter: 90_000, target: () => appRoot,
mode: CACHE_READ_MODE_STALE_WHILE_REVALIDATE, theme: 'base',
persist: true mode: 'auto',
density: 'comfortable'
}),
http: defineEngineHttp({
fetch: demoFetch,
timeout: 0,
retry: { limit: 0 }
}),
cache: defineActiveCache({
defaultMemoryAdapter: { suppressProductionWarning: true },
scopeResolver: (): ResolvedScopeValues => ({
tenantId: data.sessionSeed.tenantId,
actorId: data.sessionSeed.actorId,
permissionHash: `${actorRole}:${mfaEnabled ? AUTH_AAL.MULTI_FACTOR : AUTH_AAL.SINGLE_FACTOR}`,
locale: currentLocale
}),
policies: {
[CACHE_POLICY_INTERACTIVE]: {
freshFor: 1_500,
staleFor: 20_000,
staleIfErrorFor: 60_000,
gcAfter: 90_000,
mode: CACHE_READ_MODE_STALE_WHILE_REVALIDATE,
persist: true
}
} }
} }),
}, connections: defineActiveConnections({
connections: { defaults: {
defaults: { heartbeat: false,
heartbeat: false, reconnect: false
reconnect: false }
} }),
sium: defineEngineSium(),
auth: defineActiveAuth({ initial: initialData.authCurrent }),
session: defineActiveSession<DemoUser, undefined, DemoSessionData>({
storage: {
adapter: createMemoryAdapter(),
key: SESSION_STORAGE_KEY
}
}),
perm: defineActivePerm({
endpoint: PERM_ENDPOINT,
cacheTtlMs: 8_000,
scopeKey: () => `${data.sessionSeed.actorId}:${actorRole}:${mfaEnabled}`
})
} }
}); });
const Sium = App.createSiumEngine(); const Sium = App.sium;
const projectDraftSchema = Sium.object({ const projectDraftSchema = Sium.object({
title: Sium.pipe(Sium.string(), Sium.min(4), Sium.meta({ label: 'Title' })), title: Sium.pipe(Sium.string(), Sium.min(4), Sium.meta({ label: 'Title' })),
budget: Sium.pipe(Sium.number(), Sium.min(1000), Sium.meta({ label: 'Budget' })) budget: Sium.pipe(Sium.number(), Sium.min(1000), Sium.meta({ label: 'Budget' }))
@ -422,13 +454,8 @@
{ mergeDefaults: true } { mergeDefaults: true }
); );
const Auth = App.createActiveAuth({ initial: initialData.authCurrent }); const Auth = App.auth;
const Sess = App.createActiveSession<DemoUser, undefined, DemoSessionData>({ const Sess = App.session;
storage: {
adapter: createMemoryAdapter(),
key: SESSION_STORAGE_KEY
}
});
Sess.adoptServer({ Sess.adoptServer({
user: { user: {
id: initialData.sessionSeed.actorId, id: initialData.sessionSeed.actorId,
@ -444,13 +471,7 @@
expiresAt: initialData.sessionSeed.expiresAt expiresAt: initialData.sessionSeed.expiresAt
}); });
const Perms = setPermsContext( const Perms = setPermsContext(App.perm);
App.createActivePerms({
endpoint: PERM_ENDPOINT,
cacheTtlMs: 8_000,
scopeKey: () => `${data.sessionSeed.actorId}:${actorRole}:${mfaEnabled}`
})
);
const transport = createMockTransport(); const transport = createMockTransport();
const loggingSerializer: ConnectionSerializer<ConnectionFrame> = { const loggingSerializer: ConnectionSerializer<ConnectionFrame> = {
@ -501,7 +522,7 @@
key: `${issue.path.join('.')}:${issue.code}`, key: `${issue.path.join('.')}:${issue.code}`,
path: issue.path.length === 0 ? '<root>' : issue.path.join('.'), path: issue.path.length === 0 ? '<root>' : issue.path.join('.'),
code: issue.code, code: issue.code,
message: Sium.resolveIssue(issue, App.getLocale()) message: Sium.resolveIssue(issue, App.lang.getLocale())
})) }))
); );
@ -509,7 +530,7 @@
void App.lang.getLocale(); void App.lang.getLocale();
void App.frontend.getTheme(); void App.frontend.getTheme();
return { return {
locale: App.getLocale(), locale: App.lang.getLocale(),
dir: App.frontend.getDir(), dir: App.frontend.getDir(),
theme: App.frontend.getTheme(), theme: App.frontend.getTheme(),
mode: App.frontend.getMode(), mode: App.frontend.getMode(),
@ -552,7 +573,8 @@
function setLocale(locale: SupportedLocale): void { function setLocale(locale: SupportedLocale): void {
selectedLocale = locale; selectedLocale = locale;
App.setLocale(locale); currentLocale = locale;
App.lang.setLocale(locale);
pushEvent('lang', `locale -> ${locale}`); pushEvent('lang', `locale -> ${locale}`);
} }

@ -369,7 +369,7 @@
<aside class="resolved"> <aside class="resolved">
<h3>Resolved issue messages</h3> <h3>Resolved issue messages</h3>
<p class="hint"> <p class="hint">
<code>App.createSiumEngine().resolveIssues(r.issues)</code> — the <code>App.sium.resolveIssues(r.issues)</code> — the
raw <code>#?sium.errors.type|...</code> refs above become readable raw <code>#?sium.errors.type|...</code> refs above become readable
text via Sium's lang resolver (here lang-less, so the text via Sium's lang resolver (here lang-less, so the
<code>|fallback</code> text is interpolated with each issue's <code>|fallback</code> text is interpolated with each issue's

@ -1,6 +0,0 @@
/**
* Skip static prerender for the legacy http demo. The page still
* relies on pre-service-schema App methods that the current runtime
* no longer exposes; render it on-demand only.
*/
export const prerender = false;

@ -2,6 +2,11 @@
import { onDestroy, untrack } from 'svelte'; import { onDestroy, untrack } from 'svelte';
import { resolve } from '$app/paths'; import { resolve } from '$app/paths';
import { createActiveApp } from '$active-app'; import { createActiveApp } from '$active-app';
import {
defineActiveCache,
defineActivePerm,
defineEngineHttp
} from '$active-app/services';
import { LogLevel } from '$logger'; import { LogLevel } from '$logger';
import Can from '$perm/Can.svelte'; import Can from '$perm/Can.svelte';
import { import {
@ -267,21 +272,23 @@
const App = createActiveApp({ const App = createActiveApp({
logger: { level: LogLevel.NONE, transports: [] }, logger: { level: LogLevel.NONE, transports: [] },
http: { services: {
fetch: permFetch, http: defineEngineHttp({
timeout: 0, fetch: permFetch,
retry: { limit: 0 } timeout: 0,
}, retry: { limit: 0 }
cache: { }),
defaultMemoryAdapter: { suppressProductionWarning: true } cache: defineActiveCache({
}, defaultMemoryAdapter: { suppressProductionWarning: true }
permissions: { }),
endpoint: PERM_ENDPOINT, perm: defineActivePerm({
cacheTtlMs: 10_000 endpoint: PERM_ENDPOINT,
cacheTtlMs: 10_000
})
} }
}); });
const Perms = setPermsContext(App.createActivePerms()); const Perms = setPermsContext(App.perm);
function effectClass(decision: PermDecision | null | undefined): string { function effectClass(decision: PermDecision | null | undefined): string {
if (!decision) return 'none'; if (!decision) return 'none';

@ -1,2 +0,0 @@
/** Skip static prerender — uses pre-service-schema App API. See /test/aapp. */
export const prerender = false;

@ -25,12 +25,7 @@
// `+page.ts`; excluding them here keeps `npm run check` green until they // `+page.ts`; excluding them here keeps `npm run check` green until they
// are migrated. The codex follow-up (audit-2 §3) tracks the migration. // are migrated. The codex follow-up (audit-2 §3) tracks the migration.
"./src/web/routes/demo/**", "./src/web/routes/demo/**",
"./src/web/routes/test/aapp/**",
"./src/web/routes/test/cach/**",
"./src/web/routes/test/conn/**",
"./src/web/routes/test/ecosystem/**", "./src/web/routes/test/ecosystem/**",
"./src/web/routes/test/http/**",
"./src/web/routes/test/perm/**",
"./src/web/routes/test/auth/**/*", "./src/web/routes/test/auth/**/*",
"./src/web/routes/test/auth/+page.server.ts", "./src/web/routes/test/auth/+page.server.ts",
"./src/web/routes/active/_data/artifact-docs.ts", "./src/web/routes/active/_data/artifact-docs.ts",

Loading…
Cancel
Save

Powered by TurnKey Linux.