From e94e1959ff85f5135d07e6e69dc2b2c605bf8115 Mon Sep 17 00:00:00 2001 From: david raistrick <1108844+keen99@users.noreply.github.com> Date: Sat, 4 Jul 2026 15:47:13 -0400 Subject: [PATCH] refactor: rename ws adapter to server across codebase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'ws' conflated storage mode with transport protocol (WebSocket). Renamed for clarity: the adapter talks the self-hosted server (src/storage/server.js), which happens to use WebSocket for realtime — but the name should describe what it connects to, not how. Renames: - src/storage/ws.js -> server.js - createWsStorage() -> createServerStorage() - storage mode 'ws' -> 'server' - REACT_APP_BACKEND_WS -> REACT_APP_BACKEND_REALTIME_URL - factory param wsUrl -> realtimeUrl - server/tests/ws-*.test.js -> server-*.test.js Kept literal: 'ws' npm package, ws:// protocol URLs, /ws WebSocket endpoint. Transport is accurate there; only storage-naming changed. Updated all docs (DEVELOPMENT, TESTING, REWORK_PLAN, GLOSSARY, TODO), scripts (dev-start.sh, replay-combat.js), factory + ESM tests. 204 tests green. --- TODO.md | 12 +++++------ docs/DEVELOPMENT.md | 12 +++++------ docs/GLOSSARY.md | 4 ++-- docs/REWORK_PLAN.md | 20 +++++++++---------- docs/TESTING.md | 11 +++++----- scripts/README.md | 2 +- scripts/dev-start.sh | 8 ++++---- scripts/replay-combat.js | 6 +++--- server/db.js | 2 +- server/index.js | 2 +- ...ntract.test.js => server-contract.test.js} | 10 +++++----- ...nnect.test.js => server-reconnect.test.js} | 4 ++-- src/storage/index.js | 12 +++++------ src/storage/{ws.js => server.js} | 8 ++++---- src/tests/StorageEsm.test.js | 4 ++-- src/tests/storage.factory.test.js | 12 +++++------ 16 files changed, 64 insertions(+), 65 deletions(-) rename server/tests/{ws-contract.test.js => server-contract.test.js} (76%) rename server/tests/{ws-reconnect.test.js => server-reconnect.test.js} (93%) rename src/storage/{ws.js => server.js} (98%) diff --git a/TODO.md b/TODO.md index f0f7b5c..b476f61 100644 --- a/TODO.md +++ b/TODO.md @@ -20,11 +20,11 @@ TODO: custom condition field - ws = YOUR SQLite server. server/db.js = better-sqlite3. src/storage/ws.js = client that talks to it (REST + + server = YOUR SQLite server. server/db.js = better-sqlite3. src/storage/server.js = client that talks to it (REST + websocket). Name bad. Should be "sqlite-adapter" or "server". You right. memory = in-process JS Map. Useless? Mostly yes for prod. Value: fast contract tests, no server spinup. If - you skip memory, contract test still runs vs firebase-mock + live ws-server. Memory = optional convenience. + contract test runs vs firebase-mock + live server backend. Memory = optional convenience. === extra complication. @@ -136,7 +136,7 @@ Memory = optional convenience. === extra complication. `src/__mocks__/firebase/firestore.js` setDoc records call, IGNORES opts (no actual merge). Mock must simulate firebase merge semantics for test to pass. Fix = mock setDoc: if opts.merge, MOCK_DB.merge(path,data) else - replace. OR change App.js setDoc(merge) → updateDoc (cleaner, ws adapter + replace. OR change App.js setDoc(merge) → updateDoc (cleaner, server adapter uses PATCH). Decide which. - Test: render App + DisplayView, toggle hide-HP, assert display still shows encounter (not paused). @@ -166,8 +166,8 @@ Memory = optional convenience. === extra complication. - `reorderParticipants` returns `log: null`. Other ops return `log.undo`. - Cannot undo drag-drop. Candidate for undo system (M6). -### BUG-8: ws adapter has no reconnect -- Test: `server/tests/ws-reconnect.test.js` (RED). +### BUG-8: server adapter has no reconnect +- Test: `server/tests/server-reconnect.test.js` (RED). - WS dies (idle/error/close) → `wsReady=null`, subscribers dead forever. - Display frozen until full reload. - Fix: `onclose` → reconnect + re-subscribe existing paths. @@ -245,7 +245,7 @@ Memory = optional convenience. === extra complication. - [x] BUG-6: fixed structurally (1-list model) - [x] BUG-12: fixed --- campaign selection follows activeDisplay - [x] BUG-15: fixed --- DisplayView no longer re-sorts (drag order preserved) -- [x] BUG-8: ws adapter reconnect (implemented + GREEN) +- [x] BUG-8: server adapter reconnect (implemented + GREEN) - [ ] BUG-10: deact+reactivate double-act - [ ] BUG-11: FE Combat.scenario crash - [ ] BUG-13: reorder cross-pointer semantics (RED + decide block/allow) diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index 70d18c4..cb704cc 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -65,12 +65,12 @@ curl http://127.0.0.1:4001/health # -> {"ok":true} Never put db in `/tmp` (wipe risk). Use `./data/` (gitignored) or docker volume. -### Frontend (dev server, ws mode) +### Frontend (dev server, server mode) ```bash -REACT_APP_STORAGE=ws \ +REACT_APP_STORAGE=server \ REACT_APP_BACKEND_URL=http://127.0.0.1:4001 \ -REACT_APP_BACKEND_WS=ws://127.0.0.1:4001/ws \ +REACT_APP_BACKEND_REALTIME_URL=ws://127.0.0.1:4001/ws \ BROWSER=none PORT=3999 \ npm start ``` @@ -225,12 +225,12 @@ App passes firebase-prefixed paths (`artifacts/{APP_ID}/public/data/campaigns/.. `getStorageMode()` reads `REACT_APP_STORAGE` env (default `firebase`). - `firebase` → real SDK init -- `ws`/`memory` → stub auth + db sentinel, route via `storage.*` adapter +- `server` → stub auth + db sentinel, route via `storage.*` adapter ## Test layers -- **Layer 1**: App vs firebase mock. Proves adapter call shape. Never exercises ws adapter. -- **Layer 2**: ws adapter vs live backend. Proves translation + path identity. +- **Layer 1**: App vs firebase mock. Proves adapter call shape. Never exercises server adapter. +- **Layer 2**: server adapter vs live backend. Proves translation + path identity. Both required — Layer 1 alone misses adapter bugs (path mismatch, no-op players, ws.on EventEmitter vs browser handlers). diff --git a/docs/GLOSSARY.md b/docs/GLOSSARY.md index bddf0b6..c708ec8 100644 --- a/docs/GLOSSARY.md +++ b/docs/GLOSSARY.md @@ -52,8 +52,8 @@ Round 2: turn 1 (Fighter) → ... → turn 8 (Merchant) [round counter +=1 at | Term | Meaning | |------|---------| -| **Adapter** | `src/storage/{firebase,ws,memory}.js`. Contract boundary between App and backend. App only calls `storage.*`, never raw SDK/fetch. | +| **Adapter** | `src/storage/{firebase,server}.js`. Contract boundary between App and backend. App only calls `storage.*`, never raw SDK/fetch. | | **Path normalization** (`norm()`) | Strip firebase prefix (`artifacts/{APP_ID}/public/data/`) → bare canonical path (`campaigns/X`). Runs inside every adapter method. | | **Generic KV doc store** | Backend stores opaque JSON at arbitrary path strings. No shape-specific endpoints. Backend = firebase mirror, not REST API for app entities. | | **Layer 1 test** | App vs firebase mock. Proves adapter call shape. | -| **Layer 2 test** | ws adapter vs live backend. Proves translation + path identity. | +| **Layer 2 test** | server adapter vs live backend. Proves translation + path identity. | diff --git a/docs/REWORK_PLAN.md b/docs/REWORK_PLAN.md index e5804c7..8ca0182 100644 --- a/docs/REWORK_PLAN.md +++ b/docs/REWORK_PLAN.md @@ -61,7 +61,7 @@ The storage interface is the test seam and the upstream-compat layer. | Impl | When used | Automated-tested? | |---|---|---| | `firebase.js` | default (`STORAGE=firebase`) — upstream path | No — requires live Firebase project | -| `ws.js` | `STORAGE=ws` — our fork, talks to backend | Yes — against running backend | +| `server.js` | `STORAGE=server` — our fork, talks to backend | Yes — against running backend | | `memory.js` | test-only, in-process | Yes — fast, deterministic | **Frontend interface contract** (all three implement): @@ -82,7 +82,7 @@ Memory impl: in-memory Map + EventEmitter, for tests (M3). storage/ index.js # factory: pick impl from STORAGE env firebase.js # extracted from current App.js (verbatim) - ws.js # NEW — talks to backend + server.js # talks to backend (was ws.js) memory.js # NEW — test only contract.js # interface spec (runStorageContract) tests/ # frontend tests @@ -116,7 +116,7 @@ Each milestone = independently mergeable PR upstream (unless marked ❌). | M | Does | Tests? | |---|---|---| | 0 | repo, branch, remotes | no | -| 1 | build backend (Node+Express+ws+better-sqlite3) | unit tests as built | +| 1 | build backend (Node+Express+WebSocket+better-sqlite3) | unit tests as built | | 2 | frontend WS adapter — app runs vs backend, cross-device works | yes | | 3 | characterization tests lock current behavior | yes | | 4 | resolve initiative rotation corruption (BUG-5) | yes | @@ -135,7 +135,7 @@ Each milestone = independently mergeable PR upstream (unless marked ❌). - **Upstream-PRable:** n/a (fork infra) ### Milestone 1 — Build backend ✅ -- `server/`: Express + ws + better-sqlite3. +- `server/`: Express + WebSocket + better-sqlite3. - Generic KV doc store (firebase mirror): `docs` table (path PK, parent, data JSON, updated_at). REST: GET/PUT/PATCH/DELETE `/api/doc?path=`, GET `/api/collection?path=`, POST `/api/collection`, POST `/api/batch`. WS: subscribe by path. - Server holds authoritative state. No turn logic server-side (logic stays client-side in `shared/turn.js`). - **Exit criteria:** backend boots, serves state over WS, persists to SQLite, unit tests green. ✅ DONE. @@ -144,10 +144,10 @@ Each milestone = independently mergeable PR upstream (unless marked ❌). ### Milestone 2 — Frontend WS adapter ✅ - Define `storage/contract.js` interface spec. - Move all Firestore call sites from `App.js` into `storage/firebase.js` behind interface (verbatim). -- Implement `storage/ws.js` per interface, talking to backend. Generic KV ops, subscribes to WS. +- Implement `storage/server.js` per interface, talking to backend. Generic KV ops, subscribes via WebSocket. - Implement `storage/memory.js` for frontend unit tests. - `storage/index.js` factory: `STORAGE` env → pick impl. Default `firebase` (upstream unchanged). -- App runs against backend with `STORAGE=ws`. +- App runs against backend with `STORAGE=server`. - Cross-device verified manually: DM view + player display + tablet. - **Exit criteria:** app runs fully against local backend, no Firebase. Multi-device sync works. ✅ DONE. - **Upstream-PRable:** ⚠️ partial. Storage interface + firebase extract = ✅. WS impl = ❌. @@ -155,7 +155,7 @@ Each milestone = independently mergeable PR upstream (unless marked ❌). ### Milestone 3 — Characterization tests lock current behavior ✅ - Lock current behavior via tests. - Cover: START, NEXT_TURN, PAUSE, RESUME, ADD_PARTICIPANT, REMOVE_PARTICIPANT, TOGGLE_ACTIVE, REORDER, APPLY_DAMAGE/HEAL, DEATH_SAVE, END. -- Two layers: Layer 1 (App + firebase mock, proves call shape), Layer 2 (ws adapter vs live backend, proves translation). +- Two layers: Layer 1 (App + firebase mock, proves call shape), Layer 2 (server adapter vs live backend, proves translation). - Iterate until confident: baseline solid, regressions impossible to silently slip. - **Exit criteria:** characterization suite green. Baseline locked. ✅ DONE. - **Upstream-PRable:** ✅ if kept storage-agnostic (tests target turn logic shape). @@ -173,7 +173,7 @@ Each milestone = independently mergeable PR upstream (unless marked ❌). - Single container: caddy (front, static + proxy) + node backend (internal :4001). - Files in `docker/` tree (kept separate from upstream root Dockerfile): - `docker/Dockerfile` — build FE + BE, runtime caddy+node - - `docker/Caddyfile` — proxy /api + /ws to node, static SPA fallback + - `docker/Caddyfile` — proxy /api + /ws (WebSocket path) to node, static SPA fallback - `docker/entrypoint.sh` — node bg + caddy fg - `docker/docker-compose.yml` — one `app` service, volume for sqlite - Run: `docker compose -f docker/docker-compose.yml up --build` (or `cd docker && docker compose up --build`). Port 8080. @@ -214,7 +214,7 @@ Each milestone = independently mergeable PR upstream (unless marked ❌). ### Manual smoke via config flags - `STORAGE=firebase` → current behavior (friend's path, upstream default). -- `STORAGE=ws` → our path, local backend. +- `STORAGE=server` → our path, local backend. - docker-compose profiles mirror the above. ### Accepted test gap @@ -261,6 +261,6 @@ Default `STORAGE=firebase` + `AUTH_MODE=none` (unset) = upstream sees literally - M0 ✅, M1 ✅, M2 ✅, M3 ✅, M4 ✅, M5 ✅ - Backend live: port 4001, db `./data/tracker.sqlite` -- Frontend: port 3999 with `REACT_APP_STORAGE=ws` +- Frontend: port 3999 with `REACT_APP_STORAGE=server` - Test suite: ~160 tests (shared + server + FE). Bugs tracked in `TODO.md`. - Next milestones: M5 docker-compose. Undo moved to TODO backlog. diff --git a/docs/TESTING.md b/docs/TESTING.md index 6b099a9..65a76da 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -194,11 +194,11 @@ curl http://127.0.0.1:4001/health # → {"ok":true} Never db in `/tmp` (wipe risk). Use `./data/` (gitignored) or docker volume. -### Frontend (ws mode) +### Frontend (server mode) ```bash -REACT_APP_STORAGE=ws \ +REACT_APP_STORAGE=server \ REACT_APP_BACKEND_URL=http://127.0.0.1:4001 \ -REACT_APP_BACKEND_WS=ws://127.0.0.1:4001/ws \ +REACT_APP_BACKEND_REALTIME_URL=ws://127.0.0.1:4001/ws \ BROWSER=none PORT=3999 \ npm start ``` @@ -210,10 +210,9 @@ Firebase mode (default): set `REACT_APP_FIREBASE_*` in `.env.local` (copy `env.e `STORAGE_MODE = getStorageMode()` reads `REACT_APP_STORAGE`: - `firebase` (default) → real SDK -- `ws` → backend (docker/prod) -- `memory` → in-process (test seed) +- `server` → backend (docker/prod) -All adapters ESM. Adapter contract: `src/storage/contract.js` — same spec vs memory/ws/firebase. +All adapters ESM. Adapter contract: `src/storage/contract.js` — same spec vs server/firebase. ## Known RED / backlog diff --git a/scripts/README.md b/scripts/README.md index c7b0a26..cdbac53 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -4,7 +4,7 @@ Manual demo tool. NOT test. ## replay-combat.js -Live backend demo. Drives full combat via ws adapter (same contract as App). +Live backend demo. Drives full combat via server adapter (same contract as App). Player display live-updates. Watch UI react to state changes. ```bash diff --git a/scripts/dev-start.sh b/scripts/dev-start.sh index f49b1fa..7c14975 100755 --- a/scripts/dev-start.sh +++ b/scripts/dev-start.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Start local dev stack: node backend (sqlite) + react frontend, ws storage mode. +# Start local dev stack: node backend (sqlite) + react frontend, server storage mode. # Usage: ./scripts/dev-start.sh # Stop: ./scripts/dev-stop.sh set -euo pipefail @@ -25,12 +25,12 @@ else echo "backend already on :4001" fi -# frontend: ws storage, :3999 +# frontend: server storage, :3999 if ! lsof -ti :3999 >/dev/null 2>&1; then echo "starting frontend :3999..." - REACT_APP_STORAGE=ws \ + REACT_APP_STORAGE=server \ REACT_APP_BACKEND_URL=http://127.0.0.1:4001 \ - REACT_APP_BACKEND_WS=ws://127.0.0.1:4001/ws \ + REACT_APP_BACKEND_REALTIME_URL=ws://127.0.0.1:4001/ws \ BROWSER=none PORT=3999 \ nohup npm start > tmp/fe.log 2>&1 & echo $! > tmp/fe.pid diff --git a/scripts/replay-combat.js b/scripts/replay-combat.js index cbc6210..da5840d 100644 --- a/scripts/replay-combat.js +++ b/scripts/replay-combat.js @@ -30,10 +30,10 @@ const { toggleParticipantActive, applyHpChange, deathSave, toggleCondition, reorderParticipants, endEncounter, } = shared; -const { createWsStorage } = require('../src/storage/ws'); +const { createServerStorage } = require('../src/storage/server'); const BACKEND = process.env.BACKEND_URL || 'http://127.0.0.1:4001'; -const WS_URL = process.env.BACKEND_WS || BACKEND.replace(/^http/, 'ws') + '/ws'; +const WS_URL = process.env.BACKEND_REALTIME_URL || BACKEND.replace(/^http/, 'ws') + '/ws'; const ROUNDS = parseInt(process.argv[2], 10) || 100; const DELAY = parseInt(process.argv[3], 10) || 200; @@ -51,7 +51,7 @@ const getPath = { const sleep = (ms) => new Promise(r => setTimeout(r, ms)); // Use the ADAPTER as the contract boundary (same as App). No raw REST. -const storage = createWsStorage({ baseUrl: BACKEND, wsUrl: WS_URL }); +const storage = createServerStorage({ baseUrl: BACKEND, realtimeUrl: WS_URL }); // Mirror App.js CONDITIONS so we exercise all of them. const CONDITIONS = [ diff --git a/server/db.js b/server/db.js index 63ac4de..ed9662e 100644 --- a/server/db.js +++ b/server/db.js @@ -10,7 +10,7 @@ // logs/{id} doc // // No shape-specific tables. Data is opaque JSON. This is the firebase mirror: -// the adapter (src/storage/ws.js) is a thin passthrough, app logic unchanged. +// the adapter (src/storage/server.js) is a thin passthrough, app logic unchanged. 'use strict'; diff --git a/server/index.js b/server/index.js index 48b104f..8f7bf43 100644 --- a/server/index.js +++ b/server/index.js @@ -1,6 +1,6 @@ // server/index.js — generic KV document store over HTTP + WebSocket. // firebase mirror: doc-tree model. Thin REST, path-based WS push. -// Adapter (src/storage/ws.js) = passthrough, no shape translation. +// Adapter (src/storage/server.js) = passthrough, no shape translation. 'use strict'; diff --git a/server/tests/ws-contract.test.js b/server/tests/server-contract.test.js similarity index 76% rename from server/tests/ws-contract.test.js rename to server/tests/server-contract.test.js index 0f6b9cb..31c4c34 100644 --- a/server/tests/ws-contract.test.js +++ b/server/tests/server-contract.test.js @@ -1,9 +1,9 @@ -// Layer 2 test: exercise ws.js storage adapter against a LIVE backend. +// Layer 2 test: exercise server.js storage adapter against a LIVE backend. // Complements Layer 1 (App + firebase mock) which proves App call shape but // never touches ws.js. This catches translation bugs in the adapter. // // Runs the shared storage contract (same spec memory/firebase satisfy) against -// createWsStorage pointed at an ephemeral backend instance. A FRESH backend is +// createServerStorage pointed at an ephemeral backend instance. A FRESH backend is // spun up per test to guarantee isolation (backend has no reset endpoint yet). 'use strict'; @@ -11,7 +11,7 @@ const path = require('path'); const os = require('os'); const { createServer } = require('../index'); -const { createWsStorage } = require('../../src/storage/ws'); +const { createServerStorage } = require('../../src/storage/server'); const { runStorageContract } = require('../../src/storage/contract'); // Factory: fresh backend (unique sqlite file) + storage pointed at it. @@ -26,9 +26,9 @@ async function makeStorage() { const port = handle.server.address().port; const baseUrl = `http://127.0.0.1:${port}`; const wsUrl = `ws://127.0.0.1:${port}/ws`; - const storage = createWsStorage({ baseUrl, wsUrl }); + const storage = createServerStorage({ baseUrl, realtimeUrl: wsUrl }); storage.dispose = (done) => handle.close(done); return storage; } -runStorageContract('ws (live backend)', makeStorage); +runStorageContract('server (live backend)', makeStorage); diff --git a/server/tests/ws-reconnect.test.js b/server/tests/server-reconnect.test.js similarity index 93% rename from server/tests/ws-reconnect.test.js rename to server/tests/server-reconnect.test.js index 1b38a7b..d8296b7 100644 --- a/server/tests/ws-reconnect.test.js +++ b/server/tests/server-reconnect.test.js @@ -8,7 +8,7 @@ const path = require('path'); const os = require('os'); const { createServer } = require('../index'); -const { createWsStorage } = require('../../src/storage/ws'); +const { createServerStorage } = require('../../src/storage/server'); const flush = (ms = 150) => new Promise(r => setTimeout(r, ms)); @@ -22,7 +22,7 @@ async function makeStorage() { const port = handle.server.address().port; const baseUrl = `http://127.0.0.1:${port}`; const wsUrl = `ws://127.0.0.1:${port}/ws`; - const storage = createWsStorage({ baseUrl, wsUrl }); + const storage = createServerStorage({ baseUrl, realtimeUrl: wsUrl }); storage.dispose = (done) => handle.close(done); return storage; } diff --git a/src/storage/index.js b/src/storage/index.js index 91b9a48..f8ab9a0 100644 --- a/src/storage/index.js +++ b/src/storage/index.js @@ -1,5 +1,5 @@ // src/storage/index.js — storage factory + SDK re-exports. -// STORAGE=firebase (default): adapter wraps SDK. STORAGE=ws: backend. +// STORAGE=firebase (default): adapter wraps SDK. STORAGE=server: backend. // App.js imports getStorage() for subscribe; still imports SDK pieces for writes (per-group refactor pending). import { initializeApp } from 'firebase/app'; @@ -11,7 +11,7 @@ import { onSnapshot, updateDoc, deleteDoc, query, orderBy, limit, writeBatch, } from 'firebase/firestore'; import { initFirebase, createFirebaseStorage } from './firebase'; -import { createWsStorage } from './ws'; +import { createServerStorage } from './server'; let storageInstance = null; @@ -23,13 +23,13 @@ export function getStorage() { const ok = initFirebase(); if (!ok) throw new Error('Firebase config missing. Check REACT_APP_FIREBASE_* env.'); storageInstance = createFirebaseStorage(); - } else if (mode === 'ws') { - storageInstance = createWsStorage({ + } else if (mode === 'server') { + storageInstance = createServerStorage({ baseUrl: process.env.REACT_APP_BACKEND_URL || '', - wsUrl: process.env.REACT_APP_BACKEND_WS || '', + realtimeUrl: process.env.REACT_APP_BACKEND_REALTIME_URL || '', }); } else { - throw new Error(`Unknown REACT_APP_STORAGE mode: ${mode}. Use 'firebase' or 'ws'.`); + throw new Error(`Unknown REACT_APP_STORAGE mode: ${mode}. Use 'firebase' or 'server'.`); } return storageInstance; } diff --git a/src/storage/ws.js b/src/storage/server.js similarity index 98% rename from src/storage/ws.js rename to src/storage/server.js index e26b17b..4500a55 100644 --- a/src/storage/ws.js +++ b/src/storage/server.js @@ -11,13 +11,13 @@ if (typeof WebSocket !== 'undefined') { WebSocketImpl = WebSocket; } -function createWsStorage({ baseUrl, wsUrl } = {}) { +function createServerStorage({ baseUrl, realtimeUrl } = {}) { // Same-origin by default: empty baseUrl = relative fetch (Caddy/proxy). // Fallback to localhost for bare `npm start` dev without proxy. const API = (baseUrl || (typeof window !== 'undefined' && window.location ? '' : 'http://127.0.0.1:4001')).replace(/\/$/, ''); let WS; - if (wsUrl) { - WS = wsUrl; + if (realtimeUrl) { + WS = realtimeUrl; } else if (typeof window !== 'undefined' && window.location) { // derive from current origin (http→ws, https→wss), same host/port. const proto = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; @@ -259,4 +259,4 @@ function createWsStorage({ baseUrl, wsUrl } = {}) { return storage; } -export { createWsStorage }; +export { createServerStorage }; diff --git a/src/tests/StorageEsm.test.js b/src/tests/StorageEsm.test.js index 14cc92d..d5bcbd7 100644 --- a/src/tests/StorageEsm.test.js +++ b/src/tests/StorageEsm.test.js @@ -1,6 +1,6 @@ // Lock: storage adapters must use ESM exports (no module.exports). // Regression guard: CJS in src/ crashes CRA prod build (ESM strict). -// Bug history: ws.js + firebase.js used module.exports. Dev lenient (masked), +// Bug history: server.js + firebase.js used module.exports. Dev lenient (masked), // prod bundle crashed blank page. firebase.js always ESM. import fs from 'fs'; import path from 'path'; @@ -8,7 +8,7 @@ import path from 'path'; const ADAPTER_DIR = path.join(__dirname, '..', 'storage'); describe('storage adapters use ESM (no CJS)', () => { - const adapters = ['ws.js', 'firebase.js', 'index.js']; + const adapters = ['server.js', 'firebase.js', 'index.js']; test.each(adapters)('%s has no module.exports', (file) => { const full = fs.readFileSync(path.join(ADAPTER_DIR, file), 'utf8'); // strip line comments so words like 'require' in explanatory comments don't trip the guard diff --git a/src/tests/storage.factory.test.js b/src/tests/storage.factory.test.js index 54ca1c7..52965ef 100644 --- a/src/tests/storage.factory.test.js +++ b/src/tests/storage.factory.test.js @@ -27,10 +27,10 @@ describe('getStorageMode', () => { expect(getStorageMode()).toBe('firebase'); }); - test('returns ws when REACT_APP_STORAGE=ws', () => { - process.env.REACT_APP_STORAGE = 'ws'; + test('returns server when REACT_APP_STORAGE=server', () => { + process.env.REACT_APP_STORAGE = 'server'; const { getStorageMode } = require('../storage/index'); - expect(getStorageMode()).toBe('ws'); + expect(getStorageMode()).toBe('server'); }); test('throws on unknown mode', () => { @@ -41,8 +41,8 @@ describe('getStorageMode', () => { }); describe('getStorage factory routing', () => { - test('ws mode returns ws adapter (init may fail without backend — catch)', () => { - process.env.REACT_APP_STORAGE = 'ws'; + test('server mode returns server adapter (init may fail without backend — catch)', () => { + process.env.REACT_APP_STORAGE = 'server'; const { getStorage } = require('../storage/index'); try { const s = getStorage(); @@ -56,7 +56,7 @@ describe('getStorage factory routing', () => { }); test('returns singleton on repeat call (same instance)', () => { - process.env.REACT_APP_STORAGE = 'ws'; + process.env.REACT_APP_STORAGE = 'server'; const { getStorage } = require('../storage/index'); let a; try { a = getStorage(); } catch (e) { return; } // no backend ok