@abraca/dabra 2.19.0 → 2.20.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
|
@@ -1257,6 +1257,7 @@ interface AbracadabraProviderConfiguration extends Omit<AbracadabraBaseProviderC
|
|
|
1257
1257
|
*/
|
|
1258
1258
|
declare class AbracadabraProvider extends AbracadabraBaseProvider {
|
|
1259
1259
|
effectiveRole: EffectiveRole;
|
|
1260
|
+
private _writeDropWarned;
|
|
1260
1261
|
private _client;
|
|
1261
1262
|
private offlineStore;
|
|
1262
1263
|
private childProviders;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abraca/dabra",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.20.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.
|
|
44
|
+
"@abraca/schema": "2.20.0"
|
|
45
45
|
},
|
|
46
46
|
"scripts": {
|
|
47
47
|
"test": "node --no-warnings --conditions=source --experimental-transform-types --test 'tests/*.test.ts'"
|
|
@@ -113,6 +113,10 @@ function isValidDocId(id: string): boolean {
|
|
|
113
113
|
export class AbracadabraProvider extends AbracadabraBaseProvider {
|
|
114
114
|
public effectiveRole: EffectiveRole = null;
|
|
115
115
|
|
|
116
|
+
// Throttle the "write-drop" warning/event to once per (role) transition so a
|
|
117
|
+
// read-only editor session doesn't flood the console on every keystroke.
|
|
118
|
+
private _writeDropWarned = false;
|
|
119
|
+
|
|
116
120
|
private _client: AbracadabraClient | null;
|
|
117
121
|
private offlineStore: OfflineStore | null;
|
|
118
122
|
private childProviders = new Map<string, AbracadabraProvider>();
|
|
@@ -281,6 +285,10 @@ export class AbracadabraProvider extends AbracadabraBaseProvider {
|
|
|
281
285
|
// ── Auth / permission snapshot ────────────────────────────────────────────
|
|
282
286
|
|
|
283
287
|
override authenticatedHandler(scope: string) {
|
|
288
|
+
// NB: super.authenticatedHandler emits "authenticated" BEFORE we set
|
|
289
|
+
// effectiveRole below, so listeners of "authenticated" must not read the
|
|
290
|
+
// role from this provider — use the "roleChanged" event emitted at the end
|
|
291
|
+
// of this method instead, which fires once effectiveRole reflects `scope`.
|
|
284
292
|
super.authenticatedHandler(scope);
|
|
285
293
|
|
|
286
294
|
const roleMap: Record<string, import("./types.ts").EffectiveRole> = {
|
|
@@ -293,9 +301,28 @@ export class AbracadabraProvider extends AbracadabraBaseProvider {
|
|
|
293
301
|
"read-write": "editor",
|
|
294
302
|
readonly: "viewer",
|
|
295
303
|
};
|
|
304
|
+
const couldWrite = this.canWrite;
|
|
296
305
|
this.effectiveRole = roleMap[scope] ?? "observer";
|
|
297
306
|
|
|
298
307
|
this.offlineStore?.savePermissionSnapshot(this.effectiveRole);
|
|
308
|
+
|
|
309
|
+
// Regaining write access re-arms the one-shot write-drop warning so a later
|
|
310
|
+
// downgrade to a read-only role surfaces again.
|
|
311
|
+
if (this.canWrite) this._writeDropWarned = false;
|
|
312
|
+
|
|
313
|
+
// Reactive role signal for consumers (e.g. the editor edit-gate). Fired
|
|
314
|
+
// AFTER effectiveRole is set so `canWrite` reflects the new role.
|
|
315
|
+
this.emit("roleChanged", { role: this.effectiveRole, canWrite: this.canWrite });
|
|
316
|
+
|
|
317
|
+
// Self-heal: if the server's authentication frame arrived AFTER the initial
|
|
318
|
+
// sync (so the "synced" flush ran while the role was still null/stale and
|
|
319
|
+
// skipped, see flushPendingUpdates' canWrite guard), and we now have write
|
|
320
|
+
// access, push any updates that were queued to IndexedDB while we couldn't
|
|
321
|
+
// send. Without this, edits made before auth completed stay stuck in IDB
|
|
322
|
+
// (visible locally, never on the server) until the next reconnect.
|
|
323
|
+
if (!couldWrite && this.canWrite && this.isSynced) {
|
|
324
|
+
this.flushPendingUpdates().catch(() => null);
|
|
325
|
+
}
|
|
299
326
|
}
|
|
300
327
|
|
|
301
328
|
/**
|
|
@@ -363,6 +390,10 @@ export class AbracadabraProvider extends AbracadabraBaseProvider {
|
|
|
363
390
|
const role = await this.offlineStore.getPermissionSnapshot();
|
|
364
391
|
if (role && !this.effectiveRole) {
|
|
365
392
|
this.effectiveRole = role as EffectiveRole;
|
|
393
|
+
// Surface the cached role reactively so the offline edit-gate can open
|
|
394
|
+
// before any server round-trip. The server's authenticatedHandler will
|
|
395
|
+
// later override this with ground truth (and re-emit).
|
|
396
|
+
this.emit("roleChanged", { role: this.effectiveRole, canWrite: this.canWrite });
|
|
366
397
|
}
|
|
367
398
|
}
|
|
368
399
|
|
|
@@ -696,7 +727,32 @@ export class AbracadabraProvider extends AbracadabraBaseProvider {
|
|
|
696
727
|
this.offlineStore?.persistUpdate(update).catch(() => null);
|
|
697
728
|
|
|
698
729
|
// Don't send writes over the wire if we lack write permission.
|
|
699
|
-
if (!this.canWrite)
|
|
730
|
+
if (!this.canWrite) {
|
|
731
|
+
// Make the otherwise-silent write-drop observable. A local edit was just
|
|
732
|
+
// persisted to IndexedDB but will NOT be sent to the server because this
|
|
733
|
+
// provider's effective role can't write. If the editor UI let the user
|
|
734
|
+
// type here, the UI's write-gate diverged from this provider's — the edit
|
|
735
|
+
// lives only in local IDB (looks fine locally, empty on the server). We
|
|
736
|
+
// emit a "writeDropped" event so consumers can surface a read-only /
|
|
737
|
+
// "unsynced changes" banner, and warn once per role-transition so a
|
|
738
|
+
// read-only session doesn't flood the console on every keystroke.
|
|
739
|
+
if (!this._writeDropWarned) {
|
|
740
|
+
this._writeDropWarned = true;
|
|
741
|
+
try {
|
|
742
|
+
console.warn(
|
|
743
|
+
`[abra:write-drop] dropping wire-send for doc "${this.configuration?.name}" ` +
|
|
744
|
+
`— effectiveRole=${this.effectiveRole ?? "null"} (canWrite=false). ` +
|
|
745
|
+
`Edits are persisted to IndexedDB only; the server will NOT receive them ` +
|
|
746
|
+
`until this doc regains write access.`,
|
|
747
|
+
);
|
|
748
|
+
} catch { /* noop */ }
|
|
749
|
+
}
|
|
750
|
+
this.emit("writeDropped", {
|
|
751
|
+
name: this.configuration?.name,
|
|
752
|
+
role: this.effectiveRole ?? null,
|
|
753
|
+
});
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
700
756
|
|
|
701
757
|
super.documentUpdateHandler(update, origin);
|
|
702
758
|
}
|