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.

465 lines
15 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
/**
* Typed HTTP client over the standalone Nexo server (`servers/dating`).
*
* The dating server lives at `http://127.0.0.1:8787` by default
* (configurable through `DATING_API_BASE`) and authenticates via the
* `dating_session` HTTP-only cookie — every request goes out with
* `credentials: 'include'`.
*
Nexo P1 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
* Response shape conventions, derived from `servers/dating/routes.mjs`:
*
* - **Errors** are always `{ ok: false, error: { code, message, details? } }`
* with the appropriate HTTP status. The client maps them onto
* `DatingApiError` so call sites only need a single catch branch
* keyed on `error.code`.
* - **Successes** are not wrapped consistently — some endpoints return
* the raw payload (`{ profile }`, `{ matches }`, `{ message }`,
* `{ like, match }`), others piggy-back an `ok: true` flag onto the
* response (auth endpoints). Each method below knows exactly which
* field it needs to pluck so the typed return matches what call
* sites actually use, no envelope detail leaks out of this module.
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 {
type DatingBlockInput,
type DatingDiscoverFilters,
type DatingDiscoverResponse,
type DatingLikeInput,
type DatingLikeResponse,
type DatingLoginInput,
type DatingLoginResponse,
type DatingMatch,
type DatingMessage,
type DatingMessageInput,
type DatingMessagesResponse,
type DatingMfaVerifyInput,
type DatingModerationResolveInput,
type DatingProfile,
type DatingProfileUpdateInput,
type DatingRegisterInput,
type DatingReport,
type DatingReportInput,
type DatingReportsResponse,
type DatingResetInput,
type DatingSessionResponse,
Nexo P1 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
type DatingUser
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
} from './types.ts';
/** Default port the standalone dating server listens on. */
export const DATING_API_DEFAULT_PORT = 8787;
/**
* Resolve the API base URL.
*
* Browsers treat `localhost` and `127.0.0.1` as different sites, so
* the session cookie's `SameSite=Lax` policy drops it on cross-origin
* fetches between them — the user logs in, the cookie is set on the
* 127.0.0.1 origin, and the next `/api/session` probe from
* localhost:5173 lands without it. Result: login looks like it
* silently does nothing.
*
* To stay same-site without touching the server, reuse whatever
* hostname the page is already on (`localhost` ↔ `localhost`,
* `127.0.0.1` ↔ `127.0.0.1`). On non-browser runtimes (SSR, vitest)
* fall back to the explicit `127.0.0.1` since `window` doesn't exist.
*/
export function resolveDatingApiBase(port: number = DATING_API_DEFAULT_PORT): string {
if (typeof window !== 'undefined' && window.location?.hostname) {
const protocol = window.location.protocol === 'https:' ? 'https:' : 'http:';
return `${protocol}//${window.location.hostname}:${port}`;
}
return `http://127.0.0.1:${port}`;
}
/** @deprecated Use `resolveDatingApiBase()`. Kept for callers that import the literal. */
export const DATING_API_DEFAULT_BASE = `http://127.0.0.1:${DATING_API_DEFAULT_PORT}`;
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
export interface DatingApiClientOptions {
/** Base URL of the standalone dating server. Defaults to `DATING_API_DEFAULT_BASE`. */
readonly base?: string;
/**
* Override the global `fetch`. Useful for SSR (`event.fetch`) and for
* test harnesses that mock the network layer.
*/
readonly fetch?: typeof fetch;
/**
* Per-request signal injection point. Each method falls back to
* `signal` from this hook if the call site doesn't pass one
* explicitly — wires `arts/timer`-driven cancellation cleanly
* without threading AbortController through every site.
*/
readonly signal?: () => AbortSignal | undefined;
}
/**
* Concrete client error. Carries the structured `code` from the
* server's error envelope so UI can branch on a stable identifier
* (`weak_password`, `email_already_used`, …) rather than parsing
* messages.
*/
export class DatingApiError extends Error {
readonly code: string;
readonly status: number;
readonly details: Readonly<Record<string, unknown>>;
constructor(
code: string,
message: string,
status: number,
details: Readonly<Record<string, unknown>> = {}
) {
super(message);
this.name = 'DatingApiError';
this.code = code;
this.status = status;
this.details = details;
}
static network(message: string, cause?: unknown): DatingApiError {
return new DatingApiError('network_error', message, 0, { cause: String(cause ?? '') });
}
}
Nexo P1 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
interface ServerErrorEnvelope {
readonly ok: false;
readonly error: {
readonly code?: string;
readonly message?: string;
readonly details?: Readonly<Record<string, unknown>>;
};
}
function isErrorEnvelope(value: unknown): value is ServerErrorEnvelope {
if (typeof value !== 'object' || value === null) return false;
const candidate = value as { ok?: unknown; error?: unknown };
return (
candidate.ok === false &&
typeof candidate.error === 'object' &&
candidate.error !== null
);
}
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
export interface DatingApiClient {
// auth
register(input: DatingRegisterInput): Promise<DatingLoginResponse>;
login(input: DatingLoginInput): Promise<DatingLoginResponse>;
logout(): Promise<void>;
resetRequest(input: DatingResetInput): Promise<void>;
mfaVerify(input: DatingMfaVerifyInput): Promise<DatingSessionResponse>;
session(): Promise<DatingSessionResponse>;
// profile
getMyProfile(): Promise<DatingProfile | null>;
updateMyProfile(input: DatingProfileUpdateInput): Promise<DatingProfile>;
uploadPhoto(file: File): Promise<DatingProfile>;
deletePhoto(filename: string): Promise<DatingProfile>;
reorderPhotos(order: readonly string[]): Promise<DatingProfile>;
setPrimaryPhoto(filename: string): Promise<DatingProfile>;
// discover + matches
discover(filters?: DatingDiscoverFilters): Promise<DatingDiscoverResponse>;
like(input: DatingLikeInput): Promise<DatingLikeResponse>;
matches(): Promise<readonly DatingMatch[]>;
messages(matchId: string, cursor?: string): Promise<DatingMessagesResponse>;
sendMessage(matchId: string, input: DatingMessageInput): Promise<DatingMessage>;
// safety
block(input: DatingBlockInput): Promise<void>;
report(input: DatingReportInput): Promise<DatingReport>;
// admin
adminReports(): Promise<DatingReportsResponse>;
adminResolveReport(
reportId: string,
input: DatingModerationResolveInput
): Promise<DatingReport>;
// devtools
devtoolsSnapshot(): Promise<Readonly<Record<string, unknown>>>;
}
/**
* `createDatingApiClient` returns a thin typed wrapper that always
* sends `credentials: 'include'`. The factory is intentionally
* unaware of `arts/active-app` — apps wire it into `App.http` (or
* keep it standalone) at composition time.
*/
export function createDatingApiClient(
options: DatingApiClientOptions = {}
): DatingApiClient {
const base = options.base ?? resolveDatingApiBase();
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
const doFetch = options.fetch ?? globalThis.fetch.bind(globalThis);
Nexo P1 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
function safeJson(text: string): unknown {
try {
return JSON.parse(text);
} catch {
return null;
}
}
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
async function request<T>(
path: string,
init: RequestInit = {},
expect: 'json' | 'void' = 'json'
): Promise<T> {
const url = `${base}${path}`;
let response: Response;
try {
response = await doFetch(url, {
credentials: 'include',
...init,
signal: init.signal ?? options.signal?.()
});
} catch (cause) {
throw DatingApiError.network(`Request to ${path} failed`, cause);
}
Nexo P1 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
// The dating server returns JSON on success AND on error. Read
// the body once and decide based on `response.ok` — that's the
// only place the structured `error.code` is available.
const text = await response.text();
const payload: unknown = text === '' ? null : safeJson(text);
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
if (!response.ok) {
Nexo P1 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
if (isErrorEnvelope(payload)) {
throw new DatingApiError(
payload.error.code ?? 'http_error',
payload.error.message ?? response.statusText ?? 'Request failed',
response.status,
payload.error.details ?? {}
);
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
}
throw new DatingApiError(
Nexo P1 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
'http_error',
response.statusText || `Request failed with ${response.status}`,
response.status
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
);
}
if (expect === 'void' || response.status === 204) {
return undefined as T;
}
Nexo P1 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
return payload as T;
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
}
function jsonInit(method: string, body?: unknown): RequestInit {
return {
method,
headers: { 'content-type': 'application/json' },
body: body === undefined ? undefined : JSON.stringify(body)
};
}
function discoverQuery(filters?: DatingDiscoverFilters): string {
if (filters === undefined) return '';
const params = new URLSearchParams();
if (filters.intent !== undefined) params.set('intent', filters.intent);
Nexo P1 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
// The server query parameter names are `ageMin` / `ageMax` (see
// `servers/dating/routes.mjs:discoverGet`). The TypeScript
// fields stay `minAge` / `maxAge` because that's natural English
// reading order; the translation lives here.
if (filters.minAge !== undefined) params.set('ageMin', String(filters.minAge));
if (filters.maxAge !== undefined) params.set('ageMax', String(filters.maxAge));
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
if (filters.q !== undefined && filters.q !== '') params.set('q', filters.q);
if (filters.cursor !== undefined && filters.cursor !== '')
params.set('cursor', filters.cursor);
const query = params.toString();
return query === '' ? '' : `?${query}`;
}
Nexo P1 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
const client: DatingApiClient = {
async register(input) {
const payload = await request<{ user: DatingUser }>(
'/api/auth/register',
jsonInit('POST', input)
);
// Auth endpoints return only the `user`. The profile is
// loaded separately on the next session probe.
return { user: payload.user, profile: null };
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 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
async login(input) {
// Server normalises identity / email under the hood. Send
// both names so older deployments that only accept
// `identity` keep working.
const payload = await request<{ user: DatingUser }>(
'/api/auth/login',
jsonInit('POST', { identity: input.email, ...input })
);
return { user: payload.user, profile: null };
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 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + 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
logout() {
return request<void>('/api/auth/logout', jsonInit('POST'), 'void');
},
Nexo P1 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + 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
resetRequest(input) {
return request<void>('/api/auth/reset', jsonInit('POST', input), 'void');
},
Nexo P1 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + 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
mfaVerify(input) {
return request<DatingSessionResponse>(
'/api/auth/mfa/verify',
jsonInit('POST', input)
);
},
Nexo P1 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
async session() {
// Session endpoint returns either `{ authenticated: false }`
// or `{ authenticated: true, user }`. The profile lives at
// `/api/profile/me`; we fetch it inline so callers see a
// fully populated `DatingSessionResponse`.
const payload = await request<
{ authenticated: false } | { authenticated: true; user: DatingUser }
>('/api/session');
if (payload.authenticated === false) return { authenticated: false };
let profile: DatingProfile | null = null;
try {
profile = await client.getMyProfile();
} catch (error) {
// `profile_required` is the server's "not yet onboarded"
// signal; everything else is a real transport problem
// the caller should see, but for a session-restore
// probe we degrade silently and let the subsequent
// page-level guard surface it.
if (
!(error instanceof DatingApiError) ||
(error.code !== 'profile_required' && error.code !== 'http_error')
) {
throw error;
}
}
return { authenticated: true, user: payload.user, profile };
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 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
async getMyProfile() {
const payload = await request<{ profile: DatingProfile | null }>('/api/profile/me');
return payload.profile;
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 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
async updateMyProfile(input) {
const payload = await request<{ profile: DatingProfile }>(
'/api/profile/me',
jsonInit('PUT', input)
);
return payload.profile;
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 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
async uploadPhoto(file) {
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
const form = new FormData();
Nexo P1 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
// Server reads `form.getAll('photos')` — see
// `servers/dating/routes.mjs:profilePhotosPost`. Keep the
// field name aligned even when the client only sends one
// file at a time.
form.append('photos', file);
const payload = await request<{ profile: DatingProfile }>(
'/api/profile/photos',
{ method: 'POST', body: form }
);
return payload.profile;
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 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
async deletePhoto(filename) {
const payload = await request<{ profile: DatingProfile }>(
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
`/api/profile/photos/${encodeURIComponent(filename)}`,
{ method: 'DELETE' }
);
Nexo P1 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
return payload.profile;
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 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
async reorderPhotos(order) {
const payload = await request<{ profile: DatingProfile }>(
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
'/api/profile/photos/order',
Nexo P1 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
jsonInit('PATCH', { photos: [...order] })
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 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
return payload.profile;
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 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
async setPrimaryPhoto(filename) {
const payload = await request<{ profile: DatingProfile }>(
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
'/api/profile/photos/main',
jsonInit('PATCH', { filename })
);
Nexo P1 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
return payload.profile;
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 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
async discover(filters) {
const payload = await request<{
profiles: readonly DatingProfile[];
totalItems?: number;
}>(`/api/discover${discoverQuery(filters)}`);
// `totalItems` is exposed by the server but the public
// surface keeps the shape pagination-ready. The cursor
// stays `null` until the server lands real cursoring.
return { profiles: payload.profiles, nextCursor: null };
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 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
async like(input) {
const payload = await request<{
like: { state: 'like' | 'pass' };
match: DatingMatch | null;
}>('/api/likes', jsonInit('POST', input));
return { liked: payload.like.state === 'like', match: payload.match };
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 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
async matches() {
const payload = await request<{ matches: readonly DatingMatch[] }>('/api/matches');
return payload.matches;
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 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
async messages(matchId, cursor) {
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
const path = `/api/matches/${encodeURIComponent(matchId)}/messages${
cursor === undefined || cursor === '' ? '' : `?cursor=${encodeURIComponent(cursor)}`
}`;
Nexo P1 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
const payload = await request<{
messages: readonly DatingMessage[];
cursor?: string | null;
}>(path);
return { messages: payload.messages, cursor: payload.cursor ?? null };
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 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
async sendMessage(matchId, input) {
const payload = await request<{ message: DatingMessage }>(
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
`/api/matches/${encodeURIComponent(matchId)}/messages`,
jsonInit('POST', input)
);
Nexo P1 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
return payload.message;
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
},
block(input) {
return request<void>('/api/safety/block', jsonInit('POST', input), 'void');
},
Nexo P1 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
async report(input) {
const payload = await request<{ report: DatingReport }>(
'/api/safety/report',
jsonInit('POST', input)
);
return payload.report;
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 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
async adminReports() {
const payload = await request<{ reports: readonly DatingReport[] }>(
'/api/admin/reports'
);
return { reports: payload.reports };
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 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
async adminResolveReport(reportId, input) {
const payload = await request<{ report: DatingReport }>(
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
`/api/admin/reports/${encodeURIComponent(reportId)}/resolve`,
jsonInit('POST', input)
);
Nexo P1 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
return payload.report;
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
},
devtoolsSnapshot() {
return request<Readonly<Record<string, unknown>>>('/api/devtools/snapshot');
}
};
Nexo P1 fix — align HTTP client with the real server contract The bootstrap client was reading error envelopes flat (`payload.code`, `payload.message`) but the dating server wraps them in `{ ok: false, error: { code, message, details } }` (see `servers/dating/http.mjs:handleError`). Result: a real 400 like an invalid login showed up in the console as a generic `POST /api/auth/login 400` with the structured `error.code` discarded — UI couldn't branch on `invalid_credentials` and the user saw the bare HTTP status. Several success-shape mismatches surfaced during the audit: - `/api/auth/login` returns `{ ok: true, user }` (no `profile`, no `token`). The client now plucks `user`, defaults `profile: null`, and pages call `nexo.refreshSession()` to fetch the profile in a second round trip. - `/api/auth/register` is identical — same fix, same flow. - `/api/session` returns `{ authenticated, user }` only; the profile lives at `/api/profile/me`. `client.session()` now fetches both transparently so consumers see a populated `DatingSessionResponse`. - `/api/profile/me` (and every photo endpoint) returns `{ profile }` not the bare profile. Each method unwraps. - `/api/discover` accepts `ageMin` / `ageMax` (not `minAge` / `maxAge`). Returns `{ profiles, totalItems }` (not `nextCursor`). The TypeScript surface keeps the natural English names; the translation lives in `discoverQuery`. - `/api/likes` returns `{ like, match }`; client maps `liked` from `like.state === 'like'`. - `/api/matches` returns `{ matches }`; client returns `payload.matches`. - `/api/matches/:id/messages` (POST) returns `{ message }`; GET returns `{ messages }`. - `/api/profile/photos` reads `form.getAll('photos')` — client now appends as `photos`, not `file`. - Auth login form: server reads `body.identity || body.email`. The client sends both keys for forward/backward compatibility. Login + register pages no longer try to assemble the session manually from the auth response — they call `nexo.refreshSession()` so the layout's session cell goes through the same path as a cold session restore. That keeps the "user authenticated, profile not yet completed" branch consistent across cold-load and post-login. Gates: 1695 tests + check (0/0) + build + bundle 22.52 KB + aliases — all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
return client;
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
}

Powered by TurnKey Linux.