You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

172 lines
5.0 KiB

/**
* `createDatingApp` — opinionated `createActiveApp` composition for the
* Nexo demo. Picks a fixed service schema so consumer components can
* type their `App` prop as `DatingApp` and get autocomplete on
* `App.lang`, `App.session`, `App.cache`, … without falling back to
* `unknown`.
*
* Contract intentionally narrower than the README's full list: `auth`,
* `perm` and `connection` need port wiring that lives outside Phase 0
* scope (auth flows go through `DatingApiClient` directly while the
* artifact-level `App.auth` waits for a Nexo-shaped HTTP port). They
* will be added by their respective phases — see
* `src/web/routes/dating/plan-implementacion.md`.
*/
import { createActiveApp, type ActiveApp } from '$active-app';
import {
defineActiveCache,
defineActiveFormat,
defineActiveFrontend,
defineActiveLang,
defineActivePrefs,
defineActiveSession,
defineActiveStorage,
defineEngineHttp,
defineEngineSium
} from '$active-app/services';
import type { PrefsCapabilities } from '$libs/prefs';
import type { DatingUser } from './types.ts';
import { createDatingApiClient, type DatingApiClient } from './api.ts';
/**
* Bootstrap lang schema. Phase 0 ships only the strings the shell
* needs; later phases register their own namespaces via
* `App.lang.extend(...)` rather than threading more keys through
* here.
*/
export const NEXO_LANG_SCHEMA = {
nexo: {
title: { es: 'Nexo', en: 'Nexo' },
tagline: {
es: 'Demo del ecosistema Active sobre un producto real.',
en: 'Active ecosystem demo running on a real product.'
},
nav: {
discover: { es: 'Descubre', en: 'Discover' },
matches: { es: 'Matches', en: 'Matches' },
profile: { es: 'Perfil', en: 'Profile' },
safety: { es: 'Seguridad', en: 'Safety' },
devtools: { es: 'Devtools', en: 'Devtools' }
},
auth: {
login: { es: 'Iniciar sesión', en: 'Sign in' },
register: { es: 'Crear cuenta', en: 'Sign up' },
logout: { es: 'Cerrar sesión', en: 'Sign out' }
}
}
} as const;
export const NEXO_PREFS_CAPABILITIES: PrefsCapabilities = {
languages: ['es', 'en'],
locales: ['es-ES', 'en-US'],
currencies: ['EUR', 'USD'],
unitSystems: ['metric', 'imperial'],
themes: ['light', 'dark', 'system'],
densities: ['compact', 'comfortable', 'spacious'],
motions: ['allow', 'reduce', 'system'],
defaults: {
language: 'es',
locale: 'es-ES',
currency: 'EUR',
timezone: 'Europe/Madrid',
unitSystem: 'metric',
theme: 'light',
density: 'comfortable',
motion: 'allow',
direction: 'ltr'
}
};
export interface CreateDatingAppOptions {
/**
* Override the Nexo API base URL. Defaults to the standalone server
* at `http://127.0.0.1:8787` (see `servers/dating/README.md`).
*/
readonly apiBase?: string;
/**
* Optional `fetch` override — typically `event.fetch` during SSR or
* a mocked transport in tests.
*/
readonly fetch?: typeof fetch;
/**
* Frontend mount target. SvelteKit pages pass the layout root here
* so theme / density / direction get applied to the right node.
*/
readonly frontendTarget?: () => HTMLElement | null;
}
/**
* Concrete `ActiveApp` shape exposed by `createDatingApp`. Components
* accept `App: DatingApp` to keep autocomplete and type-checked
* service access; using a generic `ActiveApp` here would degrade
* every service slot to `unknown`.
*/
export type DatingApp = ReturnType<typeof composeApp>;
function composeApp(options: CreateDatingAppOptions) {
return createActiveApp({
services: {
lang: defineActiveLang({
schema: NEXO_LANG_SCHEMA,
defaultLocale: 'es',
fallbackChain: ['en']
}),
prefs: defineActivePrefs({ capabilities: NEXO_PREFS_CAPABILITIES }),
storage: defineActiveStorage({ namespace: 'nexo' }),
frontend: defineActiveFrontend({
target: options.frontendTarget,
theme: 'base',
mode: 'auto',
density: 'comfortable'
}),
format: defineActiveFormat(),
cache: defineActiveCache({
defaultMemoryAdapter: { suppressProductionWarning: true }
}),
http: defineEngineHttp({
fetch: options.fetch,
timeout: 10_000,
retry: { limit: 1 }
}),
session: defineActiveSession<DatingUser>({ schemas: {} }),
sium: defineEngineSium()
}
});
}
/**
* Compose a `DatingApp` plus the Nexo HTTP client. The two are
* returned together because almost every component needs both —
* `App` for ecosystem state and `api` for typed server calls — and
* keeping the construction in one helper means the demo only has one
* canonical wiring point.
*
* Disposing the wrapper tears the App down; the API client is
* stateless and needs no lifecycle.
*/
export interface DatingAppHandle {
readonly App: DatingApp;
readonly api: DatingApiClient;
dispose(): void;
}
export function createDatingApp(options: CreateDatingAppOptions = {}): DatingAppHandle {
const App = composeApp(options);
const api = createDatingApiClient({
base: options.apiBase,
fetch: options.fetch
});
return {
App,
api,
dispose: () => App.dispose()
};
}
/**
* Marker re-export so consumers that only need the type don't pull
* the runtime in just for `ActiveApp`.
*/
export type { ActiveApp };

Powered by TurnKey Linux.