@threadbase-sh/streamer 1.36.2 → 1.36.3

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.cts CHANGED
@@ -1200,6 +1200,7 @@ declare class StreamerServer {
1200
1200
  private allScanners;
1201
1201
  private scannerReady;
1202
1202
  private scannerStale;
1203
+ private conversationReconcileInFlight;
1203
1204
  private refreshInFlight;
1204
1205
  private binding;
1205
1206
  private activeWarmups;
@@ -1293,6 +1294,7 @@ declare class StreamerServer {
1293
1294
  * stale or shouldRefreshProjectsFromHdd detected disk drift.
1294
1295
  */
1295
1296
  private reconcileConversationsCacheFromDisk;
1297
+ private startBackgroundConversationReconcile;
1296
1298
  private shouldAutoReconcileConversationList;
1297
1299
  private handleListConversations;
1298
1300
  private handleConversationsCount;
package/dist/index.d.ts CHANGED
@@ -1200,6 +1200,7 @@ declare class StreamerServer {
1200
1200
  private allScanners;
1201
1201
  private scannerReady;
1202
1202
  private scannerStale;
1203
+ private conversationReconcileInFlight;
1203
1204
  private refreshInFlight;
1204
1205
  private binding;
1205
1206
  private activeWarmups;
@@ -1293,6 +1294,7 @@ declare class StreamerServer {
1293
1294
  * stale or shouldRefreshProjectsFromHdd detected disk drift.
1294
1295
  */
1295
1296
  private reconcileConversationsCacheFromDisk;
1297
+ private startBackgroundConversationReconcile;
1296
1298
  private shouldAutoReconcileConversationList;
1297
1299
  private handleListConversations;
1298
1300
  private handleConversationsCount;
package/dist/index.js CHANGED
@@ -6863,6 +6863,10 @@ var StreamerServer = class {
6863
6863
  // Set by onConversationChanged while a scan is in-flight; getScanner() does
6864
6864
  // a single rescan after the current one completes instead of restarting it.
6865
6865
  scannerStale = false;
6866
+ // Single-flight guard for the background disk reconcile: a burst of list
6867
+ // polls during active session writes shares one rescan instead of queueing
6868
+ // a full rescan per request.
6869
+ conversationReconcileInFlight = null;
6866
6870
  // Single-flight + TTL guard around scanner.refreshFile (see refreshFileGuarded).
6867
6871
  // A live file's mtime is always newer than the snapshot, so an unguarded
6868
6872
  // refresh fires on every request and re-parses the whole file from byte 0.
@@ -7880,9 +7884,9 @@ var StreamerServer = class {
7880
7884
  * the automatic freshness path when the directory watcher marked the scanner
7881
7885
  * stale or shouldRefreshProjectsFromHdd detected disk drift.
7882
7886
  */
7883
- async reconcileConversationsCacheFromDisk() {
7887
+ async reconcileConversationsCacheFromDisk(onProgress) {
7884
7888
  if (!this.cache) return;
7885
- const scanner = await this.rescanForRefresh();
7889
+ const scanner = await this.rescanForRefresh(onProgress);
7886
7890
  const metas = [...scanner.getMetadataCache().values()];
7887
7891
  try {
7888
7892
  this.cache.upsertFromScannerMeta(metas);
@@ -7910,6 +7914,18 @@ var StreamerServer = class {
7910
7914
  );
7911
7915
  }
7912
7916
  }
7917
+ // Reconcile the cache from disk without blocking the caller. Single-flighted
7918
+ // so a burst of list polls during active session writes shares one rescan
7919
+ // rather than queueing a full rescan each; tracked so close() awaits the
7920
+ // in-flight cache write before shutting the DB.
7921
+ startBackgroundConversationReconcile() {
7922
+ if (this.conversationReconcileInFlight) return;
7923
+ const task = this.reconcileConversationsCacheFromDisk().finally(() => {
7924
+ this.conversationReconcileInFlight = null;
7925
+ });
7926
+ this.conversationReconcileInFlight = task;
7927
+ this.trackCacheWrite(task);
7928
+ }
7913
7929
  shouldAutoReconcileConversationList() {
7914
7930
  if (!this.cache) return false;
7915
7931
  if (this.scannerStale) return true;
@@ -7927,13 +7943,19 @@ var StreamerServer = class {
7927
7943
  const providerFilter = url.searchParams.get("provider") ?? void 0;
7928
7944
  const bustCache = url.searchParams.get("refresh") === "1";
7929
7945
  if (this.cache && (bustCache || this.shouldAutoReconcileConversationList())) {
7930
- if (bustCache) {
7946
+ const canServeStale = !bustCache && this.cache.listConversations({ limit: 0, offset: 0 }).total > 0;
7947
+ if (canServeStale) {
7948
+ this.startBackgroundConversationReconcile();
7949
+ } else {
7950
+ const shouldEmitProgress = createScanProgressThrottle();
7931
7951
  await this.withWarmup(
7932
7952
  "conversation_refresh",
7933
- () => this.reconcileConversationsCacheFromDisk()
7953
+ () => this.reconcileConversationsCacheFromDisk((scanned, total2) => {
7954
+ if (shouldEmitProgress(scanned, total2)) {
7955
+ this.wsHub.broadcast({ type: "scan_progress", scanned, total: total2 });
7956
+ }
7957
+ })
7934
7958
  );
7935
- } else {
7936
- await this.reconcileConversationsCacheFromDisk();
7937
7959
  }
7938
7960
  }
7939
7961
  if (this.cache) {
@@ -8169,7 +8191,7 @@ var StreamerServer = class {
8169
8191
  // getFreshScanner() this does NOT discard the warm scanner. scannerReady is
8170
8192
  // only ever reassigned to a live scan promise (never nulled mid-scan), so the
8171
8193
  // getScanner() anti-infinite-loop guard is preserved.
8172
- async rescanForRefresh() {
8194
+ async rescanForRefresh(onProgress) {
8173
8195
  if (this.scannerReady) await this.scannerReady;
8174
8196
  this.scannerStale = false;
8175
8197
  if (!this.scanner) {
@@ -8180,7 +8202,8 @@ var StreamerServer = class {
8180
8202
  this.scannerReady = scanner.scan({
8181
8203
  ...this.scanProfiles ? { profiles: this.scanProfiles } : {},
8182
8204
  ...this.codexScanOpts(),
8183
- fullRescan: true
8205
+ fullRescan: true,
8206
+ ...onProgress ? { onProgress } : {}
8184
8207
  });
8185
8208
  await this.scannerReady;
8186
8209
  return scanner;