@lensmcp/core 1.23.12 → 1.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/bus-rotation.d.ts +46 -2
- package/lib/bus-rotation.js +1 -1
- package/lib/event-redactor.d.ts +2 -2
- package/lib/flow-segments.d.ts +105 -0
- package/lib/flow-segments.js +1 -1
- package/lib/flow-shape.d.ts +6 -0
- package/lib/flow-shape.js +1 -1
- package/lib/redact.d.ts +1 -1
- package/package.json +2 -2
package/lib/bus-rotation.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Cross-process rotation for the shared `events.jsonl` bus (issues/foodguard
|
|
2
|
+
* Cross-process rotation for the shared `events.jsonl` bus (issues/closed/foodguard-13).
|
|
3
3
|
*
|
|
4
4
|
* Many processes append to one bus file — the gateway, every pod, each session's MCP server, the
|
|
5
5
|
* capture sidecar — and each used to run its OWN rename cascade (`unlink .N`, `.N-1→.N` … `file→.1`)
|
|
@@ -11,6 +11,10 @@
|
|
|
11
11
|
*
|
|
12
12
|
* Node-only and dependency-free: the caller injects the fs primitives so the fs-tap-safe pristine
|
|
13
13
|
* references in `node-instrumentation/emit.ts` keep working. Same arrangement as `./redact`.
|
|
14
|
+
*
|
|
15
|
+
* The WHEN half of the contract — how often a writer looks at the size — lives here too, as
|
|
16
|
+
* {@link createBusRotator} (issues/closed/foodguard-40). It used to be mirrored in every writer, and
|
|
17
|
+
* the copies drifted: two of them banked a size check before the `statSync` that could not run.
|
|
14
18
|
*/
|
|
15
19
|
export interface BusFs {
|
|
16
20
|
statSync(path: string): {
|
|
@@ -22,16 +26,56 @@ export interface BusFs {
|
|
|
22
26
|
openSync(path: string, flags: string): number;
|
|
23
27
|
closeSync(fd: number): void;
|
|
24
28
|
}
|
|
25
|
-
|
|
29
|
+
/** The two bounds every bus writer enforces: bytes per generation, and generations of history. */
|
|
30
|
+
export interface BusCaps {
|
|
26
31
|
maxBytes: number;
|
|
27
32
|
maxFiles: number;
|
|
33
|
+
}
|
|
34
|
+
export interface BusRotationOptions extends BusCaps {
|
|
28
35
|
fs: BusFs;
|
|
29
36
|
/** A lock older than this belongs to a writer that died mid-cascade. Default 30 s. */
|
|
30
37
|
staleLockMs?: number;
|
|
31
38
|
}
|
|
39
|
+
/** The environment {@link busCapsFromEnv} reads. */
|
|
40
|
+
export type BusEnv = Readonly<Record<string, string | undefined>>;
|
|
41
|
+
/** A writer stats the bus at most ~once per this many bytes it appends itself. */
|
|
42
|
+
export declare const BUS_ROTATE_CHECK_BYTES: number;
|
|
43
|
+
/**
|
|
44
|
+
* The caps every bus writer shares: `LENSMCP_EVENT_MAX_BYTES` per generation (default 50 MB) and
|
|
45
|
+
* `LENSMCP_EVENT_MAX_FILES` rotated generations (1–20, default 5). Unset or out of range ⇒ the default.
|
|
46
|
+
*/
|
|
47
|
+
export declare function busCapsFromEnv(env?: BusEnv): BusCaps;
|
|
48
|
+
export interface BusRotatorOptions extends BusRotationOptions {
|
|
49
|
+
/**
|
|
50
|
+
* Look at the size at most once per this many of the writer's own bytes. Default
|
|
51
|
+
* {@link BUS_ROTATE_CHECK_BYTES}; never wider than `maxBytes`, or a small cap could not be enforced
|
|
52
|
+
* at all. `0` looks on every line.
|
|
53
|
+
*/
|
|
54
|
+
checkEveryBytes?: number;
|
|
55
|
+
}
|
|
56
|
+
/** Call with the size of the line ABOUT to be appended, before appending it. Never throws. */
|
|
57
|
+
export type BusRotator = (lineBytes: number) => void;
|
|
32
58
|
export type BusRotationResult = 'rotated' | 'not-needed' | 'busy';
|
|
33
59
|
/**
|
|
34
60
|
* Rotate `file` if it is over `maxBytes`. `busy` means another writer holds the cascade — it will do
|
|
35
61
|
* the work, so the caller just carries on. Never throws.
|
|
36
62
|
*/
|
|
37
63
|
export declare function rotateBusFile(file: string, opts: BusRotationOptions): BusRotationResult;
|
|
64
|
+
/**
|
|
65
|
+
* The rotation half of the bus write contract, for ONE writer appending to `file` — the throttle, the
|
|
66
|
+
* size check, and the {@link rotateBusFile} call, in one place, so `node-instrumentation/emit.ts`,
|
|
67
|
+
* `nest-instrumentation/event-sink.ts`, `bridge/env-sink.ts` and `browser-capture`'s event file sink
|
|
68
|
+
* cannot drift apart again (issues/closed/foodguard-40).
|
|
69
|
+
*
|
|
70
|
+
* A size check is due once a window of this writer's own bytes has gone by — never wider than the cap
|
|
71
|
+
* — OR as soon as this writer's running total says the live file has passed the cap, so a generation it
|
|
72
|
+
* fills ends within one line of the cap instead of a whole window past it. The first line always looks:
|
|
73
|
+
* the file may already be over the cap. A check is banked only once its `statSync` actually RAN: when
|
|
74
|
+
* the live file is momentarily absent because another writer is mid-cascade, the bytes since the last
|
|
75
|
+
* check stay counted and the next line looks again. Banking it first is what cost a writer a whole
|
|
76
|
+
* window (issues/closed/foodguard-13, full-review-21). `busy` / `not-needed` leave the known size over
|
|
77
|
+
* the cap, so the next line looks again — by then the lock holder has normally finished its cascade.
|
|
78
|
+
*
|
|
79
|
+
* Every fs call goes through `opts.fs`, so a writer the fs tap must not see (emit.ts) stays invisible.
|
|
80
|
+
*/
|
|
81
|
+
export declare function createBusRotator(file: string, opts: BusRotatorOptions): BusRotator;
|
package/lib/bus-rotation.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var l=Object.defineProperty;var y=(t,r)=>l(t,"name",{value:r,configurable:!0});var m=Object.defineProperty,u=y((t,r)=>m(t,"name",{value:r,configurable:!0}),"u");export const BUS_ROTATE_CHECK_BYTES=1048576;export function busCapsFromEnv(t=process.env){const r=Number(t.LENSMCP_EVENT_MAX_BYTES),e=Number(t.LENSMCP_EVENT_MAX_FILES);return{maxBytes:Number.isFinite(r)&&r>0?r:50*1024*1024,maxFiles:Number.isFinite(e)&&e>=1&&e<=20?Math.floor(e):5}}y(busCapsFromEnv,"busCapsFromEnv"),u(busCapsFromEnv,"busCapsFromEnv");export function rotateBusFile(t,r){const{fs:e,maxBytes:s,maxFiles:i}=r,n=`${t}.rotate.lock`,a=r.staleLockMs??3e4;let o;for(let c=0;c<2&&o===void 0;c++)try{o=e.openSync(n,"wx")}catch{try{if(Date.now()-e.statSync(n).mtimeMs<=a)return"busy";e.unlinkSync(n)}catch{}}if(o===void 0)return"busy";try{try{if(e.statSync(t).size<=s)return"not-needed"}catch{return"not-needed"}try{e.unlinkSync(`${t}.${i}`)}catch{}for(let c=i-1;c>=1;c--)try{e.renameSync(`${t}.${c}`,`${t}.${c+1}`)}catch{}try{e.renameSync(t,`${t}.1`)}catch{}return"rotated"}finally{try{e.closeSync(o)}catch{}try{e.unlinkSync(n)}catch{}}}y(rotateBusFile,"rotateBusFile"),u(rotateBusFile,"rotateBusFile");export function createBusRotator(t,r){const{fs:e,maxBytes:s}=r,i=Math.max(0,Math.min(r.checkEveryBytes??1048576,s));let n=i,a=0;return o=>{if(n>=i||a+n>s)try{a=e.statSync(t).size,n=0,a>s&&rotateBusFile(t,r)==="rotated"&&(a=0)}catch{}n+=o}}y(createBusRotator,"createBusRotator"),u(createBusRotator,"createBusRotator");
|
package/lib/event-redactor.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* and the two gateway `observability.ts` files — and nowhere else. Every other producer therefore
|
|
6
6
|
* opted out by default, which is how four separate disclosures reached disk and the dashboard:
|
|
7
7
|
*
|
|
8
|
-
* - the Postgres/Redis taps kept parameter values and returned rows (issues/codex-security
|
|
8
|
+
* - the Postgres/Redis taps kept parameter values and returned rows (issues/closed/codex-security-13),
|
|
9
9
|
* - the stdout tap copied whole log lines into event titles (36),
|
|
10
10
|
* - the Nest provider tracker put 40 characters of each cache/session key into `keyPreview` (40),
|
|
11
11
|
* - Valtio previews carried whole store values into `valtio://stores` (43).
|
|
@@ -47,7 +47,7 @@ export interface EventRedactorOptions {
|
|
|
47
47
|
*
|
|
48
48
|
* Covers `title`, `message` and `raw`. `title` and `message` matter as much as `raw` here: the
|
|
49
49
|
* structural clamp never covered them, so a stdout line promoted to a title had neither a bound nor
|
|
50
|
-
* a redaction pass (issues/codex-security
|
|
50
|
+
* a redaction pass (issues/closed/codex-security-36). Both are clipped to their documented maxima on the
|
|
51
51
|
* way through, which is the bound that was missing.
|
|
52
52
|
*/
|
|
53
53
|
export declare function createEventRedactor(opts?: EventRedactorOptions): EventRedactor;
|
package/lib/flow-segments.d.ts
CHANGED
|
@@ -61,6 +61,13 @@ export declare const DEFAULT_SEGMENT_OPTIONS: Required<SegmentOptions>;
|
|
|
61
61
|
* `flow-segments.spec.ts` so they cannot drift apart silently.
|
|
62
62
|
*/
|
|
63
63
|
export declare const FLOW_BOOKKEEPING_KINDS: ReadonlySet<string>;
|
|
64
|
+
/**
|
|
65
|
+
* A request's SQL account (`raw.kind`): what `@lensmcp/node-instrumentation`'s pg tap writes once the
|
|
66
|
+
* request ENDS (or, for a context that gives no end signal, once its SQL settles) — statements run, and
|
|
67
|
+
* how many of their events the flood bucket dropped. Pinned equal to the producer's constant by that
|
|
68
|
+
* lib's `sql-accounting.spec.ts`.
|
|
69
|
+
*/
|
|
70
|
+
export declare const SQL_ACCOUNT_KIND = "db-request-account";
|
|
64
71
|
/** Which slice of an over-cap unit a segment is: `index` of `total`, 1-based. */
|
|
65
72
|
export interface SegmentPart {
|
|
66
73
|
index: number;
|
|
@@ -101,6 +108,11 @@ export interface FlowSegment {
|
|
|
101
108
|
/** Set when a request or burst was cut by the size cap. */
|
|
102
109
|
part?: SegmentPart;
|
|
103
110
|
worstSeverity: 'debug' | 'info' | 'warning' | 'error' | 'fatal';
|
|
111
|
+
/**
|
|
112
|
+
* Whether this request's SQL on the bus is all of it — on a unit that ran SQL, and on the first
|
|
113
|
+
* part only when the unit was cut, so summing segments never counts a request twice.
|
|
114
|
+
*/
|
|
115
|
+
sql?: FlowSqlAccounting;
|
|
104
116
|
}
|
|
105
117
|
export interface FlowPhase {
|
|
106
118
|
index: number;
|
|
@@ -122,5 +134,98 @@ export interface FlowSegmentation {
|
|
|
122
134
|
phases: number;
|
|
123
135
|
largestSegment: number;
|
|
124
136
|
};
|
|
137
|
+
/** The flow's SQL completeness boundary — absent when no request in it ran SQL. */
|
|
138
|
+
sql?: FlowSqlAccounting;
|
|
125
139
|
}
|
|
126
140
|
export declare function segmentFlow(input: readonly BaseEvent[], options?: SegmentOptions): FlowSegmentation;
|
|
141
|
+
/** Whether the SQL the bus holds for a request (or a flow) is all of it. */
|
|
142
|
+
export type SqlCompleteness = 'complete' | 'incomplete' | 'unverified';
|
|
143
|
+
/**
|
|
144
|
+
* What a request's account rests on (`raw.basis`, the producer's `SqlAccountBasis`). `end` — written
|
|
145
|
+
* once the request's context ended: all of its SQL. `settled` — a context that gives no end signal,
|
|
146
|
+
* written once its SQL went quiet: later SQL in that context can still add to it. A record without a
|
|
147
|
+
* basis is read as `settled`, the weaker claim.
|
|
148
|
+
*/
|
|
149
|
+
export type SqlAccountBasis = 'end' | 'settled';
|
|
150
|
+
/** One request's SQL: what this view holds, against what the request itself accounted. */
|
|
151
|
+
export interface RequestSqlAccounting {
|
|
152
|
+
requestId: string;
|
|
153
|
+
project?: string;
|
|
154
|
+
/**
|
|
155
|
+
* `complete` — every statement the request ran is here. `incomplete` — its account says some are
|
|
156
|
+
* not, and exactly how many. `unverified` — no account to check against (an older producer, an
|
|
157
|
+
* account the bus dropped, a request whose SQL has not settled yet, or one holding more events than
|
|
158
|
+
* its latest account knows of).
|
|
159
|
+
*/
|
|
160
|
+
status: SqlCompleteness;
|
|
161
|
+
/** db-query events this view holds for the request, and their summed time. Never a census on its own. */
|
|
162
|
+
observed: number;
|
|
163
|
+
observedWorkMs: number;
|
|
164
|
+
/** What its account rests on — `settled` if any of its accounts is; absent when it has none. */
|
|
165
|
+
basis?: SqlAccountBasis;
|
|
166
|
+
/** From the request's own account; absent when it has none. */
|
|
167
|
+
statements?: number;
|
|
168
|
+
failed?: number;
|
|
169
|
+
workMs?: number;
|
|
170
|
+
/** Of `statements`: events that reached the bus, that the flood bucket dropped, that failed to write. */
|
|
171
|
+
recorded?: number;
|
|
172
|
+
suppressed?: number;
|
|
173
|
+
unwritten?: number;
|
|
174
|
+
}
|
|
175
|
+
/** The accounted requests' own census, summed. */
|
|
176
|
+
export interface SqlCensus {
|
|
177
|
+
statements: number;
|
|
178
|
+
failed: number;
|
|
179
|
+
workMs: number;
|
|
180
|
+
recorded: number;
|
|
181
|
+
suppressed: number;
|
|
182
|
+
unwritten: number;
|
|
183
|
+
}
|
|
184
|
+
/** What a flow view's SQL totals are worth: observed events, accounted census, and which requests differ. */
|
|
185
|
+
export interface FlowSqlAccounting {
|
|
186
|
+
/** `unverified` when any request is; otherwise `incomplete` when any request is; otherwise `complete`. */
|
|
187
|
+
status: SqlCompleteness;
|
|
188
|
+
/** Requests that ran SQL, and how many of them are in each state. */
|
|
189
|
+
requests: number;
|
|
190
|
+
complete: number;
|
|
191
|
+
incomplete: number;
|
|
192
|
+
unverified: number;
|
|
193
|
+
/**
|
|
194
|
+
* Of the complete and incomplete requests, those whose verdict rests on a `settled` account — a
|
|
195
|
+
* context that gives no end signal, where later SQL could still change it. 0 when every verdict was
|
|
196
|
+
* taken at its request's end.
|
|
197
|
+
*/
|
|
198
|
+
settled: number;
|
|
199
|
+
/** What this view holds — every request's db-query events. Never the census on its own. */
|
|
200
|
+
observed: SqlObserved;
|
|
201
|
+
/** The census of the `complete` and `incomplete` requests, from their own accounts. */
|
|
202
|
+
accounted: SqlCensus;
|
|
203
|
+
/** The requests whose SQL here is not all of it — incomplete first, most missing first. */
|
|
204
|
+
gaps: RequestSqlAccounting[];
|
|
205
|
+
gapsTruncated: boolean;
|
|
206
|
+
}
|
|
207
|
+
export interface SqlObserved {
|
|
208
|
+
statements: number;
|
|
209
|
+
workMs: number;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* The flow's SQL completeness boundary, per request.
|
|
213
|
+
*
|
|
214
|
+
* WHY. The pg tap's events are flood-guarded per query SHAPE, in a bucket every concurrent request in
|
|
215
|
+
* the process shares; drops vanish, and the next admitted event of that shape — whichever request it
|
|
216
|
+
* belongs to — carries the whole bucket's `floodSuppressed` count. So that count is NEVER read here:
|
|
217
|
+
* it belongs to the bucket, not to the request that happened to receive it (issues/closed/foodguard-39).
|
|
218
|
+
* Each request's own account (`SQL_ACCOUNT_KIND`) says what IT ran and what of it reached the bus, and
|
|
219
|
+
* this compares that with the events the view holds. Nothing is estimated: a request without an
|
|
220
|
+
* account is `unverified`, never assumed complete, and missing statements are counted, never invented.
|
|
221
|
+
*
|
|
222
|
+
* An account is written when its request ENDS, so a request still running SQL has none yet and reads
|
|
223
|
+
* `unverified`. Only a context that gives no end signal is written on a quiet period instead; its
|
|
224
|
+
* verdict carries `basis: 'settled'` and is counted in `settled`, because its SQL may not be over.
|
|
225
|
+
*
|
|
226
|
+
* Only the pg tap's statements are accounted (`source: 'db'`). A `@TraceMethod({ db: true })` span
|
|
227
|
+
* also says `db-query`, but it is a method, not a statement, and has no account to be checked against.
|
|
228
|
+
*
|
|
229
|
+
* Pure; depends only on the SET of events. `undefined` when no request in them ran SQL.
|
|
230
|
+
*/
|
|
231
|
+
export declare function sqlAccountingOf(events: readonly BaseEvent[]): FlowSqlAccounting | undefined;
|
package/lib/flow-segments.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var z=Object.defineProperty;var q=(e,t)=>z(e,"name",{value:t,configurable:!0});var L=Object.defineProperty,h=q((e,t)=>L(e,"name",{value:t,configurable:!0}),"h");export const DEFAULT_SEGMENT_OPTIONS={phaseGapMs:1500,burstGapMs:250,attachWindowMs:400,maxSegmentEvents:400,occurrenceSlackMs:10},FLOW_BOOKKEEPING_KINDS=new Set(["subscription-added","subscription-removed","hook-run","store-created","singleton-instance","visual-frame"]);const P=new Set(["react","valtio","client-runtime","chrome","visual"]),y=h(e=>typeof e=="object"&&e!==null&&!Array.isArray(e),"isObj"),m=h(e=>typeof e=="string"&&e!==""?e:void 0,"str"),M=h(e=>typeof e=="number"&&Number.isFinite(e)?e:void 0,"num");function E(e){const t=y(e.raw)?e.raw:{},o=y(t.request)?t.request:{},r=y(t.span)?t.span:{},u=e.context??{},n=M(e.timestamp)??0,a=m(t.kind)??"",c=M(o.startedAt)??M(r.startTime)??M(t.startedAt),g=M(o.endedAt)??M(r.endTime)??(c!==void 0&&M(t.durationMs)!==void 0?c+t.durationMs:void 0),w=Math.min(n,c??n),k=Math.max(n,g??n),b=y(t.render)?t.render:{},d=y(t.update)?t.update:{},i=a==="render"?m(b.componentName):a==="state-update"?m(d.path)?.split(".").slice(0,2).join("."):void 0,I=m(t.queue),j=m(t.jobId),s=String(e.source),f=m(t.project)??m(u.projectName);return{event:e,id:e.id,ts:n,from:w,to:k,kind:a,source:s,...m(u.requestId)?{requestId:m(u.requestId)}:{},...m(u.parentRequestId)?{parentRequestId:m(u.parentRequestId)}:{},...I&&j?{queueKey:`${I}#${j}`}:{},browser:P.has(s)||s==="memory"&&f===void 0,bookkeeping:FLOW_BOOKKEEPING_KINDS.has(a),...f?{project:f}:{},...i?{subject:i}:{},...a==="fetch-start"||a==="fetch-done"?{route:x(m(t.url)),method:m(t.method)}:a==="server-request"?{route:m(o.route),method:m(o.method)}:a==="gateway-request"?{route:x(m(t.url)),method:m(t.method)}:{}}}q(E,"F"),h(E,"readItem");function x(e){if(!e)return;const t=e.search(/[?#]/),o=t===-1?e:e.slice(0,t),r=/^[a-z][a-z0-9+.-]*:\/\/[^/]*(\/.*)?$/i.exec(o);return r?r[1]??"/":o}q(x,"S"),h(x,"pathOnly");const C=h(e=>e.split(".")[0],"rootOf"),N=h((e,t)=>e.reduce((o,r)=>Math.min(o,t(r)),1/0),"minOf"),F=h((e,t)=>e.reduce((o,r)=>Math.max(o,t(r)),-1/0),"maxOf"),O=h((e,t)=>e.ts-t.ts||(e.id<t.id?-1:e.id>t.id?1:0),"compare"),S=h(e=>e.requestId&&!e.requestId.startsWith("job:")?C(e.requestId):void 0,"directFamilyOf");function G(e){if(e.requestId?.startsWith("job:"))return e.requestId.slice(4);if(e.queueKey&&e.kind!=="queue-enqueued"&&!e.requestId)return e.queueKey}q(G,"W"),h(G,"jobKeyOf");function A(e,t){const o=e.filter(n=>!n.parentRequestId&&(n.kind==="server-request"||n.kind==="gateway-request"||n.kind==="fetch-start"||n.kind==="fetch-done")).sort((n,a)=>n.from-a.from||O(n,a));if(o.length<2)return[e];const r=[];for(const n of o){const a=r[r.length-1];a&&n.from<=a.to+t?a.to=Math.max(a.to,n.to):r.push({from:n.from,to:n.to})}if(r.length<2)return[e];const u=r.map(()=>[]);for(const n of e){let a=r.findIndex(c=>n.ts>=c.from-t&&n.ts<=c.to+t);if(a===-1){a=0;for(let c=0;c<r.length;c+=1)r[c].from<=n.ts&&(a=c)}u[a].push(n)}return u.filter(n=>n.length>0)}q(A,"T"),h(A,"occurrencesOf");const W={debug:0,info:1,warning:2,error:3,fatal:4};function K(e,t){const o=[];let r=[];for(const u of e){const n=r[r.length-1];n&&u.ts-n.ts>=t&&(o.push(r),r=[]),r.push(u)}return r.length&&o.push(r),o}q(K,"v"),h(K,"runs");export function segmentFlow(e,t={}){const o={...DEFAULT_SEGMENT_OPTIONS};for(const[s,f]of Object.entries(t))typeof f=="number"&&Number.isFinite(f)&&(o[s]=f);const r=new Set,u=[];for(const s of e)r.has(s.id)||(r.add(s.id),u.push(E(s)));u.sort(O);const n=m(u[0]?.event.context?.flowId)??m(u[0]?.event.raw?.flowId)??"",a=new Map;for(const s of u)s.kind==="queue-enqueued"&&s.queueKey&&s.requestId&&!s.requestId.startsWith("job:")&&!a.has(s.queueKey)&&a.set(s.queueKey,s);const c=new Map,g=new Map,w=[];for(const s of u){const f=G(s),p=S(s);if(f!==void 0){const l=g.get(f);l?l.push(s):g.set(f,[s])}else if(p!==void 0){const l=c.get(p);l?l.push(s):c.set(p,[s])}else w.push(s)}const k=[],b=new Map;for(const[s,f]of c)A(f,o.occurrenceSlackMs).forEach((p,l)=>{const v={id:`${n}~r:${s}${l===0?"":`@${l+1}`}`,kind:"request",items:p,requestId:s};k.push(v);for(const $ of p)b.set($,v)});for(const[s,f]of g){const p=a.get(s),l=p?b.get(p):void 0;l?l.items.push(...f):k.push({id:`${n}~r:job:${s}`,kind:"request",items:f,requestId:`job:${s}`})}const d=u.filter(s=>s.kind==="fetch-done"&&S(s)!==void 0),i=new Map;for(const s of k)s.requestId&&i.set(s.requestId,[...i.get(s.requestId)??[],s]);const I=h((s,f)=>{const p=i.get(s);if(p)return p.find(l=>l.items.some(v=>v.kind==="fetch-done"&&v.ts===f))??p[p.length-1]},"owningUnit"),j=w.filter(s=>s.browser);for(const s of K(j,o.burstGapMs)){const f=s[0].ts,p=d.filter($=>$.ts<=f&&f-$.ts<=o.attachWindowMs),l=[...new Set(p.map($=>S($)))];if(l.length===1){const $=I(l[0],p[p.length-1].ts);if($){$.items.push(...s);continue}}const v=s.some($=>$.kind==="flow-origin");k.push({id:`${n}~${v?"o":"u"}:${s[0].id}`,kind:v?"origin":"ui",items:s,...l.length>1?{after:l.sort()}:{}})}for(const s of K(w.filter(f=>!f.browser),o.phaseGapMs))k.push({id:`${n}~a:${s[0].id}`,kind:"ambient",items:s});return B(n,u.length,k,o)}q(segmentFlow,"segmentFlow"),h(segmentFlow,"segmentFlow");function B(e,t,o,r){for(const d of o)d.items.sort(O);const u=o.map(d=>({unit:d,from:N(d.items,i=>i.from),to:F(d.items,i=>i.to)}));u.sort((d,i)=>d.from-i.from||(d.unit.id<i.unit.id?-1:1));const n=new Map;let a=0,c=-1/0;for(const d of u)(a===0||d.from-c>=r.phaseGapMs)&&(a+=1),c=Math.max(c,d.to),n.set(d.unit,a);const g=[];for(const d of u){const i=R(d.unit.items,r.maxSegmentEvents);i.forEach((I,j)=>{g.push(T(e,d.unit,I,n.get(d.unit),i.length>1?{index:j+1,total:i.length}:void 0))})}const w={};for(const d of g)for(const i of d.eventIds)w[i]=d.id;const k=[];for(const d of g){let i=k[d.phase-1];i||(i={index:d.phase,start:d.start,end:d.end,segmentIds:[]},k[d.phase-1]=i),i.start=Math.min(i.start,d.start),i.end=Math.max(i.end,d.end),i.segmentIds.push(d.id)}const b=g.reduce((d,i)=>d+i.counts.signal,0);return{flowId:e,segments:g,phases:k.filter(Boolean),assignment:w,stats:{events:t,signal:b,bookkeeping:t-b,segments:g.length,phases:k.filter(Boolean).length,largestSegment:g.reduce((d,i)=>Math.max(d,i.counts.total),0)}}}q(B,"_"),h(B,"assemble");function R(e,t){if(e.filter(n=>!n.bookkeeping).length<=t)return[e];const o=[];let r=[],u=0;for(const n of e)!n.bookkeeping&&u===t&&(o.push(r),r=[],u=0),r.push(n),n.bookkeeping||(u+=1);return r.length&&o.push(r),o}q(R,"U"),h(R,"chunk");function T(e,t,o,r,u){const n={total:o.length,signal:0,bookkeeping:0,errors:0,warnings:0},a=new Set;let c="debug";for(const i of o){i.bookkeeping?n.bookkeeping+=1:n.signal+=1;const I=String(i.event.severity);I==="error"||I==="fatal"?n.errors+=1:I==="warning"&&(n.warnings+=1),(W[I]??1)>(W[c]??0)&&(c=I),i.project&&a.add(i.project)}const g=t.kind==="ui"||t.kind==="origin"?_(o):[],w=N(o,i=>i.from),k=F(o,i=>i.to),b=U(t.items),d=t.id;return{id:u&&u.index>1?`${d}#${u.index}`:d,flowId:e,kind:t.kind,label:D(t,o,b,u,g),phase:r,start:w,end:k,durationMs:k-w,eventIds:o.map(i=>i.id),counts:n,projects:[...a].sort(),...b.route?{route:b.route}:{},...b.method?{method:b.method}:{},...t.requestId?{requestId:t.requestId}:{},...t.after?{after:t.after}:{},...g.length?{hot:g}:{},...u?{part:u}:{},worstSeverity:c}}q(T,"B"),h(T,"toSegment");function U(e){const t=e.find(o=>o.kind==="fetch-start"&&o.route)??e.find(o=>o.kind==="server-request"&&!o.parentRequestId&&o.route)??e.find(o=>o.kind==="gateway-request"&&o.route)??e.find(o=>o.kind==="fetch-done"&&o.route);return{...t?.route?{route:t.route}:{},...t?.method?{method:t.method.toUpperCase()}:{}}}q(U,"D"),h(U,"routeOf");function _(e){const t=new Map;for(const o of e)o.subject&&!o.bookkeeping&&t.set(o.subject,(t.get(o.subject)??0)+1);return[...t.entries()].sort((o,r)=>r[1]-o[1]||(o[0]<r[0]?-1:1)).slice(0,3).map(([o,r])=>r>1?`${o} \xD7${r}`:o)}q(_,"L"),h(_,"hotOf");function D(e,t,o,r,u){const n=r?` (part ${r.index}/${r.total})`:"";if(e.kind==="request"){if(o.route)return`${o.method?`${o.method} `:""}${o.route}${n}`;const a=t.find(c=>c.queueKey);return a?`job ${a.queueKey}${n}`:`request ${e.requestId??""}${n}`}return e.kind==="origin"?`${t.find(a=>a.kind==="flow-origin")?.event.title||"start"}${u.length?` \xB7 ${u.join(", ")}`:""}${n}`:e.kind==="ui"?`UI update${u.length?` \xB7 ${u.join(", ")}`:""}${n}`:`background${n}`}q(D,"z"),h(D,"labelOf");
|
|
1
|
+
"use strict";var z=Object.defineProperty;var j=(e,t)=>z(e,"name",{value:t,configurable:!0});var Q=Object.defineProperty,g=j((e,t)=>Q(e,"name",{value:t,configurable:!0}),"g");export const DEFAULT_SEGMENT_OPTIONS={phaseGapMs:1500,burstGapMs:250,attachWindowMs:400,maxSegmentEvents:400,occurrenceSlackMs:10},FLOW_BOOKKEEPING_KINDS=new Set(["subscription-added","subscription-removed","hook-run","store-created","singleton-instance","visual-frame"]),SQL_ACCOUNT_KIND="db-request-account";const H=new Set(["react","valtio","client-runtime","chrome","visual"]),O=g(e=>typeof e=="object"&&e!==null&&!Array.isArray(e),"isObj"),k=g(e=>typeof e=="string"&&e!==""?e:void 0,"str"),v=g(e=>typeof e=="number"&&Number.isFinite(e)?e:void 0,"num");function E(e){const t=O(e.raw)?e.raw:{},s=O(t.request)?t.request:{},r=O(t.span)?t.span:{},d=e.context??{},n=k(t.kind)??"",f=n===SQL_ACCOUNT_KIND,p=(f?v(t.lastEndedAt):void 0)??v(e.timestamp)??0,h=v(s.startedAt)??v(r.startTime)??v(t.startedAt)??(f?v(t.firstStartedAt):void 0),$=v(s.endedAt)??v(r.endTime)??(h!==void 0&&v(t.durationMs)!==void 0?h+t.durationMs:void 0),b=Math.min(p,h??p),M=Math.max(p,$??p),c=O(t.render)?t.render:{},u=O(t.update)?t.update:{},i=n==="render"?k(c.componentName):n==="state-update"?k(u.path)?.split(".").slice(0,2).join("."):void 0,a=k(t.queue),l=k(t.jobId),I=String(e.source),o=k(t.project)??k(d.projectName);return{event:e,id:e.id,ts:p,from:b,to:M,kind:n,source:I,...k(d.requestId)?{requestId:k(d.requestId)}:{},...k(d.parentRequestId)?{parentRequestId:k(d.parentRequestId)}:{},...a&&l?{queueKey:`${a}#${l}`}:{},browser:H.has(I)||I==="memory"&&o===void 0,bookkeeping:FLOW_BOOKKEEPING_KINDS.has(n),...o?{project:o}:{},...i?{subject:i}:{},...n==="fetch-start"||n==="fetch-done"?{route:S(k(t.url)),method:k(t.method)}:n==="server-request"?{route:k(s.route),method:k(s.method)}:n==="gateway-request"?{route:S(k(t.url)),method:k(t.method)}:{}}}j(E,"W"),g(E,"readItem");function S(e){if(!e)return;const t=e.search(/[?#]/),s=t===-1?e:e.slice(0,t),r=/^[a-z][a-z0-9+.-]*:\/\/[^/]*(\/.*)?$/i.exec(s);return r?r[1]??"/":s}j(S,"A"),g(S,"pathOnly");const J=g(e=>e.split(".")[0],"rootOf"),F=g((e,t)=>e.reduce((s,r)=>Math.min(s,t(r)),1/0),"minOf"),G=g((e,t)=>e.reduce((s,r)=>Math.max(s,t(r)),-1/0),"maxOf"),K=g((e,t)=>e.ts-t.ts||(e.id<t.id?-1:e.id>t.id?1:0),"compare"),A=g(e=>e.requestId&&!e.requestId.startsWith("job:")?J(e.requestId):void 0,"directFamilyOf");function T(e){if(e.requestId?.startsWith("job:"))return e.requestId.slice(4);if(e.queueKey&&e.kind!=="queue-enqueued"&&!e.requestId)return e.queueKey}j(T,"U"),g(T,"jobKeyOf");function W(e,t){const s=e.filter(n=>!n.parentRequestId&&(n.kind==="server-request"||n.kind==="gateway-request"||n.kind==="fetch-start"||n.kind==="fetch-done")).sort((n,f)=>n.from-f.from||K(n,f));if(s.length<2)return[e];const r=[];for(const n of s){const f=r[r.length-1];f&&n.from<=f.to+t?f.to=Math.max(f.to,n.to):r.push({from:n.from,to:n.to})}if(r.length<2)return[e];const d=r.map(()=>[]);for(const n of e){let f=r.findIndex(p=>n.ts>=p.from-t&&n.ts<=p.to+t);if(f===-1){f=0;for(let p=0;p<r.length;p+=1)r[p].from<=n.ts&&(f=p)}d[f].push(n)}return d.filter(n=>n.length>0)}j(W,"L"),g(W,"occurrencesOf");const _={debug:0,info:1,warning:2,error:3,fatal:4};function N(e,t){const s=[];let r=[];for(const d of e){const n=r[r.length-1];n&&d.ts-n.ts>=t&&(s.push(r),r=[]),r.push(d)}return r.length&&s.push(r),s}j(N,"R"),g(N,"runs");export function segmentFlow(e,t={}){const s={...DEFAULT_SEGMENT_OPTIONS};for(const[o,m]of Object.entries(t))typeof m=="number"&&Number.isFinite(m)&&(s[o]=m);const r=new Set,d=[];for(const o of e)r.has(o.id)||(r.add(o.id),d.push(E(o)));d.sort(K);const n=k(d[0]?.event.context?.flowId)??k(d[0]?.event.raw?.flowId)??"",f=new Map;for(const o of d)o.kind==="queue-enqueued"&&o.queueKey&&o.requestId&&!o.requestId.startsWith("job:")&&!f.has(o.queueKey)&&f.set(o.queueKey,o);const p=new Map,h=new Map,$=[];for(const o of d){const m=T(o),w=A(o);if(m!==void 0){const q=h.get(m);q?q.push(o):h.set(m,[o])}else if(w!==void 0){const q=p.get(w);q?q.push(o):p.set(w,[o])}else $.push(o)}const b=[],M=new Map;for(const[o,m]of p)W(m,s.occurrenceSlackMs).forEach((w,q)=>{const x={id:`${n}~r:${o}${q===0?"":`@${q+1}`}`,kind:"request",items:w,requestId:o};b.push(x);for(const y of w)M.set(y,x)});for(const[o,m]of h){const w=f.get(o),q=w?M.get(w):void 0;q?q.items.push(...m):b.push({id:`${n}~r:job:${o}`,kind:"request",items:m,requestId:`job:${o}`})}const c=d.filter(o=>o.kind==="fetch-done"&&A(o)!==void 0),u=new Map;for(const o of b)o.requestId&&u.set(o.requestId,[...u.get(o.requestId)??[],o]);const i=g((o,m)=>{const w=u.get(o);if(w)return w.find(q=>q.items.some(x=>x.kind==="fetch-done"&&x.ts===m))??w[w.length-1]},"owningUnit"),a=$.filter(o=>o.browser);for(const o of N(a,s.burstGapMs)){const m=o[0].ts,w=c.filter(y=>y.ts<=m&&m-y.ts<=s.attachWindowMs),q=[...new Set(w.map(y=>A(y)))];if(q.length===1){const y=i(q[0],w[w.length-1].ts);if(y){y.items.push(...o);continue}}const x=o.some(y=>y.kind==="flow-origin");b.push({id:`${n}~${x?"o":"u"}:${o[0].id}`,kind:x?"origin":"ui",items:o,...q.length>1?{after:q.sort()}:{}})}for(const o of N($.filter(m=>!m.browser),s.phaseGapMs))b.push({id:`${n}~a:${o[0].id}`,kind:"ambient",items:o});const l=B(n,d.length,b,s),I=sqlAccountingOf(d.map(o=>o.event));return I?{...l,sql:I}:l}j(segmentFlow,"segmentFlow"),g(segmentFlow,"segmentFlow");function B(e,t,s,r){for(const c of s)c.items.sort(K);const d=s.map(c=>({unit:c,from:F(c.items,u=>u.from),to:G(c.items,u=>u.to)}));d.sort((c,u)=>c.from-u.from||(c.unit.id<u.unit.id?-1:1));const n=new Map;let f=0,p=-1/0;for(const c of d)(f===0||c.from-p>=r.phaseGapMs)&&(f+=1),p=Math.max(p,c.to),n.set(c.unit,f);const h=[];for(const c of d){const u=R(c.unit.items,r.maxSegmentEvents),i=c.unit.kind==="request"?sqlAccountingOf(c.unit.items.map(a=>a.event)):void 0;u.forEach((a,l)=>{const I=U(e,c.unit,a,n.get(c.unit),u.length>1?{index:l+1,total:u.length}:void 0);h.push(i&&l===0?{...I,sql:i}:I)})}const $={};for(const c of h)for(const u of c.eventIds)$[u]=c.id;const b=[];for(const c of h){let u=b[c.phase-1];u||(u={index:c.phase,start:c.start,end:c.end,segmentIds:[]},b[c.phase-1]=u),u.start=Math.min(u.start,c.start),u.end=Math.max(u.end,c.end),u.segmentIds.push(c.id)}const M=h.reduce((c,u)=>c+u.counts.signal,0);return{flowId:e,segments:h,phases:b.filter(Boolean),assignment:$,stats:{events:t,signal:M,bookkeeping:t-M,segments:h.length,phases:b.filter(Boolean).length,largestSegment:h.reduce((c,u)=>Math.max(c,u.counts.total),0)}}}j(B,"B"),g(B,"assemble");function R(e,t){if(e.filter(n=>!n.bookkeeping).length<=t)return[e];const s=[];let r=[],d=0;for(const n of e)!n.bookkeeping&&d===t&&(s.push(r),r=[],d=0),r.push(n),n.bookkeeping||(d+=1);return r.length&&s.push(r),s}j(R,"C"),g(R,"chunk");function U(e,t,s,r,d){const n={total:s.length,signal:0,bookkeeping:0,errors:0,warnings:0},f=new Set;let p="debug";for(const u of s){u.bookkeeping?n.bookkeeping+=1:n.signal+=1;const i=String(u.event.severity);i==="error"||i==="fatal"?n.errors+=1:i==="warning"&&(n.warnings+=1),(_[i]??1)>(_[p]??0)&&(p=i),u.project&&f.add(u.project)}const h=t.kind==="ui"||t.kind==="origin"?C(s):[],$=F(s,u=>u.from),b=G(s,u=>u.to),M=D(t.items),c=t.id;return{id:d&&d.index>1?`${c}#${d.index}`:c,flowId:e,kind:t.kind,label:L(t,s,M,d,h),phase:r,start:$,end:b,durationMs:b-$,eventIds:s.map(u=>u.id),counts:n,projects:[...f].sort(),...M.route?{route:M.route}:{},...M.method?{method:M.method}:{},...t.requestId?{requestId:t.requestId}:{},...t.after?{after:t.after}:{},...h.length?{hot:h}:{},...d?{part:d}:{},worstSeverity:p}}j(U,"D"),g(U,"toSegment");function D(e){const t=e.find(s=>s.kind==="fetch-start"&&s.route)??e.find(s=>s.kind==="server-request"&&!s.parentRequestId&&s.route)??e.find(s=>s.kind==="gateway-request"&&s.route)??e.find(s=>s.kind==="fetch-done"&&s.route);return{...t?.route?{route:t.route}:{},...t?.method?{method:t.method.toUpperCase()}:{}}}j(D,"P"),g(D,"routeOf");function C(e){const t=new Map;for(const s of e)s.subject&&!s.bookkeeping&&t.set(s.subject,(t.get(s.subject)??0)+1);return[...t.entries()].sort((s,r)=>r[1]-s[1]||(s[0]<r[0]?-1:1)).slice(0,3).map(([s,r])=>r>1?`${s} \xD7${r}`:s)}j(C,"z"),g(C,"hotOf");function L(e,t,s,r,d){const n=r?` (part ${r.index}/${r.total})`:"";if(e.kind==="request"){if(s.route)return`${s.method?`${s.method} `:""}${s.route}${n}`;const f=t.find(p=>p.queueKey);return f?`job ${f.queueKey}${n}`:`request ${e.requestId??""}${n}`}return e.kind==="origin"?`${t.find(f=>f.kind==="flow-origin")?.event.title||"start"}${d.length?` \xB7 ${d.join(", ")}`:""}${n}`:e.kind==="ui"?`UI update${d.length?` \xB7 ${d.join(", ")}`:""}${n}`:`background${n}`}j(L,"Q"),g(L,"labelOf");const P=50;export function sqlAccountingOf(e){const t=new Map,s=new Map,r=new Set;for(const i of e){if(r.has(i.id))continue;r.add(i.id);const a=O(i.raw)?i.raw:{},l=k(a.kind),I=k(i.context?.requestId);if(!I)continue;const o=k(a.project)??k(i.context?.projectName);if(l==="db-query"&&i.source==="db"){const m=s.get(I)??{count:0,workMs:0,...o?{project:o}:{}};m.count+=1,m.workMs+=v(a.durationMs)??0,s.set(I,m)}else if(l===SQL_ACCOUNT_KIND){const m=k(a.accountId)??i.id,w=v(a.seq)??0,q=t.get(m);if(q&&q.seq>=w)continue;t.set(m,{requestId:I,...o?{project:o}:{},seq:w,basis:a.basis==="end"?"end":"settled",statements:v(a.statements)??0,failed:v(a.failed)??0,workMs:v(a.workMs)??0,recorded:v(a.recorded)??0,suppressed:v(a.suppressed)??0,unwritten:v(a.unwritten)??0})}}const d=new Map;for(const i of t.values()){const a=d.get(i.requestId);if(!a){d.set(i.requestId,{...i});continue}i.basis==="settled"&&(a.basis="settled"),a.statements+=i.statements,a.failed+=i.failed,a.workMs+=i.workMs,a.recorded+=i.recorded,a.suppressed+=i.suppressed,a.unwritten+=i.unwritten}const n=[...new Set([...s.keys(),...d.keys()])].sort();if(n.length===0)return;const f={statements:0,failed:0,workMs:0,recorded:0,suppressed:0,unwritten:0},p={statements:0,workMs:0},h=[];for(const i of n){const a=s.get(i),l=d.get(i),I=a?.count??0;p.statements+=I,p.workMs+=a?.workMs??0;const o=!l||I>l.statements?"unverified":I===l.statements?"complete":"incomplete";l&&o!=="unverified"&&(f.statements+=l.statements,f.failed+=l.failed,f.workMs+=l.workMs,f.recorded+=l.recorded,f.suppressed+=l.suppressed,f.unwritten+=l.unwritten);const m=a?.project??l?.project;h.push({requestId:i,...m?{project:m}:{},status:o,observed:I,observedWorkMs:a?.workMs??0,...l?{basis:l.basis,statements:l.statements,failed:l.failed,workMs:l.workMs,recorded:l.recorded,suppressed:l.suppressed,unwritten:l.unwritten}:{}})}const $=g(i=>h.filter(a=>a.status===i).length,"tally"),b=$("unverified"),M=$("incomplete"),c=g(i=>(i.statements??0)-i.observed,"missing"),u=h.filter(i=>i.status!=="complete").sort((i,a)=>(i.status==="incomplete"?0:1)-(a.status==="incomplete"?0:1)||c(a)-c(i)||(i.requestId<a.requestId?-1:i.requestId>a.requestId?1:0));return{status:b>0?"unverified":M>0?"incomplete":"complete",requests:h.length,complete:$("complete"),incomplete:M,unverified:b,settled:h.filter(i=>i.status!=="unverified"&&i.basis==="settled").length,observed:p,accounted:f,gaps:u.slice(0,P),gapsTruncated:u.length>P}}j(sqlAccountingOf,"sqlAccountingOf"),g(sqlAccountingOf,"sqlAccountingOf");
|
package/lib/flow-shape.d.ts
CHANGED
|
@@ -54,8 +54,14 @@ export declare function trimFlowEvent(event: BaseEvent, opts?: FlowShapeOptions)
|
|
|
54
54
|
/**
|
|
55
55
|
* The pure flow transform: raw correlated events → ordered, parented,
|
|
56
56
|
* aggregated display nodes. No I/O, no server — see module header.
|
|
57
|
+
*
|
|
58
|
+
* `sql` is the flow's SQL completeness boundary (`sqlAccountingOf`): the db-query
|
|
59
|
+
* nodes are what reached the bus, and a page load's concurrent requests can drain
|
|
60
|
+
* a query shape's flood bucket, so a reader summing them needs to know whether
|
|
61
|
+
* that is every statement (issues/closed/foodguard-39). Absent when nothing ran SQL.
|
|
57
62
|
*/
|
|
58
63
|
export declare function buildFlowDetail(rawEvents: BaseEvent[], opts?: FlowShapeOptions): {
|
|
64
|
+
sql?: import("./flow-segments.js").FlowSqlAccounting | undefined;
|
|
59
65
|
events: ({
|
|
60
66
|
steps?: string[] | undefined;
|
|
61
67
|
detail?: string | undefined;
|
package/lib/flow-shape.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var ge=Object.defineProperty;var A=(u,q)=>ge(u,"name",{value:q,configurable:!0});var $e=Object.defineProperty,g=A((u,q)=>$e(u,"name",{value:q,configurable:!0}),"h");import{redactDeep as ye,redactText as ue}from"./redact.js";import{boundedClone as ke}from"@lensmcp/protocol-types";export function areaOf(u){const{source:q,category:n,title:M,kind:y}=u;return q==="ai"?"ai":y?.startsWith("memory")||y==="db-query"?"data":q==="gateway"||y==="gateway-request"?"gateway":y==="queue-process"||y==="queue-active"||y==="queue-completed"||y==="queue-failed"?"services":q==="db"||q==="redis"||q==="queue"||y==="redis-op"||y?.startsWith("queue-")?"data":q==="external"||y==="egress"?"network":q==="nestjs"||q==="nest"?/(?:Repository|Repo|Dao|Store)\./.test(M)?"data":"services":q==="valtio"?"state":n==="network"?"network":"browser"}A(areaOf,"areaOf"),g(areaOf,"areaOf");const de=240,we=new Set(["systemPrompt","prompt","prompts","messages","completion","completions","input","output","draft","text","content","result","arguments","params"]),qe=new Set(["tenantId","caller","userId","accountId","email","subject"]),Me=8;function ce(u){const q=Y(u,0);return ke(ye({redacted:!0,...q}),{rawByteLimit:16*1024}).value}A(ce,"yt"),g(ce,"aiProjection");function Y(u,q){if(typeof u=="string")return le(u);if(u===null||typeof u!="object")return u;if(q>=Me)return;if(Array.isArray(u))return u.slice(0,32).map(M=>Y(M,q+1));const n={};for(const[M,y]of Object.entries(u))if(!qe.has(M)){if(we.has(M)){typeof y=="string"?n[`${M}Chars`]=y.length:Array.isArray(y)?n[`${M}Count`]=y.length:y!=null&&(n[`${M}Present`]=!0);continue}n[M]=Y(y,q+1)}return n}A(Y,"tt"),g(Y,"projectAi");function le(u){return u.length<=de?ue(u):`${ue(u.slice(0,de))}\u2026`}A(le,"kt"),g(le,"clipText");export function trimFlowEvent(u,q={}){const n=u.raw??{},M=n.render??{},y=n.span??{},W=n.request??{},f=g(d=>typeof d=="number"?d:void 0,"num"),b=n.loop??{},T=n.effect??{},P=f(n.durationMs)??f(y.durationMs)??f(W.durationMs)??f(b.durationMs)??f(M.actualDurationMs)??f(T.durationMs),p=typeof n.kind=="string"?n.kind:void 0,_=p==="fetch-done"||p==="fetch-error",K=f(M.commitTime),R=p==="render"&&K!==void 0&&Math.abs(K-u.timestamp)<6e4,G=_?u.timestamp:R?K:f(n.startedAt)??f(y.startTime)??f(W.startedAt)??f(b.startedAt)??(P!==void 0?u.timestamp-P:u.timestamp);let j;if(typeof n.query=="string"){const d=Array.isArray(n.params)?` [${n.params.join(", ")}]`:"",w=typeof n.result=="string"?` \u2192 ${n.result}`:"";j=n.query+d+w}else if(typeof n.command=="string"){const d=typeof n.value=="string"?` \u2192 ${n.value}`:n.hit===!1?" \u2192 (miss)":"";j=n.command+d}else if(p==="state-update"){const d=n.update??{};let w="";try{w=JSON.stringify(d.preview)}catch{}j=`${d.storeId??""}.${d.path??""} = ${w}`}else p==="queue-completed"&&typeof n.returned=="string"?j=`returned ${n.returned}`:p==="queue-enqueued"&&(j=`${n.job??"job"}#${n.jobId??"?"} \u2192 ${n.queue??""}${typeof n.data=="string"?" "+n.data:""}`);let F;if(p==="gateway-request")F=`${n.host??""}${String(n.url??"").split("?")[0]}`;else if(p==="fetch-start"||p==="fetch-done"||p==="fetch-error")try{const d=new URL(String(n.href??n.url??""));F=`${d.hostname}${d.pathname}`}catch{}let C;if(p==="gateway-request"&&Array.isArray(n.steps))C=n.steps.map(d=>{const w=[String(d.step)];return d.project&&w.push(String(d.project)),d.prefix&&w.push(String(d.prefix)),d.mode&&w.push(String(d.mode)+(d.ok===!1?" \u2717":d.ok===!0?" \u2713":"")),d.caller&&w.push("by "+String(d.caller)),d.pod&&w.push(String(d.pod)),typeof d.atMs=="number"&&w.push(`@${d.atMs}ms`),w.join(" ")}),j=C.map(d=>d.replace(/ @\d+ms$/,"")).join(" \u2192 ");else if(p==="egress")j=`${n.method??"GET"} ${n.host??""} \u2192 ${n.status??"?"}`;else if(u.source==="ai"){if(p==="ai-call"){const d=n.usage??{},w=d.promptTokens!==void 0||d.completionTokens!==void 0?` \xB7 ${f(d.promptTokens)??"?"}\u2192${f(d.completionTokens)??"?"} tok`:"",S=n.status!==void 0?` \xB7 ${String(n.status)}`:n.error?" \xB7 failed":"",z=f(n.toolCallCount)?` \xB7 ${f(n.toolCallCount)} tool call${f(n.toolCallCount)===1?"":"s"}`:"",L=f(n.durationMs)!==void 0?` \xB7 ${(f(n.durationMs)/1e3).toFixed(1)}s`:"";j=`${n.provider??""}/${n.model??"?"}${w}${S}${z}${L}${n.stream===!0?" \xB7 stream":""}`}else if(p==="ai-round"){const d=n.model??{},w=n.usage??{},S=Array.isArray(n.toolCalls)?n.toolCalls:[],z=`${f(w.promptTokens)??"?"}\u2192${f(w.completionTokens)??"?"} tok`,L=S.length?` \xB7 ${S.length} tool call${S.length===1?"":"s"}`:"",V=f(n.durationMs)!==void 0?` \xB7 ${(f(n.durationMs)/1e3).toFixed(1)}s`:"";j=`${d.provider??""}/${d.model??""} \xB7 ${z}${L}${V}`}else if(p==="ai-turn-start"){const d=Array.isArray(n.tools)?n.tools:[];j=`skill ${n.skill??""} \xB7 ${n.output??""}${d.length?" \xB7 tools: "+d.join(", "):""}`}else if(p==="ai-tool-call"){const d=n.call??{},w=n.result??{},S=f(n.durationMs)!==void 0?` \xB7 ${(f(n.durationMs)/1e3).toFixed(1)}s`:"";j=`${d.name??"tool"} ${w.error!=null?"\u2717 "+String(w.error):"\u2713"}${S}`}else if(p==="ai-turn-end"){const d=n.usage??{},w=f(n.rounds)!==void 0?` \xB7 ${f(n.rounds)} round${f(n.rounds)===1?"":"s"}`:"",S=f(n.durationMs)!==void 0?` \xB7 ${(f(n.durationMs)/1e3).toFixed(1)}s`:"";j=`${n.ok===!1?"FAILED":"ok"} \xB7 ${f(d.promptTokens)??"?"}+${f(d.completionTokens)??"?"} tok${w}${S}`}}let B;p==="db-query"&&typeof n.query=="string"?B=n.query:p==="redis-op"?B=`${n.op??""} ${String(n.key??"").replace(/\d+/g,"#")}`:p==="egress"?B=`${n.method??"GET"} ${n.host??""}`:p==="ai-call"?B=`ai ${n.provider??""} ${n.model??""} ${n.api??""}`:p==="queue-enqueued"?B=`enqueue ${n.queue??""}`:p==="span"&&typeof y.name=="string"?B=y.name:p==="memory-mutation"&&(B=u.title);const D=u.context??{},H=M.commitId??T.commitId??n.commitId,h=Array.isArray(M.why)?M.why.map(d=>typeof d?.type=="string"?d.type:void 0).filter(d=>!!d):[];return{id:u.id,t:u.timestamp,start:G,...B?{sig:B}:{},...F?{urlKey:F}:{},...typeof n.queue=="string"?{queue:n.queue}:{},...n.jobId!=null?{jobId:String(n.jobId)}:{},source:u.source,category:u.category,severity:u.severity,title:u.title,kind:p,area:areaOf({source:u.source,category:u.category,title:u.title,kind:p}),...typeof y.role=="string"?{role:y.role}:{},...typeof D.requestId=="string"?{requestId:D.requestId}:{},...typeof D.parentRequestId=="string"?{parentRequestId:D.parentRequestId}:{},...typeof H=="string"?{commitId:H}:{},...typeof M.componentName=="string"?{component:M.componentName}:{},...typeof M.phase=="string"?{phase:M.phase}:{},...h.length?{why:h}:{},...p==="effect-run"&&typeof T.hookLogicalId=="string"?{hook:T.hookLogicalId}:{},...p==="effect-run"&&T.cleanupRan===!0?{cleanup:!0}:{},...p==="render-commit-truncated"?{renderTotal:f(n.total),renderPublished:f(n.published)}:{},...typeof D.projectName=="string"?{project:D.projectName}:typeof n.project=="string"?{project:n.project}:{},...P!==void 0?{durationMs:P}:{},..._&&f(n.startedAt)!==void 0?{fetchStartedAt:f(n.startedAt)}:{},...u.source==="ai"?{ai:q.includeRaw?n:ce(n)}:{},...j?{detail:j}:{},...C?{steps:C}:{}}}A(trimFlowEvent,"trimFlowEvent"),g(trimFlowEvent,"trimFlowEvent");const fe=1;function pe(u){return u==="flow-origin"?0:u?.endsWith("-start")?1:u==="gateway-request"?2:u==="server-request"?3:u==="span"?4:u==="render"||u==="render-commit-truncated"?6:u==="effect-run"?7:5}A(pe,"wt"),g(pe,"kindRank");function E(u){return u.kind==="span"&&u.role==="controller"?3.5:u.kind==="queue-process"?3:pe(u.kind)}A(E,"J"),g(E,"causalRank");export function buildFlowDetail(u,q={}){const n=u.slice().filter(e=>{const t=e.raw??{},r=typeof t.kind=="string"?t.kind:"";if(r==="subscription-added"||r==="subscription-removed"||r==="hook-run"||r==="store-created"||r==="singleton-instance")return!1;const o=t.span;return!(typeof o?.name=="string"&&o.name.startsWith("nest.request "))}).map(e=>trimFlowEvent(e,q)),M=new Set;for(let e=n.length-1;e>=0;e--)M.has(n[e].id)?n.splice(e,1):M.add(n[e].id);const y=new Map;for(const e of n)(e.kind==="gateway-request"||e.kind==="queue-process")&&e.requestId&&!y.has(e.requestId)&&y.set(e.requestId,e.start);const W=g(e=>e.source!=="react"&&e.source!=="valtio"&&e.requestId&&y.has(e.requestId)?y.get(e.requestId):e.start,"anchorOf");n.sort((e,t)=>W(e)-W(t)||e.start-t.start||E(e)-E(t)||(t.durationMs??0)-(e.durationMs??0)||e.t-t.t);const f=g(e=>(e.kind==="span"||e.kind==="server-request"||e.kind==="gateway-request"||e.kind==="queue-process")&&(e.durationMs??0)>0,"spanLike"),b=n;for(const e of b)e.group=e.source==="react"||e.source==="valtio"||e.source==="client-runtime"||e.source==="visual"?"browser":"server";const T=new Map;for(const e of b)if((e.kind==="gateway-request"||e.kind==="queue-process")&&e.requestId&&e.project){const t=T.get(e.requestId)??[];t.push({project:e.project,start:e.start,end:e.start+(e.durationMs??0)}),T.set(e.requestId,t)}const P=g((e,t)=>{const r=T.get(e);if(r?.length)return r.every(o=>o.project===r[0].project)?r[0].project:r.filter(o=>o.start<=t+1&&o.end>=t-1).sort((o,s)=>s.start-o.start)[0]?.project},"projectOf");for(const e of b)if(!e.project&&e.source==="nestjs"&&e.requestId){const t=P(e.requestId,e.start);t&&(e.project=t)}for(const e of b){let t;const r=e.requestId;for(const o of b){if(o===e||!f(o)||o.group!==e.group||o.requestId!==r)continue;const s=o.project,c=e.project;if(o.kind!=="gateway-request"&&e.kind!=="gateway-request"&&s!==void 0&&c!==void 0&&s!==c)continue;const m=o.durationMs??0,$=e.durationMs??0,a=Math.abs(o.start-e.start)<=fe,i=E(o),l=E(e),k=o.start<=e.start;if(!(o.start+m>e.start&&(k||a&&i<l)&&(!a||(i!==l?i<l:k&&m>$))))continue;const x=t?E(t):-1,I=t?Math.abs(o.start-t.start)<=fe:!1;(!t||!I&&o.start>t.start||I&&i>x||I&&i===x&&(o.durationMs??0)<(t.durationMs??0))&&(t=o)}t&&(e.parent=t.id)}const p=new Map;for(const e of b){if(!e.requestId)continue;const t=p.get(e.requestId);t?t.push(e):p.set(e.requestId,[e])}const _=new Map(b.map(e=>[e.id,e])),K=g((e,t)=>{const r=(p.get(e)??[]).filter(o=>f(o)&&o.start<=t&&o.start+(o.durationMs??0)>=t);if(r.length!==0)return r.sort((o,s)=>s.start-o.start||(o.durationMs??0)-(s.durationMs??0)),r[0]},"callerNodeFor");for(const e of b){if(!e.parentRequestId||e.kind!=="server-request"&&e.kind!=="gateway-request")continue;const t=e.parent?_.get(e.parent):void 0;if(t&&t.requestId===e.requestId)continue;const r=K(e.parentRequestId,e.start);r&&r!==e&&r.start<=e.start&&(e.parent=r.id)}const R=b,G=new Map;for(const e of R)e.kind==="gateway-request"&&e.requestId&&!G.has(e.requestId)&&G.set(e.requestId,e);for(const e of R)if(e.group==="server"&&!e.parent&&e.requestId&&e.kind!=="gateway-request"){const t=G.get(e.requestId);t&&t!==e&&t.start<=e.start&&(e.parent=t.id)}const j=new Set,F=new Set,C=[];for(const e of R){if(e.kind!=="gateway-request")continue;if(!e.requestId){C.push(e);continue}const t=p.get(e.requestId)??[];if(t.filter(s=>s.kind==="gateway-request").sort((s,c)=>s.start-c.start)[0]!==e)continue;const r=t.find(s=>s.kind==="fetch-start"),o=t.find(s=>s.kind==="fetch-done"||s.kind==="fetch-error");if(!r&&!o){C.push(e);continue}r&&(e.fetchStartId=r.id,j.add(r.id)),o&&(e.fetchDoneId=o.id,F.add(o.id))}const B=new Map;for(const e of R){if(!e.urlKey)continue;const t=B.get(e.urlKey)??{gws:[],starts:[],dones:[]};e.kind==="gateway-request"?C.includes(e)&&t.gws.push(e):e.kind==="fetch-start"?j.has(e.id)||t.starts.push(e):(e.kind==="fetch-done"||e.kind==="fetch-error")&&(F.has(e.id)||t.dones.push(e)),B.set(e.urlKey,t)}for(const{gws:e,starts:t,dones:r}of B.values()){e.sort((s,c)=>s.start-c.start),t.sort((s,c)=>s.start-c.start);const o=new Set;e.forEach((s,c)=>{const m=t[c];m&&(s.fetchStartId=m.id);const $=m?m.t:s.start;let a,i=1/0;for(const l of r){if(o.has(l.id))continue;const k=l.fetchStartedAt??l.start-(l.durationMs??0),x=Math.abs(k-$);x<i&&(i=x,a=l)}a&&(s.fetchDoneId=a.id,o.add(a.id))})}const D=new Map,H=new Map;for(const e of R)!e.queue||!e.jobId||(e.kind==="queue-enqueued"&&D.set(`${e.queue}#${e.jobId}`,e),e.kind==="queue-process"&&H.set(`${e.queue}#${e.jobId}`,e));for(const e of R){if(!e.queue||!e.jobId)continue;const t=`${e.queue}#${e.jobId}`;if(e.kind==="queue-process"&&!e.parent){const r=D.get(t);r&&r!==e&&r.start<=e.start&&(e.parent=r.id)}else if(e.kind==="queue-active"||e.kind==="queue-completed"||e.kind==="queue-failed"){const r=H.get(t);r&&r!==e&&r.start<=e.start&&(e.parent=r.id)}}let h=b;{const e=new Map;for(const r of h)r.kind==="fetch-start"&&e.set(r.id,r);const t=new Map;for(const r of h){if(r.kind!=="fetch-done"&&r.kind!=="fetch-error")continue;const o=[...e.values()].find(m=>m.requestId&&m.requestId===r.requestId)?.id,s=o?e.get(o):void 0;if(!s)continue;const c=r.fetchStartedAt??s.start;s.title=r.title,s.severity=r.severity,s.durationMs=r.durationMs??Math.max(0,r.t-c),s.kind="fetch",r.detail&&!s.detail&&(s.detail=r.detail),t.set(r.id,s.id)}if(t.size>0){h=h.filter(r=>!t.has(r.id));for(const r of h)for(r.fetchDoneId&&t.has(r.fetchDoneId)&&(r.fetchDoneId=t.get(r.fetchDoneId));r.parent&&t.has(r.parent);)r.parent=t.get(r.parent)}}const d=g(e=>!!e.commitId&&(e.kind==="render"||e.kind==="effect-run"||e.kind==="render-commit-truncated"),"commitMember"),w=g(e=>[...new Set(e)],"uniq"),S=g(e=>w(e.map(t=>t.component).filter(t=>!!t)),"named"),z=g(e=>e.length<=2?e.join(" + "):`${e[0]} + ${e.length-1} more`,"nameList"),L=g(e=>{const t=new Map;for(const r of e)r.component&&t.set(r.component,(t.get(r.component)??0)+1);return[...t].map(([r,o])=>o>1?`${r} \xD7${o}`:r).join(", ")},"tally"),V=g(e=>{if(!e)return"effect";const t=/([^/\\]+?):(\d+)(?::\d+)?$/.exec(e);return t?`${t[1]}:${t[2]}`:e},"shortHook"),X=g(e=>`${V(e.hook)}${e.cleanup?" (cleanup)":""}`,"hookLabel"),Z=g(e=>{const t=Math.min(...e.map(o=>o.start)),r=Math.max(...e.map(o=>o.start+(o.durationMs??0)));return{start:t,durationMs:r-t}},"extent"),ee=g(e=>(delete e.component,delete e.why,delete e.phase,delete e.hook,delete e.cleanup,delete e.renderTotal,delete e.renderPublished,e),"asGroup");function te(e){const t=e[0],r=e.filter(v=>v.kind==="render"),o=e.filter(v=>v.kind==="effect-run"),s=e.find(v=>v.kind==="render-commit-truncated"),c=[...r].sort((v,U)=>(U.durationMs??0)-(v.durationMs??0)),m=c.filter(v=>(v.why??[]).some(U=>U!=="parent-render")),$=w(m.flatMap(v=>(v.why??[]).filter(U=>U!=="parent-render"))),a=S(m),i=r.filter(v=>v.phase==="mount"),l=r.filter(v=>v.phase!=="mount"&&!m.includes(v)),k=c[0]?.component??"view",x=r.length>0&&i.length===r.length,I=$.length?$.join(" + "):void 0,N=x?"mount":"update",O=a.length?`view update \xB7 ${z(a)} \u2190 ${I}`:x?`view mount \xB7 ${k}`:`view update \xB7 ${k}`,ae=s?.renderTotal??r.length,he=[i.length?`mounted ${L(i)}`:"",l.length?`re-rendered ${L(l)}`:"",!a.length&&!x&&r.length?"trigger not recorded \u2014 every captured render came from its parent":"",s?`${ae} renders, ${s.renderPublished??r.length} recorded (per-commit cap)`:`${r.length} render${r.length===1?"":"s"}`,o.length?`${o.length} effect${o.length===1?"":"s"}: ${o.map(X).join(", ")}`:""].filter(Boolean).join(" \xB7 "),ie=S(r);return ee({...t,...Z(e),kind:"view-update",title:O,detail:he,commitId:t.commitId,viewPhase:N,...I?{cause:I}:{},causeComponents:a,components:ie,mounted:S(i),renderCount:ae,recordedRenders:r.length,effects:o.length,effectHooks:o.map(X),memberIds:e.map(v=>v.id),sig:`commit:${N}:${I??"-"}:${a.join(",")}:${ie.slice().sort().join(",")}`,severity:e.some(v=>v.severity==="error")?"error":t.severity})}A(te,"ct"),g(te,"viewUpdateNode");function re(e){const t=e[0],r=e.map(X);return ee({...t,...Z(e),kind:"commit-effects",title:e.length===1?`effect \xB7 ${r[0]}`:`effects \xB7 ${r[0]} + ${e.length-1} more`,detail:r.join(", "),commitId:t.commitId,effects:e.length,effectHooks:r,memberIds:e.map(o=>o.id),sig:`effects:${r.slice().sort().join(",")}`})}A(re,"dt"),g(re,"effectsNode");const me=g(e=>w(e.map(t=>t==="state-update"?"state update":t==="fetch-start"?"request":t==="flow-origin"?"interaction":"response")).join(" + "),"describeInputs");function oe(e,t){const r=e[0],o=e[e.length-1],s=e.length,c=r.detail,m=Math.min(...e.map(l=>l.start)),$=Math.max(...e.map(l=>l.start+(l.durationMs??0))),a=Math.round(e.reduce((l,k)=>l+(k.durationMs??0),0)/s*10)/10,i=Math.round(($-m)*10)/10;r.count=s,r.avgMs=a,r.wallMs=i,r.durationMs=$-m,r.items=e.slice(0,12).map(l=>({start:l.start-m,...l.durationMs!=null?{durationMs:l.durationMs}:{}})),r.memberIds=e.flatMap(l=>l.memberIds??[l.id]),r.title=`\xD7${s} ${r.title}`,e.some(l=>l.severity==="error")&&(r.severity="error"),r.kind==="view-update"?r.detail=[`${s} commits, ${t.length?`one per ${me(t)}`:"back to back"}`,`avg ${a}ms \xB7 wall ${i}ms`,c??""].filter(Boolean).join(" \xB7 "):r.kind==="state-update"&&c&&o.detail?r.detail=`${s} updates in a row \xB7 wall ${i}ms \xB7 first ${c} \xB7 last ${o.detail}`:r.detail=[`${s} in a row \xB7 wall ${i}ms`,c??""].filter(Boolean).join(" \xB7 ")}A(oe,"ft"),g(oe,"collapseRepeat");const J=[],Q=new Map,ne=new Set;for(let e=0;e<h.length;e++){if(ne.has(e))continue;const t=h[e];if(!d(t)){J.push(t);continue}const r=[e];for(let c=e+1;c<h.length;c++){const m=h[c];if(m.group==="browser"){if(!d(m)||m.commitId!==t.commitId)break;r.push(c)}}for(const c of r)ne.add(c);const o=r.map(c=>h[c]),s=o.some(c=>c.kind!=="effect-run")?te(o):re(o);for(const c of o)c.id!==s.id&&Q.set(c.id,s.id);J.push(s)}if(Q.size>0||J.some(e=>e.kind==="view-update"||e.kind==="commit-effects")){h=J;for(const e of h)for(;e.parent&&Q.has(e.parent);)e.parent=Q.get(e.parent)}{const e=new Map;for(const t of h)t.kind==="view-update"&&t.commitId&&!e.has(t.commitId)&&e.set(t.commitId,t);for(const t of h){if(t.kind!=="commit-effects"||t.parent||!t.commitId)continue;const r=e.get(t.commitId);r&&r.start<=t.start&&(t.parent=r.id)}}{const e=new Set(["state-update","fetch","fetch-start","fetch-done","fetch-error","flow-origin"]),t=h.map((a,i)=>a.group==="browser"?i:-1).filter(a=>a>=0),r=g(a=>`${a.kind}|${a.sig??a.title}`,"keyOf"),o=t.map(a=>r(h[a])),s=g((a,i,l)=>{for(let k=0;k<l;k++)if(o[a+k]!==o[i+k])return!1;return!0},"sameBlock"),c=g(a=>o[a].split("|")[0],"kindAt"),m=g(a=>{let i=0,l=1;for(let k=1;k<=4&&a+k<=o.length;k++){let x=1;for(;a+(x+1)*k<=o.length&&s(a,a+x*k,k);)x+=1;x>=3&&k*x>i*l&&(i=k,l=x)}return{k:i,r:l}},"bestBlockAt"),$=new Map;for(let a=0;a<o.length;){const{k:i,r:l}=m(a);if(i===0){a+=1;continue}const k=[...Array(i).keys()].find(I=>e.has(c(a+I)));if(k!==void 0&&k>0){const I=m(a+k);if(I.k===i&&I.r>=3&&I.r>=l-1){a+=k;continue}}const x=o.slice(a,a+i).map(I=>I.split("|")[0]).filter(I=>e.has(I));for(let I=0;I<i;I++){const N=[];for(let O=0;O<l;O++)N.push(h[t[a+I+O*i]]);oe(N,x);for(const O of N.slice(1))$.set(O.id,N[0].id)}a+=i*l}if($.size>0){h=h.filter(a=>!$.has(a.id));for(const a of h)for(;a.parent&&$.has(a.parent);)a.parent=$.get(a.parent)}}{const e=new Map(h.map(t=>[t.id,t]));for(const t of h){const r=new Set([t.id]);let o=t;for(;o?.parent;){if(r.has(o.parent)){o.parent=void 0;break}r.add(o.parent),o=e.get(o.parent)}}}const se=g(e=>{const t=new Map;for(const o of h){if(!o.sig||f(o)!==e||o.kind==="server-request"||o.kind==="gateway-request"||o.group==="browser")continue;const s=`${o.parent??"root"}|${o.kind}|${o.sig}`,c=t.get(s);c?c.push(o):t.set(s,[o])}const r=new Map;for(const o of t.values()){if(o.length<3)continue;o.sort((i,l)=>i.start-l.start);const s=o[0];let c=!1;for(let i=0;i+1<o.length;i++)if(o[i+1].start<o[i].start+(o[i].durationMs??0)){c=!0;break}const m=s.start,$=Math.max(...o.map(i=>i.start+(i.durationMs??0))),a=o.reduce((i,l)=>i+(l.durationMs??0),0);s.count=o.length,s.parallel=c,s.avgMs=Math.round(a/o.length*10)/10,s.wallMs=$-m,s.durationMs=$-m,s.title=`\xD7${o.length} ${s.title.replace(/\s*\((?:\d+ rows?, )?\d+ms\)$/,"")}`,s.detail=`${s.sig} \xD7${o.length} ${c?"\u2016 parallel":"\u21BB sequential loop"} \xB7 avg ${s.avgMs}ms \xB7 wall ${s.wallMs}ms`,s.items=o.slice(0,12).map(i=>({start:i.start-m,...i.durationMs!=null?{durationMs:i.durationMs}:{}})),s.memberIds=o.flatMap(i=>i.memberIds??[i.id]),o.some(i=>i.severity==="error")&&(s.severity="error");for(const i of o.slice(1))r.set(i.id,s.id)}if(r.size!==0){h=h.filter(o=>!r.has(o.id));for(const o of h)for(;o.parent&&r.has(o.parent);)o.parent=r.get(o.parent)}},"aggregatePass");se(!0),se(!1);for(const e of h){if(!f(e))continue;const t=e.durationMs??0;if(t<40)continue;const r=h.filter($=>$.parent===e.id&&($.durationMs??0)>0).map($=>[$.start,$.start+($.durationMs??0)]).sort(($,a)=>$[0]-a[0]);let o=0,s=-1,c=-1;for(const[$,a]of r)$>c?(c>s&&(o+=c-s),s=$,c=a):a>c&&(c=a);c>s&&(o+=c-s);const m=Math.max(0,t-o);(m>=50||m/t>=.7&&t>=40)&&(e.selfMs=m,e.detail=`\u26A0 cpu-heavy: ${m}ms self-time of ${t}ms${e.detail?" \xB7 "+e.detail:""}`)}return{events:h}}A(buildFlowDetail,"buildFlowDetail"),g(buildFlowDetail,"buildFlowDetail");
|
|
1
|
+
"use strict";var $e=Object.defineProperty;var S=(d,w)=>$e(d,"name",{value:w,configurable:!0});var ke=Object.defineProperty,h=S((d,w)=>ke(d,"name",{value:w,configurable:!0}),"p");import{redactDeep as ye,redactText as ue}from"./redact.js";import{SQL_ACCOUNT_KIND as qe,sqlAccountingOf as we}from"./flow-segments.js";import{boundedClone as Me}from"@lensmcp/protocol-types";export function areaOf(d){const{source:w,category:n,title:$,kind:y}=d;return w==="ai"?"ai":y?.startsWith("memory")||y==="db-query"?"data":w==="gateway"||y==="gateway-request"?"gateway":y==="queue-process"||y==="queue-active"||y==="queue-completed"||y==="queue-failed"?"services":w==="db"||w==="redis"||w==="queue"||y==="redis-op"||y?.startsWith("queue-")?"data":w==="external"||y==="egress"?"network":w==="nestjs"||w==="nest"?/(?:Repository|Repo|Dao|Store)\./.test($)?"data":"services":w==="valtio"?"state":n==="network"?"network":"browser"}S(areaOf,"areaOf"),h(areaOf,"areaOf");const de=240,Ie=new Set(["systemPrompt","prompt","prompts","messages","completion","completions","input","output","draft","text","content","result","arguments","params"]),ve=new Set(["tenantId","caller","userId","accountId","email","subject"]),je=8;function ce(d){const w=V(d,0);return Me(ye({redacted:!0,...w}),{rawByteLimit:16*1024}).value}S(ce,"It"),h(ce,"aiProjection");function V(d,w){if(typeof d=="string")return le(d);if(d===null||typeof d!="object")return d;if(w>=je)return;if(Array.isArray(d))return d.slice(0,32).map($=>V($,w+1));const n={};for(const[$,y]of Object.entries(d))if(!ve.has($)){if(Ie.has($)){typeof y=="string"?n[`${$}Chars`]=y.length:Array.isArray(y)?n[`${$}Count`]=y.length:y!=null&&(n[`${$}Present`]=!0);continue}n[$]=V(y,w+1)}return n}S(V,"tt"),h(V,"projectAi");function le(d){return d.length<=de?ue(d):`${ue(d.slice(0,de))}\u2026`}S(le,"qt"),h(le,"clipText");export function trimFlowEvent(d,w={}){const n=d.raw??{},$=n.render??{},y=n.span??{},R=n.request??{},f=h(s=>typeof s=="number"?s:void 0,"num"),C=n.loop??{},x=n.effect??{},T=f(n.durationMs)??f(y.durationMs)??f(R.durationMs)??f(C.durationMs)??f($.actualDurationMs)??f(x.durationMs),m=typeof n.kind=="string"?n.kind:void 0,N=m==="fetch-done"||m==="fetch-error",W=f($.commitTime),Y=m==="render"&&W!==void 0&&Math.abs(W-d.timestamp)<6e4,D=N?d.timestamp:Y?W:f(n.startedAt)??f(y.startTime)??f(R.startedAt)??f(C.startedAt)??(T!==void 0?d.timestamp-T:d.timestamp);let v;if(typeof n.query=="string"){const s=Array.isArray(n.params)?` [${n.params.join(", ")}]`:"",q=typeof n.result=="string"?` \u2192 ${n.result}`:"";v=n.query+s+q}else if(typeof n.command=="string"){const s=typeof n.value=="string"?` \u2192 ${n.value}`:n.hit===!1?" \u2192 (miss)":"";v=n.command+s}else if(m==="state-update"){const s=n.update??{};let q="";try{q=JSON.stringify(s.preview)}catch{}v=`${s.storeId??""}.${s.path??""} = ${q}`}else m==="queue-completed"&&typeof n.returned=="string"?v=`returned ${n.returned}`:m==="queue-enqueued"&&(v=`${n.job??"job"}#${n.jobId??"?"} \u2192 ${n.queue??""}${typeof n.data=="string"?" "+n.data:""}`);let F;if(m==="gateway-request")F=`${n.host??""}${String(n.url??"").split("?")[0]}`;else if(m==="fetch-start"||m==="fetch-done"||m==="fetch-error")try{const s=new URL(String(n.href??n.url??""));F=`${s.hostname}${s.pathname}`}catch{}let L;if(m==="gateway-request"&&Array.isArray(n.steps))L=n.steps.map(s=>{const q=[String(s.step)];return s.project&&q.push(String(s.project)),s.prefix&&q.push(String(s.prefix)),s.mode&&q.push(String(s.mode)+(s.ok===!1?" \u2717":s.ok===!0?" \u2713":"")),s.caller&&q.push("by "+String(s.caller)),s.pod&&q.push(String(s.pod)),typeof s.atMs=="number"&&q.push(`@${s.atMs}ms`),q.join(" ")}),v=L.map(s=>s.replace(/ @\d+ms$/,"")).join(" \u2192 ");else if(m==="egress")v=`${n.method??"GET"} ${n.host??""} \u2192 ${n.status??"?"}`;else if(d.source==="ai"){if(m==="ai-call"){const s=n.usage??{},q=s.promptTokens!==void 0||s.completionTokens!==void 0?` \xB7 ${f(s.promptTokens)??"?"}\u2192${f(s.completionTokens)??"?"} tok`:"",B=n.status!==void 0?` \xB7 ${String(n.status)}`:n.error?" \xB7 failed":"",O=f(n.toolCallCount)?` \xB7 ${f(n.toolCallCount)} tool call${f(n.toolCallCount)===1?"":"s"}`:"",U=f(n.durationMs)!==void 0?` \xB7 ${(f(n.durationMs)/1e3).toFixed(1)}s`:"";v=`${n.provider??""}/${n.model??"?"}${q}${B}${O}${U}${n.stream===!0?" \xB7 stream":""}`}else if(m==="ai-round"){const s=n.model??{},q=n.usage??{},B=Array.isArray(n.toolCalls)?n.toolCalls:[],O=`${f(q.promptTokens)??"?"}\u2192${f(q.completionTokens)??"?"} tok`,U=B.length?` \xB7 ${B.length} tool call${B.length===1?"":"s"}`:"",_=f(n.durationMs)!==void 0?` \xB7 ${(f(n.durationMs)/1e3).toFixed(1)}s`:"";v=`${s.provider??""}/${s.model??""} \xB7 ${O}${U}${_}`}else if(m==="ai-turn-start"){const s=Array.isArray(n.tools)?n.tools:[];v=`skill ${n.skill??""} \xB7 ${n.output??""}${s.length?" \xB7 tools: "+s.join(", "):""}`}else if(m==="ai-tool-call"){const s=n.call??{},q=n.result??{},B=f(n.durationMs)!==void 0?` \xB7 ${(f(n.durationMs)/1e3).toFixed(1)}s`:"";v=`${s.name??"tool"} ${q.error!=null?"\u2717 "+String(q.error):"\u2713"}${B}`}else if(m==="ai-turn-end"){const s=n.usage??{},q=f(n.rounds)!==void 0?` \xB7 ${f(n.rounds)} round${f(n.rounds)===1?"":"s"}`:"",B=f(n.durationMs)!==void 0?` \xB7 ${(f(n.durationMs)/1e3).toFixed(1)}s`:"";v=`${n.ok===!1?"FAILED":"ok"} \xB7 ${f(s.promptTokens)??"?"}+${f(s.completionTokens)??"?"} tok${q}${B}`}}let b;m==="db-query"&&typeof n.query=="string"?b=n.query:m==="redis-op"?b=`${n.op??""} ${String(n.key??"").replace(/\d+/g,"#")}`:m==="egress"?b=`${n.method??"GET"} ${n.host??""}`:m==="ai-call"?b=`ai ${n.provider??""} ${n.model??""} ${n.api??""}`:m==="queue-enqueued"?b=`enqueue ${n.queue??""}`:m==="span"&&typeof y.name=="string"?b=y.name:m==="memory-mutation"&&(b=d.title);const A=d.context??{},G=$.commitId??x.commitId??n.commitId,H=Array.isArray($.why)?$.why.map(s=>typeof s?.type=="string"?s.type:void 0).filter(s=>!!s):[];return{id:d.id,t:d.timestamp,start:D,...b?{sig:b}:{},...F?{urlKey:F}:{},...typeof n.queue=="string"?{queue:n.queue}:{},...n.jobId!=null?{jobId:String(n.jobId)}:{},source:d.source,category:d.category,severity:d.severity,title:d.title,kind:m,area:areaOf({source:d.source,category:d.category,title:d.title,kind:m}),...typeof y.role=="string"?{role:y.role}:{},...typeof A.requestId=="string"?{requestId:A.requestId}:{},...typeof A.parentRequestId=="string"?{parentRequestId:A.parentRequestId}:{},...typeof G=="string"?{commitId:G}:{},...typeof $.componentName=="string"?{component:$.componentName}:{},...typeof $.phase=="string"?{phase:$.phase}:{},...H.length?{why:H}:{},...m==="effect-run"&&typeof x.hookLogicalId=="string"?{hook:x.hookLogicalId}:{},...m==="effect-run"&&x.cleanupRan===!0?{cleanup:!0}:{},...m==="render-commit-truncated"?{renderTotal:f(n.total),renderPublished:f(n.published)}:{},...typeof A.projectName=="string"?{project:A.projectName}:typeof n.project=="string"?{project:n.project}:{},...T!==void 0?{durationMs:T}:{},...N&&f(n.startedAt)!==void 0?{fetchStartedAt:f(n.startedAt)}:{},...d.source==="ai"?{ai:w.includeRaw?n:ce(n)}:{},...v?{detail:v}:{},...L?{steps:L}:{}}}S(trimFlowEvent,"trimFlowEvent"),h(trimFlowEvent,"trimFlowEvent");const fe=1;function pe(d){return d==="flow-origin"?0:d?.endsWith("-start")?1:d==="gateway-request"?2:d==="server-request"?3:d==="span"?4:d==="render"||d==="render-commit-truncated"?6:d==="effect-run"?7:5}S(pe,"Mt"),h(pe,"kindRank");function K(d){return d.kind==="span"&&d.role==="controller"?3.5:d.kind==="queue-process"?3:pe(d.kind)}S(K,"J"),h(K,"causalRank");export function buildFlowDetail(d,w={}){const n=we(d),$=d.slice().filter(e=>{const r=e.raw??{},t=typeof r.kind=="string"?r.kind:"";if(t==="subscription-added"||t==="subscription-removed"||t==="hook-run"||t==="store-created"||t==="singleton-instance"||t===qe)return!1;const o=r.span;return!(typeof o?.name=="string"&&o.name.startsWith("nest.request "))}).map(e=>trimFlowEvent(e,w)),y=new Set;for(let e=$.length-1;e>=0;e--)y.has($[e].id)?$.splice(e,1):y.add($[e].id);const R=new Map;for(const e of $)(e.kind==="gateway-request"||e.kind==="queue-process")&&e.requestId&&!R.has(e.requestId)&&R.set(e.requestId,e.start);const f=h(e=>e.source!=="react"&&e.source!=="valtio"&&e.requestId&&R.has(e.requestId)?R.get(e.requestId):e.start,"anchorOf");$.sort((e,r)=>f(e)-f(r)||e.start-r.start||K(e)-K(r)||(r.durationMs??0)-(e.durationMs??0)||e.t-r.t);const C=h(e=>(e.kind==="span"||e.kind==="server-request"||e.kind==="gateway-request"||e.kind==="queue-process")&&(e.durationMs??0)>0,"spanLike"),x=$;for(const e of x)e.group=e.source==="react"||e.source==="valtio"||e.source==="client-runtime"||e.source==="visual"?"browser":"server";const T=new Map;for(const e of x)if((e.kind==="gateway-request"||e.kind==="queue-process")&&e.requestId&&e.project){const r=T.get(e.requestId)??[];r.push({project:e.project,start:e.start,end:e.start+(e.durationMs??0)}),T.set(e.requestId,r)}const m=h((e,r)=>{const t=T.get(e);if(t?.length)return t.every(o=>o.project===t[0].project)?t[0].project:t.filter(o=>o.start<=r+1&&o.end>=r-1).sort((o,i)=>i.start-o.start)[0]?.project},"projectOf");for(const e of x)if(!e.project&&e.source==="nestjs"&&e.requestId){const r=m(e.requestId,e.start);r&&(e.project=r)}for(const e of x){let r;const t=e.requestId;for(const o of x){if(o===e||!C(o)||o.group!==e.group||o.requestId!==t)continue;const i=o.project,c=e.project;if(o.kind!=="gateway-request"&&e.kind!=="gateway-request"&&i!==void 0&&c!==void 0&&i!==c)continue;const p=o.durationMs??0,g=e.durationMs??0,a=Math.abs(o.start-e.start)<=fe,u=K(o),l=K(e),k=o.start<=e.start;if(!(o.start+p>e.start&&(k||a&&u<l)&&(!a||(u!==l?u<l:k&&p>g))))continue;const j=r?K(r):-1,M=r?Math.abs(o.start-r.start)<=fe:!1;(!r||!M&&o.start>r.start||M&&u>j||M&&u===j&&(o.durationMs??0)<(r.durationMs??0))&&(r=o)}r&&(e.parent=r.id)}const N=new Map;for(const e of x){if(!e.requestId)continue;const r=N.get(e.requestId);r?r.push(e):N.set(e.requestId,[e])}const W=new Map(x.map(e=>[e.id,e])),Y=h((e,r)=>{const t=(N.get(e)??[]).filter(o=>C(o)&&o.start<=r&&o.start+(o.durationMs??0)>=r);if(t.length!==0)return t.sort((o,i)=>i.start-o.start||(o.durationMs??0)-(i.durationMs??0)),t[0]},"callerNodeFor");for(const e of x){if(!e.parentRequestId||e.kind!=="server-request"&&e.kind!=="gateway-request")continue;const r=e.parent?W.get(e.parent):void 0;if(r&&r.requestId===e.requestId)continue;const t=Y(e.parentRequestId,e.start);t&&t!==e&&t.start<=e.start&&(e.parent=t.id)}const D=x,v=new Map;for(const e of D)e.kind==="gateway-request"&&e.requestId&&!v.has(e.requestId)&&v.set(e.requestId,e);for(const e of D)if(e.group==="server"&&!e.parent&&e.requestId&&e.kind!=="gateway-request"){const r=v.get(e.requestId);r&&r!==e&&r.start<=e.start&&(e.parent=r.id)}const F=new Set,L=new Set,b=[];for(const e of D){if(e.kind!=="gateway-request")continue;if(!e.requestId){b.push(e);continue}const r=N.get(e.requestId)??[];if(r.filter(i=>i.kind==="gateway-request").sort((i,c)=>i.start-c.start)[0]!==e)continue;const t=r.find(i=>i.kind==="fetch-start"),o=r.find(i=>i.kind==="fetch-done"||i.kind==="fetch-error");if(!t&&!o){b.push(e);continue}t&&(e.fetchStartId=t.id,F.add(t.id)),o&&(e.fetchDoneId=o.id,L.add(o.id))}const A=new Map;for(const e of D){if(!e.urlKey)continue;const r=A.get(e.urlKey)??{gws:[],starts:[],dones:[]};e.kind==="gateway-request"?b.includes(e)&&r.gws.push(e):e.kind==="fetch-start"?F.has(e.id)||r.starts.push(e):(e.kind==="fetch-done"||e.kind==="fetch-error")&&(L.has(e.id)||r.dones.push(e)),A.set(e.urlKey,r)}for(const{gws:e,starts:r,dones:t}of A.values()){e.sort((i,c)=>i.start-c.start),r.sort((i,c)=>i.start-c.start);const o=new Set;e.forEach((i,c)=>{const p=r[c];p&&(i.fetchStartId=p.id);const g=p?p.t:i.start;let a,u=1/0;for(const l of t){if(o.has(l.id))continue;const k=l.fetchStartedAt??l.start-(l.durationMs??0),j=Math.abs(k-g);j<u&&(u=j,a=l)}a&&(i.fetchDoneId=a.id,o.add(a.id))})}const G=new Map,H=new Map;for(const e of D)!e.queue||!e.jobId||(e.kind==="queue-enqueued"&&G.set(`${e.queue}#${e.jobId}`,e),e.kind==="queue-process"&&H.set(`${e.queue}#${e.jobId}`,e));for(const e of D){if(!e.queue||!e.jobId)continue;const r=`${e.queue}#${e.jobId}`;if(e.kind==="queue-process"&&!e.parent){const t=G.get(r);t&&t!==e&&t.start<=e.start&&(e.parent=t.id)}else if(e.kind==="queue-active"||e.kind==="queue-completed"||e.kind==="queue-failed"){const t=H.get(r);t&&t!==e&&t.start<=e.start&&(e.parent=t.id)}}let s=x;{const e=new Map;for(const t of s)t.kind==="fetch-start"&&e.set(t.id,t);const r=new Map;for(const t of s){if(t.kind!=="fetch-done"&&t.kind!=="fetch-error")continue;const o=[...e.values()].find(p=>p.requestId&&p.requestId===t.requestId)?.id,i=o?e.get(o):void 0;if(!i)continue;const c=t.fetchStartedAt??i.start;i.title=t.title,i.severity=t.severity,i.durationMs=t.durationMs??Math.max(0,t.t-c),i.kind="fetch",t.detail&&!i.detail&&(i.detail=t.detail),r.set(t.id,i.id)}if(r.size>0){s=s.filter(t=>!r.has(t.id));for(const t of s)for(t.fetchDoneId&&r.has(t.fetchDoneId)&&(t.fetchDoneId=r.get(t.fetchDoneId));t.parent&&r.has(t.parent);)t.parent=r.get(t.parent)}}const q=h(e=>!!e.commitId&&(e.kind==="render"||e.kind==="effect-run"||e.kind==="render-commit-truncated"),"commitMember"),B=h(e=>[...new Set(e)],"uniq"),O=h(e=>B(e.map(r=>r.component).filter(r=>!!r)),"named"),U=h(e=>e.length<=2?e.join(" + "):`${e[0]} + ${e.length-1} more`,"nameList"),_=h(e=>{const r=new Map;for(const t of e)t.component&&r.set(t.component,(r.get(t.component)??0)+1);return[...r].map(([t,o])=>o>1?`${t} \xD7${o}`:t).join(", ")},"tally"),me=h(e=>{if(!e)return"effect";const r=/([^/\\]+?):(\d+)(?::\d+)?$/.exec(e);return r?`${r[1]}:${r[2]}`:e},"shortHook"),X=h(e=>`${me(e.hook)}${e.cleanup?" (cleanup)":""}`,"hookLabel"),Z=h(e=>{const r=Math.min(...e.map(o=>o.start)),t=Math.max(...e.map(o=>o.start+(o.durationMs??0)));return{start:r,durationMs:t-r}},"extent"),ee=h(e=>(delete e.component,delete e.why,delete e.phase,delete e.hook,delete e.cleanup,delete e.renderTotal,delete e.renderPublished,e),"asGroup");function te(e){const r=e[0],t=e.filter(I=>I.kind==="render"),o=e.filter(I=>I.kind==="effect-run"),i=e.find(I=>I.kind==="render-commit-truncated"),c=[...t].sort((I,z)=>(z.durationMs??0)-(I.durationMs??0)),p=c.filter(I=>(I.why??[]).some(z=>z!=="parent-render")),g=B(p.flatMap(I=>(I.why??[]).filter(z=>z!=="parent-render"))),a=O(p),u=t.filter(I=>I.phase==="mount"),l=t.filter(I=>I.phase!=="mount"&&!p.includes(I)),k=c[0]?.component??"view",j=t.length>0&&u.length===t.length,M=g.length?g.join(" + "):void 0,P=j?"mount":"update",E=a.length?`view update \xB7 ${U(a)} \u2190 ${M}`:j?`view mount \xB7 ${k}`:`view update \xB7 ${k}`,ie=i?.renderTotal??t.length,ge=[u.length?`mounted ${_(u)}`:"",l.length?`re-rendered ${_(l)}`:"",!a.length&&!j&&t.length?"trigger not recorded \u2014 every captured render came from its parent":"",i?`${ie} renders, ${i.renderPublished??t.length} recorded (per-commit cap)`:`${t.length} render${t.length===1?"":"s"}`,o.length?`${o.length} effect${o.length===1?"":"s"}: ${o.map(X).join(", ")}`:""].filter(Boolean).join(" \xB7 "),ae=O(t);return ee({...r,...Z(e),kind:"view-update",title:E,detail:ge,commitId:r.commitId,viewPhase:P,...M?{cause:M}:{},causeComponents:a,components:ae,mounted:O(u),renderCount:ie,recordedRenders:t.length,effects:o.length,effectHooks:o.map(X),memberIds:e.map(I=>I.id),sig:`commit:${P}:${M??"-"}:${a.join(",")}:${ae.slice().sort().join(",")}`,severity:e.some(I=>I.severity==="error")?"error":r.severity})}S(te,"dt"),h(te,"viewUpdateNode");function re(e){const r=e[0],t=e.map(X);return ee({...r,...Z(e),kind:"commit-effects",title:e.length===1?`effect \xB7 ${t[0]}`:`effects \xB7 ${t[0]} + ${e.length-1} more`,detail:t.join(", "),commitId:r.commitId,effects:e.length,effectHooks:t,memberIds:e.map(o=>o.id),sig:`effects:${t.slice().sort().join(",")}`})}S(re,"ut"),h(re,"effectsNode");const he=h(e=>B(e.map(r=>r==="state-update"?"state update":r==="fetch-start"?"request":r==="flow-origin"?"interaction":"response")).join(" + "),"describeInputs");function oe(e,r){const t=e[0],o=e[e.length-1],i=e.length,c=t.detail,p=Math.min(...e.map(l=>l.start)),g=Math.max(...e.map(l=>l.start+(l.durationMs??0))),a=Math.round(e.reduce((l,k)=>l+(k.durationMs??0),0)/i*10)/10,u=Math.round((g-p)*10)/10;t.count=i,t.avgMs=a,t.wallMs=u,t.durationMs=g-p,t.items=e.slice(0,12).map(l=>({start:l.start-p,...l.durationMs!=null?{durationMs:l.durationMs}:{}})),t.memberIds=e.flatMap(l=>l.memberIds??[l.id]),t.title=`\xD7${i} ${t.title}`,e.some(l=>l.severity==="error")&&(t.severity="error"),t.kind==="view-update"?t.detail=[`${i} commits, ${r.length?`one per ${he(r)}`:"back to back"}`,`avg ${a}ms \xB7 wall ${u}ms`,c??""].filter(Boolean).join(" \xB7 "):t.kind==="state-update"&&c&&o.detail?t.detail=`${i} updates in a row \xB7 wall ${u}ms \xB7 first ${c} \xB7 last ${o.detail}`:t.detail=[`${i} in a row \xB7 wall ${u}ms`,c??""].filter(Boolean).join(" \xB7 ")}S(oe,"lt"),h(oe,"collapseRepeat");const J=[],Q=new Map,ne=new Set;for(let e=0;e<s.length;e++){if(ne.has(e))continue;const r=s[e];if(!q(r)){J.push(r);continue}const t=[e];for(let c=e+1;c<s.length;c++){const p=s[c];if(p.group==="browser"){if(!q(p)||p.commitId!==r.commitId)break;t.push(c)}}for(const c of t)ne.add(c);const o=t.map(c=>s[c]),i=o.some(c=>c.kind!=="effect-run")?te(o):re(o);for(const c of o)c.id!==i.id&&Q.set(c.id,i.id);J.push(i)}if(Q.size>0||J.some(e=>e.kind==="view-update"||e.kind==="commit-effects")){s=J;for(const e of s)for(;e.parent&&Q.has(e.parent);)e.parent=Q.get(e.parent)}{const e=new Map;for(const r of s)r.kind==="view-update"&&r.commitId&&!e.has(r.commitId)&&e.set(r.commitId,r);for(const r of s){if(r.kind!=="commit-effects"||r.parent||!r.commitId)continue;const t=e.get(r.commitId);t&&t.start<=r.start&&(r.parent=t.id)}}{const e=new Set(["state-update","fetch","fetch-start","fetch-done","fetch-error","flow-origin"]),r=s.map((a,u)=>a.group==="browser"?u:-1).filter(a=>a>=0),t=h(a=>`${a.kind}|${a.sig??a.title}`,"keyOf"),o=r.map(a=>t(s[a])),i=h((a,u,l)=>{for(let k=0;k<l;k++)if(o[a+k]!==o[u+k])return!1;return!0},"sameBlock"),c=h(a=>o[a].split("|")[0],"kindAt"),p=h(a=>{let u=0,l=1;for(let k=1;k<=4&&a+k<=o.length;k++){let j=1;for(;a+(j+1)*k<=o.length&&i(a,a+j*k,k);)j+=1;j>=3&&k*j>u*l&&(u=k,l=j)}return{k:u,r:l}},"bestBlockAt"),g=new Map;for(let a=0;a<o.length;){const{k:u,r:l}=p(a);if(u===0){a+=1;continue}const k=[...Array(u).keys()].find(M=>e.has(c(a+M)));if(k!==void 0&&k>0){const M=p(a+k);if(M.k===u&&M.r>=3&&M.r>=l-1){a+=k;continue}}const j=o.slice(a,a+u).map(M=>M.split("|")[0]).filter(M=>e.has(M));for(let M=0;M<u;M++){const P=[];for(let E=0;E<l;E++)P.push(s[r[a+M+E*u]]);oe(P,j);for(const E of P.slice(1))g.set(E.id,P[0].id)}a+=u*l}if(g.size>0){s=s.filter(a=>!g.has(a.id));for(const a of s)for(;a.parent&&g.has(a.parent);)a.parent=g.get(a.parent)}}{const e=new Map(s.map(r=>[r.id,r]));for(const r of s){const t=new Set([r.id]);let o=r;for(;o?.parent;){if(t.has(o.parent)){o.parent=void 0;break}t.add(o.parent),o=e.get(o.parent)}}}const se=h(e=>{const r=new Map;for(const o of s){if(!o.sig||C(o)!==e||o.kind==="server-request"||o.kind==="gateway-request"||o.group==="browser")continue;const i=`${o.parent??"root"}|${o.kind}|${o.sig}`,c=r.get(i);c?c.push(o):r.set(i,[o])}const t=new Map;for(const o of r.values()){if(o.length<3)continue;o.sort((u,l)=>u.start-l.start);const i=o[0];let c=!1;for(let u=0;u+1<o.length;u++)if(o[u+1].start<o[u].start+(o[u].durationMs??0)){c=!0;break}const p=i.start,g=Math.max(...o.map(u=>u.start+(u.durationMs??0))),a=o.reduce((u,l)=>u+(l.durationMs??0),0);i.count=o.length,i.parallel=c,i.avgMs=Math.round(a/o.length*10)/10,i.wallMs=g-p,i.durationMs=g-p,i.title=`\xD7${o.length} ${i.title.replace(/\s*\((?:\d+ rows?, )?\d+ms\)$/,"")}`,i.detail=`${i.sig} \xD7${o.length} ${c?"\u2016 parallel":"\u21BB sequential loop"} \xB7 avg ${i.avgMs}ms \xB7 wall ${i.wallMs}ms`,i.items=o.slice(0,12).map(u=>({start:u.start-p,...u.durationMs!=null?{durationMs:u.durationMs}:{}})),i.memberIds=o.flatMap(u=>u.memberIds??[u.id]),o.some(u=>u.severity==="error")&&(i.severity="error");for(const u of o.slice(1))t.set(u.id,i.id)}if(t.size!==0){s=s.filter(o=>!t.has(o.id));for(const o of s)for(;o.parent&&t.has(o.parent);)o.parent=t.get(o.parent)}},"aggregatePass");se(!0),se(!1);for(const e of s){if(!C(e))continue;const r=e.durationMs??0;if(r<40)continue;const t=s.filter(g=>g.parent===e.id&&(g.durationMs??0)>0).map(g=>[g.start,g.start+(g.durationMs??0)]).sort((g,a)=>g[0]-a[0]);let o=0,i=-1,c=-1;for(const[g,a]of t)g>c?(c>i&&(o+=c-i),i=g,c=a):a>c&&(c=a);c>i&&(o+=c-i);const p=Math.max(0,r-o);(p>=50||p/r>=.7&&r>=40)&&(e.selfMs=p,e.detail=`\u26A0 cpu-heavy: ${p}ms self-time of ${r}ms${e.detail?" \xB7 "+e.detail:""}`)}return{events:s,...n?{sql:n}:{}}}S(buildFlowDetail,"buildFlowDetail"),h(buildFlowDetail,"buildFlowDetail");
|
package/lib/redact.d.ts
CHANGED
|
@@ -62,7 +62,7 @@ export declare const MAX_URL_CHARS = 1024;
|
|
|
62
62
|
* appears (`cacheKey`, `publicKey`, `wsKey`). A redactor guessing at field names is the weaker
|
|
63
63
|
* mechanism anyway: a producer that holds key material should emit a non-reversible hash instead of
|
|
64
64
|
* hoping the name looks suspicious — see `keyPreview` in `@lensmcp/memory-tracker`
|
|
65
|
-
* (issues/codex-security
|
|
65
|
+
* (issues/closed/codex-security-40).
|
|
66
66
|
*/
|
|
67
67
|
export declare function isSecretParam(name: string): boolean;
|
|
68
68
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lensmcp/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.24.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
}
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@lensmcp/protocol-types": "1.
|
|
37
|
+
"@lensmcp/protocol-types": "1.24.0",
|
|
38
38
|
"tslib": "^2.3.0"
|
|
39
39
|
},
|
|
40
40
|
"license": "Apache-2.0",
|