{"slug": "one-react-vite-product-across-pwa-and-tauri-without-pretending-they-are", "title": "One React/Vite product across PWA and Tauri—without pretending they are identical", "summary": "A developer's WorldScript Studio case study shows how a single React/Vite codebase can target GitHub Pages, an installed PWA, edge hosts, and a Tauri desktop app while keeping platform authority boundaries explicit rather than pretending the surfaces are interchangeable. The project shares domain semantics but routes persistence and networking through platform-specific adapters: browser/PWA projects use origin-scoped IndexedDB with passphrase-based encryption, while the Tauri desktop store uses filesystem-backed application data that is not currently encrypted at rest. Because GitHub Pages cannot inject response headers, the deployment relies on a meta Content Security Policy as the sole enforcement point there, whereas Vercel and Cloudflare Pages set real headers and run a shared same-origin Claude proxy.", "body_md": "One React/Vite codebase can power a GitHub Pages site, an installed PWA, an edge-hosted deployment, and a Tauri desktop app.\n\nThat does not make those surfaces interchangeable.\n\nThis distinction matters whenever an application handles user projects, offline behavior, AI providers, filesystem access, or security policy. Shared components are valuable. Shared assumptions can be dangerous.\n\n[WorldScript Studio](https://github.com/qnbs/WorldScript-Studio) is a useful case study because its React/Vite application is intentionally available across browser/PWA and Tauri desktop environments. The product shares domain semantics, but it does not pretend that a browser tab and a native shell have the same authority boundaries. Code references are from the repository at commit `2d9157c0` (2026-09-28), release v1.28.8; simplified excerpts are labeled.\n\nA cross-platform product needs a stable answer to questions such as:\n\nThose answers should be shared.\n\nThe mechanisms underneath them should not be forced to look identical.\n\nFor a PWA, the browser provides IndexedDB, Cache Storage, service workers, Web APIs, WebGPU, and origin-scoped storage. A desktop shell can provide application-data filesystem access, native networking, OS integration, and platform packaging.\n\nTrying to hide every difference behind a single universal abstraction often creates a worse outcome: browser APIs leak into desktop code, native assumptions leak into web code, and the product accumulates several accidental definitions of persistence or network policy.\n\nA better rule is:\n\nShare domain semantics and interoperability contracts. Expose platform capabilities through explicit adapters.\n\nThe same static build can be deployed to different hosts, but hosting changes what the application can guarantee.\n\n| Surface | Important capability | Important limitation | \n|---|---|---|\n| GitHub Pages | Static public deployment | Cannot inject arbitrary HTTP security headers | \n| Vercel / Cloudflare Pages | Response headers and edge-function capabilities | Still browser-origin storage for web project data | \n| Installed PWA | Cached shell and browser-native installation | Storage remains origin- and browser-specific | \n| Tauri desktop | Filesystem persistence and native HTTP | Desktop project files are not currently app-encrypted at rest | \n\nGitHub Pages is a good example of why \"deployed from the same source\" is not enough.\n\nWorldScript uses a meta Content Security Policy there because GitHub Pages cannot add the corresponding response headers — the project's deployment documentation calls that meta tag the *sole enforcement point* on that host. Vercel and Cloudflare Pages can set real response headers that mirror the meta CSP, and both can run a same-origin edge relay for supported functionality (a Claude proxy lives at `api/claude-proxy.ts` for Vercel and `functions/api/claude-proxy.ts` for Cloudflare, sharing one core module). Those are materially different deployment guarantees, even when users see the same React interface.\n\nThe right documentation does not flatten that distinction. It names it.\n\nThe PWA's live project path uses browser storage. The desktop path uses filesystem-backed stores under application data.\n\nBoth are local. They are not the same.\n\nBrowser persistence is governed by the browser's origin, quota, eviction behavior, and storage APIs. Desktop persistence is governed by filesystem access, native process boundaries, and the application's own read/write rules.\n\nThat difference becomes especially important for security language. Browser/PWA protected IndexedDB data can use the application's passphrase-based encryption lifecycle when configured. The filesystem-backed desktop project store currently does not receive that same at-rest encryption.\n\nA UI toggle with the same name is not enough to make the protection equivalent. The actual persistence path decides what is protected.\n\nA browser connecting to `localhost` is still subject to browser rules such as CORS and Private Network Access. A Tauri desktop application can use an admitted native HTTP capability for local or cloud endpoints.\n\nThat does not mean desktop networking is automatically safer. It means its policy must be defined and enforced differently — and \"narrowly admitted\" is meant literally here. The desktop shell's HTTP capability is an explicit allowlist, not an open pipe:\n\n```\n// src-tauri/capabilities/default.json (excerpt)\n{\n  \"identifier\": \"http:default\",\n  \"allow\": [\n    \"http://localhost:*/*\",\n    \"http://127.0.0.1:*/*\",\n    \"https://generativelanguage.googleapis.com/*\",\n    \"https://api.openai.com/*\"\n    // …remaining provider hosts, nothing else\n  ]\n}\n```\n\nAt runtime, the fetch adapter picks its implementation by environment: in the Tauri runtime it dynamically loads the native HTTP plugin; everywhere else it uses the browser's `fetch`. For WorldScript, that desktop-native path supports local inference-server workflows such as Ollama-compatible endpoints without requiring the WebView to bypass browser-origin rules. The browser/PWA path should not silently probe local ports or pretend that the same route will work without user-managed server configuration.\n\nThe general lesson is simple:\n\nA service worker is a powerful browser feature, but it is not a universal application runtime.\n\nWorldScript's service worker caches its web shell and handles offline fallbacks on web surfaces. In Tauri, the registration code takes the opposite path — it actively tears the browser mechanism down:\n\n```\n// register-sw.ts (excerpt — called only from the Tauri branch)\nasync function teardownServiceWorkerInTauri(): Promise<void> {\n  if (!('serviceWorker' in navigator)) return;\n  const registrations = await navigator.serviceWorker.getRegistrations();\n  await Promise.all(registrations.map((reg) => reg.unregister()));\n  const keys = await caches.keys();\n  await Promise.all(\n    keys.filter(isWorldScriptOwnedCacheName).map((k) => caches.delete(k)),\n  );\n}\n```\n\nIt unregisters any existing service worker *and* purges the app's own caches. That prevents a stale browser cache from serving an offline fallback over the real bundled desktop application.\n\nThis is a subtle example of platform adaptation done well: the feature is not merely disabled because it is inconvenient. It is disabled because its browser lifecycle would be the wrong authority for a native bundle.\n\nBefore claiming that a web and desktop product are \"the same app,\" ask:\n\nA shared codebase is an implementation advantage. It is not a reason to erase the differences users and maintainers need to understand.\n\nThe goal is parity where the product contract is shared—and honesty where the platform changes the contract.\n\n*Source note: WorldScript Studio is open source ([github.com/qnbs/WorldScript-Studio](https://github.com/qnbs/WorldScript-Studio)). Code references correspond to `main` at `2d9157c0` (2026-09-28); release anchor v1.28.8. Key files: `register-sw.ts`, `index.html`, `vercel.json`, `docs/DEPLOYMENT.md`, `src-tauri/capabilities/default.json`, `services/ai/fetchAdapter.ts`, `api/claude-proxy.ts`, `functions/api/claude-proxy.ts`. Part of the \"Engineering WorldScript Studio\" series.*\n\n*AI disclosure: AI tools helped with repository research, structure, and editing of this article. I reviewed the technical claims against the referenced source files before publication.*", "url": "https://wpnews.pro/news/one-react-vite-product-across-pwa-and-tauri-without-pretending-they-are", "canonical_source": "https://dev.to/qnbs/one-reactvite-product-across-pwa-and-tauri-without-pretending-they-are-identical-17ld", "published_at": "2026-09-27 23:32:35+00:00", "updated_at": "2026-09-28 00:00:48.149028+00:00", "lang": "en", "topics": ["developer-tools", "ai-tools", "ai-infrastructure"], "entities": ["WorldScript Studio", "React", "Vite", "Tauri", "GitHub Pages", "Vercel", "Cloudflare Pages", "Claude"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/one-react-vite-product-across-pwa-and-tauri-without-pretending-they-are", "markdown": "https://wpnews.pro/news/one-react-vite-product-across-pwa-and-tauri-without-pretending-they-are.md", "text": "https://wpnews.pro/news/one-react-vite-product-across-pwa-and-tauri-without-pretending-they-are.txt", "jsonld": "https://wpnews.pro/news/one-react-vite-product-across-pwa-and-tauri-without-pretending-they-are.jsonld"}}