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
|
|
|
}
|