F1: warmPageYdoc now returns didSync (true only on the real 'synced' event,
false on the 8s timeout / error, which are now logged). The tree menu gates
the 'available offline' success toast on result.ok AND didSync, and pushes
'editor' into the failed set otherwise — so a page whose Yjs body never
warmed is no longer reported as fully offline-available.
F2: tests for the page/space/currentUser failure labels and the didSync
true/false paths.
F3: correct the mobile-app-plan / mobile-bootstrap present-state sections to
match shipped code (Capacitor, CORS allowlist, Swagger, offline reading,
/l in the SW denylist).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Security:
- Clear the offline IndexedDB cache on sign-in (not only logout) so a previous
user's persisted query cache and Yjs page bodies cannot leak to the next user
on a shared device when the prior session ended without an explicit logout.
Regressions:
- Remove the double Yjs title write from the AI title-generation path: the title
editor is bound to the Yjs `title` fragment and the server REST update reseeds
it, so the local setContent raced that reseed and doubled/garbled the title.
Conventions / i18n / docs:
- Remove the unused showAiMenuAtom.
- Register the 3 offline-fallback strings in en-US and ru-RU.
- Fix the 5 broken links to the nonexistent docs/offline-sync-plan.md.
Stability / simplification:
- warmInfiniteAll now reports truncation (returns false) when it hits maxPages
with a cursor still pending instead of silently succeeding.
- space-tree make-offline catch logs the raw error and surfaces the real cause.
- Move the Offline/Mobile/CORS CHANGELOG entries from the released 0.93.0 section
into [Unreleased] (CORS is a documented breaking change).
- Drop the pass-through sync-flag forwarders in use-page-collab-providers; set the
atoms directly.
- Collapse the three isSwaggerEnabled true-cases into it.each.
Tests / architecture:
- Extract collabTokenNeedsRefresh (pure) and cover all four token states.
- Extract shouldPropagateTitleChange and cover the collab-origin skip; add a
TitleEditor render test for the static-h1 vs collaborative-editor switch.
- Add a use-auth test asserting the sign-in cache purge runs before login.
- Add an OFFLINE_PERSIST_ROOTS guard test asserting every persisted root maps to
an exported query-key factory; route make-offline's currentUser warm through a
new userKeys factory.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Service worker (vite-plugin-pwa/Workbox): add /share/, /mcp, and /robots.txt
to navigateFallbackDenylist so the SPA app-shell never shadows those
server-rendered routes (they mirror the server static-serve exclude list — the
share SEO/OG HTML, the MCP endpoint, and robots.txt must come from the server).
- Remove the dead /api GET NetworkFirst Workbox rule (api-get-cache): offline
reads are served by the persisted TanStack Query cache (IndexedDB) + y-indexeddb,
never by an SW HTTP cache, so caching GET /api only risked stale responses. All
/api is now NetworkOnly. clearOfflineCache still deletes any legacy api-get-cache
defensively (comment updated to note it is no longer created).
- CORS: drop the cleartext 'http://localhost' native-WebView origin. The Capacitor
shell uses the secure scheme (capacitor.config cleartext:false, default Android
scheme https, iOS hosted via CAP_SERVER_URL), so no native client uses it;
allowing it only widened the credentialed-CORS surface. Keeps capacitor://,
ionic://, and https://localhost.
- docs/mobile-bootstrap.md: replace the inaccurate 'hand-rolled service worker'
description with the real Workbox generateSW setup (prompt registration via
virtual:pwa-register, production-only, denylist, NetworkOnly, RQ/y-indexeddb
offline reads) and drop http://localhost from the CORS origins list.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Implements the §12 bootstrap from docs/mobile-app-plan.md.
Backend (§6):
- auth: optional returnToken flag on login returns the JWT in the body
(data.authToken) for native Keychain/Keystore + Bearer; web cookie flow
unchanged.
- main.ts: explicit CORS allowlist (APP_URL + CORS_ALLOWED_ORIGINS env +
Capacitor WebView origins), credentials enabled, replaces open enableCors().
- optional OpenAPI/Swagger at /api/docs behind SWAGGER_ENABLED.
- env: CORS_ALLOWED_ORIGINS, SWAGGER_ENABLED, CAP_SERVER_URL.
PWA:
- manifest metadata, hand-rolled service worker (network-first nav, SWR
assets, never intercepts /api,/socket.io,/collab), prod-only registration,
apple-touch-icon.
Capacitor:
- capacitor.config.ts (webDir apps/client/dist; iOS via CAP_SERVER_URL to
avoid bundling the AGPL client in the .ipa, see plan §9), cap:* scripts,
deps, .gitignore for native dirs.
- docs/mobile-bootstrap.md documenting what is done and the remaining manual
steps (cap add ios/android, APNs/FCM, stores).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>