@fnndsc/cumin 3.15.1 → 3.16.1

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.
@@ -4,9 +4,11 @@
4
4
  * @module
5
5
  */
6
6
  export { ListCache, listCache_get } from './listCache.js';
7
- export type { CacheStats, CacheResult, CacheOptions } from './listCache.js';
8
- export { ProcCache, procCache_get, status_isTerminal, PROC_TERMINAL_STATUSES } from './procCache.js';
9
- export type { ProcInstance, ProcFeed, ProcFeedScopeCounts, ProcWarmupProgress, ProcCacheState, ProcCacheLifecycle, ProcCacheSnapshot, } from './procCache.js';
7
+ export type { CacheStats, CacheResult, CacheOptions, ListCacheSnapshot, ListCacheEntrySnapshot } from './listCache.js';
8
+ export { listCheckpointPath_get, listCheckpoint_restore, listCheckpoint_save, listCheckpoint_watch, } from './listCheckpoint.js';
9
+ export type { ListCheckpointRestoreResult } from './listCheckpoint.js';
10
+ export { ProcCache, procCache_get, status_isTerminal, feed_isActive, feedTopology_changed, PROC_TERMINAL_STATUSES } from './procCache.js';
11
+ export type { ProcInstance, ProcFeed, ProcFeedScopeCounts, ProcWarmupProgress, ProcCacheState, ProcCacheLifecycle, ProcCacheSnapshot, ProcFeedSnapshot, ProcCacheChange, ProcCacheListener, } from './procCache.js';
10
12
  export type { ProcPromptState, ProcPromptProgress } from '@fnndsc/menu';
11
- export { procCheckpointPath_get, procCheckpoint_restore, procCheckpoint_save, procCheckpoint_watch, } from './procCheckpoint.js';
13
+ export { procCheckpointPath_get, procCheckpointDir_get, procCheckpoint_restore, procCheckpoint_save, procCheckpointFeed_save, procCheckpointRoster_save, procCheckpoint_watch, } from './procCheckpoint.js';
12
14
  export type { ProcCheckpointRestoreResult } from './procCheckpoint.js';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.procCheckpoint_watch = exports.procCheckpoint_save = exports.procCheckpoint_restore = exports.procCheckpointPath_get = exports.PROC_TERMINAL_STATUSES = exports.status_isTerminal = exports.procCache_get = exports.ProcCache = exports.listCache_get = exports.ListCache = void 0;
3
+ exports.procCheckpoint_watch = exports.procCheckpointRoster_save = exports.procCheckpointFeed_save = exports.procCheckpoint_save = exports.procCheckpoint_restore = exports.procCheckpointDir_get = exports.procCheckpointPath_get = exports.PROC_TERMINAL_STATUSES = exports.feedTopology_changed = exports.feed_isActive = exports.status_isTerminal = exports.procCache_get = exports.ProcCache = exports.listCheckpoint_watch = exports.listCheckpoint_save = exports.listCheckpoint_restore = exports.listCheckpointPath_get = exports.listCache_get = exports.ListCache = void 0;
4
4
  /**
5
5
  * @file Cache module exports.
6
6
  *
@@ -9,14 +9,24 @@ exports.procCheckpoint_watch = exports.procCheckpoint_save = exports.procCheckpo
9
9
  var listCache_js_1 = require("./listCache.js");
10
10
  Object.defineProperty(exports, "ListCache", { enumerable: true, get: function () { return listCache_js_1.ListCache; } });
11
11
  Object.defineProperty(exports, "listCache_get", { enumerable: true, get: function () { return listCache_js_1.listCache_get; } });
12
+ var listCheckpoint_js_1 = require("./listCheckpoint.js");
13
+ Object.defineProperty(exports, "listCheckpointPath_get", { enumerable: true, get: function () { return listCheckpoint_js_1.listCheckpointPath_get; } });
14
+ Object.defineProperty(exports, "listCheckpoint_restore", { enumerable: true, get: function () { return listCheckpoint_js_1.listCheckpoint_restore; } });
15
+ Object.defineProperty(exports, "listCheckpoint_save", { enumerable: true, get: function () { return listCheckpoint_js_1.listCheckpoint_save; } });
16
+ Object.defineProperty(exports, "listCheckpoint_watch", { enumerable: true, get: function () { return listCheckpoint_js_1.listCheckpoint_watch; } });
12
17
  var procCache_js_1 = require("./procCache.js");
13
18
  Object.defineProperty(exports, "ProcCache", { enumerable: true, get: function () { return procCache_js_1.ProcCache; } });
14
19
  Object.defineProperty(exports, "procCache_get", { enumerable: true, get: function () { return procCache_js_1.procCache_get; } });
15
20
  Object.defineProperty(exports, "status_isTerminal", { enumerable: true, get: function () { return procCache_js_1.status_isTerminal; } });
21
+ Object.defineProperty(exports, "feed_isActive", { enumerable: true, get: function () { return procCache_js_1.feed_isActive; } });
22
+ Object.defineProperty(exports, "feedTopology_changed", { enumerable: true, get: function () { return procCache_js_1.feedTopology_changed; } });
16
23
  Object.defineProperty(exports, "PROC_TERMINAL_STATUSES", { enumerable: true, get: function () { return procCache_js_1.PROC_TERMINAL_STATUSES; } });
17
24
  var procCheckpoint_js_1 = require("./procCheckpoint.js");
18
25
  Object.defineProperty(exports, "procCheckpointPath_get", { enumerable: true, get: function () { return procCheckpoint_js_1.procCheckpointPath_get; } });
26
+ Object.defineProperty(exports, "procCheckpointDir_get", { enumerable: true, get: function () { return procCheckpoint_js_1.procCheckpointDir_get; } });
19
27
  Object.defineProperty(exports, "procCheckpoint_restore", { enumerable: true, get: function () { return procCheckpoint_js_1.procCheckpoint_restore; } });
20
28
  Object.defineProperty(exports, "procCheckpoint_save", { enumerable: true, get: function () { return procCheckpoint_js_1.procCheckpoint_save; } });
29
+ Object.defineProperty(exports, "procCheckpointFeed_save", { enumerable: true, get: function () { return procCheckpoint_js_1.procCheckpointFeed_save; } });
30
+ Object.defineProperty(exports, "procCheckpointRoster_save", { enumerable: true, get: function () { return procCheckpoint_js_1.procCheckpointRoster_save; } });
21
31
  Object.defineProperty(exports, "procCheckpoint_watch", { enumerable: true, get: function () { return procCheckpoint_js_1.procCheckpoint_watch; } });
22
32
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":";;;AAAA;;;;GAIG;AACH,+CAA0D;AAAjD,yGAAA,SAAS,OAAA;AAAE,6GAAA,aAAa,OAAA;AAEjC,+CAAqG;AAA5F,yGAAA,SAAS,OAAA;AAAE,6GAAA,aAAa,OAAA;AAAE,iHAAA,iBAAiB,OAAA;AAAE,sHAAA,sBAAsB,OAAA;AAqB5E,yDAK6B;AAJ3B,2HAAA,sBAAsB,OAAA;AACtB,2HAAA,sBAAsB,OAAA;AACtB,wHAAA,mBAAmB,OAAA;AACnB,yHAAA,oBAAoB,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":";;;AAAA;;;;GAIG;AACH,+CAA0D;AAAjD,yGAAA,SAAS,OAAA;AAAE,6GAAA,aAAa,OAAA;AAEjC,yDAK6B;AAJ3B,2HAAA,sBAAsB,OAAA;AACtB,2HAAA,sBAAsB,OAAA;AACtB,wHAAA,mBAAmB,OAAA;AACnB,yHAAA,oBAAoB,OAAA;AAGtB,+CAA0I;AAAjI,yGAAA,SAAS,OAAA;AAAE,6GAAA,aAAa,OAAA;AAAE,iHAAA,iBAAiB,OAAA;AAAE,6GAAA,aAAa,OAAA;AAAE,oHAAA,oBAAoB,OAAA;AAAE,sHAAA,sBAAsB,OAAA;AAwBjH,yDAQ6B;AAP3B,2HAAA,sBAAsB,OAAA;AACtB,0HAAA,qBAAqB,OAAA;AACrB,2HAAA,sBAAsB,OAAA;AACtB,wHAAA,mBAAmB,OAAA;AACnB,4HAAA,uBAAuB,OAAA;AACvB,8HAAA,yBAAyB,OAAA;AACzB,yHAAA,oBAAoB,OAAA"}
@@ -32,6 +32,27 @@ export interface CacheOptions {
32
32
  /** Mark as dirty immediately */
33
33
  dirty?: boolean;
34
34
  }
35
+ /**
36
+ * A persistence-safe copy of one cache entry.
37
+ *
38
+ * @property path - The listed path.
39
+ * @property data - The cached listing.
40
+ * @property timestamp - When it was cached (ms since epoch); age is judged
41
+ * against this on restore, so a restored entry is as stale as it really is.
42
+ * @property dirty - Whether a local mutation invalidated it.
43
+ * @property ttl - Its time-to-live in milliseconds.
44
+ */
45
+ export interface ListCacheEntrySnapshot {
46
+ path: string;
47
+ data: unknown;
48
+ timestamp: number;
49
+ dirty: boolean;
50
+ ttl: number;
51
+ }
52
+ /** The whole cache, oldest entry first (the LRU order). */
53
+ export interface ListCacheSnapshot {
54
+ entries: ListCacheEntrySnapshot[];
55
+ }
35
56
  /**
36
57
  * Statistics about cache usage.
37
58
  */
@@ -97,6 +118,33 @@ export declare class ListCache {
97
118
  private ttlConfig;
98
119
  /** Cache statistics. */
99
120
  private stats;
121
+ /** Listeners told after any mutation a persister would want to see. */
122
+ private listeners;
123
+ /**
124
+ * Registers a callback for checkpoint-worthy mutations (set, update,
125
+ * dirty, invalidate).
126
+ *
127
+ * @param listener - Callback invoked after a mutation.
128
+ * @returns Function that unregisters the callback.
129
+ */
130
+ changeListener_add(listener: () => void): () => void;
131
+ private change_emit;
132
+ /**
133
+ * Creates a persistence-safe copy of every entry, oldest first.
134
+ *
135
+ * @returns The snapshot.
136
+ */
137
+ snapshot_create(): ListCacheSnapshot;
138
+ /**
139
+ * Merges a snapshot's entries into the cache, keeping their original
140
+ * timestamps: what was stale stays stale and is revalidated on its next
141
+ * visit. A path already cached in this process is newer than anything on
142
+ * disk and is kept as is. Entries beyond the LRU bound are dropped
143
+ * oldest-first.
144
+ *
145
+ * @param snapshot - A validated snapshot.
146
+ */
147
+ snapshot_restore(snapshot: ListCacheSnapshot): void;
100
148
  /**
101
149
  * Private constructor to enforce singleton pattern.
102
150
  */
@@ -53,6 +53,55 @@ exports.listCache_get = listCache_get;
53
53
  * ```
54
54
  */
55
55
  class ListCache {
56
+ /**
57
+ * Registers a callback for checkpoint-worthy mutations (set, update,
58
+ * dirty, invalidate).
59
+ *
60
+ * @param listener - Callback invoked after a mutation.
61
+ * @returns Function that unregisters the callback.
62
+ */
63
+ changeListener_add(listener) {
64
+ this.listeners.add(listener);
65
+ return () => { this.listeners.delete(listener); };
66
+ }
67
+ change_emit() {
68
+ for (const listener of this.listeners)
69
+ listener();
70
+ }
71
+ /**
72
+ * Creates a persistence-safe copy of every entry, oldest first.
73
+ *
74
+ * @returns The snapshot.
75
+ */
76
+ snapshot_create() {
77
+ const entries = [];
78
+ for (const [path, entry] of this.cache) {
79
+ entries.push({ path, data: entry.data, timestamp: entry.timestamp, dirty: entry.dirty, ttl: entry.ttl });
80
+ }
81
+ return { entries };
82
+ }
83
+ /**
84
+ * Merges a snapshot's entries into the cache, keeping their original
85
+ * timestamps: what was stale stays stale and is revalidated on its next
86
+ * visit. A path already cached in this process is newer than anything on
87
+ * disk and is kept as is. Entries beyond the LRU bound are dropped
88
+ * oldest-first.
89
+ *
90
+ * @param snapshot - A validated snapshot.
91
+ */
92
+ snapshot_restore(snapshot) {
93
+ const live = new Map(this.cache);
94
+ this.cache.clear();
95
+ for (const entry of snapshot.entries) {
96
+ if (live.has(entry.path))
97
+ continue;
98
+ this.cache.set(entry.path, { data: entry.data, timestamp: entry.timestamp, dirty: entry.dirty, ttl: entry.ttl });
99
+ }
100
+ for (const [path, entry] of live)
101
+ this.cache.set(path, entry);
102
+ this.evict_lru();
103
+ this.change_emit();
104
+ }
56
105
  /**
57
106
  * Private constructor to enforce singleton pattern.
58
107
  */
@@ -77,6 +126,8 @@ class ListCache {
77
126
  staleHits: 0,
78
127
  evictions: 0,
79
128
  };
129
+ /** Listeners told after any mutation a persister would want to see. */
130
+ this.listeners = new Set();
80
131
  }
81
132
  /**
82
133
  * Gets the singleton instance of ListCache.
@@ -136,6 +187,7 @@ class ListCache {
136
187
  });
137
188
  // LRU eviction if over limit
138
189
  this.evict_lru();
190
+ this.change_emit();
139
191
  }
140
192
  /**
141
193
  * Marks a cached path as dirty (needs refresh).
@@ -147,6 +199,7 @@ class ListCache {
147
199
  const entry = this.cache.get(path);
148
200
  if (entry) {
149
201
  entry.dirty = true;
202
+ this.change_emit();
150
203
  }
151
204
  }
152
205
  /**
@@ -162,6 +215,7 @@ class ListCache {
162
215
  entry.data = updater(entry.data);
163
216
  entry.timestamp = Date.now(); // Reset timestamp
164
217
  entry.dirty = false; // Clean after update
218
+ this.change_emit();
165
219
  }
166
220
  }
167
221
  /**
@@ -177,6 +231,7 @@ class ListCache {
177
231
  else {
178
232
  this.cache.clear();
179
233
  }
234
+ this.change_emit();
180
235
  }
181
236
  /**
182
237
  * Invalidates a path and every cached listing beneath it.
@@ -196,6 +251,7 @@ class ListCache {
196
251
  this.cache.delete(key);
197
252
  }
198
253
  }
254
+ this.change_emit();
199
255
  }
200
256
  /**
201
257
  * @deprecated Use cache without cwd tracking. Cache persists across navigation.
@@ -1 +1 @@
1
- {"version":3,"file":"listCache.js","sourceRoot":"","sources":["../../src/cache/listCache.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"listCache.js","sourceRoot":"","sources":["../../src/cache/listCache.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG;;;AAobH,sCAEC;AA5VD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAa,SAAS;IA8BpB;;;;;;OAMG;IACH,kBAAkB,CAAC,QAAoB;QACrC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC7B,OAAO,GAAS,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1D,CAAC;IAEO,WAAW;QACjB,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS;YAAE,QAAQ,EAAE,CAAC;IACpD,CAAC;IAED;;;;OAIG;IACH,eAAe;QACb,MAAM,OAAO,GAA6B,EAAE,CAAC;QAC7C,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACvC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;QAC3G,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,CAAC;IACrB,CAAC;IAED;;;;;;;;OAQG;IACH,gBAAgB,CAAC,QAA2B;QAC1C,MAAM,IAAI,GAAqC,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACnE,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACnB,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;YACrC,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC;gBAAE,SAAS;YACnC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;QACnH,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI;YAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC9D,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,IAAI,CAAC,WAAW,EAAE,CAAC;IACrB,CAAC;IAED;;OAEG;IACH;QAhFA,mCAAmC;QAC3B,UAAK,GAAqC,IAAI,GAAG,EAAE,CAAC;QAE5D,4CAA4C;QACpC,eAAU,GAAW,GAAG,CAAC;QAEjC,sDAAsD;QAC9C,eAAU,GAAW,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;QAE3C,uCAAuC;QAC/B,cAAS,GAAwB,IAAI,GAAG,CAAC;YAC/C,CAAC,SAAS,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,EAAM,oCAAoC;YACrE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,EAAU,gCAAgC;YAClE,CAAC,MAAM,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,EAAU,iCAAiC;YACnE,CAAC,UAAU,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,EAAO,uBAAuB;SAC1D,CAAC,CAAC;QAEH,wBAAwB;QAChB,UAAK,GAAG;YACd,IAAI,EAAE,CAAC;YACP,MAAM,EAAE,CAAC;YACT,SAAS,EAAE,CAAC;YACZ,SAAS,EAAE,CAAC;SACb,CAAC;QACF,uEAAuE;QAC/D,cAAS,GAAoB,IAAI,GAAG,EAAE,CAAC;IAuDxB,CAAC;IAExB;;;;OAIG;IACH,MAAM,CAAC,YAAY;QACjB,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC;YACxB,SAAS,CAAC,QAAQ,GAAG,IAAI,SAAS,EAAE,CAAC;QACvC,CAAC;QACD,OAAO,SAAS,CAAC,QAAQ,CAAC;IAC5B,CAAC;IAED;;;;;OAKG;IACH,SAAS,CAAc,IAAY;QACjC,MAAM,KAAK,GAAoC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAEpE,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YACpB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,2CAA2C;QAC3C,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACxB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAE5B,sBAAsB;QACtB,MAAM,GAAG,GAAW,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,CAAC;QACjD,MAAM,KAAK,GAAY,CAAC,KAAK,CAAC,KAAK,IAAI,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC;QAEvD,IAAI,KAAK,EAAE,CAAC;YACV,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACpB,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;QACzB,CAAC;QAED,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,IAAS,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;IAC/C,CAAC;IAED;;;;;;OAMG;IACH,SAAS,CAAc,IAAY,EAAE,IAAO,EAAE,OAAsB;QAClE,mDAAmD;QACnD,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YACzB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;QAED,MAAM,GAAG,GAAW,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAEvD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE;YACnB,IAAI;YACJ,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,KAAK,EAAE,OAAO,EAAE,KAAK,IAAI,KAAK;YAC9B,GAAG;SACJ,CAAC,CAAC;QAEH,6BAA6B;QAC7B,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,IAAI,CAAC,WAAW,EAAE,CAAC;IACrB,CAAC;IAED;;;;;OAKG;IACH,eAAe,CAAC,IAAY;QAC1B,MAAM,KAAK,GAAoC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpE,IAAI,KAAK,EAAE,CAAC;YACV,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC;YACnB,IAAI,CAAC,WAAW,EAAE,CAAC;QACrB,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACH,YAAY,CAAc,IAAY,EAAE,OAAuB;QAC7D,MAAM,KAAK,GAAoC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpE,IAAI,KAAK,EAAE,CAAC;YACV,KAAK,CAAC,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,IAAS,CAAC,CAAC;YACtC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAE,kBAAkB;YACjD,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAY,qBAAqB;YACrD,IAAI,CAAC,WAAW,EAAE,CAAC;QACrB,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,gBAAgB,CAAC,IAAa;QAC5B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC;QACD,IAAI,CAAC,WAAW,EAAE,CAAC;IACrB,CAAC;IAED;;;;;;;;;OASG;IACH,oBAAoB,CAAC,IAAY;QAC/B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACxB,MAAM,MAAM,GAAW,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC;QAC9D,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;YACpC,IAAI,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC3B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;QACD,IAAI,CAAC,WAAW,EAAE,CAAC;IACrB,CAAC;IAED;;;;;OAKG;IACH,UAAU,CAAC,OAAe;QACxB,qDAAqD;QACrD,oDAAoD;IACtD,CAAC;IAED;;;;;OAKG;IACK,OAAO,CAAC,IAAY;QAC1B,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7B,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC;QACnC,CAAC;QAED,KAAK,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YAC5C,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC1B,MAAM,KAAK,GAAW,IAAI,MAAM,CAAC,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC;gBAC3E,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;oBACrB,OAAO,GAAG,CAAC;gBACb,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED;;OAEG;IACK,SAAS;QACf,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;YACzC,MAAM,SAAS,GAAuB,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;YACrE,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;gBAC5B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;gBAC7B,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;YACzB,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAE,wCAAwC;YAClD,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,SAAS;QACP,IAAI,SAAS,GAAW,CAAC,CAAC;QAC1B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YACxC,qCAAqC;YACrC,SAAS,IAAI,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;QACjD,CAAC;QAED,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI;YACrB,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM;YACzB,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,SAAS;YAC/B,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,SAAS;YAC/B,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI;YACxB,SAAS;SACV,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,WAAW;QACT,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC;QACpB,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;QACtB,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC;QACzB,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC;IAC3B,CAAC;CACF;AA/SD,8BA+SC;AAED;;;;GAIG;AACH,SAAgB,aAAa;IAC3B,OAAO,SAAS,CAAC,YAAY,EAAE,CAAC;AAClC,CAAC"}
@@ -0,0 +1,52 @@
1
+ /** Minimum spacing between two writes of the listing checkpoint. */
2
+ export declare const LIST_CHECKPOINT_FLOOR_MS: number;
3
+ /**
4
+ * Outcome of attempting to restore a listing checkpoint.
5
+ *
6
+ * @property restored - Whether validated entries replaced the cache.
7
+ * @property count - Number of restored listings.
8
+ * @property writtenAt - Checkpoint creation time when restoration succeeded.
9
+ * @property reason - Human-readable reason when restoration was skipped.
10
+ */
11
+ export interface ListCheckpointRestoreResult {
12
+ restored: boolean;
13
+ count: number;
14
+ writtenAt?: string;
15
+ reason?: string;
16
+ }
17
+ /**
18
+ * Builds the identity-keyed checkpoint path without exposing the identity.
19
+ *
20
+ * @param identity - Canonical ChRIS identity string.
21
+ * @param root - Checkpoint directory override.
22
+ * @returns Versioned checkpoint path.
23
+ */
24
+ export declare function listCheckpointPath_get(identity: string, root?: string): string;
25
+ /**
26
+ * Restores a matching, validated listing checkpoint into the cache.
27
+ *
28
+ * @param identity - Canonical ChRIS identity string.
29
+ * @param root - Checkpoint directory override.
30
+ * @returns Restoration outcome without throwing for absent or invalid files.
31
+ */
32
+ export declare function listCheckpoint_restore(identity: string, root?: string): Promise<ListCheckpointRestoreResult>;
33
+ /**
34
+ * Atomically saves the listing cache.
35
+ *
36
+ * @param identity - Canonical ChRIS identity string.
37
+ * @param root - Checkpoint directory override.
38
+ * @returns Promise resolved after the mode-0600 file is durable in place.
39
+ */
40
+ export declare function listCheckpoint_save(identity: string, root?: string): Promise<void>;
41
+ /**
42
+ * Starts a throttled checkpoint writer for listing-cache mutations: a write
43
+ * at least `delayMs` after the first pending mutation, and no more often
44
+ * than once per `floorMs`, the last change never dropped.
45
+ *
46
+ * @param identity - Canonical ChRIS identity string.
47
+ * @param root - Checkpoint directory override.
48
+ * @param delayMs - Debounce interval in milliseconds.
49
+ * @param floorMs - Minimum spacing between two writes.
50
+ * @returns Function that stops watching and cancels a pending save.
51
+ */
52
+ export declare function listCheckpoint_watch(identity: string, root?: string, delayMs?: number, floorMs?: number): () => void;
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LIST_CHECKPOINT_FLOOR_MS = void 0;
4
+ exports.listCheckpointPath_get = listCheckpointPath_get;
5
+ exports.listCheckpoint_restore = listCheckpoint_restore;
6
+ exports.listCheckpoint_save = listCheckpoint_save;
7
+ exports.listCheckpoint_watch = listCheckpoint_watch;
8
+ /**
9
+ * @file Persistent checkpoint for the directory-listing cache.
10
+ *
11
+ * The listing cache is a set of per-path snapshots with a TTL each. Written
12
+ * to disk, restored at boot with their original timestamps, they come back
13
+ * exactly as stale as they are: the first visit to a restored path renders
14
+ * at once and revalidates behind itself, instead of starting cold. One
15
+ * identity-keyed, mode-0600 file under a mode-0700 folder, written by
16
+ * same-directory atomic rename, throttled so a burst of listings coalesces
17
+ * into one write.
18
+ *
19
+ * @module
20
+ */
21
+ const crypto_1 = require("crypto");
22
+ const fs_1 = require("fs");
23
+ const os_1 = require("os");
24
+ const path_1 = require("path");
25
+ const listCache_1 = require("./listCache");
26
+ const LIST_CHECKPOINT_SCHEMA = 1;
27
+ /** Minimum spacing between two writes of the listing checkpoint. */
28
+ exports.LIST_CHECKPOINT_FLOOR_MS = 10 * 1000;
29
+ /** Returns the default directory for persistent listing files. */
30
+ function listCheckpointRoot_get() {
31
+ const cacheHome = process.env.XDG_CACHE_HOME ?? (0, path_1.join)((0, os_1.homedir)(), '.cache');
32
+ return (0, path_1.join)(cacheHome, 'chell', 'vfs');
33
+ }
34
+ /**
35
+ * Builds the identity-keyed checkpoint path without exposing the identity.
36
+ *
37
+ * @param identity - Canonical ChRIS identity string.
38
+ * @param root - Checkpoint directory override.
39
+ * @returns Versioned checkpoint path.
40
+ */
41
+ function listCheckpointPath_get(identity, root = listCheckpointRoot_get()) {
42
+ const key = (0, crypto_1.createHash)('sha256').update(identity).digest('hex').slice(0, 16);
43
+ return (0, path_1.join)(root, `listings-${key}-v${LIST_CHECKPOINT_SCHEMA}.json`);
44
+ }
45
+ /** Reports whether a value is a structurally valid persisted entry. */
46
+ function entry_check(value) {
47
+ if (!value || typeof value !== 'object')
48
+ return false;
49
+ const entry = value;
50
+ return typeof entry.path === 'string' && entry.path.startsWith('/') &&
51
+ entry.data !== undefined && entry.data !== null &&
52
+ typeof entry.timestamp === 'number' && Number.isFinite(entry.timestamp) &&
53
+ typeof entry.dirty === 'boolean' &&
54
+ typeof entry.ttl === 'number' && Number.isFinite(entry.ttl) && entry.ttl > 0;
55
+ }
56
+ /** Reports whether parsed JSON is a compatible checkpoint for an identity. */
57
+ function file_check(value, identity) {
58
+ if (!value || typeof value !== 'object')
59
+ return false;
60
+ const file = value;
61
+ return file.schemaVersion === LIST_CHECKPOINT_SCHEMA &&
62
+ file.identity === identity &&
63
+ typeof file.writtenAt === 'string' &&
64
+ !Number.isNaN(Date.parse(file.writtenAt)) &&
65
+ Array.isArray(file.entries) &&
66
+ file.entries.every(entry_check) &&
67
+ new Set(file.entries.map((entry) => entry.path)).size === file.entries.length;
68
+ }
69
+ /**
70
+ * Restores a matching, validated listing checkpoint into the cache.
71
+ *
72
+ * @param identity - Canonical ChRIS identity string.
73
+ * @param root - Checkpoint directory override.
74
+ * @returns Restoration outcome without throwing for absent or invalid files.
75
+ */
76
+ async function listCheckpoint_restore(identity, root = listCheckpointRoot_get()) {
77
+ try {
78
+ const raw = await fs_1.promises.readFile(listCheckpointPath_get(identity, root), 'utf8');
79
+ const parsed = JSON.parse(raw);
80
+ if (!file_check(parsed, identity)) {
81
+ return { restored: false, count: 0, reason: 'incompatible listing checkpoint' };
82
+ }
83
+ const snapshot = { entries: parsed.entries };
84
+ (0, listCache_1.listCache_get)().snapshot_restore(snapshot);
85
+ return { restored: true, count: parsed.entries.length, writtenAt: parsed.writtenAt };
86
+ }
87
+ catch (error) {
88
+ const code = error.code;
89
+ return {
90
+ restored: false,
91
+ count: 0,
92
+ reason: code === 'ENOENT' ? 'no listing checkpoint' : (error instanceof Error ? error.message : String(error)),
93
+ };
94
+ }
95
+ }
96
+ /**
97
+ * Atomically saves the listing cache.
98
+ *
99
+ * @param identity - Canonical ChRIS identity string.
100
+ * @param root - Checkpoint directory override.
101
+ * @returns Promise resolved after the mode-0600 file is durable in place.
102
+ */
103
+ async function listCheckpoint_save(identity, root = listCheckpointRoot_get()) {
104
+ const path = listCheckpointPath_get(identity, root);
105
+ const temporaryPath = `${path}.${process.pid}-${(0, crypto_1.randomUUID)()}.tmp`;
106
+ const file = {
107
+ schemaVersion: LIST_CHECKPOINT_SCHEMA,
108
+ identity,
109
+ writtenAt: new Date().toISOString(),
110
+ entries: (0, listCache_1.listCache_get)().snapshot_create().entries,
111
+ };
112
+ await fs_1.promises.mkdir((0, path_1.dirname)(path), { recursive: true, mode: 0o700 });
113
+ await fs_1.promises.chmod((0, path_1.dirname)(path), 0o700);
114
+ await fs_1.promises.writeFile(temporaryPath, `${JSON.stringify(file)}\n`, { encoding: 'utf8', mode: 0o600 });
115
+ await fs_1.promises.rename(temporaryPath, path);
116
+ await fs_1.promises.chmod(path, 0o600);
117
+ }
118
+ /**
119
+ * Starts a throttled checkpoint writer for listing-cache mutations: a write
120
+ * at least `delayMs` after the first pending mutation, and no more often
121
+ * than once per `floorMs`, the last change never dropped.
122
+ *
123
+ * @param identity - Canonical ChRIS identity string.
124
+ * @param root - Checkpoint directory override.
125
+ * @param delayMs - Debounce interval in milliseconds.
126
+ * @param floorMs - Minimum spacing between two writes.
127
+ * @returns Function that stops watching and cancels a pending save.
128
+ */
129
+ function listCheckpoint_watch(identity, root = listCheckpointRoot_get(), delayMs = 2000, floorMs = exports.LIST_CHECKPOINT_FLOOR_MS) {
130
+ let timer = null;
131
+ let lastWrite = 0;
132
+ const listener_remove = (0, listCache_1.listCache_get)().changeListener_add(() => {
133
+ if (timer)
134
+ return;
135
+ const wait = Math.max(delayMs, lastWrite + floorMs - Date.now());
136
+ timer = setTimeout(() => {
137
+ timer = null;
138
+ lastWrite = Date.now();
139
+ void listCheckpoint_save(identity, root).catch(() => { });
140
+ }, wait);
141
+ timer.unref();
142
+ });
143
+ return () => {
144
+ listener_remove();
145
+ if (timer)
146
+ clearTimeout(timer);
147
+ timer = null;
148
+ };
149
+ }
150
+ //# sourceMappingURL=listCheckpoint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listCheckpoint.js","sourceRoot":"","sources":["../../src/cache/listCheckpoint.ts"],"names":[],"mappings":"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"}
@@ -49,6 +49,21 @@ export interface ProcFeedScopeCounts {
49
49
  shared: number;
50
50
  total: number;
51
51
  }
52
+ /**
53
+ * Reports whether a feed has work which can still change after checkpointing.
54
+ *
55
+ * @param feed - Feed summary with CUBE job counters.
56
+ * @returns True when any job is created, scheduled, or started.
57
+ */
58
+ export declare function feed_isActive(feed: ProcFeed): boolean;
59
+ /**
60
+ * Reports whether two feed summaries indicate different instance topology.
61
+ *
62
+ * @param previous - The cached feed summary.
63
+ * @param current - A freshly fetched feed summary.
64
+ * @returns True when any job counter differs.
65
+ */
66
+ export declare function feedTopology_changed(previous: ProcFeed, current: ProcFeed): boolean;
52
67
  /**
53
68
  * Instance entry. Topology (id/feedID/parentID/pluginName) is permanent.
54
69
  * Status is cached only once terminal (settled); active status is refreshed live.
@@ -141,6 +156,38 @@ export interface ProcCacheSnapshot {
141
156
  instances: ProcInstance[];
142
157
  topologyLoaded: number[];
143
158
  }
159
+ /**
160
+ * One feed's persistence-safe topology: what a per-feed checkpoint shard holds.
161
+ *
162
+ * @property feedID - The feed these instances belong to.
163
+ * @property loaded - Whether the feed's topology is known to be complete.
164
+ * @property instances - Normalized instances with terminal status only.
165
+ */
166
+ export interface ProcFeedSnapshot {
167
+ feedID: number;
168
+ loaded: boolean;
169
+ instances: ProcInstance[];
170
+ }
171
+ /**
172
+ * What a cache mutation touched, so a persister can write only that part.
173
+ *
174
+ * - `roster`: the feed index (a feed row added, updated, or removed);
175
+ * - `feed`: one feed's topology, status, metrics, joins, or loaded mark;
176
+ * - `all`: every feed at once (a completed global sweep);
177
+ * - `lifecycle`: freshness state only — nothing durable changed.
178
+ */
179
+ export type ProcCacheChange = {
180
+ scope: 'roster';
181
+ } | {
182
+ scope: 'feed';
183
+ feedID: number;
184
+ } | {
185
+ scope: 'all';
186
+ } | {
187
+ scope: 'lifecycle';
188
+ };
189
+ /** A cache mutation listener, told what the mutation touched. */
190
+ export type ProcCacheListener = (change: ProcCacheChange) => void;
144
191
  /**
145
192
  * Session-scoped cache of feed and instance topology with restorable terminal state.
146
193
  */
@@ -181,10 +228,10 @@ export declare class ProcCache {
181
228
  /**
182
229
  * Registers a callback for checkpoint-worthy cache mutations.
183
230
  *
184
- * @param listener - Callback invoked after a cache mutation.
231
+ * @param listener - Callback invoked after a cache mutation with what it touched.
185
232
  * @returns Function that unregisters the callback.
186
233
  */
187
- changeListener_add(listener: () => void): () => void;
234
+ changeListener_add(listener: ProcCacheListener): () => void;
188
235
  private change_emit;
189
236
  /**
190
237
  * Adds or updates a feed entry.
@@ -346,6 +393,22 @@ export declare class ProcCache {
346
393
  * @returns Serializable feed and terminal-topology state.
347
394
  */
348
395
  snapshot_create(): ProcCacheSnapshot;
396
+ /**
397
+ * Creates one feed's persistence-safe snapshot: its instances (terminal
398
+ * status only, no params or output paths) and whether its topology is
399
+ * complete. A per-feed checkpoint shard is exactly this.
400
+ *
401
+ * @param feedID - The feed to snapshot.
402
+ * @returns The feed's shard content; empty when the feed holds no instances.
403
+ */
404
+ feedSnapshot_create(feedID: number): ProcFeedSnapshot;
405
+ /**
406
+ * Feed IDs that hold any instances or a loaded mark — the feeds a full
407
+ * checkpoint must shard.
408
+ *
409
+ * @returns Feed IDs with persistent topology state.
410
+ */
411
+ shardedFeedIDs_get(): number[];
349
412
  /**
350
413
  * Replaces in-memory topology with a validated persistent snapshot.
351
414
  *