@x12i/memorix-service 3.7.6 → 3.7.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.
- package/dist/activity-ring.d.ts +16 -0
- package/dist/activity-ring.d.ts.map +1 -1
- package/dist/activity-ring.js +15 -0
- package/dist/activity-ring.js.map +1 -1
- package/dist/activity-routes.d.ts.map +1 -1
- package/dist/activity-routes.js +6 -0
- package/dist/activity-routes.js.map +1 -1
- package/dist/activity-ui.d.ts +1 -1
- package/dist/activity-ui.d.ts.map +1 -1
- package/dist/activity-ui.js +383 -177
- package/dist/activity-ui.js.map +1 -1
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +8 -1
- package/dist/app.js.map +1 -1
- package/dist/catalog-bridge.d.ts.map +1 -1
- package/dist/catalog-bridge.js +6 -0
- package/dist/catalog-bridge.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +9 -1
- package/dist/cli.js.map +1 -1
- package/dist/data-changes-ui.js +1 -1
- package/dist/graph-ui.d.ts +3 -0
- package/dist/graph-ui.d.ts.map +1 -0
- package/dist/graph-ui.js +676 -0
- package/dist/graph-ui.js.map +1 -0
- package/dist/knowledge-upstream.d.ts +13 -0
- package/dist/knowledge-upstream.d.ts.map +1 -0
- package/dist/knowledge-upstream.js +38 -0
- package/dist/knowledge-upstream.js.map +1 -0
- package/dist/live-ops-nav.d.ts +2 -0
- package/dist/live-ops-nav.d.ts.map +1 -0
- package/dist/live-ops-nav.js +53 -0
- package/dist/live-ops-nav.js.map +1 -0
- package/dist/live.d.ts.map +1 -1
- package/dist/mcp/nexum-tools.d.ts +28 -0
- package/dist/mcp/nexum-tools.d.ts.map +1 -0
- package/dist/mcp/nexum-tools.js +257 -0
- package/dist/mcp/nexum-tools.js.map +1 -0
- package/dist/mcp/service-public-url.d.ts +7 -0
- package/dist/mcp/service-public-url.d.ts.map +1 -0
- package/dist/mcp/service-public-url.js +19 -0
- package/dist/mcp/service-public-url.js.map +1 -0
- package/dist/openapi/openapi.json +449 -4
- package/dist/routes/data.d.ts.map +1 -1
- package/dist/routes/data.js +242 -16
- package/dist/routes/data.js.map +1 -1
- package/dist/routes/knowledge.d.ts.map +1 -1
- package/dist/routes/knowledge.js +9 -37
- package/dist/routes/knowledge.js.map +1 -1
- package/dist/routes/nexum-mcp.d.ts +9 -0
- package/dist/routes/nexum-mcp.d.ts.map +1 -0
- package/dist/routes/nexum-mcp.js +95 -0
- package/dist/routes/nexum-mcp.js.map +1 -0
- package/dist/safe-query.d.ts +31 -0
- package/dist/safe-query.d.ts.map +1 -0
- package/dist/safe-query.js +125 -0
- package/dist/safe-query.js.map +1 -0
- package/openapi/openapi.json +449 -4
- package/package.json +2 -2
package/dist/activity-ui.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** HTML for GET /_live/activity —
|
|
1
|
+
/** HTML for GET /_live/activity — pushed / processed / fetched operator feeds. */
|
|
2
2
|
export function activityHtml() {
|
|
3
3
|
return `<!DOCTYPE html>
|
|
4
4
|
<html lang="en">
|
|
@@ -42,6 +42,13 @@ export function activityHtml() {
|
|
|
42
42
|
margin: 0 0 10px; font-size: 12px; font-family: var(--mono);
|
|
43
43
|
letter-spacing: 0.08em; text-transform: uppercase; color: var(--dim);
|
|
44
44
|
}
|
|
45
|
+
.quiet-banner {
|
|
46
|
+
margin: 0 0 18px; padding: 14px 16px;
|
|
47
|
+
border: 1px solid #e2b25a; background: #1c1810;
|
|
48
|
+
color: var(--text); font-size: 15px; line-height: 1.5;
|
|
49
|
+
}
|
|
50
|
+
.quiet-banner[hidden] { display: none; }
|
|
51
|
+
.quiet-banner strong { color: #e2b25a; font-weight: 600; }
|
|
45
52
|
.health-grid {
|
|
46
53
|
display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
|
47
54
|
gap: 14px; margin-bottom: 20px;
|
|
@@ -60,28 +67,30 @@ export function activityHtml() {
|
|
|
60
67
|
.health-card .val span { color: var(--muted); font-size: 12px; font-weight: 500; margin-left: 6px; }
|
|
61
68
|
.health-card svg { width: 100%; height: 56px; display: block; }
|
|
62
69
|
.health-card .hint { margin-top: 6px; font-size: 11px; color: var(--dim); font-family: var(--mono); }
|
|
63
|
-
.
|
|
64
|
-
display: grid;
|
|
65
|
-
|
|
70
|
+
.tabs {
|
|
71
|
+
display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
72
|
+
gap: 8px; margin: 0 0 16px;
|
|
66
73
|
}
|
|
67
|
-
.
|
|
68
|
-
|
|
69
|
-
font-
|
|
74
|
+
.tab, .btn {
|
|
75
|
+
appearance: none; border: 1px solid var(--line); background: var(--bg-elev); color: var(--muted);
|
|
76
|
+
font: 500 12px/1.3 var(--sans); padding: 12px 14px; cursor: pointer; border-radius: 2px;
|
|
70
77
|
}
|
|
71
|
-
.
|
|
72
|
-
|
|
73
|
-
text-transform: uppercase; color: var(--accent); font-weight: 600;
|
|
78
|
+
.tab {
|
|
79
|
+
display: flex; flex-direction: column; align-items: flex-start; gap: 4px; text-align: left;
|
|
74
80
|
}
|
|
75
|
-
.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
appearance: none; border: 1px solid var(--line); background: transparent; color: var(--muted);
|
|
79
|
-
font: 500 12px/1 var(--sans); letter-spacing: 0.04em; text-transform: uppercase;
|
|
80
|
-
padding: 10px 16px; cursor: pointer; border-radius: 2px;
|
|
81
|
+
.tab-name {
|
|
82
|
+
font-family: var(--mono); font-size: 12px; letter-spacing: 0.06em;
|
|
83
|
+
text-transform: uppercase; font-weight: 600; color: var(--text);
|
|
81
84
|
}
|
|
85
|
+
.tab-desc { font-size: 12px; font-weight: 400; color: var(--muted); }
|
|
82
86
|
.tab[aria-selected="true"] {
|
|
83
87
|
background: var(--accent); color: var(--accent-ink); border-color: var(--accent);
|
|
84
88
|
}
|
|
89
|
+
.tab[aria-selected="true"] .tab-name,
|
|
90
|
+
.tab[aria-selected="true"] .tab-desc { color: var(--accent-ink); }
|
|
91
|
+
@media (max-width: 720px) {
|
|
92
|
+
.tabs { grid-template-columns: 1fr; }
|
|
93
|
+
}
|
|
85
94
|
.btn { background: var(--bg-row); color: var(--text); text-transform: none; letter-spacing: 0; }
|
|
86
95
|
.btn:hover { border-color: var(--accent); }
|
|
87
96
|
.lane-hint { margin: 0 0 14px; color: var(--muted); font-size: 13px; max-width: 72ch; }
|
|
@@ -113,10 +122,9 @@ export function activityHtml() {
|
|
|
113
122
|
font-family: var(--mono); font-size: 12px;
|
|
114
123
|
}
|
|
115
124
|
.pause-bar[hidden] { display: none; }
|
|
116
|
-
.table-
|
|
117
|
-
max-height: min(62vh, 720px);
|
|
118
|
-
overflow: auto;
|
|
125
|
+
.table-wrap {
|
|
119
126
|
border: 1px solid var(--line);
|
|
127
|
+
background: var(--bg-elev);
|
|
120
128
|
}
|
|
121
129
|
table.list {
|
|
122
130
|
width: 100%; table-layout: fixed; border-collapse: collapse;
|
|
@@ -126,23 +134,21 @@ export function activityHtml() {
|
|
|
126
134
|
text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--line);
|
|
127
135
|
vertical-align: middle; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
128
136
|
}
|
|
129
|
-
table.list td.detail-cell {
|
|
130
|
-
white-space: normal; overflow: visible; text-overflow: clip; vertical-align: top;
|
|
131
|
-
}
|
|
132
137
|
th {
|
|
133
138
|
font-family: var(--mono); font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase;
|
|
134
139
|
color: var(--dim); font-weight: 500; background: #0d1016;
|
|
140
|
+
position: sticky; top: 0; z-index: 2;
|
|
135
141
|
}
|
|
136
142
|
tr:last-child td { border-bottom: 0; }
|
|
137
143
|
tr:hover td { background: var(--bg-row); }
|
|
138
144
|
tr.clickable { cursor: pointer; }
|
|
139
|
-
tr.open td { background: #1a2214; }
|
|
140
145
|
.mono { font-family: var(--mono); font-size: 12px; color: var(--muted); }
|
|
141
146
|
.badge {
|
|
142
147
|
display: inline-flex; align-items: center; gap: 6px; font-family: var(--mono);
|
|
143
148
|
font-size: 11px; padding: 3px 8px; border: 1px solid var(--line);
|
|
149
|
+
max-width: 100%;
|
|
144
150
|
}
|
|
145
|
-
.badge::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--dim); }
|
|
151
|
+
.badge::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--dim); flex: 0 0 auto; }
|
|
146
152
|
.badge.ok { color: var(--ok); }
|
|
147
153
|
.badge.ok::before { background: var(--ok); }
|
|
148
154
|
.badge.bad { color: var(--bad); }
|
|
@@ -151,11 +157,13 @@ export function activityHtml() {
|
|
|
151
157
|
.badge.queued::before { background: var(--api); }
|
|
152
158
|
.badge.ai { color: var(--ai); }
|
|
153
159
|
.badge.ai::before { background: var(--ai); }
|
|
160
|
+
.badge.skip { color: var(--dim); }
|
|
154
161
|
.detail {
|
|
155
162
|
margin-top: 8px; padding: 12px; background: #0d1016; border: 1px solid var(--line);
|
|
156
163
|
font-family: var(--mono); font-size: 11px; color: var(--muted);
|
|
157
164
|
}
|
|
158
165
|
.detail h3 { margin: 0 0 8px; font-size: 12px; color: var(--text); font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; }
|
|
166
|
+
.detail .row { margin: 4px 0; }
|
|
159
167
|
.edge-list { margin: 0; padding-left: 18px; }
|
|
160
168
|
.edge-list li { margin: 2px 0; }
|
|
161
169
|
.graph-svg {
|
|
@@ -167,26 +175,49 @@ export function activityHtml() {
|
|
|
167
175
|
.graph-svg line { stroke: var(--api); stroke-width: 1.2; opacity: 0.85; }
|
|
168
176
|
.empty { color: var(--muted); padding: 24px 0; }
|
|
169
177
|
.dialog-back {
|
|
170
|
-
position: fixed; inset: 0; z-index:
|
|
171
|
-
background: rgba(6, 8, 10, 0.
|
|
172
|
-
display: flex; align-items: flex-start; justify-content: center;
|
|
173
|
-
padding: 40px 20px; overflow: auto;
|
|
178
|
+
position: fixed; inset: 0; z-index: 40;
|
|
179
|
+
background: rgba(6, 8, 10, 0.45);
|
|
174
180
|
}
|
|
175
|
-
.dialog-back[hidden] { display: none; }
|
|
181
|
+
.dialog-back[hidden] { display: none !important; }
|
|
176
182
|
.dialog {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
183
|
+
position: fixed; top: 0; right: 0; bottom: 0;
|
|
184
|
+
width: min(440px, 100vw);
|
|
185
|
+
margin: 0;
|
|
186
|
+
display: flex; flex-direction: column;
|
|
187
|
+
background: var(--bg-elev);
|
|
188
|
+
border-left: 1px solid var(--line);
|
|
189
|
+
box-shadow: -20px 0 48px rgba(0, 0, 0, 0.5);
|
|
180
190
|
}
|
|
181
191
|
.dialog-bar {
|
|
182
|
-
|
|
183
|
-
|
|
192
|
+
flex: 0 0 auto;
|
|
193
|
+
display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
|
|
194
|
+
padding: 16px 16px 14px;
|
|
195
|
+
background: #0d1016;
|
|
196
|
+
border-bottom: 1px solid var(--line);
|
|
197
|
+
}
|
|
198
|
+
.dialog-kicker {
|
|
199
|
+
font-family: var(--mono); font-size: 10px; letter-spacing: 0.08em;
|
|
200
|
+
text-transform: uppercase; color: var(--accent); margin-bottom: 4px;
|
|
184
201
|
}
|
|
185
202
|
.dialog-bar strong {
|
|
186
|
-
font-size:
|
|
203
|
+
display: block; font-size: 14px; font-weight: 600; letter-spacing: 0;
|
|
204
|
+
text-transform: none; line-height: 1.35; word-break: break-word;
|
|
187
205
|
}
|
|
188
|
-
|
|
189
|
-
|
|
206
|
+
#dialog-body {
|
|
207
|
+
flex: 1 1 auto; overflow: auto;
|
|
208
|
+
padding: 16px 16px 32px;
|
|
209
|
+
}
|
|
210
|
+
.facts {
|
|
211
|
+
margin: 0; display: grid; grid-template-columns: 96px 1fr; gap: 10px 12px;
|
|
212
|
+
font-size: 13px;
|
|
213
|
+
}
|
|
214
|
+
.facts dt {
|
|
215
|
+
margin: 0; font-family: var(--mono); font-size: 10px; letter-spacing: 0.06em;
|
|
216
|
+
text-transform: uppercase; color: var(--dim); padding-top: 3px;
|
|
217
|
+
}
|
|
218
|
+
.facts dd { margin: 0; color: var(--text); word-break: break-word; }
|
|
219
|
+
.dialog .detail { margin-top: 16px; }
|
|
220
|
+
.dialog .graph-svg { max-width: none; height: 220px; }
|
|
190
221
|
footer { margin-top: 28px; color: var(--dim); font-size: 12px; }
|
|
191
222
|
footer a { margin-right: 12px; }
|
|
192
223
|
</style>
|
|
@@ -196,41 +227,33 @@ export function activityHtml() {
|
|
|
196
227
|
<header>
|
|
197
228
|
<div>
|
|
198
229
|
<div class="brand">Memorix <span>activity</span></div>
|
|
199
|
-
<p class="sub">
|
|
230
|
+
<p class="sub">Three feeds: data pushed in, data Nexum is processing, and data fetched out. In-memory — last ~10 000 per feed; clears on process restart.</p>
|
|
200
231
|
</div>
|
|
201
232
|
<div class="meta">
|
|
202
233
|
<div><strong>UI</strong> /_live/activity</div>
|
|
203
234
|
<div><strong>API</strong> /_live/api/activity</div>
|
|
204
|
-
<div><a href="/_live">← live view</a> · <a href="/_live/changes">data changes</a></div>
|
|
235
|
+
<div><a href="/_live">← live view</a> · <a href="/_live/graph">stored graph</a> · <a href="/_live/changes">data changes</a></div>
|
|
205
236
|
</div>
|
|
206
237
|
</header>
|
|
207
238
|
|
|
239
|
+
<div class="quiet-banner" id="quiet-banner" hidden></div>
|
|
240
|
+
|
|
208
241
|
<p class="section-label">Activity · last 60 minutes</p>
|
|
209
242
|
<div class="health-grid" id="charts"></div>
|
|
210
243
|
|
|
211
|
-
<
|
|
212
|
-
<
|
|
213
|
-
<
|
|
214
|
-
<span
|
|
215
|
-
</
|
|
216
|
-
<
|
|
217
|
-
<
|
|
218
|
-
<span>Nexum
|
|
219
|
-
</
|
|
220
|
-
<
|
|
221
|
-
<
|
|
222
|
-
<span>Data
|
|
223
|
-
</
|
|
224
|
-
<div class="legend-row">
|
|
225
|
-
<strong>Nexum gate</strong>
|
|
226
|
-
<span>Auto-analyze is <em>on by default</em> for types named by an installed <span class="mono">nexum.indexing-policy</span> (built-in default: <span class="mono">content</span> / <span class="mono">snapshots|knowx</span>; security-map estate products when that policy is installed). A type can be stored and still show <em>skipped · no policy</em> — that is the analyze allowlist, not the switch.</span>
|
|
227
|
-
</div>
|
|
228
|
-
</div>
|
|
229
|
-
|
|
230
|
-
<nav class="tabs" role="tablist">
|
|
231
|
-
<button class="tab" role="tab" aria-selected="true" data-lane="land">Land · writes</button>
|
|
232
|
-
<button class="tab" role="tab" aria-selected="false" data-lane="index">Connect · Nexum</button>
|
|
233
|
-
<button class="tab" role="tab" aria-selected="false" data-lane="fetch">Fetch · reads</button>
|
|
244
|
+
<nav class="tabs" role="tablist" aria-label="Activity type">
|
|
245
|
+
<button class="tab" role="tab" aria-selected="true" data-lane="land">
|
|
246
|
+
<span class="tab-name">Pushed</span>
|
|
247
|
+
<span class="tab-desc">Data written into Memorix</span>
|
|
248
|
+
</button>
|
|
249
|
+
<button class="tab" role="tab" aria-selected="false" data-lane="index">
|
|
250
|
+
<span class="tab-name">Processed</span>
|
|
251
|
+
<span class="tab-desc">Nexum working on that data</span>
|
|
252
|
+
</button>
|
|
253
|
+
<button class="tab" role="tab" aria-selected="false" data-lane="fetch">
|
|
254
|
+
<span class="tab-name">Fetched</span>
|
|
255
|
+
<span class="tab-desc">Data read out of Memorix</span>
|
|
256
|
+
</button>
|
|
234
257
|
</nav>
|
|
235
258
|
<p class="lane-hint" id="lane-hint"></p>
|
|
236
259
|
|
|
@@ -244,10 +267,10 @@ export function activityHtml() {
|
|
|
244
267
|
<label>Caller
|
|
245
268
|
<select id="f-caller"><option value="">All</option></select>
|
|
246
269
|
</label>
|
|
247
|
-
<label>
|
|
270
|
+
<label>Object type
|
|
248
271
|
<select id="f-ot"><option value="">All</option></select>
|
|
249
272
|
</label>
|
|
250
|
-
<label>
|
|
273
|
+
<label>Content type
|
|
251
274
|
<select id="f-ct"><option value="">All</option></select>
|
|
252
275
|
</label>
|
|
253
276
|
<label id="f-nexum-wrap">Nexum
|
|
@@ -283,17 +306,21 @@ export function activityHtml() {
|
|
|
283
306
|
<span id="pause-msg">List held</span>
|
|
284
307
|
<button class="btn" id="resume" type="button">Show new</button>
|
|
285
308
|
</div>
|
|
286
|
-
<div class="table-
|
|
309
|
+
<div class="table-wrap" id="table-wrap"></div>
|
|
287
310
|
<footer>
|
|
288
311
|
<a href="/_live">/_live</a>
|
|
312
|
+
<a href="/_live/graph">/_live/graph</a>
|
|
289
313
|
<a href="/_live/changes">/_live/changes</a>
|
|
290
|
-
Click a row to open
|
|
314
|
+
Click a row to open a dialog. Live updates pause while a dialog is open or the page is scrolled down.
|
|
291
315
|
</footer>
|
|
292
316
|
</div>
|
|
293
317
|
<div class="dialog-back" id="dialog-back" hidden>
|
|
294
318
|
<div class="dialog" role="dialog" aria-modal="true" aria-labelledby="dialog-title">
|
|
295
319
|
<div class="dialog-bar">
|
|
296
|
-
<
|
|
320
|
+
<div>
|
|
321
|
+
<div class="dialog-kicker" id="dialog-kicker">Detail</div>
|
|
322
|
+
<strong id="dialog-title">Item</strong>
|
|
323
|
+
</div>
|
|
297
324
|
<button class="btn" id="dialog-close" type="button">Close</button>
|
|
298
325
|
</div>
|
|
299
326
|
<div id="dialog-body"></div>
|
|
@@ -301,7 +328,17 @@ export function activityHtml() {
|
|
|
301
328
|
</div>
|
|
302
329
|
<script>
|
|
303
330
|
const $ = (id) => document.getElementById(id);
|
|
304
|
-
|
|
331
|
+
const LANE_FROM_HASH = {
|
|
332
|
+
pushed: "land", land: "land",
|
|
333
|
+
processed: "index", index: "index", connect: "index",
|
|
334
|
+
fetched: "fetch", fetch: "fetch",
|
|
335
|
+
};
|
|
336
|
+
const HASH_FROM_LANE = { land: "pushed", index: "processed", fetch: "fetched" };
|
|
337
|
+
function hashLane() {
|
|
338
|
+
const key = (location.hash || "").replace(/^#/, "").toLowerCase();
|
|
339
|
+
return LANE_FROM_HASH[key] || "land";
|
|
340
|
+
}
|
|
341
|
+
let lane = hashLane();
|
|
305
342
|
let entries = [];
|
|
306
343
|
let liveEntries = [];
|
|
307
344
|
let followLive = true;
|
|
@@ -310,6 +347,8 @@ export function activityHtml() {
|
|
|
310
347
|
let sizes = { land: 0, index: 0, fetch: 0 };
|
|
311
348
|
let capacity = 10000;
|
|
312
349
|
let seriesBuckets = [];
|
|
350
|
+
let startedAt = "";
|
|
351
|
+
let latest = { land: null, index: null, fetch: null };
|
|
313
352
|
let filterState = { q: "", org: "", caller: "", ot: "", ct: "", nexum: "", how: "", hit: "" };
|
|
314
353
|
|
|
315
354
|
function listHeld() {
|
|
@@ -317,39 +356,78 @@ export function activityHtml() {
|
|
|
317
356
|
}
|
|
318
357
|
|
|
319
358
|
function dialogTitle(e) {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
359
|
+
return e.recordId || (e.source && e.source.recordId) || (e.snapshot && e.snapshot.recordId) || "Item";
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function laneLabel(e) {
|
|
363
|
+
if (e.lane === "index") return "Processed";
|
|
364
|
+
if (e.lane === "fetch") return "Fetched";
|
|
365
|
+
return "Pushed";
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function fact(label, valueHtml) {
|
|
369
|
+
return "<dt>" + escapeHtml(label) + "</dt><dd>" + valueHtml + "</dd>";
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function dialogOpen() {
|
|
373
|
+
return !$("dialog-back").hidden;
|
|
323
374
|
}
|
|
324
375
|
|
|
325
376
|
function openDialog(e) {
|
|
377
|
+
followLive = false;
|
|
378
|
+
document.body.style.overflow = "hidden";
|
|
379
|
+
$("dialog-kicker").textContent = laneLabel(e);
|
|
326
380
|
$("dialog-title").textContent = dialogTitle(e);
|
|
327
381
|
$("dialog-body").innerHTML = renderDetail(e);
|
|
328
382
|
$("dialog-back").hidden = false;
|
|
383
|
+
updatePauseBar();
|
|
329
384
|
}
|
|
330
385
|
|
|
331
386
|
function closeDialog() {
|
|
332
387
|
$("dialog-back").hidden = true;
|
|
333
388
|
$("dialog-body").innerHTML = "";
|
|
389
|
+
document.body.style.overflow = "";
|
|
390
|
+
if (window.scrollY < 8) {
|
|
391
|
+
followLive = true;
|
|
392
|
+
applyLive();
|
|
393
|
+
} else {
|
|
394
|
+
updatePauseBar();
|
|
395
|
+
}
|
|
334
396
|
}
|
|
335
397
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
tab.setAttribute("aria-selected", "true");
|
|
340
|
-
lane = tab.dataset.lane;
|
|
341
|
-
closeDialog();
|
|
342
|
-
followLive = true;
|
|
343
|
-
pendingNew = 0;
|
|
344
|
-
syncFilterVisibility();
|
|
345
|
-
load(true);
|
|
398
|
+
function applyLaneChrome() {
|
|
399
|
+
document.querySelectorAll(".tab").forEach((t) => {
|
|
400
|
+
t.setAttribute("aria-selected", t.dataset.lane === lane ? "true" : "false");
|
|
346
401
|
});
|
|
402
|
+
const hash = "#" + (HASH_FROM_LANE[lane] || "pushed");
|
|
403
|
+
if (location.hash !== hash) history.replaceState(null, "", hash);
|
|
404
|
+
syncFilterVisibility();
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function switchLane(next) {
|
|
408
|
+
if (!next || next === lane) return;
|
|
409
|
+
lane = next;
|
|
410
|
+
$("dialog-back").hidden = true;
|
|
411
|
+
$("dialog-body").innerHTML = "";
|
|
412
|
+
document.body.style.overflow = "";
|
|
413
|
+
followLive = true;
|
|
414
|
+
pendingNew = 0;
|
|
415
|
+
applyLaneChrome();
|
|
416
|
+
load(true);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
document.querySelectorAll(".tab").forEach((tab) => {
|
|
420
|
+
tab.addEventListener("click", () => switchLane(tab.dataset.lane));
|
|
347
421
|
});
|
|
422
|
+
window.addEventListener("hashchange", () => switchLane(hashLane()));
|
|
348
423
|
$("refresh").onclick = () => {
|
|
349
424
|
followLive = true;
|
|
350
425
|
load(true);
|
|
351
426
|
};
|
|
352
427
|
$("resume").onclick = () => {
|
|
428
|
+
$("dialog-back").hidden = true;
|
|
429
|
+
$("dialog-body").innerHTML = "";
|
|
430
|
+
document.body.style.overflow = "";
|
|
353
431
|
followLive = true;
|
|
354
432
|
applyLive();
|
|
355
433
|
};
|
|
@@ -360,9 +438,9 @@ export function activityHtml() {
|
|
|
360
438
|
document.addEventListener("keydown", (ev) => {
|
|
361
439
|
if (ev.key === "Escape" && !$("dialog-back").hidden) closeDialog();
|
|
362
440
|
});
|
|
363
|
-
|
|
364
|
-
if (ignoreScroll) return;
|
|
365
|
-
const atTop =
|
|
441
|
+
window.addEventListener("scroll", () => {
|
|
442
|
+
if (ignoreScroll || dialogOpen()) return;
|
|
443
|
+
const atTop = window.scrollY < 8;
|
|
366
444
|
if (atTop && !followLive) {
|
|
367
445
|
followLive = true;
|
|
368
446
|
if (pendingNew) applyLive();
|
|
@@ -371,7 +449,7 @@ export function activityHtml() {
|
|
|
371
449
|
followLive = false;
|
|
372
450
|
updatePauseBar();
|
|
373
451
|
}
|
|
374
|
-
});
|
|
452
|
+
}, { passive: true });
|
|
375
453
|
$("f-clear").onclick = () => {
|
|
376
454
|
filterState = { q: "", org: "", caller: "", ot: "", ct: "", nexum: "", how: "", hit: "" };
|
|
377
455
|
$("f-q").value = "";
|
|
@@ -407,7 +485,7 @@ export function activityHtml() {
|
|
|
407
485
|
$("f-how-wrap").hidden = lane !== "index";
|
|
408
486
|
$("f-hit-wrap").hidden = lane !== "fetch";
|
|
409
487
|
}
|
|
410
|
-
|
|
488
|
+
applyLaneChrome();
|
|
411
489
|
|
|
412
490
|
function escapeHtml(s) {
|
|
413
491
|
return String(s ?? "").replace(/[&<>"']/g, (c) => ({
|
|
@@ -433,31 +511,55 @@ export function activityHtml() {
|
|
|
433
511
|
return '<span class="badge ' + cls + '">' + escapeHtml(s) + "</span>";
|
|
434
512
|
}
|
|
435
513
|
|
|
436
|
-
function
|
|
514
|
+
function skipKind(reason) {
|
|
437
515
|
const r = String(reason || "").toLowerCase();
|
|
438
|
-
if (!r) return "skipped";
|
|
439
|
-
if (r.includes("no matching")) return "skipped · no policy";
|
|
440
|
-
if (r.includes("disabled")) return "skipped · off";
|
|
441
|
-
if (r.includes("worker unset")) return "skipped · no worker";
|
|
442
|
-
if (r.includes("no analyzeable") || r.includes("no payload")) return "skipped · no payload";
|
|
443
|
-
if (r.includes("operational")) return "skipped · no ops db";
|
|
444
|
-
return
|
|
516
|
+
if (!r) return { short: "skip", label: "skipped" };
|
|
517
|
+
if (r.includes("no matching")) return { short: "no-pol", label: "skipped · no policy" };
|
|
518
|
+
if (r.includes("disabled")) return { short: "off", label: "skipped · off" };
|
|
519
|
+
if (r.includes("worker unset")) return { short: "no-wk", label: "skipped · no worker" };
|
|
520
|
+
if (r.includes("no analyzeable") || r.includes("no payload")) return { short: "empty", label: "skipped · no payload" };
|
|
521
|
+
if (r.includes("operational")) return { short: "no-db", label: "skipped · no ops db" };
|
|
522
|
+
return { short: "skip", label: "skipped" };
|
|
445
523
|
}
|
|
446
524
|
|
|
447
|
-
/**
|
|
525
|
+
/** Compact land-row Nexum badge (full text lives in the dialog). */
|
|
448
526
|
function nexumLandCell(indexing) {
|
|
449
527
|
if (!indexing) return "—";
|
|
450
528
|
if (indexing.status === "skipped") {
|
|
451
|
-
|
|
529
|
+
const kind = skipKind(indexing.reason);
|
|
530
|
+
const tip = indexing.reason ? kind.label + " — " + indexing.reason : kind.label;
|
|
531
|
+
return '<span class="badge skip" title="' + escapeHtml(tip) + '">' + escapeHtml(kind.short) + "</span>";
|
|
452
532
|
}
|
|
453
533
|
if (indexing.status === "queued") {
|
|
454
|
-
return '<span class="badge queued"
|
|
534
|
+
return '<span class="badge queued" title="queued · analyze">queued</span>';
|
|
535
|
+
}
|
|
536
|
+
const s = String(indexing.status || "");
|
|
537
|
+
return '<span class="badge" title="' + escapeHtml(s) + '">' + escapeHtml(s.slice(0, 10)) + "</span>";
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function nexumLandFull(indexing) {
|
|
541
|
+
if (!indexing) return "—";
|
|
542
|
+
if (indexing.status === "skipped") {
|
|
543
|
+
const kind = skipKind(indexing.reason);
|
|
544
|
+
return '<span class="badge skip">' + escapeHtml(kind.label) + "</span>" +
|
|
545
|
+
(indexing.reason
|
|
546
|
+
? ' <span class="mono">' + escapeHtml(indexing.reason) + "</span>"
|
|
547
|
+
: "");
|
|
548
|
+
}
|
|
549
|
+
if (indexing.status === "queued") {
|
|
550
|
+
return '<span class="badge queued">queued · analyze</span>' +
|
|
551
|
+
(indexing.policy ? ' <span class="mono">policy ' + escapeHtml(indexing.policy) + "</span>" : "") +
|
|
552
|
+
(indexing.operationId ? ' <span class="mono">' + escapeHtml(indexing.operationId) + "</span>" : "");
|
|
455
553
|
}
|
|
456
554
|
return statusBadge(indexing.status);
|
|
457
555
|
}
|
|
458
556
|
|
|
459
|
-
function modeBadge(mode) {
|
|
460
|
-
if (mode === "rules")
|
|
557
|
+
function modeBadge(mode, compact) {
|
|
558
|
+
if (mode === "rules") {
|
|
559
|
+
return compact
|
|
560
|
+
? '<span class="badge ok" title="rules · no AI">rules</span>'
|
|
561
|
+
: '<span class="badge ok">rules · no AI</span>';
|
|
562
|
+
}
|
|
461
563
|
if (mode === "ai") return '<span class="badge ai">AI</span>';
|
|
462
564
|
if (!mode) return "—";
|
|
463
565
|
return '<span class="badge">unknown</span>';
|
|
@@ -492,7 +594,9 @@ export function activityHtml() {
|
|
|
492
594
|
return;
|
|
493
595
|
}
|
|
494
596
|
bar.hidden = false;
|
|
495
|
-
const why =
|
|
597
|
+
const why = dialogOpen()
|
|
598
|
+
? "Dialog open — list held"
|
|
599
|
+
: "Scrolled down — list held";
|
|
496
600
|
$("pause-msg").textContent = pendingNew > 0 ? why + " · " + pendingNew + " new" : why;
|
|
497
601
|
$("resume").textContent = pendingNew > 0 ? "Show " + pendingNew + " new" : "Resume live";
|
|
498
602
|
}
|
|
@@ -501,10 +605,9 @@ export function activityHtml() {
|
|
|
501
605
|
entries = liveEntries;
|
|
502
606
|
pendingNew = 0;
|
|
503
607
|
followLive = true;
|
|
504
|
-
const sc = $("table-scroll");
|
|
505
608
|
ignoreScroll = true;
|
|
506
609
|
render();
|
|
507
|
-
|
|
610
|
+
window.scrollTo(0, 0);
|
|
508
611
|
updatePauseBar();
|
|
509
612
|
requestAnimationFrame(() => { ignoreScroll = false; });
|
|
510
613
|
}
|
|
@@ -538,17 +641,17 @@ export function activityHtml() {
|
|
|
538
641
|
const last = (key) => (b.length ? Number(b[b.length - 1][key]) || 0 : 0);
|
|
539
642
|
const cards = [
|
|
540
643
|
{
|
|
541
|
-
title: "
|
|
644
|
+
title: "Pushed",
|
|
542
645
|
value: sizes.land,
|
|
543
|
-
unit: "in
|
|
646
|
+
unit: "in feed",
|
|
544
647
|
series: b.map((x) => x.land),
|
|
545
648
|
color: "#c8f082",
|
|
546
649
|
hint: "last min " + last("land") + " · Σ60m " + sum("land"),
|
|
547
650
|
},
|
|
548
651
|
{
|
|
549
|
-
title: "
|
|
652
|
+
title: "Processed",
|
|
550
653
|
value: sizes.index,
|
|
551
|
-
unit: "in
|
|
654
|
+
unit: "in feed",
|
|
552
655
|
series: b.map((x) => x.connect),
|
|
553
656
|
color: "#7eb8d4",
|
|
554
657
|
hint: "rules Σ " + sum("connectRules") + " · AI Σ " + sum("connectAi"),
|
|
@@ -559,19 +662,20 @@ export function activityHtml() {
|
|
|
559
662
|
unit: "Σ60m",
|
|
560
663
|
series: b.map((x) => x.relations),
|
|
561
664
|
color: "#d4a574",
|
|
562
|
-
hint: "
|
|
665
|
+
hint: "Relationship plans from Nexum graphs",
|
|
563
666
|
},
|
|
564
667
|
{
|
|
565
|
-
title: "
|
|
668
|
+
title: "Fetched",
|
|
566
669
|
value: sizes.fetch,
|
|
567
|
-
unit: "in
|
|
670
|
+
unit: "in feed",
|
|
568
671
|
series: b.map((x) => x.fetch),
|
|
569
672
|
color: "#3dba7a",
|
|
570
673
|
hint: "last min " + last("fetch") + " · Σ60m " + sum("fetch"),
|
|
571
674
|
},
|
|
572
675
|
];
|
|
573
676
|
$("charts").innerHTML = cards.map((c) =>
|
|
574
|
-
'<div class="health-card"
|
|
677
|
+
'<div class="health-card">' +
|
|
678
|
+
"<h3>" + escapeHtml(c.title) + "</h3>" +
|
|
575
679
|
'<div class="val">' + escapeHtml(c.value) + "<span>" + escapeHtml(c.unit) + "</span></div>" +
|
|
576
680
|
sparkline(c.series, c.color) +
|
|
577
681
|
'<div class="hint">' + escapeHtml(c.hint) + "</div></div>"
|
|
@@ -619,57 +723,80 @@ export function activityHtml() {
|
|
|
619
723
|
}
|
|
620
724
|
|
|
621
725
|
function renderDetail(e) {
|
|
726
|
+
const t = typeOf(e);
|
|
622
727
|
if (e.lane === "land") {
|
|
623
|
-
return '<
|
|
624
|
-
|
|
625
|
-
"
|
|
626
|
-
(e.
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
"</
|
|
728
|
+
return '<dl class="facts">' +
|
|
729
|
+
fact("Time", escapeHtml(fmtTime(e.ts))) +
|
|
730
|
+
fact("Org", escapeHtml(e.orgId || "—")) +
|
|
731
|
+
fact("Caller", escapeHtml(e.caller || "—")) +
|
|
732
|
+
fact("Object type", '<span class="mono">' + escapeHtml(t.ot || "—") + "</span>") +
|
|
733
|
+
fact("Content type", '<span class="mono">' + escapeHtml(t.ct || "—") + "</span>") +
|
|
734
|
+
fact("Record", '<span class="mono">' + escapeHtml(e.recordId || "—") + "</span>") +
|
|
735
|
+
fact("Revision", escapeHtml(e.revision)) +
|
|
736
|
+
fact("Outcome", escapeHtml(e.outcome || "—")) +
|
|
737
|
+
(e.indexing ? fact("Nexum", nexumLandFull(e.indexing)) : "") +
|
|
738
|
+
"</dl>";
|
|
634
739
|
}
|
|
635
740
|
if (e.lane === "fetch") {
|
|
636
|
-
return '<
|
|
637
|
-
"
|
|
638
|
-
(
|
|
639
|
-
(e.
|
|
640
|
-
"</
|
|
741
|
+
return '<dl class="facts">' +
|
|
742
|
+
fact("Time", escapeHtml(fmtTime(e.ts))) +
|
|
743
|
+
fact("Org", escapeHtml(e.orgId || "—")) +
|
|
744
|
+
fact("Caller", escapeHtml(e.caller || "—")) +
|
|
745
|
+
fact("Object type", '<span class="mono">' + escapeHtml(t.ot || "—") + "</span>") +
|
|
746
|
+
fact("Content type", '<span class="mono">' + escapeHtml(t.ct || "—") + "</span>") +
|
|
747
|
+
fact("Mode", escapeHtml(e.mode || "—")) +
|
|
748
|
+
fact("Record", '<span class="mono">' + escapeHtml(e.recordId || "—") + "</span>") +
|
|
749
|
+
fact("Hit", e.hit ? "yes" : "no") +
|
|
750
|
+
(e.count != null ? fact("Count", escapeHtml(e.count)) : "") +
|
|
751
|
+
(e.knowledgeGraph ? fact("Kind", "knowledge-graph subject") : "") +
|
|
752
|
+
"</dl>";
|
|
641
753
|
}
|
|
642
|
-
let html = '<
|
|
643
|
-
html += "
|
|
754
|
+
let html = '<dl class="facts">';
|
|
755
|
+
html += fact("Time", escapeHtml(fmtTime(e.ts)));
|
|
756
|
+
html += fact("Org", escapeHtml(e.orgId || "—"));
|
|
757
|
+
html += fact("Status", statusBadge(e.status));
|
|
644
758
|
if (e.analyzeMode || e.analyzeMethod) {
|
|
645
|
-
html += "
|
|
646
|
-
(e.analyzeMethod ? ' <span class="mono">
|
|
647
|
-
"</div>";
|
|
759
|
+
html += fact("How", modeBadge(e.analyzeMode || "unknown", false) +
|
|
760
|
+
(e.analyzeMethod ? ' <span class="mono">' + escapeHtml(e.analyzeMethod) + "</span>" : ""));
|
|
648
761
|
}
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
if (e.
|
|
762
|
+
html += fact("Object type", '<span class="mono">' + escapeHtml(t.ot || "—") + "</span>");
|
|
763
|
+
html += fact("Content type", '<span class="mono">' + escapeHtml(t.ct || "—") + "</span>");
|
|
764
|
+
if (e.operationId) html += fact("Operation", '<span class="mono">' + escapeHtml(e.operationId) + "</span>");
|
|
765
|
+
if (e.source) html += fact("Source", addr(e.source));
|
|
766
|
+
if (e.snapshot) html += fact("Snapshot", addr(e.snapshot));
|
|
652
767
|
if (e.sourceRefs && e.sourceRefs.length) {
|
|
653
|
-
html += '<
|
|
768
|
+
html += fact("Source refs", '<span class="mono">' + escapeHtml(e.sourceRefs.join(", ")) + "</span>");
|
|
654
769
|
}
|
|
655
|
-
html += "
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
770
|
+
html += fact("Nodes", escapeHtml(e.nodeCount ?? 0));
|
|
771
|
+
html += fact("Graph edges", escapeHtml(e.edgeCount ?? 0));
|
|
772
|
+
html += fact("Relations", escapeHtml(e.relationCount ?? 0));
|
|
773
|
+
{
|
|
774
|
+
const snapId = e.snapshot && e.snapshot.recordId;
|
|
775
|
+
const src0 = e.sourceRefs && e.sourceRefs[0];
|
|
776
|
+
const graphHref = snapId
|
|
777
|
+
? "/_live/graph?recordId=" + encodeURIComponent(snapId)
|
|
778
|
+
: src0
|
|
779
|
+
? "/_live/graph?source=" + encodeURIComponent(src0)
|
|
780
|
+
: "/_live/graph";
|
|
781
|
+
html += fact("Graph", '<a href="' + escapeHtml(graphHref) + '">Open stored graph</a>');
|
|
782
|
+
}
|
|
783
|
+
if (e.error) html += fact("Error", '<span style="color:var(--bad)">' + escapeHtml(e.error) + "</span>");
|
|
784
|
+
html += "</dl>";
|
|
659
785
|
const edges = e.edgeSummaries || [];
|
|
660
|
-
if (edges.length) {
|
|
661
|
-
html +=
|
|
662
|
-
|
|
663
|
-
html += "<
|
|
664
|
-
|
|
665
|
-
"
|
|
786
|
+
if (edges.length || (e.nodeLabels && e.nodeLabels.length)) {
|
|
787
|
+
html += '<div class="detail">';
|
|
788
|
+
if (edges.length) {
|
|
789
|
+
html += "<h3>Edges</h3><ul class='edge-list'>";
|
|
790
|
+
for (const ed of edges) {
|
|
791
|
+
html += "<li>" + escapeHtml(ed.from) + " → " + escapeHtml(ed.to) +
|
|
792
|
+
(ed.label ? " <span class='mono'>(" + escapeHtml(ed.label) + ")</span>" : "") +
|
|
793
|
+
"</li>";
|
|
794
|
+
}
|
|
795
|
+
html += "</ul>";
|
|
666
796
|
}
|
|
667
|
-
html += "</ul>";
|
|
668
|
-
}
|
|
669
|
-
if ((e.nodeLabels && e.nodeLabels.length) || edges.length) {
|
|
670
797
|
html += renderGraphSvg(e.nodeLabels || [], edges);
|
|
798
|
+
html += "</div>";
|
|
671
799
|
}
|
|
672
|
-
html += "</div>";
|
|
673
800
|
return html;
|
|
674
801
|
}
|
|
675
802
|
|
|
@@ -717,33 +844,95 @@ export function activityHtml() {
|
|
|
717
844
|
fmtTime(e.ts),
|
|
718
845
|
escapeHtml(e.orgId),
|
|
719
846
|
statusBadge(e.status),
|
|
720
|
-
modeBadge(e.analyzeMode || (e.status === "connected" ? "unknown" : "")),
|
|
847
|
+
modeBadge(e.analyzeMode || (e.status === "connected" ? "unknown" : ""), true),
|
|
721
848
|
...typeCells(e),
|
|
722
849
|
e.snapshot
|
|
723
850
|
? '<span class="mono">' + escapeHtml(e.snapshot.recordId) + "</span>"
|
|
724
851
|
: "—",
|
|
725
852
|
(e.relationCount != null || e.edgeCount != null)
|
|
726
|
-
? escapeHtml((e.relationCount ?? 0) + " rel / " + (e.edgeCount ?? 0) + " edges")
|
|
853
|
+
? '<span class="mono" title="' + escapeHtml((e.relationCount ?? 0) + " rel / " + (e.edgeCount ?? 0) + " edges") + '">' +
|
|
854
|
+
escapeHtml((e.relationCount ?? 0) + "/" + (e.edgeCount ?? 0)) + "</span>"
|
|
727
855
|
: "—",
|
|
728
856
|
];
|
|
729
857
|
}
|
|
730
858
|
|
|
731
859
|
function headersForLane() {
|
|
732
|
-
if (lane === "land") return ["Time", "Org", "Caller", "
|
|
733
|
-
if (lane === "fetch") return ["Time", "Org", "Caller", "
|
|
734
|
-
return ["Time", "Org", "Status", "How", "
|
|
860
|
+
if (lane === "land") return ["Time", "Org", "Caller", "Object type", "Content type", "Record", "Outcome", "Nexum"];
|
|
861
|
+
if (lane === "fetch") return ["Time", "Org", "Caller", "Object type", "Content type", "Mode", "Record", "Hit"];
|
|
862
|
+
return ["Time", "Org", "Status", "How", "Object type", "Content type", "Snapshot", "Rel/Edges"];
|
|
735
863
|
}
|
|
736
864
|
|
|
737
865
|
const LANE_HINT = {
|
|
738
|
-
land: "<strong>
|
|
739
|
-
index: "<strong>
|
|
740
|
-
fetch: "<strong>
|
|
866
|
+
land: "<strong>Pushed</strong> — data written into Memorix. The <em>Nexum</em> column is short (<em>no-pol</em>, <em>queued</em>, …): whether this write was sent on to processing. Open a row for the full reason. Rules vs AI show on Processed after Nexum finishes.",
|
|
867
|
+
index: "<strong>Processed</strong> — Nexum working on pushed data. How = <em>rules</em> (no AI) or <em>AI</em>. Rel/Edges = relationship plans / graph edges (full counts in the dialog).",
|
|
868
|
+
fetch: "<strong>Fetched</strong> — data read out of Memorix (up to " + capacity + ").",
|
|
741
869
|
};
|
|
742
870
|
|
|
871
|
+
function fmtWhen(iso) {
|
|
872
|
+
if (!iso) return "";
|
|
873
|
+
const d = new Date(iso);
|
|
874
|
+
return Number.isNaN(d.getTime()) ? String(iso) : d.toLocaleString();
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
function joinOr(names) {
|
|
878
|
+
if (names.length <= 1) return names[0] || "";
|
|
879
|
+
if (names.length === 2) return names[0] + " or " + names[1];
|
|
880
|
+
return names.slice(0, -1).join(", ") + ", or " + names[names.length - 1];
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
function applyFreshness(src) {
|
|
884
|
+
if (!src) return;
|
|
885
|
+
if (src.startedAt) startedAt = src.startedAt;
|
|
886
|
+
if (src.latest) {
|
|
887
|
+
latest = {
|
|
888
|
+
land: src.latest.land || null,
|
|
889
|
+
index: src.latest.index || null,
|
|
890
|
+
fetch: src.latest.fetch || null,
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
function renderBanner() {
|
|
896
|
+
const el = $("quiet-banner");
|
|
897
|
+
if (!el) return;
|
|
898
|
+
const rows = [
|
|
899
|
+
{ quiet: "pushed", last: "push", n: sizes.land || 0, ts: latest.land },
|
|
900
|
+
{ quiet: "processed", last: "processed event", n: sizes.index || 0, ts: latest.index },
|
|
901
|
+
{ quiet: "fetched", last: "fetch", n: sizes.fetch || 0, ts: latest.fetch },
|
|
902
|
+
];
|
|
903
|
+
const silent = rows.filter((r) => !r.n);
|
|
904
|
+
const active = rows.filter((r) => r.n && r.ts);
|
|
905
|
+
if (!silent.length) {
|
|
906
|
+
el.hidden = true;
|
|
907
|
+
el.innerHTML = "";
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
const since = fmtWhen(startedAt);
|
|
911
|
+
let html = "<strong>Nothing " + escapeHtml(joinOr(silent.map((r) => r.quiet))) + "</strong>";
|
|
912
|
+
html += since
|
|
913
|
+
? " since " + escapeHtml(since) + ", when this process started."
|
|
914
|
+
: " in this process.";
|
|
915
|
+
if (active.length) {
|
|
916
|
+
html += " " + active.map((r) =>
|
|
917
|
+
"Last " + escapeHtml(r.last) + " " + escapeHtml(fmtWhen(r.ts)) + "."
|
|
918
|
+
).join(" ");
|
|
919
|
+
}
|
|
920
|
+
el.hidden = false;
|
|
921
|
+
el.innerHTML = html;
|
|
922
|
+
}
|
|
923
|
+
|
|
743
924
|
function emptyMessage() {
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
925
|
+
const n = laneSize();
|
|
926
|
+
const since = fmtWhen(startedAt);
|
|
927
|
+
const sinceBit = since ? " since " + since + "." : " in this process.";
|
|
928
|
+
if (!n) {
|
|
929
|
+
if (lane === "land") return "Nothing pushed" + sinceBit;
|
|
930
|
+
if (lane === "index") return "Nothing processed" + sinceBit;
|
|
931
|
+
return "Nothing fetched" + sinceBit;
|
|
932
|
+
}
|
|
933
|
+
if (lane === "land") return "No pushed data matches these filters.";
|
|
934
|
+
if (lane === "index") return "No processed data matches these filters.";
|
|
935
|
+
return "No fetched data matches these filters.";
|
|
747
936
|
}
|
|
748
937
|
|
|
749
938
|
function fillSelect(sel, values, current) {
|
|
@@ -802,37 +991,44 @@ export function activityHtml() {
|
|
|
802
991
|
});
|
|
803
992
|
}
|
|
804
993
|
|
|
994
|
+
function laneSize() {
|
|
995
|
+
if (lane === "land") return sizes.land || 0;
|
|
996
|
+
if (lane === "index") return sizes.index || 0;
|
|
997
|
+
return sizes.fetch || 0;
|
|
998
|
+
}
|
|
999
|
+
|
|
805
1000
|
function updateSummary(shownCount, totalCount) {
|
|
806
1001
|
$("summary").innerHTML =
|
|
807
|
-
"<span>
|
|
808
|
-
"<span>
|
|
809
|
-
"<span>
|
|
810
|
-
"<span>showing <strong>" + shownCount + "</strong> of <strong>" + totalCount + "</strong></span>";
|
|
1002
|
+
"<span>pushed <strong>" + sizes.land + "/" + capacity + "</strong></span>" +
|
|
1003
|
+
"<span>processed <strong>" + sizes.index + "/" + capacity + "</strong></span>" +
|
|
1004
|
+
"<span>fetched <strong>" + sizes.fetch + "/" + capacity + "</strong></span>" +
|
|
1005
|
+
"<span>showing <strong>" + shownCount + "</strong> latest of <strong>" + totalCount + "</strong></span>";
|
|
811
1006
|
}
|
|
812
1007
|
|
|
813
1008
|
function render() {
|
|
1009
|
+
renderBanner();
|
|
814
1010
|
$("lane-hint").innerHTML = LANE_HINT[lane] || "";
|
|
815
1011
|
refreshFilterOptions(entries);
|
|
816
1012
|
const shown = filteredEntries();
|
|
817
|
-
updateSummary(shown.length,
|
|
1013
|
+
updateSummary(shown.length, laneSize());
|
|
818
1014
|
renderCharts();
|
|
819
1015
|
updatePauseBar();
|
|
820
1016
|
|
|
821
|
-
const
|
|
822
|
-
const
|
|
1017
|
+
const wrap = $("table-wrap");
|
|
1018
|
+
const prevY = window.scrollY;
|
|
823
1019
|
ignoreScroll = true;
|
|
824
1020
|
|
|
825
1021
|
if (!shown.length) {
|
|
826
|
-
|
|
1022
|
+
wrap.innerHTML = "<p class='empty'>" + escapeHtml(emptyMessage()) + "</p>";
|
|
827
1023
|
requestAnimationFrame(() => { ignoreScroll = false; });
|
|
828
1024
|
return;
|
|
829
1025
|
}
|
|
830
1026
|
const heads = headersForLane();
|
|
831
1027
|
const colW = lane === "fetch"
|
|
832
|
-
? ["8%","7%","
|
|
1028
|
+
? ["8%","7%","9%","14%","16%","7%","27%","12%"]
|
|
833
1029
|
: lane === "index"
|
|
834
|
-
? ["8%","7%","
|
|
835
|
-
: ["8%","7%","
|
|
1030
|
+
? ["8%","7%","8%","10%","14%","16%","25%","12%"]
|
|
1031
|
+
: ["8%","7%","10%","14%","16%","26%","9%","10%"];
|
|
836
1032
|
let html = '<table class="list"><colgroup>' +
|
|
837
1033
|
colW.map((w) => '<col style="width:' + w + '">').join("") +
|
|
838
1034
|
"</colgroup><thead><tr>" + heads.map((h) => "<th>" + h + "</th>").join("") + "</tr></thead><tbody>";
|
|
@@ -846,10 +1042,10 @@ export function activityHtml() {
|
|
|
846
1042
|
}).join("") + "</tr>";
|
|
847
1043
|
});
|
|
848
1044
|
html += "</tbody></table>";
|
|
849
|
-
|
|
850
|
-
if (listHeld())
|
|
1045
|
+
wrap.innerHTML = html;
|
|
1046
|
+
if (listHeld()) window.scrollTo(0, prevY);
|
|
851
1047
|
requestAnimationFrame(() => { ignoreScroll = false; });
|
|
852
|
-
|
|
1048
|
+
wrap.querySelectorAll("tr.clickable").forEach((tr) => {
|
|
853
1049
|
tr.addEventListener("click", () => {
|
|
854
1050
|
const k = tr.getAttribute("data-key");
|
|
855
1051
|
const e = shown.find((row) => stableKey(row) === k);
|
|
@@ -858,22 +1054,30 @@ export function activityHtml() {
|
|
|
858
1054
|
});
|
|
859
1055
|
}
|
|
860
1056
|
|
|
1057
|
+
let loadSeq = 0;
|
|
861
1058
|
async function load(applyNow) {
|
|
1059
|
+
const seq = ++loadSeq;
|
|
1060
|
+
const requested = lane;
|
|
862
1061
|
try {
|
|
863
1062
|
const [actRes, serRes] = await Promise.all([
|
|
864
|
-
fetch("/_live/api/activity?lane=" + encodeURIComponent(
|
|
1063
|
+
fetch("/_live/api/activity?lane=" + encodeURIComponent(requested) + "&limit=400"),
|
|
865
1064
|
fetch("/_live/api/activity-series?minutes=60"),
|
|
866
1065
|
]);
|
|
1066
|
+
if (seq !== loadSeq || requested !== lane) return;
|
|
867
1067
|
const body = await actRes.json();
|
|
1068
|
+
if (seq !== loadSeq || requested !== lane) return;
|
|
868
1069
|
if (!actRes.ok || body.ok === false) throw new Error(body?.error?.message || ("HTTP " + actRes.status));
|
|
869
1070
|
liveEntries = body.entries || [];
|
|
870
1071
|
sizes = body.sizes || sizes;
|
|
871
1072
|
capacity = body.capacity || 10000;
|
|
1073
|
+
applyFreshness(body);
|
|
872
1074
|
if (serRes.ok) {
|
|
873
1075
|
const ser = await serRes.json();
|
|
874
1076
|
seriesBuckets = ser.buckets || [];
|
|
875
1077
|
if (ser.sizes) sizes = ser.sizes;
|
|
1078
|
+
applyFreshness(ser);
|
|
876
1079
|
}
|
|
1080
|
+
if (seq !== loadSeq || requested !== lane) return;
|
|
877
1081
|
renderCharts();
|
|
878
1082
|
if (applyNow || !listHeld()) {
|
|
879
1083
|
entries = liveEntries;
|
|
@@ -882,11 +1086,13 @@ export function activityHtml() {
|
|
|
882
1086
|
} else {
|
|
883
1087
|
countNew();
|
|
884
1088
|
const shown = filteredEntries();
|
|
885
|
-
updateSummary(shown.length,
|
|
1089
|
+
updateSummary(shown.length, laneSize());
|
|
886
1090
|
updatePauseBar();
|
|
1091
|
+
renderBanner();
|
|
887
1092
|
}
|
|
888
1093
|
} catch (err) {
|
|
889
|
-
|
|
1094
|
+
if (seq !== loadSeq || requested !== lane) return;
|
|
1095
|
+
$("table-wrap").innerHTML = "<p class='empty'>" + escapeHtml(err.message || String(err)) + "</p>";
|
|
890
1096
|
}
|
|
891
1097
|
}
|
|
892
1098
|
|