dsh-cline-pass 0.1.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.
package/lib/store.js ADDED
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Runtime observation state: pipeline detection, discovered upstream channels,
3
+ * per-channel verdicts, and request history.
4
+ *
5
+ * Configuration lives in the settings document instead (see `index.js`). This
6
+ * is derived data any probe can rebuild, so it is never persisted.
7
+ *
8
+ * @module dsh-cline-pass/store
9
+ */
10
+
11
+ /** How many request-history rows are retained. */
12
+ export const DEFAULT_HISTORY_LIMIT = 100
13
+
14
+ /** One upstream availability verdict. */
15
+ function verdict(status, note, ms) {
16
+ return { status, note: String(note ?? '').slice(0, 200), ms: Number(ms ?? 0), checkedAt: Date.now() }
17
+ }
18
+
19
+ /**
20
+ * Create the observation store.
21
+ * @param options - `{ historyLimit }`.
22
+ */
23
+ export function createStore({ historyLimit = DEFAULT_HISTORY_LIMIT } = {}) {
24
+ /** @type {Map<string, object>} model id -> discovered metadata */
25
+ const models = new Map()
26
+ /** @type {object[]} newest first */
27
+ const history = []
28
+ let catalog = { ids: [], sources: [], fetchedAt: 0 }
29
+
30
+ return {
31
+ /** Discovered metadata for one model (never undefined). */
32
+ metaOf(model) {
33
+ return models.get(model) ?? {}
34
+ },
35
+ /** Every model with discovered metadata. */
36
+ allMeta() {
37
+ return [...models.entries()].map(([id, meta]) => ({ id, ...meta }))
38
+ },
39
+ /** Merge one discovery result into a model's metadata. */
40
+ learn(model, patch) {
41
+ const current = models.get(model) ?? {}
42
+ const next = { ...current, ...patch }
43
+ if (patch?.upstreamStatus !== undefined) {
44
+ next.upstreamStatus = { ...(current.upstreamStatus ?? {}), ...patch.upstreamStatus }
45
+ }
46
+ if (next.upstreams !== undefined) {
47
+ next.upstreams = [...new Set(next.upstreams.map(String).filter((name) => name.length > 0))].slice(0, 25)
48
+ }
49
+ models.set(model, next)
50
+ return next
51
+ },
52
+ /** Record one upstream's availability verdict; an inconclusive one is dropped. */
53
+ learnUpstream(model, upstream, status, note, ms) {
54
+ if (upstream === undefined || upstream === null || upstream === '') return
55
+ if (status === 'unknown') return
56
+ const current = models.get(model) ?? {}
57
+ models.set(model, {
58
+ ...current,
59
+ upstreamStatus: { ...(current.upstreamStatus ?? {}), [upstream]: verdict(status, note, ms) },
60
+ })
61
+ },
62
+ /** Append one request-history row (newest first, bounded). */
63
+ record(entry) {
64
+ history.unshift({ ts: Date.now(), ...entry })
65
+ if (history.length > historyLimit) history.length = historyLimit
66
+ },
67
+ /**
68
+ * Read request history.
69
+ * @param limit - maximum rows (defaults to the configured cap).
70
+ * @param model - optional case-insensitive substring filter.
71
+ */
72
+ readHistory(limit = historyLimit, model = '') {
73
+ const needle = String(model ?? '').trim().toLowerCase()
74
+ return history
75
+ .filter((entry) => needle === '' || String(entry.model ?? '').toLowerCase().includes(needle))
76
+ .slice(0, Math.max(0, Math.min(limit, historyLimit)))
77
+ },
78
+ /** Total rows retained, before any filter. */
79
+ historySize() {
80
+ return history.length
81
+ },
82
+ /** The last cached official-catalog scan. */
83
+ catalog() {
84
+ return catalog
85
+ },
86
+ /** Replace the cached official-catalog scan. */
87
+ setCatalog(next) {
88
+ catalog = { ...next, fetchedAt: Date.now() }
89
+ return catalog
90
+ },
91
+ }
92
+ }