@abraca/dabra 2.20.0 → 2.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -158,9 +158,12 @@ declare class OfflineStore {
158
158
  private db;
159
159
  /**
160
160
  * @param docId The document UUID.
161
- * @param serverOrigin Hostname of the server (e.g. "abra.cou.sh").
162
- * When provided the IndexedDB database is namespaced
163
- * per-server, preventing cross-server data contamination.
161
+ * @param serverOrigin Host of the server, including a non-default port
162
+ * (e.g. "abra.cou.sh", "localhost:3001"). When provided
163
+ * the IndexedDB database is namespaced per-server,
164
+ * preventing cross-server data contamination — the port
165
+ * matters because two same-host servers sharing the
166
+ * default root_doc_id would otherwise collide.
164
167
  */
165
168
  constructor(docId: string, serverOrigin?: string);
166
169
  private dbPromise;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abraca/dabra",
3
- "version": "2.20.0",
3
+ "version": "2.21.0",
4
4
  "description": "abracadabra provider",
5
5
  "keywords": [
6
6
  "abracadabra",
@@ -41,7 +41,7 @@
41
41
  "yjs": "^13.6.8"
42
42
  },
43
43
  "devDependencies": {
44
- "@abraca/schema": "2.20.0"
44
+ "@abraca/schema": "2.21.0"
45
45
  },
46
46
  "scripts": {
47
47
  "test": "node --no-warnings --conditions=source --experimental-transform-types --test 'tests/*.test.ts'"
@@ -717,9 +717,18 @@ export class AbracadabraBaseProvider extends EventEmitter {
717
717
 
718
718
  this.configuration.websocketProvider.on("rateLimited", this.forwardRateLimited);
719
719
 
720
- this.configuration.websocketProvider.attach(this);
721
-
720
+ // Mark attached BEFORE registering with the socket: when the shared
721
+ // socket is already connected (the loadChild case), wsp.attach()
722
+ // synchronously invokes onOpen() → sendToken(), and send() drops frames
723
+ // while _isAttached is false. With a crypto identity OBJECT sendToken()
724
+ // hits no await before send(), so the AUTH frame was silently swallowed
725
+ // and the doc never authenticated on the connection — the server then
726
+ // (correctly) ignored every subsequent frame for it: child providers
727
+ // never synced and their writes were dropped. (JWT auth only survived
728
+ // by accident: `await getToken()` defers send() past attach().)
722
729
  this._isAttached = true;
730
+
731
+ this.configuration.websocketProvider.attach(this);
723
732
  }
724
733
 
725
734
  permissionDeniedHandler(reason: string) {
@@ -244,7 +244,13 @@ export class AbracadabraProvider extends AbracadabraBaseProvider {
244
244
  config.url ??
245
245
  (config.websocketProvider as AbracadabraWS | undefined)?.url ??
246
246
  client?.wsUrl;
247
- if (url) return new URL(url).hostname;
247
+ // `host` (NOT `hostname`): the port is part of a server's identity.
248
+ // Two servers on the same host but different ports would otherwise
249
+ // share one IDB namespace — and since default-config servers also
250
+ // share the same root_doc_id, one server's cached doc state silently
251
+ // hydrated into the other's Y.Doc (cross-server contamination that
252
+ // can then sync back upstream).
253
+ if (url) return new URL(url).host;
248
254
  } catch {
249
255
  // Malformed URL — fall back to no scoping
250
256
  }
@@ -165,10 +165,12 @@ export class BackgroundSyncManager extends EventEmitter {
165
165
  maxRetries: opts?.maxRetries ?? 2,
166
166
  };
167
167
 
168
- // Derive server origin from client URL for IDB namespacing
168
+ // Derive server origin from client URL for IDB namespacing.
169
+ // `host` (not `hostname`): port-scoped so same-host servers on
170
+ // different ports don't share a namespace.
169
171
  let serverOrigin = "default";
170
172
  try {
171
- serverOrigin = new URL((client as any).baseUrl ?? "").hostname;
173
+ serverOrigin = new URL((client as any).baseUrl ?? "").host;
172
174
  } catch {}
173
175
 
174
176
  this.persistence = new BackgroundSyncPersistence(serverOrigin);
@@ -377,10 +379,10 @@ export class BackgroundSyncManager extends EventEmitter {
377
379
  docIds.add(docId);
378
380
  }
379
381
 
380
- // Derive server origin the same way the provider does
382
+ // Derive server origin the same way the provider does (`host`, port-scoped)
381
383
  let serverOrigin: string | undefined;
382
384
  try {
383
- serverOrigin = new URL((this.client as any).baseUrl ?? "").hostname;
385
+ serverOrigin = new URL((this.client as any).baseUrl ?? "").host;
384
386
  } catch {}
385
387
 
386
388
  // Clear each document's offline store contents
@@ -69,9 +69,12 @@ export class OfflineStore {
69
69
 
70
70
  /**
71
71
  * @param docId The document UUID.
72
- * @param serverOrigin Hostname of the server (e.g. "abra.cou.sh").
73
- * When provided the IndexedDB database is namespaced
74
- * per-server, preventing cross-server data contamination.
72
+ * @param serverOrigin Host of the server, including a non-default port
73
+ * (e.g. "abra.cou.sh", "localhost:3001"). When provided
74
+ * the IndexedDB database is namespaced per-server,
75
+ * preventing cross-server data contamination — the port
76
+ * matters because two same-host servers sharing the
77
+ * default root_doc_id would otherwise collide.
75
78
  */
76
79
  constructor(docId: string, serverOrigin?: string) {
77
80
  this.storeKey = serverOrigin ? `${serverOrigin}/${docId}` : docId;