@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.
@@ -2294,6 +2294,7 @@ var AbracadabraBaseProvider = class extends EventEmitter {
2294
2294
  this.isSynced = false;
2295
2295
  this.unsyncedChanges = 0;
2296
2296
  this.isAuthenticated = false;
2297
+ this._hasEverAuthenticated = false;
2297
2298
  this.authorizedScope = void 0;
2298
2299
  this.manageSocket = false;
2299
2300
  this._isAttached = false;
@@ -2504,6 +2505,7 @@ var AbracadabraBaseProvider = class extends EventEmitter {
2504
2505
  onClose() {
2505
2506
  this.isAuthenticated = false;
2506
2507
  this.synced = false;
2508
+ this._hasEverAuthenticated = false;
2507
2509
  if (this.awareness) removeAwarenessStates(this.awareness, Array.from(this.awareness.getStates().keys()).filter((client) => client !== this.document.clientID), this);
2508
2510
  }
2509
2511
  destroy() {
@@ -2560,12 +2562,13 @@ var AbracadabraBaseProvider = class extends EventEmitter {
2560
2562
  permissionDeniedHandler(reason) {
2561
2563
  this.emit("authenticationFailed", { reason });
2562
2564
  this.isAuthenticated = false;
2563
- if (this.manageSocket) try {
2565
+ if (this.manageSocket && !this._hasEverAuthenticated) try {
2564
2566
  this.configuration.websocketProvider.disconnect();
2565
2567
  } catch {}
2566
2568
  }
2567
2569
  authenticatedHandler(scope) {
2568
2570
  this.isAuthenticated = true;
2571
+ this._hasEverAuthenticated = true;
2569
2572
  this.authorizedScope = scope;
2570
2573
  this.emit("authenticated", { scope });
2571
2574
  }
@@ -2803,7 +2806,7 @@ function isValidDocId(id) {
2803
2806
  */
2804
2807
  var AbracadabraProvider = class AbracadabraProvider extends AbracadabraBaseProvider {
2805
2808
  static {
2806
- this.DEFAULT_MAX_CHILDREN = 20;
2809
+ this.DEFAULT_MAX_CHILDREN = 64;
2807
2810
  }
2808
2811
  constructor(configuration) {
2809
2812
  const resolved = { ...configuration };
@@ -2819,6 +2822,7 @@ var AbracadabraProvider = class AbracadabraProvider extends AbracadabraBaseProvi
2819
2822
  this.pendingLoads = /* @__PURE__ */ new Map();
2820
2823
  this.childAccessTimes = /* @__PURE__ */ new Map();
2821
2824
  this.pinnedChildren = /* @__PURE__ */ new Set();
2825
+ this.transientChildren = /* @__PURE__ */ new Set();
2822
2826
  this.boundHandleYSubdocsChange = this.handleYSubdocsChange.bind(this);
2823
2827
  this.hasCachedContent = false;
2824
2828
  this._client = client;
@@ -3016,22 +3020,34 @@ var AbracadabraProvider = class AbracadabraProvider extends AbracadabraBaseProvi
3016
3020
  }
3017
3021
  /**
3018
3022
  * Create (or return cached) a child AbracadabraProvider for a given
3019
- * child document id. Each child opens its own WebSocket connection because
3020
- * the server is document-scoped (one WebSocket ↔ one document).
3023
+ * child document id. Each child shares the parent's WebSocket via
3024
+ * multiplexing.
3025
+ *
3026
+ * `evictable` (default `true`) controls whether the child enters the
3027
+ * LRU pool. Pass `evictable: false` for transient loads — typically
3028
+ * batch indexers (search, file extraction) that touch every doc in
3029
+ * the tree and would otherwise blow out the cache, silently evicting
3030
+ * subdocs the UI is actively using. Transient children are excluded
3031
+ * from the LRU budget AND don't trigger eviction of other children.
3032
+ * Callers MUST pair `loadChild(id, { evictable: false })` with an
3033
+ * explicit `unloadChild(id)` once they're done.
3021
3034
  */
3022
- loadChild(childId) {
3035
+ loadChild(childId, options = {}) {
3023
3036
  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.`));
3037
+ if (childId === this.configuration.name) return Promise.resolve(this);
3038
+ const evictable = options.evictable !== false;
3024
3039
  if (this.childProviders.has(childId)) {
3025
3040
  this.childAccessTimes.set(childId, Date.now());
3041
+ if (evictable) this.transientChildren.delete(childId);
3026
3042
  return Promise.resolve(this.childProviders.get(childId));
3027
3043
  }
3028
3044
  if (this.pendingLoads.has(childId)) return this.pendingLoads.get(childId);
3029
- const load = this._doLoadChild(childId);
3045
+ const load = this._doLoadChild(childId, evictable);
3030
3046
  this.pendingLoads.set(childId, load);
3031
3047
  load.finally(() => this.pendingLoads.delete(childId));
3032
3048
  return load;
3033
3049
  }
3034
- async _doLoadChild(childId) {
3050
+ async _doLoadChild(childId, evictable) {
3035
3051
  const childDoc = new yjs.Doc({ guid: childId });
3036
3052
  this.registerSubdoc(childDoc);
3037
3053
  const childProvider = new AbracadabraProvider({
@@ -3050,7 +3066,8 @@ var AbracadabraProvider = class AbracadabraProvider extends AbracadabraBaseProvi
3050
3066
  childProvider.attach();
3051
3067
  this.childProviders.set(childId, childProvider);
3052
3068
  this.childAccessTimes.set(childId, Date.now());
3053
- this.evictLRU();
3069
+ if (!evictable) this.transientChildren.add(childId);
3070
+ if (evictable) this.evictLRU();
3054
3071
  this.emit("subdocLoaded", {
3055
3072
  childId,
3056
3073
  provider: childProvider
@@ -3064,6 +3081,7 @@ var AbracadabraProvider = class AbracadabraProvider extends AbracadabraBaseProvi
3064
3081
  this.childProviders.delete(childId);
3065
3082
  this.childAccessTimes.delete(childId);
3066
3083
  this.pinnedChildren.delete(childId);
3084
+ this.transientChildren.delete(childId);
3067
3085
  }
3068
3086
  }
3069
3087
  /**
@@ -3081,21 +3099,27 @@ var AbracadabraProvider = class AbracadabraProvider extends AbracadabraBaseProvi
3081
3099
  this.evictLRU();
3082
3100
  }
3083
3101
  /**
3084
- * Evict least-recently-used unpinned child providers until the cache is
3085
- * at or below MAX_CHILDREN.
3102
+ * Evict least-recently-used unpinned, non-transient child providers
3103
+ * until the cache is at or below MAX_CHILDREN. Transient children
3104
+ * (loaded with `{ evictable: false }`) are excluded from both the
3105
+ * budget *and* the eviction list — they're invisible to the LRU and
3106
+ * only go away when their loader explicitly calls `unloadChild`.
3086
3107
  */
3087
3108
  evictLRU() {
3088
- if (this.childProviders.size <= this.maxChildren) return;
3109
+ let nonTransientCount = 0;
3110
+ for (const id of this.childProviders.keys()) if (!this.transientChildren.has(id)) nonTransientCount++;
3111
+ if (nonTransientCount <= this.maxChildren) return;
3089
3112
  const evictable = [];
3090
3113
  for (const [id] of this.childProviders) {
3091
3114
  if (this.pinnedChildren.has(id)) continue;
3115
+ if (this.transientChildren.has(id)) continue;
3092
3116
  evictable.push({
3093
3117
  id,
3094
3118
  accessTime: this.childAccessTimes.get(id) ?? 0
3095
3119
  });
3096
3120
  }
3097
3121
  evictable.sort((a, b) => a.accessTime - b.accessTime);
3098
- let toEvict = this.childProviders.size - this.maxChildren;
3122
+ let toEvict = nonTransientCount - this.maxChildren;
3099
3123
  for (const entry of evictable) {
3100
3124
  if (toEvict <= 0) break;
3101
3125
  this.unloadChild(entry.id);