@fnndsc/cumin 3.15.0 → 3.16.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.
@@ -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,31 @@ 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
+ * Replaces the cache with a snapshot's entries, keeping their original
140
+ * timestamps: what was stale stays stale and is revalidated on its next
141
+ * visit. Entries beyond the LRU bound are dropped oldest-first.
142
+ *
143
+ * @param snapshot - A validated snapshot.
144
+ */
145
+ snapshot_restore(snapshot: ListCacheSnapshot): void;
100
146
  /**
101
147
  * Private constructor to enforce singleton pattern.
102
148
  */
@@ -53,6 +53,48 @@ 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
+ * Replaces the cache with a snapshot's entries, keeping their original
85
+ * timestamps: what was stale stays stale and is revalidated on its next
86
+ * visit. Entries beyond the LRU bound are dropped oldest-first.
87
+ *
88
+ * @param snapshot - A validated snapshot.
89
+ */
90
+ snapshot_restore(snapshot) {
91
+ this.cache.clear();
92
+ for (const entry of snapshot.entries) {
93
+ this.cache.set(entry.path, { data: entry.data, timestamp: entry.timestamp, dirty: entry.dirty, ttl: entry.ttl });
94
+ }
95
+ this.evict_lru();
96
+ this.change_emit();
97
+ }
56
98
  /**
57
99
  * Private constructor to enforce singleton pattern.
58
100
  */
@@ -77,6 +119,8 @@ class ListCache {
77
119
  staleHits: 0,
78
120
  evictions: 0,
79
121
  };
122
+ /** Listeners told after any mutation a persister would want to see. */
123
+ this.listeners = new Set();
80
124
  }
81
125
  /**
82
126
  * Gets the singleton instance of ListCache.
@@ -136,6 +180,7 @@ class ListCache {
136
180
  });
137
181
  // LRU eviction if over limit
138
182
  this.evict_lru();
183
+ this.change_emit();
139
184
  }
140
185
  /**
141
186
  * Marks a cached path as dirty (needs refresh).
@@ -147,6 +192,7 @@ class ListCache {
147
192
  const entry = this.cache.get(path);
148
193
  if (entry) {
149
194
  entry.dirty = true;
195
+ this.change_emit();
150
196
  }
151
197
  }
152
198
  /**
@@ -162,6 +208,7 @@ class ListCache {
162
208
  entry.data = updater(entry.data);
163
209
  entry.timestamp = Date.now(); // Reset timestamp
164
210
  entry.dirty = false; // Clean after update
211
+ this.change_emit();
165
212
  }
166
213
  }
167
214
  /**
@@ -177,6 +224,7 @@ class ListCache {
177
224
  else {
178
225
  this.cache.clear();
179
226
  }
227
+ this.change_emit();
180
228
  }
181
229
  /**
182
230
  * Invalidates a path and every cached listing beneath it.
@@ -196,6 +244,7 @@ class ListCache {
196
244
  this.cache.delete(key);
197
245
  }
198
246
  }
247
+ this.change_emit();
199
248
  }
200
249
  /**
201
250
  * @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":"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"}
@@ -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
+ Array.isArray(entry.data) &&
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
  *