Revert "M2: fix ws adapter for browser WebSocket + firebase path prefix"
This reverts commit e743d40e8d.
This commit is contained in:
+22
-61
@@ -3,26 +3,12 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
// Use native browser WebSocket when available (production). Fallback to the
|
||||
// `ws` npm package in Node/jest where global WebSocket is absent.
|
||||
let WebSocketImpl;
|
||||
if (typeof WebSocket !== 'undefined') {
|
||||
WebSocketImpl = WebSocket;
|
||||
} else {
|
||||
// require inside else so webpack ignores it in browser bundle
|
||||
WebSocketImpl = require('ws').WebSocket;
|
||||
}
|
||||
const { WebSocket } = require('ws');
|
||||
|
||||
function createWsStorage({ baseUrl, wsUrl } = {}) {
|
||||
const API = (baseUrl || 'http://127.0.0.1:4001').replace(/\/$/, '');
|
||||
const WS = wsUrl || (API.replace(/^http/, 'ws') + '/ws');
|
||||
|
||||
// App.js passes firebase-prefixed paths: artifacts/{APP_ID}/public/data/campaigns/...
|
||||
// Backend uses canonical: campaigns/... Strip the prefix so all matchers work.
|
||||
function norm(p) {
|
||||
return p.replace(/^[\s\S]*\/public\/data\//, '');
|
||||
}
|
||||
|
||||
const docSubs = new Map(); // path -> Set<cb>
|
||||
const collSubs = new Map(); // collPath -> Set<cb>
|
||||
let ws = null;
|
||||
@@ -32,28 +18,14 @@ function createWsStorage({ baseUrl, wsUrl } = {}) {
|
||||
function ensureWs() {
|
||||
if (wsReady) return wsReady;
|
||||
wsReady = new Promise((resolve, reject) => {
|
||||
ws = new WebSocketImpl(WS);
|
||||
// addEventListener works on both browser WebSocket and Node ws pkg.
|
||||
const onOpen = () => resolve(ws);
|
||||
const onError = (err) => { wsReady = null; reject(err instanceof Event ? new Error('ws error') : err); };
|
||||
const onClose = () => { wsReady = null; };
|
||||
const onMessage = (ev) => {
|
||||
const raw = typeof ev === 'string' ? ev : (ev.data !== undefined ? ev.data : ev);
|
||||
let msg; try { msg = JSON.parse(typeof raw === 'string' ? raw : raw.toString()); } catch { return; }
|
||||
ws = new WebSocket(WS);
|
||||
ws.on('open', () => resolve(ws));
|
||||
ws.on('error', (err) => { wsReady = null; reject(err); });
|
||||
ws.on('close', () => { wsReady = null; });
|
||||
ws.on('message', (raw) => {
|
||||
let msg; try { msg = JSON.parse(raw.toString()); } catch { return; }
|
||||
handleMessage(msg);
|
||||
};
|
||||
// browser-style property handlers
|
||||
ws.onopen = onOpen;
|
||||
ws.onerror = onError;
|
||||
ws.onclose = onClose;
|
||||
ws.onmessage = onMessage;
|
||||
// Node ws-style addEventListener fallback (noop in browser if absent)
|
||||
if (typeof ws.addEventListener === 'function') {
|
||||
ws.addEventListener('open', onOpen);
|
||||
ws.addEventListener('error', onError);
|
||||
ws.addEventListener('close', onClose);
|
||||
ws.addEventListener('message', onMessage);
|
||||
}
|
||||
});
|
||||
});
|
||||
return wsReady;
|
||||
}
|
||||
@@ -62,16 +34,14 @@ function createWsStorage({ baseUrl, wsUrl } = {}) {
|
||||
async function handleMessage(msg) {
|
||||
if (msg.type !== 'change' || !msg.change) return;
|
||||
const c = msg.change;
|
||||
// Notify doc subscribers whose normalized path we cached.
|
||||
for (const [rawPath, cbs] of docSubs) {
|
||||
const path = norm(rawPath);
|
||||
// Notify doc subscribers whose path we cached.
|
||||
for (const [path, cbs] of docSubs) {
|
||||
if (pathMatchesChange(path, c)) {
|
||||
const doc = await storage.getDoc(path);
|
||||
cbs.forEach(cb => cb(doc));
|
||||
}
|
||||
}
|
||||
for (const [rawCollPath, cbs] of collSubs) {
|
||||
const collPath = norm(rawCollPath);
|
||||
for (const [collPath, cbs] of collSubs) {
|
||||
if (collMatchesChange(collPath, c)) {
|
||||
const docs = await storage.getCollection(collPath);
|
||||
cbs.forEach(cb => cb(docs));
|
||||
@@ -113,8 +83,8 @@ function createWsStorage({ baseUrl, wsUrl } = {}) {
|
||||
|
||||
const storage = {
|
||||
// --- reads ---
|
||||
async getDoc(rawPath) {
|
||||
const path = norm(rawPath);
|
||||
async getDoc(path) {
|
||||
// canonical paths used by App.js
|
||||
if (path === 'activeDisplay/status') {
|
||||
const ad = await api('GET', '/api/activeDisplay');
|
||||
return ad;
|
||||
@@ -132,8 +102,7 @@ function createWsStorage({ baseUrl, wsUrl } = {}) {
|
||||
return null;
|
||||
},
|
||||
|
||||
async getCollection(rawCollPath) {
|
||||
const collPath = norm(rawCollPath);
|
||||
async getCollection(collPath) {
|
||||
if (collPath === 'campaigns') return await api('GET', '/api/campaigns');
|
||||
const m = collPath.match(/^campaigns\/([^/]+)\/encounters$/);
|
||||
if (m) return await api('GET', `/api/campaigns/${m[1]}/encounters`);
|
||||
@@ -142,8 +111,7 @@ function createWsStorage({ baseUrl, wsUrl } = {}) {
|
||||
},
|
||||
|
||||
// --- writes (translated to backend action endpoints) ---
|
||||
async setDoc(rawPath, data) {
|
||||
const path = norm(rawPath);
|
||||
async setDoc(path, data) {
|
||||
// activeDisplay merges
|
||||
if (path === 'activeDisplay/status') {
|
||||
if ('activeCampaignId' in data || 'activeEncounterId' in data) {
|
||||
@@ -167,8 +135,7 @@ function createWsStorage({ baseUrl, wsUrl } = {}) {
|
||||
}
|
||||
},
|
||||
|
||||
async updateDoc(rawPath, patch) {
|
||||
const path = norm(rawPath);
|
||||
async updateDoc(path, patch) {
|
||||
const cm = path.match(/^campaigns\/([^/]+)$/);
|
||||
if (cm) {
|
||||
if (Array.isArray(patch.players)) {
|
||||
@@ -192,16 +159,14 @@ function createWsStorage({ baseUrl, wsUrl } = {}) {
|
||||
}
|
||||
},
|
||||
|
||||
async deleteDoc(rawPath) {
|
||||
const path = norm(rawPath);
|
||||
async deleteDoc(path) {
|
||||
const cm = path.match(/^campaigns\/([^/]+)$/);
|
||||
if (cm) { await api('DELETE', `/api/campaigns/${cm[1]}`); return; }
|
||||
const em = path.match(/^campaigns\/([^/]+)\/encounters\/([^/]+)$/);
|
||||
if (em) { await api('DELETE', `/api/campaigns/${em[1]}/encounters/${em[2]}`); return; }
|
||||
},
|
||||
|
||||
async addDoc(rawCollPath, data) {
|
||||
const collPath = norm(rawCollPath);
|
||||
async addDoc(collPath, data) {
|
||||
if (collPath === 'logs') {
|
||||
// backend auto-logs; direct insert not needed
|
||||
return { id: 'auto', path: 'logs/auto' };
|
||||
@@ -216,8 +181,7 @@ function createWsStorage({ baseUrl, wsUrl } = {}) {
|
||||
}
|
||||
},
|
||||
|
||||
subscribeDoc(rawPath, cb) {
|
||||
const path = norm(rawPath);
|
||||
subscribeDoc(path, cb) {
|
||||
ensureWs().then(() => {
|
||||
// subscribe to coarse change types that could affect this path
|
||||
const types = changeTypesForDocPath(path);
|
||||
@@ -230,8 +194,7 @@ function createWsStorage({ baseUrl, wsUrl } = {}) {
|
||||
return () => { docSubs.get(path)?.delete(cb); };
|
||||
},
|
||||
|
||||
subscribeCollection(rawCollPath, cb) {
|
||||
const collPath = norm(rawCollPath);
|
||||
subscribeCollection(collPath, cb) {
|
||||
ensureWs().then(() => {
|
||||
const types = changeTypesForCollPath(collPath);
|
||||
types.forEach(t => ws.send(JSON.stringify({ type: 'subscribe', key: t })));
|
||||
@@ -250,15 +213,13 @@ function createWsStorage({ baseUrl, wsUrl } = {}) {
|
||||
return storage;
|
||||
}
|
||||
|
||||
function changeTypesForDocPath(rawPath) {
|
||||
const path = rawPath.replace(/^[\s\S]*\/public\/data\//, '');
|
||||
function changeTypesForDocPath(path) {
|
||||
if (path === 'activeDisplay/status') return ['activeDisplay'];
|
||||
if (path.match(/^campaigns\/[^/]+\/encounters\//)) return ['encounter', 'activeDisplay'];
|
||||
if (path.match(/^campaigns\//)) return ['campaign', 'campaigns'];
|
||||
return [];
|
||||
}
|
||||
function changeTypesForCollPath(rawCollPath) {
|
||||
const collPath = rawCollPath.replace(/^[\s\S]*\/public\/data\//, '');
|
||||
function changeTypesForCollPath(collPath) {
|
||||
if (collPath === 'campaigns') return ['campaigns'];
|
||||
if (collPath.match(/^campaigns\/[^/]+\/encounters$/)) return ['encounters'];
|
||||
if (collPath === 'logs') return ['logs'];
|
||||
|
||||
Reference in New Issue
Block a user