Files
multica/apps/mobile/lib/parse-response.ts
Naiyuan Qing af79a7fb4e feat(mobile): ApiClient capability set + issue detail v1 + lessons in CLAUDE.md
ApiClient hardening (data/api.ts):
- onUnauthorized callback wired in _layout.tsx — 401 clears token,
  workspace store, TanStack Query cache, replaces nav to /login.
  Idempotent via signingOutRef. Mirrors packages/core/api/client.ts
  handleUnauthorized.
- X-Request-ID per request (lib/request-id.ts)
- Structured logger: `[api] -> METHOD path (rid)` on start, `[api] <-
  STATUS path (rid, duration)` on end. console.error for 5xx,
  console.warn for 404, console.log for success.
- Zod parseWithFallback for listIssues + listTimeline (the only two
  endpoints with schemas in packages/core/api/schemas.ts today —
  matches web's current coverage; new schemas should land on the web
  side first and both clients pick them up).

Core export (packages/core/package.json):
- Add `./api/schemas` to exports map so mobile can import the shared
  Zod schemas + EMPTY_* fallbacks (pure data, on the mobile sharing
  whitelist per CLAUDE.md).

Issue detail v1 (app/(app)/[workspace]/issue/[id].tsx):
- Read issue + infinite-scroll timeline + comment composer
- Stack header shows MUL-XXX once detail loads
- Supporting files: data/queries/issues.ts, data/mutations/issues.ts,
  components/issue/{timeline-list,comment-composer,...},
  lib/{format-activity,timeline-coalesce,timeline-thread}.ts
- Property edits, reactions, mentions, image lightbox deferred to V2+

apps/mobile/CLAUDE.md — Lessons learned (encode into reflexes):
1. Install/upgrade deps: `pnpm view <pkg> dist-tags` first; `expo
   install` for Expo packages, never `pnpm add` blindly
2. New source subdirectory: `git check-ignore -v` to verify against
   root .gitignore generic rules (data/, build/, bin/); add !data/
   override if matched. Cost a 14-file missing commit before.
3. ApiClient capability list (Zod parse / 401 callback / X-Request-ID
   / structured logger) — all baseline, not polish
4. Visual alignment is baseline, not polish — tab icons, screen titles,
   right-column vertical alignment of trailing elements, type-aware
   secondary lines (mirror InboxDetailLabel, not raw item.body)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:05:00 +08:00

32 lines
990 B
TypeScript

/**
* Mobile-owned parseWithFallback. Mirrors packages/core/api/schema.ts —
* the boundary defense for installed-app schema drift required by root
* CLAUDE.md "API Response Compatibility" and apps/mobile/CLAUDE.md
* "Type drift defense".
*
* Why we mirror instead of import: keeps mobile fully decoupled and lets
* us route the warning into mobile's own logger instead of the core
* schemaLogger singleton. Behavior is identical: safeParse → on success
* return parsed; on failure log + return fallback (never throw into UI).
*/
import { type ZodType } from "zod";
export interface ParseOptions {
endpoint: string;
}
export function parseWithFallback<T>(
data: unknown,
schema: ZodType,
fallback: T,
opts: ParseOptions,
): T {
const result = schema.safeParse(data);
if (result.success) return result.data as T;
console.warn(`[api] schema validation failed: ${opts.endpoint}`, {
issues: result.error.issues,
received: data,
});
return fallback;
}