0011 — Native Taro Mini Program client instead of a WebView shell
0011 — Native Taro Mini Program client instead of a WebView shell
Status
Accepted. Supersedes 0009 and 0010; revives the client separation of 0007 under a new constraint.
Context
WeChat does not open the embedded-web-page capability to personal accounts:
<web-view> requires a configured 业务域名, which is only available to verified
organization accounts. OpenTrip's Mini Program account is a personal account and
an organization upgrade is not available to the project.
That makes the 0009/0010 architecture unshippable, not merely suboptimal: every
product screen in it lived inside a <web-view> hosting apps/web. The
alternatives were to stop shipping on WeChat, or to render the product with Mini
Program components.
The reasons 0007 was originally reversed — a duplicated design system, and MapLibre GL not running in the Mini Program renderer — still hold. They are now costs to absorb rather than reasons to choose a WebView.
Decision
- Rebuild
apps/miniappas a native Taro 4 + React + TypeScript client organized with FSD, replacing the dependency-free WebView shell. One Mini Program path only. - Authenticate with
wx.loginagainst the existingPOST /api/auth/wechat-mini-program/sign-inand send the Better Auth token as a bearer on every request. Persist the token in Mini Program storage so cold starts restore the session; renew once on401. - Reuse the HTTP contracts, DTOs, and write-echo caching semantics of the PWA; rebuild the UI, navigation, and sheets with Mini Program components and a parallel token set (hex colors, system fonts).
- Replace MapLibre with WeChat's native
<map>behind the planner's map panel: day-colored circles plus a polyline,Taro.chooseLocationfor picking, andTaro.openLocationfor navigation hand-off. - Poll
GET …/agent/eventsfor agent chat and useTaro.connectSocketfor trip realtime, since Mini Program networking cannot stream a response body. - Delete the WebView bridge from the product: the
/miniappbootstrap route, the JSSDK bridge, embedded-mode branches inapps/web, and themobile-auth/webview/mint+mobile-auth/webview/exchangeendpoints. - Do not release publicly on WeChat until the client reaches near parity with the PWA (hub, planner panels, agent, budget, invite, settings).
Consequences
- The Mini Program ships without any WeChat account upgrade; only the API origin needs to be on the request-domain allowlist.
- The product UI exists twice. Contract and product-model changes must be applied to both clients, and the parallel design system will drift unless changes stay token-driven.
- Documented parity gaps: MapLibre vector styling, custom marker art, and the Mapillary street-view viewer. Journal entries stay device-local, as in the PWA.
- Browser-only auth flows (OAuth redirects, Turnstile) are out of scope for the Mini Program, which is WeChat-login-first.
apps/webno longer has an embedded host mode, so its routing, headers, and session handling are simpler and browser-only.
Addendum (2026-07)
- The realtime WebSocket upgrade accepts requests without an
Originheader: browsers always send one (so the trusted-origin check only fires when an Origin is present), while WeChatwx.connectSocketsends none and is authenticated by the Bearer token at the session check instead. Without this, the Mini Program could never open a realtime connection. - The Mini Program has no sign-out UI: on a
401the client clears the token and silently re-runswx.login, so a sign-out button would be undone by the next automatic login. - The write-echo caching rule applies to the Mini Program too: invite-accept
and reservation mutations merge their response DTOs into the React Query
cache (
upsertTripSummaryintoqueryKeys.trips) instead of invalidating list queries, which would read stale through Hyperdrive.