@abraca/dabra 2.19.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.
@@ -2771,8 +2771,8 @@ var AbracadabraBaseProvider = class extends EventEmitter {
2771
2771
  this.configuration.websocketProvider.on("destroy", this.configuration.onDestroy);
2772
2772
  this.configuration.websocketProvider.on("destroy", this.forwardDestroy);
2773
2773
  this.configuration.websocketProvider.on("rateLimited", this.forwardRateLimited);
2774
- this.configuration.websocketProvider.attach(this);
2775
2774
  this._isAttached = true;
2775
+ this.configuration.websocketProvider.attach(this);
2776
2776
  }
2777
2777
  permissionDeniedHandler(reason) {
2778
2778
  this.emit("authenticationFailed", { reason });
@@ -2824,9 +2824,12 @@ function txPromise$2(store, request) {
2824
2824
  var OfflineStore = class {
2825
2825
  /**
2826
2826
  * @param docId The document UUID.
2827
- * @param serverOrigin Hostname of the server (e.g. "abra.cou.sh").
2828
- * When provided the IndexedDB database is namespaced
2829
- * per-server, preventing cross-server data contamination.
2827
+ * @param serverOrigin Host of the server, including a non-default port
2828
+ * (e.g. "abra.cou.sh", "localhost:3001"). When provided
2829
+ * the IndexedDB database is namespaced per-server,
2830
+ * preventing cross-server data contamination — the port
2831
+ * matters because two same-host servers sharing the
2832
+ * default root_doc_id would otherwise collide.
2830
2833
  */
2831
2834
  constructor(docId, serverOrigin) {
2832
2835
  this.db = null;
@@ -3033,6 +3036,7 @@ var AbracadabraProvider = class AbracadabraProvider extends AbracadabraBaseProvi
3033
3036
  }
3034
3037
  super(resolved);
3035
3038
  this.effectiveRole = null;
3039
+ this._writeDropWarned = false;
3036
3040
  this.childProviders = /* @__PURE__ */ new Map();
3037
3041
  this.pendingLoads = /* @__PURE__ */ new Map();
3038
3042
  this.childAccessTimes = /* @__PURE__ */ new Map();
@@ -3063,7 +3067,7 @@ var AbracadabraProvider = class AbracadabraProvider extends AbracadabraBaseProvi
3063
3067
  static deriveServerOrigin(config, client) {
3064
3068
  try {
3065
3069
  const url = config.url ?? config.websocketProvider?.url ?? client?.wsUrl;
3066
- if (url) return new URL(url).hostname;
3070
+ if (url) return new URL(url).host;
3067
3071
  } catch {}
3068
3072
  }
3069
3073
  /**
@@ -3089,7 +3093,7 @@ var AbracadabraProvider = class AbracadabraProvider extends AbracadabraBaseProvi
3089
3093
  }
3090
3094
  authenticatedHandler(scope) {
3091
3095
  super.authenticatedHandler(scope);
3092
- this.effectiveRole = {
3096
+ const roleMap = {
3093
3097
  service: "service",
3094
3098
  admin: "admin",
3095
3099
  owner: "owner",
@@ -3098,8 +3102,16 @@ var AbracadabraProvider = class AbracadabraProvider extends AbracadabraBaseProvi
3098
3102
  observer: "observer",
3099
3103
  "read-write": "editor",
3100
3104
  readonly: "viewer"
3101
- }[scope] ?? "observer";
3105
+ };
3106
+ const couldWrite = this.canWrite;
3107
+ this.effectiveRole = roleMap[scope] ?? "observer";
3102
3108
  this.offlineStore?.savePermissionSnapshot(this.effectiveRole);
3109
+ if (this.canWrite) this._writeDropWarned = false;
3110
+ this.emit("roleChanged", {
3111
+ role: this.effectiveRole,
3112
+ canWrite: this.canWrite
3113
+ });
3114
+ if (!couldWrite && this.canWrite && this.isSynced) this.flushPendingUpdates().catch(() => null);
3103
3115
  }
3104
3116
  /**
3105
3117
  * Override sendToken to send a pubkey-only identity declaration instead of a
@@ -3150,7 +3162,13 @@ var AbracadabraProvider = class AbracadabraProvider extends AbracadabraBaseProvi
3150
3162
  async restorePermissionSnapshot() {
3151
3163
  if (!this.offlineStore) return;
3152
3164
  const role = await this.offlineStore.getPermissionSnapshot();
3153
- if (role && !this.effectiveRole) this.effectiveRole = role;
3165
+ if (role && !this.effectiveRole) {
3166
+ this.effectiveRole = role;
3167
+ this.emit("roleChanged", {
3168
+ role: this.effectiveRole,
3169
+ canWrite: this.canWrite
3170
+ });
3171
+ }
3154
3172
  }
3155
3173
  get canWrite() {
3156
3174
  return this.effectiveRole != null && this.effectiveRole !== "viewer" && this.effectiveRole !== "observer";
@@ -3357,7 +3375,19 @@ var AbracadabraProvider = class AbracadabraProvider extends AbracadabraBaseProvi
3357
3375
  if (origin === this) return;
3358
3376
  if (this.offlineStore !== null && origin === this.offlineStore) return;
3359
3377
  this.offlineStore?.persistUpdate(update).catch(() => null);
3360
- if (!this.canWrite) return;
3378
+ if (!this.canWrite) {
3379
+ if (!this._writeDropWarned) {
3380
+ this._writeDropWarned = true;
3381
+ try {
3382
+ console.warn(`[abra:write-drop] dropping wire-send for doc "${this.configuration?.name}" — effectiveRole=${this.effectiveRole ?? "null"} (canWrite=false). Edits are persisted to IndexedDB only; the server will NOT receive them until this doc regains write access.`);
3383
+ } catch {}
3384
+ }
3385
+ this.emit("writeDropped", {
3386
+ name: this.configuration?.name,
3387
+ role: this.effectiveRole ?? null
3388
+ });
3389
+ return;
3390
+ }
3361
3391
  super.documentUpdateHandler(update, origin);
3362
3392
  }
3363
3393
  /**
@@ -16066,7 +16096,7 @@ var BackgroundSyncManager = class extends EventEmitter {
16066
16096
  };
16067
16097
  let serverOrigin = "default";
16068
16098
  try {
16069
- serverOrigin = new URL(client.baseUrl ?? "").hostname;
16099
+ serverOrigin = new URL(client.baseUrl ?? "").host;
16070
16100
  } catch {}
16071
16101
  this.persistence = new BackgroundSyncPersistence(serverOrigin);
16072
16102
  this.semaphore = new Semaphore(this.opts.concurrency);
@@ -16213,7 +16243,7 @@ var BackgroundSyncManager = class extends EventEmitter {
16213
16243
  for (const docId of treeMap.keys()) docIds.add(docId);
16214
16244
  let serverOrigin;
16215
16245
  try {
16216
- serverOrigin = new URL(this.client.baseUrl ?? "").hostname;
16246
+ serverOrigin = new URL(this.client.baseUrl ?? "").host;
16217
16247
  } catch {}
16218
16248
  const clearPromises = Array.from(docIds).map(async (docId) => {
16219
16249
  try {