@abraca/dabra 2.0.5 → 2.0.8

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.
@@ -2264,6 +2264,7 @@ var AbracadabraBaseProvider = class extends EventEmitter {
2264
2264
  this.isSynced = false;
2265
2265
  this.unsyncedChanges = 0;
2266
2266
  this.isAuthenticated = false;
2267
+ this._hasEverAuthenticated = false;
2267
2268
  this.authorizedScope = void 0;
2268
2269
  this.manageSocket = false;
2269
2270
  this._isAttached = false;
@@ -2474,6 +2475,7 @@ var AbracadabraBaseProvider = class extends EventEmitter {
2474
2475
  onClose() {
2475
2476
  this.isAuthenticated = false;
2476
2477
  this.synced = false;
2478
+ this._hasEverAuthenticated = false;
2477
2479
  if (this.awareness) removeAwarenessStates(this.awareness, Array.from(this.awareness.getStates().keys()).filter((client) => client !== this.document.clientID), this);
2478
2480
  }
2479
2481
  destroy() {
@@ -2530,12 +2532,13 @@ var AbracadabraBaseProvider = class extends EventEmitter {
2530
2532
  permissionDeniedHandler(reason) {
2531
2533
  this.emit("authenticationFailed", { reason });
2532
2534
  this.isAuthenticated = false;
2533
- if (this.manageSocket) try {
2535
+ if (this.manageSocket && !this._hasEverAuthenticated) try {
2534
2536
  this.configuration.websocketProvider.disconnect();
2535
2537
  } catch {}
2536
2538
  }
2537
2539
  authenticatedHandler(scope) {
2538
2540
  this.isAuthenticated = true;
2541
+ this._hasEverAuthenticated = true;
2539
2542
  this.authorizedScope = scope;
2540
2543
  this.emit("authenticated", { scope });
2541
2544
  }
@@ -2773,7 +2776,7 @@ function isValidDocId(id) {
2773
2776
  */
2774
2777
  var AbracadabraProvider = class AbracadabraProvider extends AbracadabraBaseProvider {
2775
2778
  static {
2776
- this.DEFAULT_MAX_CHILDREN = 20;
2779
+ this.DEFAULT_MAX_CHILDREN = 64;
2777
2780
  }
2778
2781
  constructor(configuration) {
2779
2782
  const resolved = { ...configuration };
@@ -2789,6 +2792,7 @@ var AbracadabraProvider = class AbracadabraProvider extends AbracadabraBaseProvi
2789
2792
  this.pendingLoads = /* @__PURE__ */ new Map();
2790
2793
  this.childAccessTimes = /* @__PURE__ */ new Map();
2791
2794
  this.pinnedChildren = /* @__PURE__ */ new Set();
2795
+ this.transientChildren = /* @__PURE__ */ new Set();
2792
2796
  this.boundHandleYSubdocsChange = this.handleYSubdocsChange.bind(this);
2793
2797
  this.hasCachedContent = false;
2794
2798
  this._client = client;
@@ -2986,22 +2990,34 @@ var AbracadabraProvider = class AbracadabraProvider extends AbracadabraBaseProvi
2986
2990
  }
2987
2991
  /**
2988
2992
  * Create (or return cached) a child AbracadabraProvider for a given
2989
- * child document id. Each child opens its own WebSocket connection because
2990
- * the server is document-scoped (one WebSocket ↔ one document).
2993
+ * child document id. Each child shares the parent's WebSocket via
2994
+ * multiplexing.
2995
+ *
2996
+ * `evictable` (default `true`) controls whether the child enters the
2997
+ * LRU pool. Pass `evictable: false` for transient loads — typically
2998
+ * batch indexers (search, file extraction) that touch every doc in
2999
+ * the tree and would otherwise blow out the cache, silently evicting
3000
+ * subdocs the UI is actively using. Transient children are excluded
3001
+ * from the LRU budget AND don't trigger eviction of other children.
3002
+ * Callers MUST pair `loadChild(id, { evictable: false })` with an
3003
+ * explicit `unloadChild(id)` once they're done.
2991
3004
  */
2992
- loadChild(childId) {
3005
+ loadChild(childId, options = {}) {
2993
3006
  if (!isValidDocId(childId)) return Promise.reject(/* @__PURE__ */ new Error(`loadChild: "${childId}" is not a valid document ID (must be a UUID). If this node was created with an older version of the app, delete it and recreate it.`));
3007
+ if (childId === this.configuration.name) return Promise.resolve(this);
3008
+ const evictable = options.evictable !== false;
2994
3009
  if (this.childProviders.has(childId)) {
2995
3010
  this.childAccessTimes.set(childId, Date.now());
3011
+ if (evictable) this.transientChildren.delete(childId);
2996
3012
  return Promise.resolve(this.childProviders.get(childId));
2997
3013
  }
2998
3014
  if (this.pendingLoads.has(childId)) return this.pendingLoads.get(childId);
2999
- const load = this._doLoadChild(childId);
3015
+ const load = this._doLoadChild(childId, evictable);
3000
3016
  this.pendingLoads.set(childId, load);
3001
3017
  load.finally(() => this.pendingLoads.delete(childId));
3002
3018
  return load;
3003
3019
  }
3004
- async _doLoadChild(childId) {
3020
+ async _doLoadChild(childId, evictable) {
3005
3021
  const childDoc = new Y.Doc({ guid: childId });
3006
3022
  this.registerSubdoc(childDoc);
3007
3023
  const childProvider = new AbracadabraProvider({
@@ -3020,7 +3036,8 @@ var AbracadabraProvider = class AbracadabraProvider extends AbracadabraBaseProvi
3020
3036
  childProvider.attach();
3021
3037
  this.childProviders.set(childId, childProvider);
3022
3038
  this.childAccessTimes.set(childId, Date.now());
3023
- this.evictLRU();
3039
+ if (!evictable) this.transientChildren.add(childId);
3040
+ if (evictable) this.evictLRU();
3024
3041
  this.emit("subdocLoaded", {
3025
3042
  childId,
3026
3043
  provider: childProvider
@@ -3034,6 +3051,7 @@ var AbracadabraProvider = class AbracadabraProvider extends AbracadabraBaseProvi
3034
3051
  this.childProviders.delete(childId);
3035
3052
  this.childAccessTimes.delete(childId);
3036
3053
  this.pinnedChildren.delete(childId);
3054
+ this.transientChildren.delete(childId);
3037
3055
  }
3038
3056
  }
3039
3057
  /**
@@ -3051,21 +3069,27 @@ var AbracadabraProvider = class AbracadabraProvider extends AbracadabraBaseProvi
3051
3069
  this.evictLRU();
3052
3070
  }
3053
3071
  /**
3054
- * Evict least-recently-used unpinned child providers until the cache is
3055
- * at or below MAX_CHILDREN.
3072
+ * Evict least-recently-used unpinned, non-transient child providers
3073
+ * until the cache is at or below MAX_CHILDREN. Transient children
3074
+ * (loaded with `{ evictable: false }`) are excluded from both the
3075
+ * budget *and* the eviction list — they're invisible to the LRU and
3076
+ * only go away when their loader explicitly calls `unloadChild`.
3056
3077
  */
3057
3078
  evictLRU() {
3058
- if (this.childProviders.size <= this.maxChildren) return;
3079
+ let nonTransientCount = 0;
3080
+ for (const id of this.childProviders.keys()) if (!this.transientChildren.has(id)) nonTransientCount++;
3081
+ if (nonTransientCount <= this.maxChildren) return;
3059
3082
  const evictable = [];
3060
3083
  for (const [id] of this.childProviders) {
3061
3084
  if (this.pinnedChildren.has(id)) continue;
3085
+ if (this.transientChildren.has(id)) continue;
3062
3086
  evictable.push({
3063
3087
  id,
3064
3088
  accessTime: this.childAccessTimes.get(id) ?? 0
3065
3089
  });
3066
3090
  }
3067
3091
  evictable.sort((a, b) => a.accessTime - b.accessTime);
3068
- let toEvict = this.childProviders.size - this.maxChildren;
3092
+ let toEvict = nonTransientCount - this.maxChildren;
3069
3093
  for (const entry of evictable) {
3070
3094
  if (toEvict <= 0) break;
3071
3095
  this.unloadChild(entry.id);