@lensmcp/protocol-types 1.25.8 → 1.25.10

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.
@@ -2518,7 +2518,8 @@ export interface DashAiRun {
2518
2518
  label: string;
2519
2519
  why: string;
2520
2520
  }>;
2521
- turns?: number;
2521
+ turns?: number; /** Why its last turn ended, the provider's word (`end_turn`, `max_tokens`). */
2522
+ stopReason?: string;
2522
2523
  };
2523
2524
  /**
2524
2525
  * How the frontier model worked, turn by turn, as it streamed: its reasoning (summarized), what it wrote, each tool it
package/lib/event.d.ts CHANGED
@@ -22,6 +22,7 @@ export declare const EventSourceSchema: z.ZodEnum<{
22
22
  external: "external";
23
23
  ai: "ai";
24
24
  lens: "lens";
25
+ custom: "custom";
25
26
  }>;
26
27
  export type EventSource = z.infer<typeof EventSourceSchema>;
27
28
  export declare const EventCategorySchema: z.ZodEnum<{
@@ -81,6 +82,7 @@ export declare const BaseEventSchema: z.ZodObject<{
81
82
  external: "external";
82
83
  ai: "ai";
83
84
  lens: "lens";
85
+ custom: "custom";
84
86
  }>;
85
87
  category: z.ZodEnum<{
86
88
  db: "db";
package/lib/event.js CHANGED
@@ -1 +1 @@
1
- "use strict";import{z as e}from"zod";import{SourceLocationSchema as t}from"./source-location.js";import{TraceContextSchema as r}from"./context.js";export const EventSourceSchema=e.enum(["nx","vite","typescript","eslint","rollup","client-runtime","chrome","react","valtio","nestjs","nextjs","db","redis","queue","memory","visual","perf","security","gateway","external","ai","lens"]),EventCategorySchema=e.enum(["build","lint","typecheck","test","runtime","render","visual","trace","state","network","backend","db","memory","performance","security","deps","cluster","ai","agent"]),SeveritySchema=e.enum(["debug","info","warning","error","fatal"]),BaseEventSchema=e.object({id:e.string(),sessionId:e.string(),timestamp:e.number().int().nonnegative(),source:EventSourceSchema,category:EventCategorySchema,severity:SeveritySchema,context:r,fingerprint:e.string(),title:e.string(),message:e.string().optional(),location:t.optional(),relatedFiles:e.array(e.string()).optional(),relatedUrls:e.array(e.string()).optional(),raw:e.unknown().optional()});
1
+ "use strict";import{z as e}from"zod";import{SourceLocationSchema as t}from"./source-location.js";import{TraceContextSchema as r}from"./context.js";export const EventSourceSchema=e.enum(["nx","vite","typescript","eslint","rollup","client-runtime","chrome","react","valtio","nestjs","nextjs","db","redis","queue","memory","visual","perf","security","gateway","external","ai","lens","custom"]),EventCategorySchema=e.enum(["build","lint","typecheck","test","runtime","render","visual","trace","state","network","backend","db","memory","performance","security","deps","cluster","ai","agent"]),SeveritySchema=e.enum(["debug","info","warning","error","fatal"]),BaseEventSchema=e.object({id:e.string(),sessionId:e.string(),timestamp:e.number().int().nonnegative(),source:EventSourceSchema,category:EventCategorySchema,severity:SeveritySchema,context:r,fingerprint:e.string(),title:e.string(),message:e.string().optional(),location:t.optional(),relatedFiles:e.array(e.string()).optional(),relatedUrls:e.array(e.string()).optional(),raw:e.unknown().optional()});
package/lib/sources.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * CUSTOM SOURCES (issues/open/foodguard-81) — an application REPORTS what the lens cannot observe, as one of a closed
2
+ * CUSTOM SOURCES (issues/closed/foodguard-81) — an application REPORTS what the lens cannot observe, as one of a closed
3
3
  * set of typed kinds the lens knows how to draw, threshold and explain to an agent: `queue` · `loop` · `store` ·
4
4
  * `traffic`. Phase 1 reads `queue` only — work a service keeps waiting in its OWN storage (an outbox table), which no
5
5
  * event stream can show: FoodGuard's lens showed its BullMQ queues nearly empty while 6,389 outbox rows waited.
@@ -19,26 +19,103 @@
19
19
  * 3. The READ — `apps/agent` folds them into `process://sources` ({@link SourcesReport}); the `observe.sources` and
20
20
  * `source.get` tools, the status signal and the dashboard read that.
21
21
  *
22
+ * Phase 2 adds:
23
+ * - the `loop` kind — a background loop, scheduler or sweep answering `lensmcp.loop/v1` ({@link LoopSnapshotRaw});
24
+ * `feeds` links it to the queue source it drains;
25
+ * - PUSHED events — `lensEmitCustom` (`@lensmcp/node-instrumentation`) writes a `source: 'custom'` event
26
+ * ({@link SourceEventRaw}) with the flow context of the request or job it ran in, so an outbox row's `enqueued` shows
27
+ * in the flow of the answer that wrote it;
28
+ * - reports ON CHANGE: a poll whose snapshot did not change carries `same` ({@link SourceSameRaw}) instead of the whole
29
+ * snapshot, and a full one goes at least every {@link SOURCE_FULL_REPORT_MS};
30
+ * - the lens's history of each source, and the findings it needs: `queue/stalled`, `loop/overdue`, `loop/failing`,
31
+ * `loop/idle-while-fed`.
32
+ *
33
+ * Phase 3 adds the last two kinds:
34
+ * - `store` — a storage backend the taps do not model (document storage on disk or in a bucket, a chunk store):
35
+ * `lensmcp.store/v1` ({@link StoreSnapshotRaw}) says how many objects and bytes it holds against its quota, and its
36
+ * window's writes, deletes and errors. Findings `store/near-quota`, `store/errors`, `store/orphans` (it grows and
37
+ * nothing is deleted);
38
+ * - `traffic` — messages over a channel the taps do not classify (a partner's API answering 429s, a webhook, a custom
39
+ * transport): `lensmcp.traffic/v1` ({@link TrafficSnapshotRaw}) per channel — messages, failures, rate-limited,
40
+ * p95 — or, with no `path`, PUSH-ONLY: the lens counts what the app pushes (`inbound` / `outbound`) over the last
41
+ * {@link TRAFFIC_PUSH_WINDOW_MS}. Findings `traffic/failures`, `traffic/latency`, `traffic/rate-limited`.
42
+ *
22
43
  * Everything here is REPORTED by the app, never measured by the lens: every read carries `provenance: 'reported'`.
23
44
  * Additive only.
24
45
  */
25
46
  /** The closed set of source kinds. */
26
47
  export declare const SOURCE_KINDS: readonly ["queue", "loop", "store", "traffic"];
27
48
  export type SourceKind = (typeof SOURCE_KINDS)[number];
28
- /** The kinds this release reads; a declared kind outside it is said, not polled. */
29
- export declare const SOURCE_KINDS_READ: readonly ["queue"];
49
+ /** The kinds this release reads — all of them since phase 3; a kind outside the set is said, not polled. */
50
+ export declare const SOURCE_KINDS_READ: readonly ["queue", "loop", "store", "traffic"];
30
51
  export type SourceKindRead = (typeof SOURCE_KINDS_READ)[number];
31
52
  /** Every snapshot's schema: `lensmcp.<kind>/v1`. */
32
53
  export declare const sourceWireSchema: (kind: SourceKind) => string;
33
54
  export declare const QUEUE_WIRE_SCHEMA = "lensmcp.queue/v1";
55
+ export declare const LOOP_WIRE_SCHEMA = "lensmcp.loop/v1";
56
+ export declare const STORE_WIRE_SCHEMA = "lensmcp.store/v1";
57
+ export declare const TRAFFIC_WIRE_SCHEMA = "lensmcp.traffic/v1";
58
+ /** Where each kind's last good snapshot rides on a `source-poll` raw — never another kind's field. */
59
+ export declare const SOURCE_SNAPSHOT_FIELD: {
60
+ readonly queue: "snapshot";
61
+ readonly loop: "loop";
62
+ readonly store: "store";
63
+ readonly traffic: "traffic";
64
+ };
34
65
  /** The resource the lens folds the polls into. */
35
66
  export declare const URI_SOURCES = "process://sources";
36
67
  /** Said on every read of a source: the app's claim, not the lens's measurement. */
37
68
  export declare const SOURCE_PROVENANCE: "reported";
38
- /** The finding's rule id: a queue group whose oldest pending item has waited longer than `thresholds.oldestWaitMin`. */
69
+ /** A queue group whose oldest pending item has waited longer than `thresholds.oldestWaitMin`. */
39
70
  export declare const QUEUE_OLDEST_WAIT_RULE = "queue/oldest-wait";
40
- export declare const SOURCE_EVENT_KINDS: readonly ["source-poll", "source-declaration"];
71
+ /** A queue group with pending work that has not moved for `thresholds.stalledMin` (nothing taken, nothing finished). */
72
+ export declare const QUEUE_STALLED_RULE = "queue/stalled";
73
+ /** A loop whose next run is late by more than {@link LOOP_OVERDUE_INTERVALS} of its intervals. */
74
+ export declare const LOOP_OVERDUE_RULE = "loop/overdue";
75
+ /** A loop whose last {@link LOOP_FAILING_RUNS} runs all failed. */
76
+ export declare const LOOP_FAILING_RULE = "loop/failing";
77
+ /** A loop that runs and takes nothing, for `thresholds.idleMin`, while the queue it `feeds` has work. */
78
+ export declare const LOOP_IDLE_RULE = "loop/idle-while-fed";
79
+ /** A store whose bytes are at or over `thresholds.quotaPct` of its quota. */
80
+ export declare const STORE_QUOTA_RULE = "store/near-quota";
81
+ /** A store with errors in its window — over `thresholds.errorRatePct` of its operations when it says how many. */
82
+ export declare const STORE_ERRORS_RULE = "store/errors";
83
+ /** A store that has grown for `thresholds.orphanMin` and deleted nothing — what nothing cleans up. */
84
+ export declare const STORE_ORPHANS_RULE = "store/orphans";
85
+ /** A channel whose failures are over `thresholds.failurePct` of at least {@link TRAFFIC_MIN_MESSAGES} messages. */
86
+ export declare const TRAFFIC_FAILURES_RULE = "traffic/failures";
87
+ /** A channel whose p95 is over `thresholds.p95Ms`. */
88
+ export declare const TRAFFIC_LATENCY_RULE = "traffic/latency";
89
+ /** A channel whose counterpart answered «too many requests» `thresholds.rateLimited` times or more in the window. */
90
+ export declare const TRAFFIC_RATE_LIMITED_RULE = "traffic/rate-limited";
91
+ export declare const SOURCE_RULES: readonly ["queue/oldest-wait", "queue/stalled", "loop/overdue", "loop/failing", "loop/idle-while-fed", "store/near-quota", "store/errors", "store/orphans", "traffic/failures", "traffic/latency", "traffic/rate-limited"];
92
+ export type SourceRule = (typeof SOURCE_RULES)[number];
93
+ export declare const SOURCE_EVENT_KINDS: readonly ["source-poll", "source-declaration", "source-event"];
41
94
  export type SourceEventKind = (typeof SOURCE_EVENT_KINDS)[number];
95
+ /** A pushed event's `source` — the app's own words, never a tap's. */
96
+ export declare const CUSTOM_EVENT_SOURCE: "custom";
97
+ /** What a queue source's app may push: an item written, taken, finished, failed, or set aside. */
98
+ export declare const QUEUE_EVENTS: readonly ["enqueued", "admitted", "completed", "failed", "parked"];
99
+ export type QueueEventName = (typeof QUEUE_EVENTS)[number];
100
+ /** What a loop source's app may push: a run began, a run ended. */
101
+ export declare const LOOP_EVENTS: readonly ["run-start", "run-end"];
102
+ export type LoopEventName = (typeof LOOP_EVENTS)[number];
103
+ /** How a loop's run ended: it worked, it failed, or it chose not to (a gate held it). */
104
+ export declare const LOOP_OUTCOMES: readonly ["ok", "failed", "skipped"];
105
+ export type LoopOutcome = (typeof LOOP_OUTCOMES)[number];
106
+ /** What a store source's app may push: an object written, deleted, or an operation that failed. */
107
+ export declare const STORE_EVENTS: readonly ["write", "delete", "error"];
108
+ export type StoreEventName = (typeof STORE_EVENTS)[number];
109
+ /** What a traffic source's app may push: a message that came in, a message that went out. */
110
+ export declare const TRAFFIC_EVENTS: readonly ["inbound", "outbound"];
111
+ export type TrafficEventName = (typeof TRAFFIC_EVENTS)[number];
112
+ /** How a message went: answered, failed, or refused as too many (an HTTP 429, a quota). */
113
+ export declare const TRAFFIC_OUTCOMES: readonly ["ok", "failed", "rate-limited"];
114
+ export type TrafficOutcome = (typeof TRAFFIC_OUTCOMES)[number];
115
+ /** The events each kind may push. */
116
+ export declare const SOURCE_PUSH_EVENTS: Readonly<Record<SourceKindRead, readonly string[]>>;
117
+ /** The outcomes each kind's pushed events may say (a queue's and a store's say none). */
118
+ export declare const SOURCE_PUSH_OUTCOMES: Readonly<Record<SourceKindRead, readonly string[]>>;
42
119
  /** What ONE poll found. */
43
120
  export declare const SOURCE_POLL_STATES: readonly ["current", "unreachable", "refused", "malformed", "not-running"];
44
121
  export type SourcePollState = (typeof SOURCE_POLL_STATES)[number];
@@ -52,6 +129,39 @@ export type SourceState = (typeof SOURCE_STATES)[number];
52
129
  export declare const SOURCE_DEFAULT_INTERVAL_SEC = 10;
53
130
  export declare const SOURCE_MIN_INTERVAL_SEC = 2;
54
131
  export declare const QUEUE_DEFAULT_OLDEST_WAIT_MIN = 10;
132
+ export declare const QUEUE_DEFAULT_STALLED_MIN = 15;
133
+ export declare const LOOP_DEFAULT_IDLE_MIN = 10;
134
+ /** `loop/overdue`: its next run is late by more than this many of its own intervals. */
135
+ export declare const LOOP_OVERDUE_INTERVALS = 2;
136
+ /** `loop/failing`: this many runs in a row failed. */
137
+ export declare const LOOP_FAILING_RUNS = 3;
138
+ /**
139
+ * A poll whose snapshot did not change since the one last sent in full carries {@link SourceSameRaw} instead — but a
140
+ * full snapshot goes at least this often, so a lens that starts later (its replay is a few MB of the bus) has one
141
+ * within a minute. Before this, every poll carried the whole snapshot: ~2 MB an hour at FoodGuard's size, ~60 MB at
142
+ * the 500-group cap.
143
+ */
144
+ export declare const SOURCE_FULL_REPORT_MS = 60000;
145
+ /** History points the lens keeps per source (in memory; a lens restart starts it again). */
146
+ export declare const SOURCE_HISTORY_POINTS = 60;
147
+ /** A loop's runs the lens keeps. */
148
+ export declare const SOURCE_RUNS_KEPT = 30;
149
+ /** Pushed events the lens keeps per source. */
150
+ export declare const SOURCE_PUSHED_KEPT = 20;
151
+ export declare const STORE_DEFAULT_QUOTA_PCT = 90;
152
+ export declare const STORE_DEFAULT_ERROR_RATE_PCT = 5;
153
+ /** `store/orphans`: growing this long with nothing deleted. An append-only store by design raises it (at most 10080). */
154
+ export declare const STORE_DEFAULT_ORPHAN_MIN = 60;
155
+ export declare const TRAFFIC_DEFAULT_FAILURE_PCT = 5;
156
+ export declare const TRAFFIC_DEFAULT_P95_MS = 2000;
157
+ export declare const TRAFFIC_DEFAULT_RATE_LIMITED = 1;
158
+ /** `traffic/failures` is judged on at least this many messages in the window — 1 failure of 2 is not a rate. */
159
+ export declare const TRAFFIC_MIN_MESSAGES = 10;
160
+ /** A push-only traffic source: the lens counts what was pushed over this window. */
161
+ export declare const TRAFFIC_PUSH_WINDOW_MS: number;
162
+ /** A push-only traffic source: channels and events per channel the lens keeps to count from. */
163
+ export declare const TRAFFIC_PUSH_CHANNELS = 50;
164
+ export declare const TRAFFIC_PUSH_EVENTS_PER_CHANNEL = 1000;
55
165
  /** A snapshot holds at most this many groups; a producer with more caps it and says `truncated: { groups }`. */
56
166
  export declare const SOURCE_MAX_GROUPS = 500;
57
167
  export declare const SOURCE_MAX_BYTES: number;
@@ -60,18 +170,55 @@ export declare const SOURCE_STALE_PERIODS = 3;
60
170
  export declare const SOURCE_STALE_MIN_MS = 30000;
61
171
  /** When a source's newest report stops being trusted. */
62
172
  export declare function sourceStaleAfterMs(reportEveryMs: number): number;
63
- /** A queue source's thresholds. `stalledMin` is accepted now and judged once the lens keeps a history (phase 2). */
173
+ /** A queue source's thresholds, in minutes. The gateway fills both defaults; a 1.25.8 gateway sent no `stalledMin`. */
64
174
  export interface QueueThresholds {
65
175
  oldestWaitMin: number;
66
176
  stalledMin?: number;
67
177
  }
178
+ /** A loop source's thresholds, in minutes (the gateway fills the default). */
179
+ export interface LoopThresholds {
180
+ idleMin?: number;
181
+ }
182
+ /** A store source's thresholds (the gateway fills the defaults). */
183
+ export interface StoreThresholds {
184
+ /** Percent of `quotaBytes` (0–100). */
185
+ quotaPct?: number;
186
+ /** Percent of the window's operations (0–100). */
187
+ errorRatePct?: number;
188
+ /** Minutes. */
189
+ orphanMin?: number;
190
+ }
191
+ /** A traffic source's thresholds (the gateway fills the defaults). */
192
+ export interface TrafficThresholds {
193
+ /** Percent of a channel's messages in the window (0–100). */
194
+ failurePct?: number;
195
+ /** Milliseconds. */
196
+ p95Ms?: number;
197
+ /** «Too many requests» answers in the window, at least 1. */
198
+ rateLimited?: number;
199
+ }
200
+ /**
201
+ * A source's thresholds, any kind's: a queue has `oldestWaitMin` (always) and `stalledMin`; a loop `idleMin`; a store
202
+ * `quotaPct`, `errorRatePct`, `orphanMin`; a traffic source `failurePct`, `p95Ms`, `rateLimited`.
203
+ */
204
+ export interface SourceThresholds extends StoreThresholds, TrafficThresholds {
205
+ oldestWaitMin?: number;
206
+ stalledMin?: number;
207
+ idleMin?: number;
208
+ }
68
209
  /** One declared source, as the gateway polls it. */
69
210
  export interface SourceDeclRaw {
70
211
  sourceKind: SourceKindRead;
71
212
  name: string;
72
- path: string;
213
+ /**
214
+ * Absent only on a PUSH-ONLY loop or traffic source: never polled; the lens reads what the app pushes. A push-only
215
+ * loop's `intervalSec` is the loop's OWN period (the lens judges «overdue» by it); a polled one's is the poll's.
216
+ */
217
+ path?: string;
73
218
  intervalSec: number;
74
- thresholds: QueueThresholds;
219
+ thresholds: SourceThresholds;
220
+ /** A loop: the NAME of the queue source on the same service that it drains — what `loop/idle-while-fed` reads. */
221
+ feeds?: string;
75
222
  }
76
223
  /** One group of a queue snapshot, as the bus carries it. */
77
224
  export interface QueueGroupRaw {
@@ -106,6 +253,114 @@ export interface QueueSnapshotRaw {
106
253
  truncated?: {
107
254
  groups: number;
108
255
  };
256
+ /** Names the snapshot's content (not its times): a later poll that read the same says so with {@link SourceSameRaw}. */
257
+ digest?: string;
258
+ }
259
+ /**
260
+ * A loop snapshot that parsed — what a background loop, scheduler or sweep says of itself. The wire
261
+ * (`lensmcp.loop/v1`) is the same fields plus `schema`, without `receivedAt`/`digest`; only `at`, `intervalSec` and
262
+ * `lastRunAt` are required.
263
+ */
264
+ export interface LoopSnapshotRaw {
265
+ /** When the SERVICE measured it (ISO). */
266
+ at: string;
267
+ /** When the gateway received it (epoch ms). */
268
+ receivedAt: number;
269
+ digest?: string;
270
+ /** The loop's own period, in seconds (not the poll's). */
271
+ intervalSec: number;
272
+ /** When its last run began (ISO); `null`: it has not run since the service started. */
273
+ lastRunAt: string | null;
274
+ lastDurationMs?: number;
275
+ lastOutcome?: LoopOutcome;
276
+ /** Items its last run took. */
277
+ lastItems?: number;
278
+ /** When it will run next (ISO), if it knows. */
279
+ nextRunAt?: string | null;
280
+ /** Over the service's own window of `windowSec`. */
281
+ runsInWindow?: number;
282
+ itemsInWindow?: number;
283
+ failuresInWindow?: number;
284
+ windowSec?: number;
285
+ /** The last failed run's error, at most 500 characters. */
286
+ lastError?: string;
287
+ }
288
+ /** One group of a store — a bucket, a tenant's folder — as the bus carries it (the wire's `key` travels as `group`). */
289
+ export interface StoreGroupRaw {
290
+ group: string;
291
+ labels?: Record<string, string>;
292
+ objects: number;
293
+ bytes: number;
294
+ }
295
+ /**
296
+ * A store snapshot that parsed — what a storage backend the taps do not model says of itself. The wire
297
+ * (`lensmcp.store/v1`) is the same fields plus `schema`, without `receivedAt`/`digest`, its groups keyed `key`; only
298
+ * `at`, `objects` and `bytes` are required.
299
+ */
300
+ export interface StoreSnapshotRaw {
301
+ at: string;
302
+ receivedAt: number;
303
+ digest?: string;
304
+ objects: number;
305
+ bytes: number;
306
+ /** What it may hold; absent or `null`: no quota it knows of. */
307
+ quotaBytes?: number | null;
308
+ /** Operations of every kind over the service's window of `windowSec` — what `errorsInWindow` is a rate of. */
309
+ opsInWindow?: number;
310
+ writesInWindow?: number;
311
+ deletesInWindow?: number;
312
+ errorsInWindow?: number;
313
+ windowSec?: number;
314
+ lastWriteAt?: string | null;
315
+ /** The last failed operation's error, at most 500 characters. */
316
+ lastError?: string;
317
+ /** At most {@link SOURCE_MAX_GROUPS}, the largest first. */
318
+ groups?: StoreGroupRaw[];
319
+ truncated?: {
320
+ groups: number;
321
+ };
322
+ }
323
+ /** One channel of a traffic source, as the bus carries it (the wire's `key` travels as `channel`). */
324
+ export interface TrafficChannelRaw {
325
+ channel: string;
326
+ labels?: Record<string, string>;
327
+ direction?: 'inbound' | 'outbound';
328
+ /** Who is at the other end — a partner, a host, a queue — at most 200 characters. */
329
+ counterpart?: string;
330
+ /** Over the service's window of `windowSec`. */
331
+ messagesInWindow?: number;
332
+ failuresInWindow?: number;
333
+ /** Answered «too many requests» (an HTTP 429, a quota) — counted apart from `failuresInWindow`. */
334
+ rateLimitedInWindow?: number;
335
+ p95Ms?: number;
336
+ lastAt?: string | null;
337
+ lastError?: string;
338
+ }
339
+ /**
340
+ * A traffic snapshot that parsed — messages over channels the taps do not classify. The wire (`lensmcp.traffic/v1`) is
341
+ * the same plus `schema`, each channel keyed `key`; only `at` and `channels` are required.
342
+ */
343
+ export interface TrafficSnapshotRaw {
344
+ at: string;
345
+ receivedAt: number;
346
+ digest?: string;
347
+ windowSec?: number;
348
+ /** At most {@link SOURCE_MAX_GROUPS}. */
349
+ channels: TrafficChannelRaw[];
350
+ truncated?: {
351
+ channels: number;
352
+ };
353
+ }
354
+ /**
355
+ * In place of the snapshot, on a poll that read what an earlier report carried in full (the same `digest`): the lens
356
+ * takes the one it holds, measured again at `at` and received at `receivedAt`. On a poll that did not read (`unreachable`
357
+ * …) `at`/`receivedAt` are the held snapshot's own. A lens that holds no snapshot of that digest (it started after the
358
+ * full report) says the source is not read yet — never «nothing pending» — until the next full report.
359
+ */
360
+ export interface SourceSameRaw {
361
+ digest: string;
362
+ at: string;
363
+ receivedAt: number;
109
364
  }
110
365
  /** One poll of one declared source (`raw.kind: 'source-poll'`). */
111
366
  export interface SourcePollRaw extends SourceDeclRaw {
@@ -124,12 +379,62 @@ export interface SourcePollRaw extends SourceDeclRaw {
124
379
  reportEveryMs: number;
125
380
  /** Polls in a row that failed (`unreachable`/`refused`/`malformed`) — the gateway backs off while it grows. */
126
381
  failures?: number;
127
- /** The LAST GOOD snapshot: this poll's when `current`, else the one before (kept, with its age), if any. */
382
+ /**
383
+ * A QUEUE source's LAST GOOD snapshot: this poll's when `current`, else the one before (kept, with its age), if any.
384
+ * Absent with `same` when it is the one an earlier report carried.
385
+ */
128
386
  snapshot?: QueueSnapshotRaw;
387
+ /** A LOOP source's last good snapshot — the same rules as `snapshot`. */
388
+ loop?: LoopSnapshotRaw;
389
+ /** A STORE source's. */
390
+ store?: StoreSnapshotRaw;
391
+ /** A TRAFFIC source's (a push-only one is never polled). */
392
+ traffic?: TrafficSnapshotRaw;
393
+ /** In place of the kind's snapshot ({@link SOURCE_SNAPSHOT_FIELD}): unchanged since the report that carried it in full. */
394
+ same?: SourceSameRaw;
129
395
  /** The owning workspace (stamped by the gateway on every service event). */
130
396
  wsKey?: string;
131
397
  floodSuppressed?: number;
132
398
  }
399
+ /**
400
+ * One event an app PUSHED about a source (`source: 'custom'`, `category: 'backend'`, `raw.kind: 'source-event'`),
401
+ * written by `lensEmitCustom` in the app's own process with the flow context it ran in (`context.flowId`/`requestId`/
402
+ * `runId`) — so it joins that request's or job's flow like any tapped event. Bounded: one flood bucket per (service,
403
+ * kind, name, event) — and per channel for traffic — so a burst of `enqueued` never starves `completed`.
404
+ */
405
+ export interface SourceEventRaw {
406
+ kind: 'source-event';
407
+ provenance: typeof SOURCE_PROVENANCE;
408
+ /** The service (its Nx project) — what the source's declaration is under. */
409
+ project: string;
410
+ sourceKind: SourceKindRead;
411
+ /** The declared source's `name`. */
412
+ name: string;
413
+ /** One of the kind's {@link SOURCE_PUSH_EVENTS}. */
414
+ event: string;
415
+ /** The item's own id (an outbox row), at most 200 characters. */
416
+ item?: string;
417
+ /** The snapshot group it belongs to (the wire's group `key`), at most 200 characters. */
418
+ group?: string;
419
+ /** A loop's `run-end`: items the run took. */
420
+ items?: number;
421
+ durationMs?: number;
422
+ /** A loop's `run-end` ({@link LOOP_OUTCOMES}); a traffic message's ({@link TRAFFIC_OUTCOMES}). */
423
+ outcome?: LoopOutcome | TrafficOutcome;
424
+ /** A store's: the object — an id or a hash, never a name that holds personal data — at most 200 characters. */
425
+ object?: string;
426
+ /** A store's `write`; a traffic message's size. */
427
+ bytes?: number;
428
+ /** A traffic message's channel (the snapshot channel's key), at most 200 characters. */
429
+ channel?: string;
430
+ /** A traffic message's other end, at most 200 characters. */
431
+ counterpart?: string;
432
+ /** A traffic message's correlation id (what joins an inbound to the outbound it caused), at most 200 characters. */
433
+ correlation?: string;
434
+ /** A `failed` item or a failed run: why, at most 500 characters. */
435
+ error?: string;
436
+ floodSuppressed?: number;
437
+ }
133
438
  /** A service's `cluster.sources`, as the gateway read it (`raw.kind: 'source-declaration'`). */
134
439
  export interface SourceDeclarationRaw {
135
440
  kind: 'source-declaration';
@@ -142,6 +447,8 @@ export interface SourceDeclarationRaw {
142
447
  }
143
448
  /** A `source-poll` event's `raw`, checked just enough to be read safely (telemetry is only partly trusted). */
144
449
  export declare function asSourcePollRaw(raw: unknown): SourcePollRaw | undefined;
450
+ /** A `source-event` event's `raw` (pushed by an app), checked just enough to be read safely. */
451
+ export declare function asSourceEventRaw(raw: unknown): SourceEventRaw | undefined;
145
452
  /** A `source-declaration` event's `raw`, checked just enough to be read safely. */
146
453
  export declare function asSourceDeclarationRaw(raw: unknown): SourceDeclarationRaw | undefined;
147
454
  /** One queue group, as the lens shows it. */
@@ -150,6 +457,14 @@ export interface QueueGroupView extends QueueGroupRaw {
150
457
  oldestWaitMs?: number;
151
458
  /** That wait is longer than the source's `thresholds.oldestWaitMin`. */
152
459
  overThreshold: boolean;
460
+ /**
461
+ * How long its pending work has not moved, as far as the lens has watched it (since this lens started, or since the
462
+ * work last moved): nothing taken (`pending` did not fall, `oldestPendingAt` did not advance), nothing finished
463
+ * (`completedInWindow` 0), no `admitted`/`completed` pushed. Absent when nothing is pending or it moved just now.
464
+ */
465
+ stillMs?: number;
466
+ /** `stillMs` is at least the source's `thresholds.stalledMin`. */
467
+ stalled: boolean;
153
468
  }
154
469
  /** A queue source's headline numbers. */
155
470
  export interface QueueHeadline extends QueueTotals {
@@ -157,6 +472,95 @@ export interface QueueHeadline extends QueueTotals {
157
472
  oldestWaitMs?: number;
158
473
  /** Groups whose oldest wait is over the threshold. */
159
474
  overThreshold: number;
475
+ /** Groups stalled. */
476
+ stalled: number;
477
+ }
478
+ /** One point of a queue source's history: what one `current` read said. */
479
+ export interface QueueHistoryPoint {
480
+ /** When the gateway received it (epoch ms). */
481
+ t: number;
482
+ pending: number;
483
+ inFlight: number;
484
+ failed: number;
485
+ oldestWaitMs?: number;
486
+ }
487
+ /** One run of a loop: seen in a snapshot (its `lastRunAt` moved) or pushed (`run-end`). */
488
+ export interface LoopRunView {
489
+ /** When it began (epoch ms) — `lastRunAt`, or the pushed `run-end`'s time less its duration. */
490
+ at: number;
491
+ outcome?: LoopOutcome;
492
+ items?: number;
493
+ durationMs?: number;
494
+ error?: string;
495
+ via: 'snapshot' | 'pushed';
496
+ }
497
+ /** A loop source's headline, from its last good snapshot and the runs the lens saw. */
498
+ export interface LoopView {
499
+ intervalSec: number;
500
+ lastRunAt: string | null;
501
+ lastDurationMs?: number;
502
+ lastOutcome?: LoopOutcome;
503
+ lastItems?: number;
504
+ nextRunAt?: string | null;
505
+ runsInWindow?: number;
506
+ itemsInWindow?: number;
507
+ failuresInWindow?: number;
508
+ windowSec?: number;
509
+ lastError?: string;
510
+ /** How late its next run was when the service measured it — past `nextRunAt` (or `lastRunAt` + its interval). */
511
+ lateMs?: number;
512
+ /** Late by more than {@link LOOP_OVERDUE_INTERVALS} of its intervals. */
513
+ overdue: boolean;
514
+ /** Runs in a row that failed, the newest back. */
515
+ failingRuns: number;
516
+ /** Since when (epoch ms) it has run and taken nothing while the queue it feeds had work — when it does now. */
517
+ idleSince?: number;
518
+ /** The queue source it `feeds`, as read now (absent: it declares none, or none of that name). */
519
+ fed?: {
520
+ name: string;
521
+ state: SourceState;
522
+ pending?: number;
523
+ };
524
+ /**
525
+ * PUSH-ONLY (declared with no route): built from the runs the app pushed (`run-start` / `run-end`), judged on the
526
+ * lens's own clock — `lateMs` is how long after its period its next run is now, with `intervalSec` its declared period.
527
+ */
528
+ pushOnly?: boolean;
529
+ /** Push-only: a run began (`run-start`) and has not ended — since when (epoch ms). */
530
+ runningSince?: number;
531
+ }
532
+ /** One event an app pushed about a source. */
533
+ export interface SourcePushedEvent {
534
+ at: number;
535
+ event: string;
536
+ item?: string;
537
+ group?: string;
538
+ items?: number;
539
+ durationMs?: number;
540
+ outcome?: LoopOutcome | TrafficOutcome;
541
+ /** A store's object (an id or a hash). */
542
+ object?: string;
543
+ bytes?: number;
544
+ /** A traffic message's channel, other end and correlation id. */
545
+ channel?: string;
546
+ counterpart?: string;
547
+ correlation?: string;
548
+ error?: string;
549
+ /** The flow it ran in — open it with flow.get. */
550
+ flowId?: string;
551
+ requestId?: string;
552
+ }
553
+ /** What an app pushed about one source since this lens started. */
554
+ export interface SourcePushedView {
555
+ /** Per event name. */
556
+ counts: Record<string, number>;
557
+ /** When the first one reached this lens (epoch ms). */
558
+ since: number;
559
+ lastAt: number;
560
+ /** The newest first, at most {@link SOURCE_PUSHED_KEPT}. */
561
+ recent: SourcePushedEvent[];
562
+ /** Events the app's flood guard held back (they are in no count). */
563
+ suppressed?: number;
160
564
  }
161
565
  /** One declared source, as the lens shows it. */
162
566
  export interface SourceView extends SourceDeclRaw {
@@ -173,25 +577,121 @@ export interface SourceView extends SourceDeclRaw {
173
577
  ageMs?: number;
174
578
  /** When the service measured the last good snapshot (its `at`). */
175
579
  snapshotAt?: string;
580
+ /** A queue's. */
176
581
  headline?: QueueHeadline;
177
- /** The oldest-waiting first. Empty with `state: 'current'` is a real «nothing pending». */
582
+ /** A queue's, the oldest-waiting first. Empty with `state: 'current'` and a headline is a real «nothing pending». */
178
583
  groups: QueueGroupView[];
179
584
  truncated?: {
180
585
  groups: number;
181
586
  };
587
+ /** A queue's `current` reads, oldest first, at most {@link SOURCE_HISTORY_POINTS}. */
588
+ history?: QueueHistoryPoint[];
589
+ /** A loop's, from its last good snapshot. */
590
+ loop?: LoopView;
591
+ /** A loop's runs, the newest first, at most {@link SOURCE_RUNS_KEPT}. */
592
+ runs?: LoopRunView[];
593
+ /** A store's, from its last good snapshot. */
594
+ store?: StoreView;
595
+ /** A store's `current` reads, oldest first, at most {@link SOURCE_HISTORY_POINTS}. */
596
+ storeHistory?: StoreHistoryPoint[];
597
+ /** A traffic source's: from its last good snapshot, or — push-only — from what the app pushed in the window. */
598
+ traffic?: TrafficView;
599
+ /** What the app pushed about it (`lensEmitCustom`), when it pushed anything. */
600
+ pushed?: SourcePushedView;
601
+ }
602
+ /** A store's headline, from its last good snapshot and the reads the lens watched. */
603
+ export interface StoreView {
604
+ objects: number;
605
+ bytes: number;
606
+ quotaBytes?: number | null;
607
+ /** `bytes` as a percent of `quotaBytes` (one decimal). */
608
+ usedPct?: number;
609
+ /** `usedPct` is at or over `thresholds.quotaPct`. */
610
+ nearQuota: boolean;
611
+ opsInWindow?: number;
612
+ writesInWindow?: number;
613
+ deletesInWindow?: number;
614
+ errorsInWindow?: number;
615
+ /** `errorsInWindow` as a percent of `opsInWindow`, when it says both. */
616
+ errorRatePct?: number;
617
+ windowSec?: number;
618
+ lastWriteAt?: string | null;
619
+ lastError?: string;
620
+ /**
621
+ * Since when (epoch ms) it has grown with nothing deleted, as the lens watched it: its objects rose, never fell, no
622
+ * `deletesInWindow`, no pushed `delete`. Absent when it deleted lately, did not grow, or was not watched long enough.
623
+ */
624
+ growingSince?: number;
625
+ /** Its objects when that spell began. */
626
+ growingFrom?: number;
627
+ /** The largest first. */
628
+ groups?: StoreGroupRaw[];
629
+ truncated?: {
630
+ groups: number;
631
+ };
632
+ }
633
+ /** One point of a store's history: what one `current` read said. */
634
+ export interface StoreHistoryPoint {
635
+ t: number;
636
+ objects: number;
637
+ bytes: number;
638
+ }
639
+ /** One channel of a traffic source, as the lens shows it. */
640
+ export interface TrafficChannelView extends TrafficChannelRaw {
641
+ /** `failuresInWindow` as a percent of `messagesInWindow` (one decimal). */
642
+ failurePct?: number;
643
+ /** Over `thresholds.failurePct`, on at least {@link TRAFFIC_MIN_MESSAGES} messages. */
644
+ overFailure: boolean;
645
+ /** `p95Ms` over `thresholds.p95Ms`. */
646
+ overLatency: boolean;
647
+ /** `rateLimitedInWindow` at or over `thresholds.rateLimited`. */
648
+ rateLimited: boolean;
649
+ }
650
+ /** A traffic source's headline. */
651
+ export interface TrafficView {
652
+ /** Counted by the lens from the app's pushed events (no route declared), not answered by the service. */
653
+ pushOnly: boolean;
654
+ /** The window its counts are over, in seconds — the service's, or {@link TRAFFIC_PUSH_WINDOW_MS} push-only. */
655
+ windowSec?: number;
656
+ /** The channel with the most messages first. */
657
+ channels: TrafficChannelView[];
658
+ truncated?: {
659
+ channels: number;
660
+ };
661
+ totals: {
662
+ channels: number;
663
+ messages: number;
664
+ failures: number;
665
+ rateLimited: number;
666
+ };
667
+ /** Push-only: events the app's flood guard held back in the window — every count is then a floor. */
668
+ heldBack?: number;
182
669
  }
183
- /** A queue group whose oldest pending item has waited longer than its source allows. */
670
+ /**
671
+ * Something a source's numbers say is wrong — from what was read NOW (`current`) only: anything older is not known to
672
+ * be true. Which fields are set depends on the rule.
673
+ */
184
674
  export interface SourceFinding {
185
- rule: typeof QUEUE_OLDEST_WAIT_RULE;
675
+ rule: SourceRule;
186
676
  provenance: typeof SOURCE_PROVENANCE;
187
677
  project: string;
188
678
  name: string;
189
679
  sourceKind: SourceKindRead;
190
- group: string;
680
+ /** A queue rule's group. */
681
+ group?: string;
682
+ /** A traffic rule's channel. */
683
+ channel?: string;
191
684
  labels?: Record<string, string>;
192
- pending: number;
193
- oldestWaitMs: number;
194
- thresholdMin: number;
685
+ pending?: number;
686
+ /** `queue/oldest-wait`. */
687
+ oldestWaitMs?: number;
688
+ /** How long it has held: the stall, the idle spell, how late the run is, how long the runs have failed, the growth. */
689
+ forMs?: number;
690
+ /** The threshold it is over, in minutes (`loop/overdue` and `loop/failing` have none). */
691
+ thresholdMin?: number;
692
+ /** A store or traffic rule's measure — a percent, milliseconds or a count, as its title says — and its threshold. */
693
+ value?: number;
694
+ threshold?: number;
195
695
  title: string;
196
696
  fix: string[];
197
697
  verify: string;
@@ -215,6 +715,14 @@ export interface SourcesReport {
215
715
  project: string;
216
716
  problem: string;
217
717
  }>;
718
+ /** Sources an app pushes events about that no `cluster.sources` entry of that service declares — read nowhere. */
719
+ undeclared?: Array<{
720
+ project: string;
721
+ sourceKind: SourceKindRead;
722
+ name: string;
723
+ events: number;
724
+ lastAt: number;
725
+ }>;
218
726
  counts: Record<SourceState, number>;
219
727
  /** Over the `current` queue snapshots only. */
220
728
  totals: {
package/lib/sources.js CHANGED
@@ -1 +1 @@
1
- "use strict";var E=Object.defineProperty;var n=(r,t)=>E(r,"name",{value:t,configurable:!0});var f=Object.defineProperty,e=n((r,t)=>f(r,"name",{value:t,configurable:!0}),"n");export const SOURCE_KINDS=["queue","loop","store","traffic"],SOURCE_KINDS_READ=["queue"],sourceWireSchema=e(r=>`lensmcp.${r}/v1`,"sourceWireSchema"),QUEUE_WIRE_SCHEMA="lensmcp.queue/v1",URI_SOURCES="process://sources",SOURCE_PROVENANCE="reported",QUEUE_OLDEST_WAIT_RULE="queue/oldest-wait",SOURCE_EVENT_KINDS=["source-poll","source-declaration"],SOURCE_POLL_STATES=["current","unreachable","refused","malformed","not-running"],SOURCE_STATES=[...SOURCE_POLL_STATES,"stale","never-polled"],SOURCE_DEFAULT_INTERVAL_SEC=10,SOURCE_MIN_INTERVAL_SEC=2,QUEUE_DEFAULT_OLDEST_WAIT_MIN=10,SOURCE_MAX_GROUPS=500,SOURCE_MAX_BYTES=256*1024,SOURCE_STALE_PERIODS=3,SOURCE_STALE_MIN_MS=3e4;export function sourceStaleAfterMs(r){return Math.max(SOURCE_STALE_MIN_MS,SOURCE_STALE_PERIODS*Math.max(0,r))}n(sourceStaleAfterMs,"sourceStaleAfterMs"),e(sourceStaleAfterMs,"sourceStaleAfterMs");const o=e(r=>typeof r=="object"&&r!==null&&!Array.isArray(r),"isObj"),i=e(r=>typeof r=="number"&&Number.isInteger(r)&&r>=0,"isCount"),c=e(r=>typeof r=="number"&&Number.isFinite(r)&&r>0,"isTime"),a=e((r,t)=>typeof r=="string"&&r.length>0&&r.length<=t,"isStr");function l(r){if(!SOURCE_KINDS_READ.includes(r.sourceKind)||!a(r.name,200)||typeof r.path!="string"||typeof r.intervalSec!="number")return!1;const t=r.thresholds;return o(t)&&typeof t.oldestWaitMin=="number"&&t.oldestWaitMin>0}n(l,"f"),e(l,"asDecl");function p(r){if(!o(r)||!a(r.group,1e3)||!i(r.pending))return!1;for(const u of["inFlight","failed","completedInWindow"])if(r[u]!==void 0&&!i(r[u]))return!1;const t=r.oldestPendingAt;if(t!=null&&typeof t!="string")return!1;const s=r.labels;return s===void 0||o(s)&&Object.values(s).every(u=>typeof u=="string")}n(p,"l"),e(p,"asGroup");function S(r){if(!o(r)||typeof r.at!="string"||!c(r.receivedAt)||!Array.isArray(r.groups))return!1;const t=r.totals;if(!o(t)||!i(t.groups)||!i(t.pending)||!i(t.inFlight)||!i(t.failed))return!1;const s=r.truncated;return s!==void 0&&!(o(s)&&i(s.groups))?!1:r.groups.length<=SOURCE_MAX_GROUPS&&r.groups.every(p)}n(S,"d"),e(S,"asSnapshot");export function asSourcePollRaw(r){if(!(!o(r)||r.kind!=="source-poll"||!a(r.project,200)||!l(r))&&SOURCE_POLL_STATES.includes(r.state)&&!(!c(r.polledAt)||!c(r.reportEveryMs))&&!(r.snapshot!==void 0&&!S(r.snapshot))&&!(r.state==="current"&&r.snapshot===void 0))return r}n(asSourcePollRaw,"asSourcePollRaw"),e(asSourcePollRaw,"asSourcePollRaw");export function asSourceDeclarationRaw(r){if(!(!o(r)||r.kind!=="source-declaration"||!a(r.project,200))&&!(!Array.isArray(r.sources)||!r.sources.every(t=>o(t)&&l(t)))&&!(!Array.isArray(r.problems)||!r.problems.every(t=>typeof t=="string")))return r}n(asSourceDeclarationRaw,"asSourceDeclarationRaw"),e(asSourceDeclarationRaw,"asSourceDeclarationRaw");
1
+ "use strict";var T=Object.defineProperty;var a=(t,r)=>T(t,"name",{value:r,configurable:!0});var U=Object.defineProperty,e=a((t,r)=>U(t,"name",{value:r,configurable:!0}),"r");export const SOURCE_KINDS=["queue","loop","store","traffic"],SOURCE_KINDS_READ=["queue","loop","store","traffic"],sourceWireSchema=e(t=>`lensmcp.${t}/v1`,"sourceWireSchema"),QUEUE_WIRE_SCHEMA="lensmcp.queue/v1",LOOP_WIRE_SCHEMA="lensmcp.loop/v1",STORE_WIRE_SCHEMA="lensmcp.store/v1",TRAFFIC_WIRE_SCHEMA="lensmcp.traffic/v1",SOURCE_SNAPSHOT_FIELD={queue:"snapshot",loop:"loop",store:"store",traffic:"traffic"},URI_SOURCES="process://sources",SOURCE_PROVENANCE="reported",QUEUE_OLDEST_WAIT_RULE="queue/oldest-wait",QUEUE_STALLED_RULE="queue/stalled",LOOP_OVERDUE_RULE="loop/overdue",LOOP_FAILING_RULE="loop/failing",LOOP_IDLE_RULE="loop/idle-while-fed",STORE_QUOTA_RULE="store/near-quota",STORE_ERRORS_RULE="store/errors",STORE_ORPHANS_RULE="store/orphans",TRAFFIC_FAILURES_RULE="traffic/failures",TRAFFIC_LATENCY_RULE="traffic/latency",TRAFFIC_RATE_LIMITED_RULE="traffic/rate-limited",SOURCE_RULES=[QUEUE_OLDEST_WAIT_RULE,QUEUE_STALLED_RULE,LOOP_OVERDUE_RULE,LOOP_FAILING_RULE,LOOP_IDLE_RULE,STORE_QUOTA_RULE,STORE_ERRORS_RULE,STORE_ORPHANS_RULE,TRAFFIC_FAILURES_RULE,TRAFFIC_LATENCY_RULE,TRAFFIC_RATE_LIMITED_RULE],SOURCE_EVENT_KINDS=["source-poll","source-declaration","source-event"],CUSTOM_EVENT_SOURCE="custom",QUEUE_EVENTS=["enqueued","admitted","completed","failed","parked"],LOOP_EVENTS=["run-start","run-end"],LOOP_OUTCOMES=["ok","failed","skipped"],STORE_EVENTS=["write","delete","error"],TRAFFIC_EVENTS=["inbound","outbound"],TRAFFIC_OUTCOMES=["ok","failed","rate-limited"],SOURCE_PUSH_EVENTS={queue:QUEUE_EVENTS,loop:LOOP_EVENTS,store:STORE_EVENTS,traffic:TRAFFIC_EVENTS},SOURCE_PUSH_OUTCOMES={queue:[],loop:LOOP_OUTCOMES,store:[],traffic:TRAFFIC_OUTCOMES},SOURCE_POLL_STATES=["current","unreachable","refused","malformed","not-running"],SOURCE_STATES=[...SOURCE_POLL_STATES,"stale","never-polled"],SOURCE_DEFAULT_INTERVAL_SEC=10,SOURCE_MIN_INTERVAL_SEC=2,QUEUE_DEFAULT_OLDEST_WAIT_MIN=10,QUEUE_DEFAULT_STALLED_MIN=15,LOOP_DEFAULT_IDLE_MIN=10,LOOP_OVERDUE_INTERVALS=2,LOOP_FAILING_RUNS=3,SOURCE_FULL_REPORT_MS=6e4,SOURCE_HISTORY_POINTS=60,SOURCE_RUNS_KEPT=30,SOURCE_PUSHED_KEPT=20,STORE_DEFAULT_QUOTA_PCT=90,STORE_DEFAULT_ERROR_RATE_PCT=5,STORE_DEFAULT_ORPHAN_MIN=60,TRAFFIC_DEFAULT_FAILURE_PCT=5,TRAFFIC_DEFAULT_P95_MS=2e3,TRAFFIC_DEFAULT_RATE_LIMITED=1,TRAFFIC_MIN_MESSAGES=10,TRAFFIC_PUSH_WINDOW_MS=10*6e4,TRAFFIC_PUSH_CHANNELS=50,TRAFFIC_PUSH_EVENTS_PER_CHANNEL=1e3,SOURCE_MAX_GROUPS=500,SOURCE_MAX_BYTES=256*1024,SOURCE_STALE_PERIODS=3,SOURCE_STALE_MIN_MS=3e4;export function sourceStaleAfterMs(t){return Math.max(SOURCE_STALE_MIN_MS,SOURCE_STALE_PERIODS*Math.max(0,t))}a(sourceStaleAfterMs,"sourceStaleAfterMs"),e(sourceStaleAfterMs,"sourceStaleAfterMs");const n=e(t=>typeof t=="object"&&t!==null&&!Array.isArray(t),"isObj"),s=e(t=>typeof t=="number"&&Number.isInteger(t)&&t>=0,"isCount"),E=e(t=>typeof t=="number"&&Number.isFinite(t)&&t>0,"isTime"),u=e((t,r)=>typeof t=="string"&&t.length>0&&t.length<=r,"isStr"),v=e(t=>t===void 0||typeof t=="number"&&t>0,"isMin");function _(t){if(!SOURCE_KINDS_READ.includes(t.sourceKind)||typeof t.path!="string"&&!(t.path===void 0&&(t.sourceKind==="traffic"||t.sourceKind==="loop"))||!u(t.name,200)||typeof t.intervalSec!="number"||t.feeds!==void 0&&!u(t.feeds,200))return!1;const r=t.thresholds;if(!n(r))return!1;for(const o of["stalledMin","idleMin","oldestWaitMin","quotaPct","errorRatePct","orphanMin","failurePct","p95Ms","rateLimited"])if(!v(r[o]))return!1;return t.sourceKind!=="queue"||typeof r.oldestWaitMin=="number"}a(_,"l"),e(_,"asDecl");const l=e(t=>t===null||typeof t=="string","isIsoOrNull"),c=e(t=>t===void 0||s(t),"isOptCount");function d(t){if(!n(t)||typeof t.at!="string"||!E(t.receivedAt)||!E(t.intervalSec)||!l(t.lastRunAt)||t.nextRunAt!==void 0&&!l(t.nextRunAt))return!1;for(const r of["lastDurationMs","lastItems","runsInWindow","itemsInWindow","failuresInWindow","windowSec"])if(!c(t[r]))return!1;return t.lastOutcome!==void 0&&!LOOP_OUTCOMES.includes(t.lastOutcome)?!1:t.lastError===void 0||typeof t.lastError=="string"}a(d,"_"),e(d,"asLoopSnapshot");const f=e(t=>t===void 0||n(t)&&Object.values(t).every(r=>typeof r=="string"),"isLabels");function S(t){if(!n(t)||typeof t.at!="string"||!E(t.receivedAt)||!s(t.objects)||!s(t.bytes)||t.quotaBytes!==void 0&&t.quotaBytes!==null&&!s(t.quotaBytes))return!1;for(const i of["opsInWindow","writesInWindow","deletesInWindow","errorsInWindow","windowSec"])if(!c(t[i]))return!1;if(t.lastWriteAt!==void 0&&!l(t.lastWriteAt)||t.lastError!==void 0&&typeof t.lastError!="string")return!1;const r=t.truncated;if(r!==void 0&&!(n(r)&&s(r.groups)))return!1;const o=t.groups;return o===void 0?!0:Array.isArray(o)&&o.length<=SOURCE_MAX_GROUPS&&o.every(i=>n(i)&&u(i.group,1e3)&&s(i.objects)&&s(i.bytes)&&f(i.labels))}a(S,"S"),e(S,"asStoreSnapshot");function p(t){if(!n(t)||typeof t.at!="string"||!E(t.receivedAt)||!Array.isArray(t.channels)||!c(t.windowSec))return!1;const r=t.truncated;return r!==void 0&&!(n(r)&&s(r.channels))?!1:t.channels.length<=SOURCE_MAX_GROUPS&&t.channels.every(o=>{if(!n(o)||!u(o.channel,1e3)||!f(o.labels)||o.direction!==void 0&&o.direction!=="inbound"&&o.direction!=="outbound"||o.counterpart!==void 0&&!u(o.counterpart,200))return!1;for(const i of["messagesInWindow","failuresInWindow","rateLimitedInWindow"])if(!c(o[i]))return!1;return o.p95Ms!==void 0&&!(typeof o.p95Ms=="number"&&Number.isFinite(o.p95Ms)&&o.p95Ms>=0)||o.lastAt!==void 0&&!l(o.lastAt)?!1:o.lastError===void 0||typeof o.lastError=="string"})}a(p,"R"),e(p,"asTrafficSnapshot");function R(t){return n(t)&&u(t.digest,100)&&typeof t.at=="string"&&E(t.receivedAt)}a(R,"A"),e(R,"asSame");function A(t){if(!n(t)||!u(t.group,1e3)||!s(t.pending))return!1;for(const i of["inFlight","failed","completedInWindow"])if(t[i]!==void 0&&!s(t[i]))return!1;const r=t.oldestPendingAt;if(r!=null&&typeof r!="string")return!1;const o=t.labels;return o===void 0||n(o)&&Object.values(o).every(i=>typeof i=="string")}a(A,"O"),e(A,"asGroup");function O(t){if(!n(t)||typeof t.at!="string"||!E(t.receivedAt)||!Array.isArray(t.groups))return!1;const r=t.totals;if(!n(r)||!s(r.groups)||!s(r.pending)||!s(r.inFlight)||!s(r.failed))return!1;const o=t.truncated;return o!==void 0&&!(n(o)&&s(o.groups))?!1:t.groups.length<=SOURCE_MAX_GROUPS&&t.groups.every(A)}a(O,"T"),e(O,"asSnapshot");export function asSourcePollRaw(t){if(!n(t)||t.kind!=="source-poll"||!u(t.project,200)||!_(t)||!SOURCE_POLL_STATES.includes(t.state)||!E(t.polledAt)||!E(t.reportEveryMs)||t.path===void 0)return;const r=SOURCE_SNAPSHOT_FIELD[t.sourceKind];for(const i of Object.values(SOURCE_SNAPSHOT_FIELD))if(i!==r&&t[i]!==void 0)return;const o={snapshot:O,loop:d,store:S,traffic:p}[r];if(!(t[r]!==void 0&&!o(t[r]))&&!(t.same!==void 0&&(!R(t.same)||t[r]!==void 0))&&!(t.state==="current"&&t[r]===void 0&&t.same===void 0))return t}a(asSourcePollRaw,"asSourcePollRaw"),e(asSourcePollRaw,"asSourcePollRaw");export function asSourceEventRaw(t){if(!n(t)||t.kind!=="source-event"||!u(t.project,200)||!u(t.name,200))return;const r=t.sourceKind;if(SOURCE_KINDS_READ.includes(r)&&SOURCE_PUSH_EVENTS[r].includes(t.event)&&!(t.item!==void 0&&!u(t.item,200)||t.group!==void 0&&!u(t.group,200))&&!(!c(t.items)||!c(t.durationMs)||!c(t.bytes))&&!(t.outcome!==void 0&&!SOURCE_PUSH_OUTCOMES[r].includes(t.outcome))){for(const o of["object","channel","counterpart","correlation"])if(t[o]!==void 0&&!u(t[o],200))return;if(!(t.error!==void 0&&typeof t.error!="string"))return t}}a(asSourceEventRaw,"asSourceEventRaw"),e(asSourceEventRaw,"asSourceEventRaw");export function asSourceDeclarationRaw(t){if(!(!n(t)||t.kind!=="source-declaration"||!u(t.project,200))&&!(!Array.isArray(t.sources)||!t.sources.every(r=>n(r)&&_(r)))&&!(!Array.isArray(t.problems)||!t.problems.every(r=>typeof r=="string")))return t}a(asSourceDeclarationRaw,"asSourceDeclarationRaw"),e(asSourceDeclarationRaw,"asSourceDeclarationRaw");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lensmcp/protocol-types",
3
- "version": "1.25.8",
3
+ "version": "1.25.10",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "module": "./index.js",