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.

55 lines
2.1 KiB

Nexo P0 — dating client bootstrap: types, API client, app factory, shell Phase 0 of the Nexo demo (`src/web/routes/dating/plan-implementacion.md`). The standalone server lives at `servers/dating` and is already running; this commit lands the SvelteKit client that consumes it. `_lib/types.ts` — public contracts mirroring the `*View` functions in `servers/dating/domain.mjs`: `DatingUser`, `DatingProfile`, `DatingMatch`, `DatingMessage`, `DatingReport` plus enums (`DatingRole`, `DatingStatus`, `DatingIntent`, …) and the structured event names (`DATING_EVENTS.AUTH_LOGIN`, …) the bus and orca will publish later. Request/response payloads are split into dedicated interfaces (`DatingRegisterInput`, `DatingDiscoverFilters`, `DatingLikeResponse`, …) so the API client doesn't grow ad-hoc shapes. `_lib/api.ts` — typed wrapper over the standalone server. Always sends `credentials: 'include'` (the dating session is an HTTP-only `dating_session` cookie) and normalises error responses into a `DatingApiError` carrying the server's structured `code` (e.g. `weak_password`, `email_already_used`) so UI branches on a stable identifier instead of message strings. Network / abort failures surface as the same class with `code: 'network_error'`. The factory takes `fetch` + `signal` overrides for SSR (`event.fetch`) and tests. `_lib/app.ts` — `createDatingApp()` opinionated `createActiveApp` composition that fixes the service schema (lang, prefs, frontend, storage, format, cache, http, session, sium) so consumer components can type their `App` prop as `DatingApp` and get autocomplete on every slot. `auth`, `perm`, and `connection` are intentionally not in this commit — they need port-level wiring (an `AuthClientHttpPort` against the Nexo endpoints, a perm endpoint, the connection transport) that belongs to Phases 2 / 4. The factory returns `{ App, api, dispose }` so callers don't have to compose the App and the HTTP client separately. `_lib/context.ts` — symbol-keyed `setNexoApp` / `getNexoApp` bridge so the layout sets the handle once and nested pages retrieve it without rebuilding `createActiveApp`. `+layout.ts` — `prerender = false` for the entire `/dating` subtree. The demo authenticates via cookies against a runtime-only server, so static prerender doesn't make sense. `+layout.svelte` — Nexo shell: instantiates `DatingApp` once, wires the frontend `target`, eagerly probes `/api/session`, renders the top nav (Discover, Matches, Profile, Safety, Devtools) with auth state on the right, and shows an offline banner pointing to `npm run dating:server` when the API can't be reached. Disposes the App on unmount. `+page.svelte` — landing card grid that links every route the plan will materialise (Auth & Session / Profile / Discover & Match / Safety & Moderación / Diagnóstico). Plain hrefs (not the typed `resolve(...)`) so the page compiles ahead of the targets being created — the links 404 until each phase lands its `+page.svelte`. Gates: 1695 tests + check (0 errors) + build + bundle smoke (22.52 KB gzip) + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
/**
Nexo P1+P2 — user-facing surface: auth, onboarding, discover, matches, chat, profile Builds the actual product the user sees, on top of the P0 bootstrap. The previous landing was a phase-card grid for navigating the plan; that's been replaced with a real auth-aware router and the routes the cards used to point at now exist as functional pages. Layout / context (`_lib/context.ts`, `+layout.svelte`): - Layout owns the session cell. `getNexoContext()` exposes `session()` / `setSession()` / `refreshSession()` so pages don't each issue their own `/api/session` and so a successful `login` / `register` propagates the user to every gate without a round-trip. - Logout clears the cell even when `api.logout()` rejects, so the UI never gets stuck on "authenticated" after a server hiccup. Landing (`+page.svelte`): - Pure router: anonymous → `/dating/login`, authenticated without a completed profile → `/dating/onboarding`, otherwise → `/dating/discover`. Renders a minimal pulse during the initial session probe. Auth (`login`, `register`, `reset`): - `AuthLayout` + `Field` shared shell so every form has the same centered card, focus ring and per-field error wiring. - Login maps server error codes (`invalid_credentials`, `account_restricted`, `mfa_required`) to localised messages and redirects to `/dating/onboarding` when the user has no profile yet. - Register validates locally (display name, email shape, ≥8-char password, password match, adult + rules confirm) and surfaces server codes (`email_already_used`, `weak_password`, `password_mismatch`, `adult_confirmation_required`) on the right field. Live password-strength meter. - Reset is anti-enumeration: same UI whether the email exists or not (matches the contract documented in `auth-y-fotos.md`). Onboarding (`/dating/onboarding`): - Four-step flow (Identity → About → Preferences → Review) with a progress stepper. Each step validates its own fields; clicking "Publicar" re-runs validation across every step so the user lands back on the broken one if anything regressed. On success the page PUTs `/api/profile/me` with `completed: true` and refreshes the session cell so guards downstream see the published profile. Discover (`/dating/discover`): - Filter sidebar (intent + min/max age) + card stack of profiles. - `ProfileCard` component renders the canonical profile card (photo, name + age, intent pill, location, bio, interests). - Like / pass actions call `/api/likes`; a match opens a small modal offering "Open chat" → `/dating/chat/{id}`. - Auto-fetches the next page when the queue drops to two cards. Matches (`/dating/matches`): - List of active matches with avatar, name, last update, and the start of the counterpart's bio. Each row links to the chat thread. Chat (`/dating/chat/[matchId]`): - Header with counterpart name + match state, scrollable timeline, composer at the bottom. - Optimistic send: the message lands as `queued` immediately, swapped for the server response on success. On failure the bubble flips to `failed` with retry/discard inline actions. Each provisional message carries a `clientNonce` for idempotency on the server side. Profile editor (`/dating/profile`): - Full-form editor mirroring onboarding's fields plus visibility. Tracks dirty state, surfaces server errors per-field, refreshes the session cell after saving so the cache and Discover stay coherent. - Header links to the photo manager. Photo manager (`/dating/profile/photos`): - Drop-zone + click-to-upload. Client validates format (JPEG/PNG/WebP), 5 MB cap, and the 6-photo ceiling before sending. Photos render in a grid with primary badge, order pills and per-photo actions (set primary, move up/down, delete). Each mutation reuses the API client and re-syncs the session cell. All pages use the typed `DatingApiClient` from P0 — no ad-hoc fetch calls — and route through `getNexoContext()` so the session is the single source of truth across the demo. Gates: 1695 tests + check (0 errors / 0 warnings) + build + bundle smoke (22.52 KB gzip) + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
* Svelte context bridge for the Nexo runtime: the `DatingAppHandle`
* (App + API) plus a session cell owned by the shell layout. Nested
* pages and components consume both via `getNexoContext()` instead of
* each rebuilding their own.
Nexo P0 — dating client bootstrap: types, API client, app factory, shell Phase 0 of the Nexo demo (`src/web/routes/dating/plan-implementacion.md`). The standalone server lives at `servers/dating` and is already running; this commit lands the SvelteKit client that consumes it. `_lib/types.ts` — public contracts mirroring the `*View` functions in `servers/dating/domain.mjs`: `DatingUser`, `DatingProfile`, `DatingMatch`, `DatingMessage`, `DatingReport` plus enums (`DatingRole`, `DatingStatus`, `DatingIntent`, …) and the structured event names (`DATING_EVENTS.AUTH_LOGIN`, …) the bus and orca will publish later. Request/response payloads are split into dedicated interfaces (`DatingRegisterInput`, `DatingDiscoverFilters`, `DatingLikeResponse`, …) so the API client doesn't grow ad-hoc shapes. `_lib/api.ts` — typed wrapper over the standalone server. Always sends `credentials: 'include'` (the dating session is an HTTP-only `dating_session` cookie) and normalises error responses into a `DatingApiError` carrying the server's structured `code` (e.g. `weak_password`, `email_already_used`) so UI branches on a stable identifier instead of message strings. Network / abort failures surface as the same class with `code: 'network_error'`. The factory takes `fetch` + `signal` overrides for SSR (`event.fetch`) and tests. `_lib/app.ts` — `createDatingApp()` opinionated `createActiveApp` composition that fixes the service schema (lang, prefs, frontend, storage, format, cache, http, session, sium) so consumer components can type their `App` prop as `DatingApp` and get autocomplete on every slot. `auth`, `perm`, and `connection` are intentionally not in this commit — they need port-level wiring (an `AuthClientHttpPort` against the Nexo endpoints, a perm endpoint, the connection transport) that belongs to Phases 2 / 4. The factory returns `{ App, api, dispose }` so callers don't have to compose the App and the HTTP client separately. `_lib/context.ts` — symbol-keyed `setNexoApp` / `getNexoApp` bridge so the layout sets the handle once and nested pages retrieve it without rebuilding `createActiveApp`. `+layout.ts` — `prerender = false` for the entire `/dating` subtree. The demo authenticates via cookies against a runtime-only server, so static prerender doesn't make sense. `+layout.svelte` — Nexo shell: instantiates `DatingApp` once, wires the frontend `target`, eagerly probes `/api/session`, renders the top nav (Discover, Matches, Profile, Safety, Devtools) with auth state on the right, and shows an offline banner pointing to `npm run dating:server` when the API can't be reached. Disposes the App on unmount. `+page.svelte` — landing card grid that links every route the plan will materialise (Auth & Session / Profile / Discover & Match / Safety & Moderación / Diagnóstico). Plain hrefs (not the typed `resolve(...)`) so the page compiles ahead of the targets being created — the links 404 until each phase lands its `+page.svelte`. Gates: 1695 tests + check (0 errors) + build + bundle smoke (22.52 KB gzip) + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
*
Nexo P1+P2 — user-facing surface: auth, onboarding, discover, matches, chat, profile Builds the actual product the user sees, on top of the P0 bootstrap. The previous landing was a phase-card grid for navigating the plan; that's been replaced with a real auth-aware router and the routes the cards used to point at now exist as functional pages. Layout / context (`_lib/context.ts`, `+layout.svelte`): - Layout owns the session cell. `getNexoContext()` exposes `session()` / `setSession()` / `refreshSession()` so pages don't each issue their own `/api/session` and so a successful `login` / `register` propagates the user to every gate without a round-trip. - Logout clears the cell even when `api.logout()` rejects, so the UI never gets stuck on "authenticated" after a server hiccup. Landing (`+page.svelte`): - Pure router: anonymous → `/dating/login`, authenticated without a completed profile → `/dating/onboarding`, otherwise → `/dating/discover`. Renders a minimal pulse during the initial session probe. Auth (`login`, `register`, `reset`): - `AuthLayout` + `Field` shared shell so every form has the same centered card, focus ring and per-field error wiring. - Login maps server error codes (`invalid_credentials`, `account_restricted`, `mfa_required`) to localised messages and redirects to `/dating/onboarding` when the user has no profile yet. - Register validates locally (display name, email shape, ≥8-char password, password match, adult + rules confirm) and surfaces server codes (`email_already_used`, `weak_password`, `password_mismatch`, `adult_confirmation_required`) on the right field. Live password-strength meter. - Reset is anti-enumeration: same UI whether the email exists or not (matches the contract documented in `auth-y-fotos.md`). Onboarding (`/dating/onboarding`): - Four-step flow (Identity → About → Preferences → Review) with a progress stepper. Each step validates its own fields; clicking "Publicar" re-runs validation across every step so the user lands back on the broken one if anything regressed. On success the page PUTs `/api/profile/me` with `completed: true` and refreshes the session cell so guards downstream see the published profile. Discover (`/dating/discover`): - Filter sidebar (intent + min/max age) + card stack of profiles. - `ProfileCard` component renders the canonical profile card (photo, name + age, intent pill, location, bio, interests). - Like / pass actions call `/api/likes`; a match opens a small modal offering "Open chat" → `/dating/chat/{id}`. - Auto-fetches the next page when the queue drops to two cards. Matches (`/dating/matches`): - List of active matches with avatar, name, last update, and the start of the counterpart's bio. Each row links to the chat thread. Chat (`/dating/chat/[matchId]`): - Header with counterpart name + match state, scrollable timeline, composer at the bottom. - Optimistic send: the message lands as `queued` immediately, swapped for the server response on success. On failure the bubble flips to `failed` with retry/discard inline actions. Each provisional message carries a `clientNonce` for idempotency on the server side. Profile editor (`/dating/profile`): - Full-form editor mirroring onboarding's fields plus visibility. Tracks dirty state, surfaces server errors per-field, refreshes the session cell after saving so the cache and Discover stay coherent. - Header links to the photo manager. Photo manager (`/dating/profile/photos`): - Drop-zone + click-to-upload. Client validates format (JPEG/PNG/WebP), 5 MB cap, and the 6-photo ceiling before sending. Photos render in a grid with primary badge, order pills and per-photo actions (set primary, move up/down, delete). Each mutation reuses the API client and re-syncs the session cell. All pages use the typed `DatingApiClient` from P0 — no ad-hoc fetch calls — and route through `getNexoContext()` so the session is the single source of truth across the demo. Gates: 1695 tests + check (0 errors / 0 warnings) + build + bundle smoke (22.52 KB gzip) + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
* Symbol-keyed (instead of `setContext('app', ...)`) so it can't
* collide with whatever else the surrounding SvelteKit app registers.
*
* `session` is exposed as a getter — pages call `nexo.session()` so
* `$derived(...)` and `$effect(...)` track session-aware UI without
* each page re-issuing its own `/api/session` probe.
Nexo P0 — dating client bootstrap: types, API client, app factory, shell Phase 0 of the Nexo demo (`src/web/routes/dating/plan-implementacion.md`). The standalone server lives at `servers/dating` and is already running; this commit lands the SvelteKit client that consumes it. `_lib/types.ts` — public contracts mirroring the `*View` functions in `servers/dating/domain.mjs`: `DatingUser`, `DatingProfile`, `DatingMatch`, `DatingMessage`, `DatingReport` plus enums (`DatingRole`, `DatingStatus`, `DatingIntent`, …) and the structured event names (`DATING_EVENTS.AUTH_LOGIN`, …) the bus and orca will publish later. Request/response payloads are split into dedicated interfaces (`DatingRegisterInput`, `DatingDiscoverFilters`, `DatingLikeResponse`, …) so the API client doesn't grow ad-hoc shapes. `_lib/api.ts` — typed wrapper over the standalone server. Always sends `credentials: 'include'` (the dating session is an HTTP-only `dating_session` cookie) and normalises error responses into a `DatingApiError` carrying the server's structured `code` (e.g. `weak_password`, `email_already_used`) so UI branches on a stable identifier instead of message strings. Network / abort failures surface as the same class with `code: 'network_error'`. The factory takes `fetch` + `signal` overrides for SSR (`event.fetch`) and tests. `_lib/app.ts` — `createDatingApp()` opinionated `createActiveApp` composition that fixes the service schema (lang, prefs, frontend, storage, format, cache, http, session, sium) so consumer components can type their `App` prop as `DatingApp` and get autocomplete on every slot. `auth`, `perm`, and `connection` are intentionally not in this commit — they need port-level wiring (an `AuthClientHttpPort` against the Nexo endpoints, a perm endpoint, the connection transport) that belongs to Phases 2 / 4. The factory returns `{ App, api, dispose }` so callers don't have to compose the App and the HTTP client separately. `_lib/context.ts` — symbol-keyed `setNexoApp` / `getNexoApp` bridge so the layout sets the handle once and nested pages retrieve it without rebuilding `createActiveApp`. `+layout.ts` — `prerender = false` for the entire `/dating` subtree. The demo authenticates via cookies against a runtime-only server, so static prerender doesn't make sense. `+layout.svelte` — Nexo shell: instantiates `DatingApp` once, wires the frontend `target`, eagerly probes `/api/session`, renders the top nav (Discover, Matches, Profile, Safety, Devtools) with auth state on the right, and shows an offline banner pointing to `npm run dating:server` when the API can't be reached. Disposes the App on unmount. `+page.svelte` — landing card grid that links every route the plan will materialise (Auth & Session / Profile / Discover & Match / Safety & Moderación / Diagnóstico). Plain hrefs (not the typed `resolve(...)`) so the page compiles ahead of the targets being created — the links 404 until each phase lands its `+page.svelte`. Gates: 1695 tests + check (0 errors) + build + bundle smoke (22.52 KB gzip) + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
*/
import { getContext, setContext } from 'svelte';
import type { DatingAppHandle } from './app.ts';
Nexo client — WebSocket subscription + visibility pass on the auth + product surfaces Realtime (`_lib/realtime.ts` + layout integration): - `connectDatingRealtime(...)` opens a single WebSocket against `/api/realtime` (URL derived from the same hostname as the page so `SameSite=Lax` lets the session cookie travel during upgrade) and exposes a `subscribe(listener)` surface plus exponential-backoff reconnect (cap 30 s). - The shell layout owns the connection. `setNexoContext` exposes `realtime()` so any nested page can `subscribe(...)` against the same socket — no per-page connection storms. - Chat replaces its 3-second polling with a `dating.message.created` subscription. Optimistic bubbles reconcile by `clientNonce`; remote messages append and only auto-scroll when the user was already pinned to the bottom. - Matches replaces its poll loop with the same subscription — receiving a push is the cue to re-issue `/api/matches` so the "nuevo" badge appears next to the changed row. Visibility pass: - Primary buttons across login / register / reset / onboarding / profile / chat / matches / discover use the brand pink (#c44a78) with white text instead of `color-mix(currentColor 88%, transparent)`, which collapsed to an invisible "fog" on dark themes where `currentColor` and `canvas` were too close in luminance. - AuthLayout footer links are now styled as branded actions (pink, weight 600, hover underline) with a divider above so "¿Has olvidado la contraseña?" / "Crear cuenta" stop reading as decorative text. - Field component bumps input border contrast from 22% currentColor to 38% so the field outline is visible without focus. - Matches list gains a brand-pink badge + tinted row background for unread conversations; clicking the row marks it seen so the badge clears for the next push. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
import type { DatingRealtimeHandle } from './realtime.ts';
Nexo P1+P2 — user-facing surface: auth, onboarding, discover, matches, chat, profile Builds the actual product the user sees, on top of the P0 bootstrap. The previous landing was a phase-card grid for navigating the plan; that's been replaced with a real auth-aware router and the routes the cards used to point at now exist as functional pages. Layout / context (`_lib/context.ts`, `+layout.svelte`): - Layout owns the session cell. `getNexoContext()` exposes `session()` / `setSession()` / `refreshSession()` so pages don't each issue their own `/api/session` and so a successful `login` / `register` propagates the user to every gate without a round-trip. - Logout clears the cell even when `api.logout()` rejects, so the UI never gets stuck on "authenticated" after a server hiccup. Landing (`+page.svelte`): - Pure router: anonymous → `/dating/login`, authenticated without a completed profile → `/dating/onboarding`, otherwise → `/dating/discover`. Renders a minimal pulse during the initial session probe. Auth (`login`, `register`, `reset`): - `AuthLayout` + `Field` shared shell so every form has the same centered card, focus ring and per-field error wiring. - Login maps server error codes (`invalid_credentials`, `account_restricted`, `mfa_required`) to localised messages and redirects to `/dating/onboarding` when the user has no profile yet. - Register validates locally (display name, email shape, ≥8-char password, password match, adult + rules confirm) and surfaces server codes (`email_already_used`, `weak_password`, `password_mismatch`, `adult_confirmation_required`) on the right field. Live password-strength meter. - Reset is anti-enumeration: same UI whether the email exists or not (matches the contract documented in `auth-y-fotos.md`). Onboarding (`/dating/onboarding`): - Four-step flow (Identity → About → Preferences → Review) with a progress stepper. Each step validates its own fields; clicking "Publicar" re-runs validation across every step so the user lands back on the broken one if anything regressed. On success the page PUTs `/api/profile/me` with `completed: true` and refreshes the session cell so guards downstream see the published profile. Discover (`/dating/discover`): - Filter sidebar (intent + min/max age) + card stack of profiles. - `ProfileCard` component renders the canonical profile card (photo, name + age, intent pill, location, bio, interests). - Like / pass actions call `/api/likes`; a match opens a small modal offering "Open chat" → `/dating/chat/{id}`. - Auto-fetches the next page when the queue drops to two cards. Matches (`/dating/matches`): - List of active matches with avatar, name, last update, and the start of the counterpart's bio. Each row links to the chat thread. Chat (`/dating/chat/[matchId]`): - Header with counterpart name + match state, scrollable timeline, composer at the bottom. - Optimistic send: the message lands as `queued` immediately, swapped for the server response on success. On failure the bubble flips to `failed` with retry/discard inline actions. Each provisional message carries a `clientNonce` for idempotency on the server side. Profile editor (`/dating/profile`): - Full-form editor mirroring onboarding's fields plus visibility. Tracks dirty state, surfaces server errors per-field, refreshes the session cell after saving so the cache and Discover stay coherent. - Header links to the photo manager. Photo manager (`/dating/profile/photos`): - Drop-zone + click-to-upload. Client validates format (JPEG/PNG/WebP), 5 MB cap, and the 6-photo ceiling before sending. Photos render in a grid with primary badge, order pills and per-photo actions (set primary, move up/down, delete). Each mutation reuses the API client and re-syncs the session cell. All pages use the typed `DatingApiClient` from P0 — no ad-hoc fetch calls — and route through `getNexoContext()` so the session is the single source of truth across the demo. Gates: 1695 tests + check (0 errors / 0 warnings) + build + bundle smoke (22.52 KB gzip) + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
import type { DatingSessionResponse } from './types.ts';
export interface NexoContext extends DatingAppHandle {
/** Current session snapshot. `null` while the initial probe is in flight. */
session(): DatingSessionResponse | null;
/** Replace the session cell after a login / logout / refresh. */
setSession(next: DatingSessionResponse | null): void;
/** Re-issue `/api/session`; layout uses it on mount, pages on demand. */
refreshSession(): Promise<DatingSessionResponse | null>;
Nexo client — WebSocket subscription + visibility pass on the auth + product surfaces Realtime (`_lib/realtime.ts` + layout integration): - `connectDatingRealtime(...)` opens a single WebSocket against `/api/realtime` (URL derived from the same hostname as the page so `SameSite=Lax` lets the session cookie travel during upgrade) and exposes a `subscribe(listener)` surface plus exponential-backoff reconnect (cap 30 s). - The shell layout owns the connection. `setNexoContext` exposes `realtime()` so any nested page can `subscribe(...)` against the same socket — no per-page connection storms. - Chat replaces its 3-second polling with a `dating.message.created` subscription. Optimistic bubbles reconcile by `clientNonce`; remote messages append and only auto-scroll when the user was already pinned to the bottom. - Matches replaces its poll loop with the same subscription — receiving a push is the cue to re-issue `/api/matches` so the "nuevo" badge appears next to the changed row. Visibility pass: - Primary buttons across login / register / reset / onboarding / profile / chat / matches / discover use the brand pink (#c44a78) with white text instead of `color-mix(currentColor 88%, transparent)`, which collapsed to an invisible "fog" on dark themes where `currentColor` and `canvas` were too close in luminance. - AuthLayout footer links are now styled as branded actions (pink, weight 600, hover underline) with a divider above so "¿Has olvidado la contraseña?" / "Crear cuenta" stop reading as decorative text. - Field component bumps input border contrast from 22% currentColor to 38% so the field outline is visible without focus. - Matches list gains a brand-pink badge + tinted row background for unread conversations; clicking the row marks it seen so the badge clears for the next push. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
/**
* Shared WebSocket subscription to `/api/realtime`. Pages call
* `realtime().subscribe(...)` to react to server-pushed events
* (new chat messages, match updates, …). The layout owns the
* single underlying connection.
*/
realtime(): DatingRealtimeHandle;
Nexo P1+P2 — user-facing surface: auth, onboarding, discover, matches, chat, profile Builds the actual product the user sees, on top of the P0 bootstrap. The previous landing was a phase-card grid for navigating the plan; that's been replaced with a real auth-aware router and the routes the cards used to point at now exist as functional pages. Layout / context (`_lib/context.ts`, `+layout.svelte`): - Layout owns the session cell. `getNexoContext()` exposes `session()` / `setSession()` / `refreshSession()` so pages don't each issue their own `/api/session` and so a successful `login` / `register` propagates the user to every gate without a round-trip. - Logout clears the cell even when `api.logout()` rejects, so the UI never gets stuck on "authenticated" after a server hiccup. Landing (`+page.svelte`): - Pure router: anonymous → `/dating/login`, authenticated without a completed profile → `/dating/onboarding`, otherwise → `/dating/discover`. Renders a minimal pulse during the initial session probe. Auth (`login`, `register`, `reset`): - `AuthLayout` + `Field` shared shell so every form has the same centered card, focus ring and per-field error wiring. - Login maps server error codes (`invalid_credentials`, `account_restricted`, `mfa_required`) to localised messages and redirects to `/dating/onboarding` when the user has no profile yet. - Register validates locally (display name, email shape, ≥8-char password, password match, adult + rules confirm) and surfaces server codes (`email_already_used`, `weak_password`, `password_mismatch`, `adult_confirmation_required`) on the right field. Live password-strength meter. - Reset is anti-enumeration: same UI whether the email exists or not (matches the contract documented in `auth-y-fotos.md`). Onboarding (`/dating/onboarding`): - Four-step flow (Identity → About → Preferences → Review) with a progress stepper. Each step validates its own fields; clicking "Publicar" re-runs validation across every step so the user lands back on the broken one if anything regressed. On success the page PUTs `/api/profile/me` with `completed: true` and refreshes the session cell so guards downstream see the published profile. Discover (`/dating/discover`): - Filter sidebar (intent + min/max age) + card stack of profiles. - `ProfileCard` component renders the canonical profile card (photo, name + age, intent pill, location, bio, interests). - Like / pass actions call `/api/likes`; a match opens a small modal offering "Open chat" → `/dating/chat/{id}`. - Auto-fetches the next page when the queue drops to two cards. Matches (`/dating/matches`): - List of active matches with avatar, name, last update, and the start of the counterpart's bio. Each row links to the chat thread. Chat (`/dating/chat/[matchId]`): - Header with counterpart name + match state, scrollable timeline, composer at the bottom. - Optimistic send: the message lands as `queued` immediately, swapped for the server response on success. On failure the bubble flips to `failed` with retry/discard inline actions. Each provisional message carries a `clientNonce` for idempotency on the server side. Profile editor (`/dating/profile`): - Full-form editor mirroring onboarding's fields plus visibility. Tracks dirty state, surfaces server errors per-field, refreshes the session cell after saving so the cache and Discover stay coherent. - Header links to the photo manager. Photo manager (`/dating/profile/photos`): - Drop-zone + click-to-upload. Client validates format (JPEG/PNG/WebP), 5 MB cap, and the 6-photo ceiling before sending. Photos render in a grid with primary badge, order pills and per-photo actions (set primary, move up/down, delete). Each mutation reuses the API client and re-syncs the session cell. All pages use the typed `DatingApiClient` from P0 — no ad-hoc fetch calls — and route through `getNexoContext()` so the session is the single source of truth across the demo. Gates: 1695 tests + check (0 errors / 0 warnings) + build + bundle smoke (22.52 KB gzip) + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
}
Nexo P0 — dating client bootstrap: types, API client, app factory, shell Phase 0 of the Nexo demo (`src/web/routes/dating/plan-implementacion.md`). The standalone server lives at `servers/dating` and is already running; this commit lands the SvelteKit client that consumes it. `_lib/types.ts` — public contracts mirroring the `*View` functions in `servers/dating/domain.mjs`: `DatingUser`, `DatingProfile`, `DatingMatch`, `DatingMessage`, `DatingReport` plus enums (`DatingRole`, `DatingStatus`, `DatingIntent`, …) and the structured event names (`DATING_EVENTS.AUTH_LOGIN`, …) the bus and orca will publish later. Request/response payloads are split into dedicated interfaces (`DatingRegisterInput`, `DatingDiscoverFilters`, `DatingLikeResponse`, …) so the API client doesn't grow ad-hoc shapes. `_lib/api.ts` — typed wrapper over the standalone server. Always sends `credentials: 'include'` (the dating session is an HTTP-only `dating_session` cookie) and normalises error responses into a `DatingApiError` carrying the server's structured `code` (e.g. `weak_password`, `email_already_used`) so UI branches on a stable identifier instead of message strings. Network / abort failures surface as the same class with `code: 'network_error'`. The factory takes `fetch` + `signal` overrides for SSR (`event.fetch`) and tests. `_lib/app.ts` — `createDatingApp()` opinionated `createActiveApp` composition that fixes the service schema (lang, prefs, frontend, storage, format, cache, http, session, sium) so consumer components can type their `App` prop as `DatingApp` and get autocomplete on every slot. `auth`, `perm`, and `connection` are intentionally not in this commit — they need port-level wiring (an `AuthClientHttpPort` against the Nexo endpoints, a perm endpoint, the connection transport) that belongs to Phases 2 / 4. The factory returns `{ App, api, dispose }` so callers don't have to compose the App and the HTTP client separately. `_lib/context.ts` — symbol-keyed `setNexoApp` / `getNexoApp` bridge so the layout sets the handle once and nested pages retrieve it without rebuilding `createActiveApp`. `+layout.ts` — `prerender = false` for the entire `/dating` subtree. The demo authenticates via cookies against a runtime-only server, so static prerender doesn't make sense. `+layout.svelte` — Nexo shell: instantiates `DatingApp` once, wires the frontend `target`, eagerly probes `/api/session`, renders the top nav (Discover, Matches, Profile, Safety, Devtools) with auth state on the right, and shows an offline banner pointing to `npm run dating:server` when the API can't be reached. Disposes the App on unmount. `+page.svelte` — landing card grid that links every route the plan will materialise (Auth & Session / Profile / Discover & Match / Safety & Moderación / Diagnóstico). Plain hrefs (not the typed `resolve(...)`) so the page compiles ahead of the targets being created — the links 404 until each phase lands its `+page.svelte`. Gates: 1695 tests + check (0 errors) + build + bundle smoke (22.52 KB gzip) + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
Nexo P1+P2 — user-facing surface: auth, onboarding, discover, matches, chat, profile Builds the actual product the user sees, on top of the P0 bootstrap. The previous landing was a phase-card grid for navigating the plan; that's been replaced with a real auth-aware router and the routes the cards used to point at now exist as functional pages. Layout / context (`_lib/context.ts`, `+layout.svelte`): - Layout owns the session cell. `getNexoContext()` exposes `session()` / `setSession()` / `refreshSession()` so pages don't each issue their own `/api/session` and so a successful `login` / `register` propagates the user to every gate without a round-trip. - Logout clears the cell even when `api.logout()` rejects, so the UI never gets stuck on "authenticated" after a server hiccup. Landing (`+page.svelte`): - Pure router: anonymous → `/dating/login`, authenticated without a completed profile → `/dating/onboarding`, otherwise → `/dating/discover`. Renders a minimal pulse during the initial session probe. Auth (`login`, `register`, `reset`): - `AuthLayout` + `Field` shared shell so every form has the same centered card, focus ring and per-field error wiring. - Login maps server error codes (`invalid_credentials`, `account_restricted`, `mfa_required`) to localised messages and redirects to `/dating/onboarding` when the user has no profile yet. - Register validates locally (display name, email shape, ≥8-char password, password match, adult + rules confirm) and surfaces server codes (`email_already_used`, `weak_password`, `password_mismatch`, `adult_confirmation_required`) on the right field. Live password-strength meter. - Reset is anti-enumeration: same UI whether the email exists or not (matches the contract documented in `auth-y-fotos.md`). Onboarding (`/dating/onboarding`): - Four-step flow (Identity → About → Preferences → Review) with a progress stepper. Each step validates its own fields; clicking "Publicar" re-runs validation across every step so the user lands back on the broken one if anything regressed. On success the page PUTs `/api/profile/me` with `completed: true` and refreshes the session cell so guards downstream see the published profile. Discover (`/dating/discover`): - Filter sidebar (intent + min/max age) + card stack of profiles. - `ProfileCard` component renders the canonical profile card (photo, name + age, intent pill, location, bio, interests). - Like / pass actions call `/api/likes`; a match opens a small modal offering "Open chat" → `/dating/chat/{id}`. - Auto-fetches the next page when the queue drops to two cards. Matches (`/dating/matches`): - List of active matches with avatar, name, last update, and the start of the counterpart's bio. Each row links to the chat thread. Chat (`/dating/chat/[matchId]`): - Header with counterpart name + match state, scrollable timeline, composer at the bottom. - Optimistic send: the message lands as `queued` immediately, swapped for the server response on success. On failure the bubble flips to `failed` with retry/discard inline actions. Each provisional message carries a `clientNonce` for idempotency on the server side. Profile editor (`/dating/profile`): - Full-form editor mirroring onboarding's fields plus visibility. Tracks dirty state, surfaces server errors per-field, refreshes the session cell after saving so the cache and Discover stay coherent. - Header links to the photo manager. Photo manager (`/dating/profile/photos`): - Drop-zone + click-to-upload. Client validates format (JPEG/PNG/WebP), 5 MB cap, and the 6-photo ceiling before sending. Photos render in a grid with primary badge, order pills and per-photo actions (set primary, move up/down, delete). Each mutation reuses the API client and re-syncs the session cell. All pages use the typed `DatingApiClient` from P0 — no ad-hoc fetch calls — and route through `getNexoContext()` so the session is the single source of truth across the demo. Gates: 1695 tests + check (0 errors / 0 warnings) + build + bundle smoke (22.52 KB gzip) + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
const NEXO_CONTEXT_KEY = Symbol('nexo.context');
Nexo P0 — dating client bootstrap: types, API client, app factory, shell Phase 0 of the Nexo demo (`src/web/routes/dating/plan-implementacion.md`). The standalone server lives at `servers/dating` and is already running; this commit lands the SvelteKit client that consumes it. `_lib/types.ts` — public contracts mirroring the `*View` functions in `servers/dating/domain.mjs`: `DatingUser`, `DatingProfile`, `DatingMatch`, `DatingMessage`, `DatingReport` plus enums (`DatingRole`, `DatingStatus`, `DatingIntent`, …) and the structured event names (`DATING_EVENTS.AUTH_LOGIN`, …) the bus and orca will publish later. Request/response payloads are split into dedicated interfaces (`DatingRegisterInput`, `DatingDiscoverFilters`, `DatingLikeResponse`, …) so the API client doesn't grow ad-hoc shapes. `_lib/api.ts` — typed wrapper over the standalone server. Always sends `credentials: 'include'` (the dating session is an HTTP-only `dating_session` cookie) and normalises error responses into a `DatingApiError` carrying the server's structured `code` (e.g. `weak_password`, `email_already_used`) so UI branches on a stable identifier instead of message strings. Network / abort failures surface as the same class with `code: 'network_error'`. The factory takes `fetch` + `signal` overrides for SSR (`event.fetch`) and tests. `_lib/app.ts` — `createDatingApp()` opinionated `createActiveApp` composition that fixes the service schema (lang, prefs, frontend, storage, format, cache, http, session, sium) so consumer components can type their `App` prop as `DatingApp` and get autocomplete on every slot. `auth`, `perm`, and `connection` are intentionally not in this commit — they need port-level wiring (an `AuthClientHttpPort` against the Nexo endpoints, a perm endpoint, the connection transport) that belongs to Phases 2 / 4. The factory returns `{ App, api, dispose }` so callers don't have to compose the App and the HTTP client separately. `_lib/context.ts` — symbol-keyed `setNexoApp` / `getNexoApp` bridge so the layout sets the handle once and nested pages retrieve it without rebuilding `createActiveApp`. `+layout.ts` — `prerender = false` for the entire `/dating` subtree. The demo authenticates via cookies against a runtime-only server, so static prerender doesn't make sense. `+layout.svelte` — Nexo shell: instantiates `DatingApp` once, wires the frontend `target`, eagerly probes `/api/session`, renders the top nav (Discover, Matches, Profile, Safety, Devtools) with auth state on the right, and shows an offline banner pointing to `npm run dating:server` when the API can't be reached. Disposes the App on unmount. `+page.svelte` — landing card grid that links every route the plan will materialise (Auth & Session / Profile / Discover & Match / Safety & Moderación / Diagnóstico). Plain hrefs (not the typed `resolve(...)`) so the page compiles ahead of the targets being created — the links 404 until each phase lands its `+page.svelte`. Gates: 1695 tests + check (0 errors) + build + bundle smoke (22.52 KB gzip) + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
Nexo P1+P2 — user-facing surface: auth, onboarding, discover, matches, chat, profile Builds the actual product the user sees, on top of the P0 bootstrap. The previous landing was a phase-card grid for navigating the plan; that's been replaced with a real auth-aware router and the routes the cards used to point at now exist as functional pages. Layout / context (`_lib/context.ts`, `+layout.svelte`): - Layout owns the session cell. `getNexoContext()` exposes `session()` / `setSession()` / `refreshSession()` so pages don't each issue their own `/api/session` and so a successful `login` / `register` propagates the user to every gate without a round-trip. - Logout clears the cell even when `api.logout()` rejects, so the UI never gets stuck on "authenticated" after a server hiccup. Landing (`+page.svelte`): - Pure router: anonymous → `/dating/login`, authenticated without a completed profile → `/dating/onboarding`, otherwise → `/dating/discover`. Renders a minimal pulse during the initial session probe. Auth (`login`, `register`, `reset`): - `AuthLayout` + `Field` shared shell so every form has the same centered card, focus ring and per-field error wiring. - Login maps server error codes (`invalid_credentials`, `account_restricted`, `mfa_required`) to localised messages and redirects to `/dating/onboarding` when the user has no profile yet. - Register validates locally (display name, email shape, ≥8-char password, password match, adult + rules confirm) and surfaces server codes (`email_already_used`, `weak_password`, `password_mismatch`, `adult_confirmation_required`) on the right field. Live password-strength meter. - Reset is anti-enumeration: same UI whether the email exists or not (matches the contract documented in `auth-y-fotos.md`). Onboarding (`/dating/onboarding`): - Four-step flow (Identity → About → Preferences → Review) with a progress stepper. Each step validates its own fields; clicking "Publicar" re-runs validation across every step so the user lands back on the broken one if anything regressed. On success the page PUTs `/api/profile/me` with `completed: true` and refreshes the session cell so guards downstream see the published profile. Discover (`/dating/discover`): - Filter sidebar (intent + min/max age) + card stack of profiles. - `ProfileCard` component renders the canonical profile card (photo, name + age, intent pill, location, bio, interests). - Like / pass actions call `/api/likes`; a match opens a small modal offering "Open chat" → `/dating/chat/{id}`. - Auto-fetches the next page when the queue drops to two cards. Matches (`/dating/matches`): - List of active matches with avatar, name, last update, and the start of the counterpart's bio. Each row links to the chat thread. Chat (`/dating/chat/[matchId]`): - Header with counterpart name + match state, scrollable timeline, composer at the bottom. - Optimistic send: the message lands as `queued` immediately, swapped for the server response on success. On failure the bubble flips to `failed` with retry/discard inline actions. Each provisional message carries a `clientNonce` for idempotency on the server side. Profile editor (`/dating/profile`): - Full-form editor mirroring onboarding's fields plus visibility. Tracks dirty state, surfaces server errors per-field, refreshes the session cell after saving so the cache and Discover stay coherent. - Header links to the photo manager. Photo manager (`/dating/profile/photos`): - Drop-zone + click-to-upload. Client validates format (JPEG/PNG/WebP), 5 MB cap, and the 6-photo ceiling before sending. Photos render in a grid with primary badge, order pills and per-photo actions (set primary, move up/down, delete). Each mutation reuses the API client and re-syncs the session cell. All pages use the typed `DatingApiClient` from P0 — no ad-hoc fetch calls — and route through `getNexoContext()` so the session is the single source of truth across the demo. Gates: 1695 tests + check (0 errors / 0 warnings) + build + bundle smoke (22.52 KB gzip) + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
export function setNexoContext(value: NexoContext): NexoContext {
return setContext(NEXO_CONTEXT_KEY, value);
Nexo P0 — dating client bootstrap: types, API client, app factory, shell Phase 0 of the Nexo demo (`src/web/routes/dating/plan-implementacion.md`). The standalone server lives at `servers/dating` and is already running; this commit lands the SvelteKit client that consumes it. `_lib/types.ts` — public contracts mirroring the `*View` functions in `servers/dating/domain.mjs`: `DatingUser`, `DatingProfile`, `DatingMatch`, `DatingMessage`, `DatingReport` plus enums (`DatingRole`, `DatingStatus`, `DatingIntent`, …) and the structured event names (`DATING_EVENTS.AUTH_LOGIN`, …) the bus and orca will publish later. Request/response payloads are split into dedicated interfaces (`DatingRegisterInput`, `DatingDiscoverFilters`, `DatingLikeResponse`, …) so the API client doesn't grow ad-hoc shapes. `_lib/api.ts` — typed wrapper over the standalone server. Always sends `credentials: 'include'` (the dating session is an HTTP-only `dating_session` cookie) and normalises error responses into a `DatingApiError` carrying the server's structured `code` (e.g. `weak_password`, `email_already_used`) so UI branches on a stable identifier instead of message strings. Network / abort failures surface as the same class with `code: 'network_error'`. The factory takes `fetch` + `signal` overrides for SSR (`event.fetch`) and tests. `_lib/app.ts` — `createDatingApp()` opinionated `createActiveApp` composition that fixes the service schema (lang, prefs, frontend, storage, format, cache, http, session, sium) so consumer components can type their `App` prop as `DatingApp` and get autocomplete on every slot. `auth`, `perm`, and `connection` are intentionally not in this commit — they need port-level wiring (an `AuthClientHttpPort` against the Nexo endpoints, a perm endpoint, the connection transport) that belongs to Phases 2 / 4. The factory returns `{ App, api, dispose }` so callers don't have to compose the App and the HTTP client separately. `_lib/context.ts` — symbol-keyed `setNexoApp` / `getNexoApp` bridge so the layout sets the handle once and nested pages retrieve it without rebuilding `createActiveApp`. `+layout.ts` — `prerender = false` for the entire `/dating` subtree. The demo authenticates via cookies against a runtime-only server, so static prerender doesn't make sense. `+layout.svelte` — Nexo shell: instantiates `DatingApp` once, wires the frontend `target`, eagerly probes `/api/session`, renders the top nav (Discover, Matches, Profile, Safety, Devtools) with auth state on the right, and shows an offline banner pointing to `npm run dating:server` when the API can't be reached. Disposes the App on unmount. `+page.svelte` — landing card grid that links every route the plan will materialise (Auth & Session / Profile / Discover & Match / Safety & Moderación / Diagnóstico). Plain hrefs (not the typed `resolve(...)`) so the page compiles ahead of the targets being created — the links 404 until each phase lands its `+page.svelte`. Gates: 1695 tests + check (0 errors) + build + bundle smoke (22.52 KB gzip) + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
}
Nexo P1+P2 — user-facing surface: auth, onboarding, discover, matches, chat, profile Builds the actual product the user sees, on top of the P0 bootstrap. The previous landing was a phase-card grid for navigating the plan; that's been replaced with a real auth-aware router and the routes the cards used to point at now exist as functional pages. Layout / context (`_lib/context.ts`, `+layout.svelte`): - Layout owns the session cell. `getNexoContext()` exposes `session()` / `setSession()` / `refreshSession()` so pages don't each issue their own `/api/session` and so a successful `login` / `register` propagates the user to every gate without a round-trip. - Logout clears the cell even when `api.logout()` rejects, so the UI never gets stuck on "authenticated" after a server hiccup. Landing (`+page.svelte`): - Pure router: anonymous → `/dating/login`, authenticated without a completed profile → `/dating/onboarding`, otherwise → `/dating/discover`. Renders a minimal pulse during the initial session probe. Auth (`login`, `register`, `reset`): - `AuthLayout` + `Field` shared shell so every form has the same centered card, focus ring and per-field error wiring. - Login maps server error codes (`invalid_credentials`, `account_restricted`, `mfa_required`) to localised messages and redirects to `/dating/onboarding` when the user has no profile yet. - Register validates locally (display name, email shape, ≥8-char password, password match, adult + rules confirm) and surfaces server codes (`email_already_used`, `weak_password`, `password_mismatch`, `adult_confirmation_required`) on the right field. Live password-strength meter. - Reset is anti-enumeration: same UI whether the email exists or not (matches the contract documented in `auth-y-fotos.md`). Onboarding (`/dating/onboarding`): - Four-step flow (Identity → About → Preferences → Review) with a progress stepper. Each step validates its own fields; clicking "Publicar" re-runs validation across every step so the user lands back on the broken one if anything regressed. On success the page PUTs `/api/profile/me` with `completed: true` and refreshes the session cell so guards downstream see the published profile. Discover (`/dating/discover`): - Filter sidebar (intent + min/max age) + card stack of profiles. - `ProfileCard` component renders the canonical profile card (photo, name + age, intent pill, location, bio, interests). - Like / pass actions call `/api/likes`; a match opens a small modal offering "Open chat" → `/dating/chat/{id}`. - Auto-fetches the next page when the queue drops to two cards. Matches (`/dating/matches`): - List of active matches with avatar, name, last update, and the start of the counterpart's bio. Each row links to the chat thread. Chat (`/dating/chat/[matchId]`): - Header with counterpart name + match state, scrollable timeline, composer at the bottom. - Optimistic send: the message lands as `queued` immediately, swapped for the server response on success. On failure the bubble flips to `failed` with retry/discard inline actions. Each provisional message carries a `clientNonce` for idempotency on the server side. Profile editor (`/dating/profile`): - Full-form editor mirroring onboarding's fields plus visibility. Tracks dirty state, surfaces server errors per-field, refreshes the session cell after saving so the cache and Discover stay coherent. - Header links to the photo manager. Photo manager (`/dating/profile/photos`): - Drop-zone + click-to-upload. Client validates format (JPEG/PNG/WebP), 5 MB cap, and the 6-photo ceiling before sending. Photos render in a grid with primary badge, order pills and per-photo actions (set primary, move up/down, delete). Each mutation reuses the API client and re-syncs the session cell. All pages use the typed `DatingApiClient` from P0 — no ad-hoc fetch calls — and route through `getNexoContext()` so the session is the single source of truth across the demo. Gates: 1695 tests + check (0 errors / 0 warnings) + build + bundle smoke (22.52 KB gzip) + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
export function getNexoContext(): NexoContext {
const value = getContext<NexoContext | undefined>(NEXO_CONTEXT_KEY);
if (value === undefined) {
throw new Error('Nexo context is not set. Did you mount /dating without its layout?');
Nexo P0 — dating client bootstrap: types, API client, app factory, shell Phase 0 of the Nexo demo (`src/web/routes/dating/plan-implementacion.md`). The standalone server lives at `servers/dating` and is already running; this commit lands the SvelteKit client that consumes it. `_lib/types.ts` — public contracts mirroring the `*View` functions in `servers/dating/domain.mjs`: `DatingUser`, `DatingProfile`, `DatingMatch`, `DatingMessage`, `DatingReport` plus enums (`DatingRole`, `DatingStatus`, `DatingIntent`, …) and the structured event names (`DATING_EVENTS.AUTH_LOGIN`, …) the bus and orca will publish later. Request/response payloads are split into dedicated interfaces (`DatingRegisterInput`, `DatingDiscoverFilters`, `DatingLikeResponse`, …) so the API client doesn't grow ad-hoc shapes. `_lib/api.ts` — typed wrapper over the standalone server. Always sends `credentials: 'include'` (the dating session is an HTTP-only `dating_session` cookie) and normalises error responses into a `DatingApiError` carrying the server's structured `code` (e.g. `weak_password`, `email_already_used`) so UI branches on a stable identifier instead of message strings. Network / abort failures surface as the same class with `code: 'network_error'`. The factory takes `fetch` + `signal` overrides for SSR (`event.fetch`) and tests. `_lib/app.ts` — `createDatingApp()` opinionated `createActiveApp` composition that fixes the service schema (lang, prefs, frontend, storage, format, cache, http, session, sium) so consumer components can type their `App` prop as `DatingApp` and get autocomplete on every slot. `auth`, `perm`, and `connection` are intentionally not in this commit — they need port-level wiring (an `AuthClientHttpPort` against the Nexo endpoints, a perm endpoint, the connection transport) that belongs to Phases 2 / 4. The factory returns `{ App, api, dispose }` so callers don't have to compose the App and the HTTP client separately. `_lib/context.ts` — symbol-keyed `setNexoApp` / `getNexoApp` bridge so the layout sets the handle once and nested pages retrieve it without rebuilding `createActiveApp`. `+layout.ts` — `prerender = false` for the entire `/dating` subtree. The demo authenticates via cookies against a runtime-only server, so static prerender doesn't make sense. `+layout.svelte` — Nexo shell: instantiates `DatingApp` once, wires the frontend `target`, eagerly probes `/api/session`, renders the top nav (Discover, Matches, Profile, Safety, Devtools) with auth state on the right, and shows an offline banner pointing to `npm run dating:server` when the API can't be reached. Disposes the App on unmount. `+page.svelte` — landing card grid that links every route the plan will materialise (Auth & Session / Profile / Discover & Match / Safety & Moderación / Diagnóstico). Plain hrefs (not the typed `resolve(...)`) so the page compiles ahead of the targets being created — the links 404 until each phase lands its `+page.svelte`. Gates: 1695 tests + check (0 errors) + build + bundle smoke (22.52 KB gzip) + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
}
Nexo P1+P2 — user-facing surface: auth, onboarding, discover, matches, chat, profile Builds the actual product the user sees, on top of the P0 bootstrap. The previous landing was a phase-card grid for navigating the plan; that's been replaced with a real auth-aware router and the routes the cards used to point at now exist as functional pages. Layout / context (`_lib/context.ts`, `+layout.svelte`): - Layout owns the session cell. `getNexoContext()` exposes `session()` / `setSession()` / `refreshSession()` so pages don't each issue their own `/api/session` and so a successful `login` / `register` propagates the user to every gate without a round-trip. - Logout clears the cell even when `api.logout()` rejects, so the UI never gets stuck on "authenticated" after a server hiccup. Landing (`+page.svelte`): - Pure router: anonymous → `/dating/login`, authenticated without a completed profile → `/dating/onboarding`, otherwise → `/dating/discover`. Renders a minimal pulse during the initial session probe. Auth (`login`, `register`, `reset`): - `AuthLayout` + `Field` shared shell so every form has the same centered card, focus ring and per-field error wiring. - Login maps server error codes (`invalid_credentials`, `account_restricted`, `mfa_required`) to localised messages and redirects to `/dating/onboarding` when the user has no profile yet. - Register validates locally (display name, email shape, ≥8-char password, password match, adult + rules confirm) and surfaces server codes (`email_already_used`, `weak_password`, `password_mismatch`, `adult_confirmation_required`) on the right field. Live password-strength meter. - Reset is anti-enumeration: same UI whether the email exists or not (matches the contract documented in `auth-y-fotos.md`). Onboarding (`/dating/onboarding`): - Four-step flow (Identity → About → Preferences → Review) with a progress stepper. Each step validates its own fields; clicking "Publicar" re-runs validation across every step so the user lands back on the broken one if anything regressed. On success the page PUTs `/api/profile/me` with `completed: true` and refreshes the session cell so guards downstream see the published profile. Discover (`/dating/discover`): - Filter sidebar (intent + min/max age) + card stack of profiles. - `ProfileCard` component renders the canonical profile card (photo, name + age, intent pill, location, bio, interests). - Like / pass actions call `/api/likes`; a match opens a small modal offering "Open chat" → `/dating/chat/{id}`. - Auto-fetches the next page when the queue drops to two cards. Matches (`/dating/matches`): - List of active matches with avatar, name, last update, and the start of the counterpart's bio. Each row links to the chat thread. Chat (`/dating/chat/[matchId]`): - Header with counterpart name + match state, scrollable timeline, composer at the bottom. - Optimistic send: the message lands as `queued` immediately, swapped for the server response on success. On failure the bubble flips to `failed` with retry/discard inline actions. Each provisional message carries a `clientNonce` for idempotency on the server side. Profile editor (`/dating/profile`): - Full-form editor mirroring onboarding's fields plus visibility. Tracks dirty state, surfaces server errors per-field, refreshes the session cell after saving so the cache and Discover stay coherent. - Header links to the photo manager. Photo manager (`/dating/profile/photos`): - Drop-zone + click-to-upload. Client validates format (JPEG/PNG/WebP), 5 MB cap, and the 6-photo ceiling before sending. Photos render in a grid with primary badge, order pills and per-photo actions (set primary, move up/down, delete). Each mutation reuses the API client and re-syncs the session cell. All pages use the typed `DatingApiClient` from P0 — no ad-hoc fetch calls — and route through `getNexoContext()` so the session is the single source of truth across the demo. Gates: 1695 tests + check (0 errors / 0 warnings) + build + bundle smoke (22.52 KB gzip) + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
return value;
Nexo P0 — dating client bootstrap: types, API client, app factory, shell Phase 0 of the Nexo demo (`src/web/routes/dating/plan-implementacion.md`). The standalone server lives at `servers/dating` and is already running; this commit lands the SvelteKit client that consumes it. `_lib/types.ts` — public contracts mirroring the `*View` functions in `servers/dating/domain.mjs`: `DatingUser`, `DatingProfile`, `DatingMatch`, `DatingMessage`, `DatingReport` plus enums (`DatingRole`, `DatingStatus`, `DatingIntent`, …) and the structured event names (`DATING_EVENTS.AUTH_LOGIN`, …) the bus and orca will publish later. Request/response payloads are split into dedicated interfaces (`DatingRegisterInput`, `DatingDiscoverFilters`, `DatingLikeResponse`, …) so the API client doesn't grow ad-hoc shapes. `_lib/api.ts` — typed wrapper over the standalone server. Always sends `credentials: 'include'` (the dating session is an HTTP-only `dating_session` cookie) and normalises error responses into a `DatingApiError` carrying the server's structured `code` (e.g. `weak_password`, `email_already_used`) so UI branches on a stable identifier instead of message strings. Network / abort failures surface as the same class with `code: 'network_error'`. The factory takes `fetch` + `signal` overrides for SSR (`event.fetch`) and tests. `_lib/app.ts` — `createDatingApp()` opinionated `createActiveApp` composition that fixes the service schema (lang, prefs, frontend, storage, format, cache, http, session, sium) so consumer components can type their `App` prop as `DatingApp` and get autocomplete on every slot. `auth`, `perm`, and `connection` are intentionally not in this commit — they need port-level wiring (an `AuthClientHttpPort` against the Nexo endpoints, a perm endpoint, the connection transport) that belongs to Phases 2 / 4. The factory returns `{ App, api, dispose }` so callers don't have to compose the App and the HTTP client separately. `_lib/context.ts` — symbol-keyed `setNexoApp` / `getNexoApp` bridge so the layout sets the handle once and nested pages retrieve it without rebuilding `createActiveApp`. `+layout.ts` — `prerender = false` for the entire `/dating` subtree. The demo authenticates via cookies against a runtime-only server, so static prerender doesn't make sense. `+layout.svelte` — Nexo shell: instantiates `DatingApp` once, wires the frontend `target`, eagerly probes `/api/session`, renders the top nav (Discover, Matches, Profile, Safety, Devtools) with auth state on the right, and shows an offline banner pointing to `npm run dating:server` when the API can't be reached. Disposes the App on unmount. `+page.svelte` — landing card grid that links every route the plan will materialise (Auth & Session / Profile / Discover & Match / Safety & Moderación / Diagnóstico). Plain hrefs (not the typed `resolve(...)`) so the page compiles ahead of the targets being created — the links 404 until each phase lands its `+page.svelte`. Gates: 1695 tests + check (0 errors) + build + bundle smoke (22.52 KB gzip) + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
}
Nexo P1+P2 — user-facing surface: auth, onboarding, discover, matches, chat, profile Builds the actual product the user sees, on top of the P0 bootstrap. The previous landing was a phase-card grid for navigating the plan; that's been replaced with a real auth-aware router and the routes the cards used to point at now exist as functional pages. Layout / context (`_lib/context.ts`, `+layout.svelte`): - Layout owns the session cell. `getNexoContext()` exposes `session()` / `setSession()` / `refreshSession()` so pages don't each issue their own `/api/session` and so a successful `login` / `register` propagates the user to every gate without a round-trip. - Logout clears the cell even when `api.logout()` rejects, so the UI never gets stuck on "authenticated" after a server hiccup. Landing (`+page.svelte`): - Pure router: anonymous → `/dating/login`, authenticated without a completed profile → `/dating/onboarding`, otherwise → `/dating/discover`. Renders a minimal pulse during the initial session probe. Auth (`login`, `register`, `reset`): - `AuthLayout` + `Field` shared shell so every form has the same centered card, focus ring and per-field error wiring. - Login maps server error codes (`invalid_credentials`, `account_restricted`, `mfa_required`) to localised messages and redirects to `/dating/onboarding` when the user has no profile yet. - Register validates locally (display name, email shape, ≥8-char password, password match, adult + rules confirm) and surfaces server codes (`email_already_used`, `weak_password`, `password_mismatch`, `adult_confirmation_required`) on the right field. Live password-strength meter. - Reset is anti-enumeration: same UI whether the email exists or not (matches the contract documented in `auth-y-fotos.md`). Onboarding (`/dating/onboarding`): - Four-step flow (Identity → About → Preferences → Review) with a progress stepper. Each step validates its own fields; clicking "Publicar" re-runs validation across every step so the user lands back on the broken one if anything regressed. On success the page PUTs `/api/profile/me` with `completed: true` and refreshes the session cell so guards downstream see the published profile. Discover (`/dating/discover`): - Filter sidebar (intent + min/max age) + card stack of profiles. - `ProfileCard` component renders the canonical profile card (photo, name + age, intent pill, location, bio, interests). - Like / pass actions call `/api/likes`; a match opens a small modal offering "Open chat" → `/dating/chat/{id}`. - Auto-fetches the next page when the queue drops to two cards. Matches (`/dating/matches`): - List of active matches with avatar, name, last update, and the start of the counterpart's bio. Each row links to the chat thread. Chat (`/dating/chat/[matchId]`): - Header with counterpart name + match state, scrollable timeline, composer at the bottom. - Optimistic send: the message lands as `queued` immediately, swapped for the server response on success. On failure the bubble flips to `failed` with retry/discard inline actions. Each provisional message carries a `clientNonce` for idempotency on the server side. Profile editor (`/dating/profile`): - Full-form editor mirroring onboarding's fields plus visibility. Tracks dirty state, surfaces server errors per-field, refreshes the session cell after saving so the cache and Discover stay coherent. - Header links to the photo manager. Photo manager (`/dating/profile/photos`): - Drop-zone + click-to-upload. Client validates format (JPEG/PNG/WebP), 5 MB cap, and the 6-photo ceiling before sending. Photos render in a grid with primary badge, order pills and per-photo actions (set primary, move up/down, delete). Each mutation reuses the API client and re-syncs the session cell. All pages use the typed `DatingApiClient` from P0 — no ad-hoc fetch calls — and route through `getNexoContext()` so the session is the single source of truth across the demo. Gates: 1695 tests + check (0 errors / 0 warnings) + build + bundle smoke (22.52 KB gzip) + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
// Back-compat aliases — pages that only need the App handle keep the
// terser names. New code uses `getNexoContext()` for the session
// surface too.
export const setNexoApp = setNexoContext;
export const getNexoApp = getNexoContext;

Powered by TurnKey Linux.