fix(storage): neutral queryConstraints honored by both adapters

Bug: server adapter accepted queryConstraints (orderBy/limit) but ignored them.
Combat log [orderBy('timestamp','desc'), limit(500)] returned ALL logs unordered
in server mode — unbounded growth. No test caught it: shared contract tested
subscribeCollection with 0 constraints; the firebase-only queryConstraint test
never touched the server adapter. Parity gap.

Fix (Plan A — neutral shape):
- index.js: orderBy()/limit() now NEUTRAL builders returning {__type}. Removed
  SDK orderBy/limit re-export.
- firebase.js: subscribeCollection translates neutral -> SDK query constraints.
- server.js: applyConstraints() helper sorts/limits client-side (backend returns
  all). Constraints stored per collection, applied on initial fetch + WS change.
- contract.js: added 3 queryConstraint tests (orderBy desc, limit, no-constraints)
  to SHARED contract — both adapters now run identical assertions.
- firebase.contract.test.js: removed now-redundant firebase-only constraint
  block (covered by shared contract).

Data impact: ZERO. Constraints are query-time only, never stored. Combat log
docs keep timestamp field. No Firebase data migration needed.

208 tests green.
This commit is contained in:
david raistrick
2026-07-04 16:06:28 -04:00
parent e94e1959ff
commit 27b2272ced
5 changed files with 98 additions and 50 deletions
+11 -3
View File
@@ -130,8 +130,15 @@ export function createFirebaseStorage() {
subscribeCollection(collectionPath, cb, queryConstraints = [], errCb) {
recordAdapterCall({ fn: 'subscribeCollection', path: collectionPath });
const q = queryConstraints.length > 0
? query(collection(db, collectionPath), ...queryConstraints)
// queryConstraints = neutral {__type} builders (from index.js).
// Translate to SDK orderBy/limit.
const sdkConstraints = queryConstraints.map(c => {
if (c.__type === 'orderBy') return orderBy(c.field, c.dir);
if (c.__type === 'limit') return limit(c.n);
return c; // pass-through (forward compat)
});
const q = sdkConstraints.length > 0
? query(collection(db, collectionPath), ...sdkConstraints)
: collection(db, collectionPath);
return onSnapshot(q, (snap) => {
cb(snap.docs.map(d => ({ id: d.id, ...d.data() })));
@@ -146,7 +153,8 @@ export function createFirebaseStorage() {
}
// Re-export SDK pieces App.js uses directly (until full refactor).
// orderBy/limit NOT re-exported: App uses neutral builders from index.js.
export {
doc, setDoc, updateDoc, deleteDoc, addDoc, collection, onSnapshot,
query, orderBy, limit, writeBatch,
query, writeBatch,
};