@lensmcp/core 1.22.4 → 1.22.6

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/index.d.ts CHANGED
@@ -7,6 +7,7 @@ export * from './lib/graph-store.js';
7
7
  export * from './lib/fingerprint.js';
8
8
  export * from './lib/reducer-registry.js';
9
9
  export * from './lib/story.js';
10
+ export * from './lib/flow-shape.js';
10
11
  export * from './lib/patterns.js';
11
12
  export * from './lib/thresholds.js';
12
13
  export * from './lib/graph-from-events.js';
package/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";export*from"./lib/common.js";export*from"./lib/ulid.js";export*from"./lib/event-bus.js";export*from"./lib/event-redactor.js";export*from"./lib/resource-store.js";export*from"./lib/graph-store.js";export*from"./lib/fingerprint.js";export*from"./lib/reducer-registry.js";export*from"./lib/story.js";export*from"./lib/patterns.js";export*from"./lib/thresholds.js";export*from"./lib/graph-from-events.js";
1
+ "use strict";export*from"./lib/common.js";export*from"./lib/ulid.js";export*from"./lib/event-bus.js";export*from"./lib/event-redactor.js";export*from"./lib/resource-store.js";export*from"./lib/graph-store.js";export*from"./lib/fingerprint.js";export*from"./lib/reducer-registry.js";export*from"./lib/story.js";export*from"./lib/flow-shape.js";export*from"./lib/patterns.js";export*from"./lib/thresholds.js";export*from"./lib/graph-from-events.js";
@@ -0,0 +1,100 @@
1
+ import { type BaseEvent } from '@lensmcp/protocol-types';
2
+ /**
3
+ * Architectural area of one flow event — the swimlane it renders in, and a
4
+ * classification agents can reuse. Order mirrors the request's journey:
5
+ * browser → state → network → gateway → services → data.
6
+ */
7
+ export type FlowArea = 'browser' | 'state' | 'network' | 'gateway' | 'services' | 'data' | 'ai';
8
+ export declare function areaOf(event: {
9
+ source: string;
10
+ category: string;
11
+ title: string;
12
+ kind?: string | undefined;
13
+ }): FlowArea;
14
+ /** One chart-ready node per flow event: area-typed, titled, with start/duration/payload. */
15
+ export interface FlowShapeOptions {
16
+ /**
17
+ * Inline an AI turn's full `raw` payload — prompt messages, completion text, tool-call inputs and
18
+ * outputs. Off by default; `/api/flow` requires `?raw=1` AND a loopback-bound dashboard.
19
+ */
20
+ includeRaw?: boolean;
21
+ }
22
+ export declare function trimFlowEvent(event: BaseEvent, opts?: FlowShapeOptions): {
23
+ steps?: string[] | undefined;
24
+ detail?: string | undefined;
25
+ ai?: Record<string, unknown> | undefined;
26
+ fetchStartedAt?: number | undefined;
27
+ durationMs?: number | undefined;
28
+ project?: string | undefined;
29
+ why?: string[] | undefined;
30
+ phase?: string | undefined;
31
+ component?: string | undefined;
32
+ commitId?: string | undefined;
33
+ parentRequestId?: string | undefined;
34
+ requestId?: string | undefined;
35
+ role?: string | undefined;
36
+ source: "nx" | "vite" | "typescript" | "eslint" | "rollup" | "client-runtime" | "chrome" | "react" | "valtio" | "nestjs" | "nextjs" | "db" | "redis" | "queue" | "memory" | "visual" | "perf" | "security" | "gateway" | "external" | "ai";
37
+ category: "db" | "memory" | "visual" | "security" | "ai" | "build" | "lint" | "typecheck" | "test" | "runtime" | "render" | "trace" | "state" | "network" | "backend" | "performance" | "deps" | "cluster";
38
+ severity: "error" | "debug" | "info" | "warning" | "fatal";
39
+ title: string;
40
+ kind: string | undefined;
41
+ area: FlowArea;
42
+ jobId?: string | undefined;
43
+ queue?: string | undefined;
44
+ urlKey?: string | undefined;
45
+ sig?: string | undefined;
46
+ id: string;
47
+ t: number;
48
+ start: number;
49
+ };
50
+ /**
51
+ * The pure flow transform: raw correlated events → ordered, parented,
52
+ * aggregated display nodes. No I/O, no server — see module header.
53
+ */
54
+ export declare function buildFlowDetail(rawEvents: BaseEvent[], opts?: FlowShapeOptions): {
55
+ events: ({
56
+ steps?: string[] | undefined;
57
+ detail?: string | undefined;
58
+ ai?: Record<string, unknown> | undefined;
59
+ fetchStartedAt?: number | undefined;
60
+ durationMs?: number | undefined;
61
+ project?: string | undefined;
62
+ why?: string[] | undefined;
63
+ phase?: string | undefined;
64
+ component?: string | undefined;
65
+ commitId?: string | undefined;
66
+ parentRequestId?: string | undefined;
67
+ requestId?: string | undefined;
68
+ role?: string | undefined;
69
+ source: "nx" | "vite" | "typescript" | "eslint" | "rollup" | "client-runtime" | "chrome" | "react" | "valtio" | "nestjs" | "nextjs" | "db" | "redis" | "queue" | "memory" | "visual" | "perf" | "security" | "gateway" | "external" | "ai";
70
+ category: "db" | "memory" | "visual" | "security" | "ai" | "build" | "lint" | "typecheck" | "test" | "runtime" | "render" | "trace" | "state" | "network" | "backend" | "performance" | "deps" | "cluster";
71
+ severity: "error" | "debug" | "info" | "warning" | "fatal";
72
+ title: string;
73
+ kind: string | undefined;
74
+ area: FlowArea;
75
+ jobId?: string | undefined;
76
+ queue?: string | undefined;
77
+ urlKey?: string | undefined;
78
+ sig?: string | undefined;
79
+ id: string;
80
+ t: number;
81
+ start: number;
82
+ } & {
83
+ parent?: string;
84
+ group?: string;
85
+ } & {
86
+ sig?: string;
87
+ count?: number;
88
+ parallel?: boolean;
89
+ avgMs?: number;
90
+ wallMs?: number;
91
+ items?: Array<{
92
+ start: number;
93
+ durationMs?: number;
94
+ }>;
95
+ selfMs?: number;
96
+ detail?: string;
97
+ /** Every event id this node stands for, so a collapsed node can be opened again. */
98
+ memberIds?: string[];
99
+ })[];
100
+ };
@@ -0,0 +1 @@
1
+ "use strict";var Z=Object.defineProperty;var x=(a,m)=>Z(a,"name",{value:m,configurable:!0});var _=Object.defineProperty,M=x((a,m)=>_(a,"name",{value:m,configurable:!0}),"k");import{redactDeep as tt,redactText as J}from"./redact.js";import{boundedClone as et}from"@lensmcp/protocol-types";export function areaOf(a){const{source:m,category:r,title:y,kind:c}=a;return m==="ai"?"ai":c?.startsWith("memory")||c==="db-query"?"data":m==="gateway"||c==="gateway-request"?"gateway":c==="queue-process"||c==="queue-active"||c==="queue-completed"||c==="queue-failed"?"services":m==="db"||m==="redis"||m==="queue"||c==="redis-op"||c?.startsWith("queue-")?"data":m==="external"||c==="egress"?"network":m==="nestjs"||m==="nest"?/(?:Repository|Repo|Dao|Store)\./.test(y)?"data":"services":m==="valtio"?"state":r==="network"?"network":"browser"}x(areaOf,"areaOf"),M(areaOf,"areaOf");const U=240,rt=new Set(["systemPrompt","prompt","prompts","messages","completion","completions","input","output","draft","text","content","result","arguments","params"]),st=new Set(["tenantId","caller","userId","accountId","email","subject"]),nt=8;function H(a){const m=z(a,0);return et(tt({redacted:!0,...m}),{rawByteLimit:16*1024}).value}x(H,"tt"),M(H,"aiProjection");function z(a,m){if(typeof a=="string")return Q(a);if(a===null||typeof a!="object")return a;if(m>=nt)return;if(Array.isArray(a))return a.slice(0,32).map(y=>z(y,m+1));const r={};for(const[y,c]of Object.entries(a))if(!st.has(y)){if(rt.has(y)){typeof c=="string"?r[`${y}Chars`]=c.length:Array.isArray(c)?r[`${y}Count`]=c.length:c!=null&&(r[`${y}Present`]=!0);continue}r[y]=z(c,m+1)}return r}x(z,"W"),M(z,"projectAi");function Q(a){return a.length<=U?J(a):`${J(a.slice(0,U))}\u2026`}x(Q,"et"),M(Q,"clipText");export function trimFlowEvent(a,m={}){const r=a.raw??{},y=r.render??{},c=r.span??{},C=r.request??{},l=M(o=>typeof o=="number"?o:void 0,"num"),I=r.loop??{},B=l(r.durationMs)??l(c.durationMs)??l(C.durationMs)??l(I.durationMs)??l(y.actualDurationMs),p=typeof r.kind=="string"?r.kind:void 0,L=p==="fetch-done"||p==="fetch-error",G=L?a.timestamp:l(r.startedAt)??l(c.startTime)??l(C.startedAt)??l(I.startedAt)??(B!==void 0?a.timestamp-B:a.timestamp);let w;if(typeof r.query=="string"){const o=Array.isArray(r.params)?` [${r.params.join(", ")}]`:"",g=typeof r.result=="string"?` \u2192 ${r.result}`:"";w=r.query+o+g}else if(typeof r.command=="string"){const o=typeof r.value=="string"?` \u2192 ${r.value}`:r.hit===!1?" \u2192 (miss)":"";w=r.command+o}else if(p==="state-update"){const o=r.update??{};let g="";try{g=JSON.stringify(o.preview)}catch{}w=`${o.storeId??""}.${o.path??""} = ${g}`}else p==="queue-completed"&&typeof r.returned=="string"?w=`returned ${r.returned}`:p==="queue-enqueued"&&(w=`${r.job??"job"}#${r.jobId??"?"} \u2192 ${r.queue??""}${typeof r.data=="string"?" "+r.data:""}`);let S;if(p==="gateway-request")S=`${r.host??""}${String(r.url??"").split("?")[0]}`;else if(p==="fetch-start"||p==="fetch-done"||p==="fetch-error")try{const o=new URL(String(r.href??r.url??""));S=`${o.hostname}${o.pathname}`}catch{}let D;if(p==="gateway-request"&&Array.isArray(r.steps))D=r.steps.map(o=>{const g=[String(o.step)];return o.project&&g.push(String(o.project)),o.prefix&&g.push(String(o.prefix)),o.mode&&g.push(String(o.mode)+(o.ok===!1?" \u2717":o.ok===!0?" \u2713":"")),o.caller&&g.push("by "+String(o.caller)),o.pod&&g.push(String(o.pod)),typeof o.atMs=="number"&&g.push(`@${o.atMs}ms`),g.join(" ")}),w=D.map(o=>o.replace(/ @\d+ms$/,"")).join(" \u2192 ");else if(p==="egress")w=`${r.method??"GET"} ${r.host??""} \u2192 ${r.status??"?"}`;else if(a.source==="ai"){if(p==="ai-round"){const o=r.model??{},g=r.usage??{},v=Array.isArray(r.toolCalls)?r.toolCalls:[],F=`${l(g.promptTokens)??"?"}\u2192${l(g.completionTokens)??"?"} tok`,W=v.length?` \xB7 ${v.length} tool call${v.length===1?"":"s"}`:"",t=l(r.durationMs)!==void 0?` \xB7 ${(l(r.durationMs)/1e3).toFixed(1)}s`:"";w=`${o.provider??""}/${o.model??""} \xB7 ${F}${W}${t}`}else if(p==="ai-turn-start"){const o=Array.isArray(r.tools)?r.tools:[];w=`skill ${r.skill??""} \xB7 ${r.output??""}${o.length?" \xB7 tools: "+o.join(", "):""}`}else if(p==="ai-tool-call"){const o=r.call??{},g=r.result??{},v=l(r.durationMs)!==void 0?` \xB7 ${(l(r.durationMs)/1e3).toFixed(1)}s`:"";w=`${o.name??"tool"} ${g.error!=null?"\u2717 "+String(g.error):"\u2713"}${v}`}else if(p==="ai-turn-end"){const o=r.usage??{},g=l(r.rounds)!==void 0?` \xB7 ${l(r.rounds)} round${l(r.rounds)===1?"":"s"}`:"",v=l(r.durationMs)!==void 0?` \xB7 ${(l(r.durationMs)/1e3).toFixed(1)}s`:"";w=`${r.ok===!1?"FAILED":"ok"} \xB7 ${l(o.promptTokens)??"?"}+${l(o.completionTokens)??"?"} tok${g}${v}`}}let j;p==="db-query"&&typeof r.query=="string"?j=r.query:p==="redis-op"?j=`${r.op??""} ${String(r.key??"").replace(/\d+/g,"#")}`:p==="egress"?j=`${r.method??"GET"} ${r.host??""}`:p==="queue-enqueued"?j=`enqueue ${r.queue??""}`:p==="span"&&typeof c.name=="string"&&(j=c.name);const b=a.context??{},O=r.effect??{},P=y.commitId??O.commitId??r.commitId,K=Array.isArray(y.why)?y.why.map(o=>typeof o?.type=="string"?o.type:void 0).filter(o=>!!o):[];return{id:a.id,t:a.timestamp,start:G,...j?{sig:j}:{},...S?{urlKey:S}:{},...typeof r.queue=="string"?{queue:r.queue}:{},...r.jobId!=null?{jobId:String(r.jobId)}:{},source:a.source,category:a.category,severity:a.severity,title:a.title,kind:p,area:areaOf({source:a.source,category:a.category,title:a.title,kind:p}),...typeof c.role=="string"?{role:c.role}:{},...typeof b.requestId=="string"?{requestId:b.requestId}:{},...typeof b.parentRequestId=="string"?{parentRequestId:b.parentRequestId}:{},...typeof P=="string"?{commitId:P}:{},...typeof y.componentName=="string"?{component:y.componentName}:{},...typeof y.phase=="string"?{phase:y.phase}:{},...K.length?{why:K}:{},...typeof b.projectName=="string"?{project:b.projectName}:typeof r.project=="string"?{project:r.project}:{},...B!==void 0?{durationMs:B}:{},...L&&l(r.startedAt)!==void 0?{fetchStartedAt:l(r.startedAt)}:{},...a.source==="ai"?{ai:m.includeRaw?r:H(r)}:{},...w?{detail:w}:{},...D?{steps:D}:{}}}x(trimFlowEvent,"trimFlowEvent"),M(trimFlowEvent,"trimFlowEvent");const V=1;function X(a){return a==="flow-origin"?0:a?.endsWith("-start")?1:a==="gateway-request"?2:a==="server-request"?3:a==="span"?4:5}x(X,"nt"),M(X,"kindRank");function N(a){return a.kind==="span"&&a.role==="controller"?3.5:a.kind==="queue-process"?3:X(a.kind)}x(N,"O"),M(N,"causalRank");export function buildFlowDetail(a,m={}){const r=a.slice().filter(t=>{const s=t.raw??{},n=typeof s.kind=="string"?s.kind:"";if(n==="subscription-added"||n==="subscription-removed"||n==="hook-run"||n==="store-created"||n==="singleton-instance")return!1;const e=s.span;return!(typeof e?.name=="string"&&e.name.startsWith("nest.request "))}).map(t=>trimFlowEvent(t,m)),y=new Set;for(let t=r.length-1;t>=0;t--)y.has(r[t].id)?r.splice(t,1):y.add(r[t].id);const c=new Map;for(const t of r)(t.kind==="gateway-request"||t.kind==="queue-process")&&t.requestId&&!c.has(t.requestId)&&c.set(t.requestId,t.start);const C=M(t=>t.source!=="react"&&t.source!=="valtio"&&t.requestId&&c.has(t.requestId)?c.get(t.requestId):t.start,"anchorOf");r.sort((t,s)=>C(t)-C(s)||t.start-s.start||N(t)-N(s)||(s.durationMs??0)-(t.durationMs??0)||t.t-s.t);const l=M(t=>(t.kind==="span"||t.kind==="server-request"||t.kind==="gateway-request"||t.kind==="queue-process")&&(t.durationMs??0)>0,"spanLike"),I=r;for(const t of I)t.group=t.source==="react"||t.source==="valtio"||t.source==="client-runtime"||t.source==="visual"?"browser":"server";const B=new Map;for(const t of I)(t.kind==="gateway-request"||t.kind==="queue-process")&&t.requestId&&t.project&&B.set(t.requestId,t.project);for(const t of I)if(!t.project&&t.source==="nestjs"&&t.requestId){const s=B.get(t.requestId);s&&(t.project=s)}for(const t of I){let s;const n=t.requestId;for(const e of I){if(e===t||!l(e)||e.group!==t.group||e.requestId!==n)continue;const i=e.project,d=t.project;if(e.kind!=="gateway-request"&&t.kind!=="gateway-request"&&i!==void 0&&d!==void 0&&i!==d)continue;const q=e.durationMs??0,h=t.durationMs??0,$=Math.abs(e.start-t.start)<=V,u=N(e),k=N(t),T=e.start<=t.start;if(!(e.start+q>t.start&&(T||$&&u<k)&&(!$||(u!==k?u<k:T&&q>h))))continue;const R=s?N(s):-1,A=s?Math.abs(e.start-s.start)<=V:!1;(!s||!A&&e.start>s.start||A&&u>R||A&&u===R&&(e.durationMs??0)<(s.durationMs??0))&&(s=e)}s&&(t.parent=s.id)}const p=new Map;for(const t of I){if(!t.requestId)continue;const s=p.get(t.requestId);s?s.push(t):p.set(t.requestId,[t])}const L=new Map(I.map(t=>[t.id,t])),G=M((t,s)=>{const n=(p.get(t)??[]).filter(e=>l(e)&&e.start<=s&&e.start+(e.durationMs??0)>=s);if(n.length!==0)return n.sort((e,i)=>i.start-e.start||(e.durationMs??0)-(i.durationMs??0)),n[0]},"callerNodeFor");for(const t of I){if(!t.parentRequestId||t.kind!=="server-request"&&t.kind!=="gateway-request")continue;const s=t.parent?L.get(t.parent):void 0;if(s&&s.requestId===t.requestId)continue;const n=G(t.parentRequestId,t.start);n&&n!==t&&n.start<=t.start&&(t.parent=n.id)}const w=I,S=new Map;for(const t of w)t.kind==="gateway-request"&&t.requestId&&!S.has(t.requestId)&&S.set(t.requestId,t);for(const t of w)if(t.group==="server"&&!t.parent&&t.requestId&&t.kind!=="gateway-request"){const s=S.get(t.requestId);s&&s!==t&&s.start<=t.start&&(t.parent=s.id)}const D=new Set,j=new Set,b=[];for(const t of w){if(t.kind!=="gateway-request")continue;if(!t.requestId){b.push(t);continue}const s=p.get(t.requestId)??[];if(s.filter(i=>i.kind==="gateway-request").sort((i,d)=>i.start-d.start)[0]!==t)continue;const n=s.find(i=>i.kind==="fetch-start"),e=s.find(i=>i.kind==="fetch-done"||i.kind==="fetch-error");if(!n&&!e){b.push(t);continue}n&&(t.fetchStartId=n.id,D.add(n.id)),e&&(t.fetchDoneId=e.id,j.add(e.id))}const O=new Map;for(const t of w){if(!t.urlKey)continue;const s=O.get(t.urlKey)??{gws:[],starts:[],dones:[]};t.kind==="gateway-request"?b.includes(t)&&s.gws.push(t):t.kind==="fetch-start"?D.has(t.id)||s.starts.push(t):(t.kind==="fetch-done"||t.kind==="fetch-error")&&(j.has(t.id)||s.dones.push(t)),O.set(t.urlKey,s)}for(const{gws:t,starts:s,dones:n}of O.values()){t.sort((i,d)=>i.start-d.start),s.sort((i,d)=>i.start-d.start);const e=new Set;t.forEach((i,d)=>{const q=s[d];q&&(i.fetchStartId=q.id);const h=q?q.t:i.start;let $,u=1/0;for(const k of n){if(e.has(k.id))continue;const T=k.fetchStartedAt??k.start-(k.durationMs??0),R=Math.abs(T-h);R<u&&(u=R,$=k)}$&&(i.fetchDoneId=$.id,e.add($.id))})}const P=new Map,K=new Map;for(const t of w)!t.queue||!t.jobId||(t.kind==="queue-enqueued"&&P.set(`${t.queue}#${t.jobId}`,t),t.kind==="queue-process"&&K.set(`${t.queue}#${t.jobId}`,t));for(const t of w){if(!t.queue||!t.jobId)continue;const s=`${t.queue}#${t.jobId}`;if(t.kind==="queue-process"&&!t.parent){const n=P.get(s);n&&n!==t&&n.start<=t.start&&(t.parent=n.id)}else if(t.kind==="queue-active"||t.kind==="queue-completed"||t.kind==="queue-failed"){const n=K.get(s);n&&n!==t&&n.start<=t.start&&(t.parent=n.id)}}let o=I;{const t=new Map;for(const n of o)n.kind==="fetch-start"&&t.set(n.id,n);const s=new Map;for(const n of o){if(n.kind!=="fetch-done"&&n.kind!=="fetch-error")continue;const e=[...t.values()].find(q=>q.requestId&&q.requestId===n.requestId)?.id,i=e?t.get(e):void 0;if(!i)continue;const d=n.fetchStartedAt??i.start;i.title=n.title,i.severity=n.severity,i.durationMs=n.durationMs??Math.max(0,n.t-d),i.kind="fetch",n.detail&&!i.detail&&(i.detail=n.detail),s.set(n.id,i.id)}if(s.size>0){o=o.filter(n=>!s.has(n.id));for(const n of o)for(n.fetchDoneId&&s.has(n.fetchDoneId)&&(n.fetchDoneId=s.get(n.fetchDoneId));n.parent&&s.has(n.parent);)n.parent=s.get(n.parent)}}const g=M(t=>!!t.commitId&&(t.kind==="render"||t.kind==="effect-run"),"commitMember"),v=[],F=new Map;for(let t=0;t<o.length;){const s=o[t];if(!g(s)){v.push(s),t+=1;continue}let n=t+1;for(;n<o.length;){const f=o[n];if(!g(f)||f.commitId!==s.commitId)break;n+=1}const e=o.slice(t,n);if(t=n,e.length<2){v.push(...e);continue}const i=e.filter(f=>f.kind==="render"),d=e.filter(f=>f.kind==="effect-run"),q=i.filter(f=>!(f.why??[]).includes("parent-render")),h=[...new Set(q.flatMap(f=>f.why??[]))],$=[...new Set(q.map(f=>f.component).filter(Boolean))],u=[...new Set(i.map(f=>f.component).filter(Boolean))],k=Math.min(...e.map(f=>f.start)),T=Math.max(...e.map(f=>f.start+(f.durationMs??0))),R=$.length===1?$[0]:$.length>1?`${$.length} roots`:u[0]??"view",A=h.length===1?h[0]:h.length>1?h.join(" + "):void 0,Y=i.some(f=>f.phase==="mount"),E={...s,kind:"view-update",title:`view update \xB7 ${R}${A?` \u2190 ${A}`:""}`,start:k,durationMs:T-k,commitId:s.commitId,cause:A,components:u,effects:d.length,memberIds:e.map(f=>f.id),sig:`commit:${A??"-"}:${$.join(",")}:${u.slice().sort().join(",")}`,detail:[`${u.length} component${u.length===1?"":"s"}${Y?" (mount)":""}`,d.length?`${d.length} effect${d.length===1?"":"s"}`:"",u.join(", ")].filter(Boolean).join(" \xB7 "),severity:e.some(f=>f.severity==="error")?"error":s.severity};delete E.component,delete E.why,delete E.phase;for(const f of e)f.id!==E.id&&F.set(f.id,E.id);v.push(E)}if(F.size>0){o=v;for(const t of o)for(;t.parent&&F.has(t.parent);)t.parent=F.get(t.parent)}for(let t=1;t<o.length;t++){const s=o[t-1],n=o[t];n.kind!=="view-update"||s.kind!=="view-update"||n.sig&&n.sig===s.sig&&(s.redundant=!0,n.redundant=!0)}{const t=new Map(o.map(s=>[s.id,s]));for(const s of o){const n=new Set([s.id]);let e=s;for(;e?.parent;){if(n.has(e.parent)){e.parent=void 0;break}n.add(e.parent),e=t.get(e.parent)}}}const W=M(t=>{const s=new Map;for(const e of o){if(!e.sig||l(e)!==t||e.kind==="server-request"||e.kind==="gateway-request")continue;const i=`${e.parent??"root"}|${e.kind}|${e.sig}`,d=s.get(i);d?d.push(e):s.set(i,[e])}const n=new Map;for(const e of s.values()){if(e.length<3)continue;e.sort((u,k)=>u.start-k.start);const i=e[0];let d=!1;for(let u=0;u+1<e.length;u++)if(e[u+1].start<e[u].start+(e[u].durationMs??0)){d=!0;break}const q=i.start,h=Math.max(...e.map(u=>u.start+(u.durationMs??0))),$=e.reduce((u,k)=>u+(k.durationMs??0),0);if(i.count=e.length,i.parallel=d,i.avgMs=Math.round($/e.length*10)/10,i.wallMs=h-q,i.durationMs=h-q,i.title=`\xD7${e.length} ${i.title.replace(/\s*\((?:\d+ rows?, )?\d+ms\)$/,"")}`,i.kind==="view-update"){const u=i.redundant===!0;i.detail=[`${e.length} commits \xB7 avg ${i.avgMs}ms \xB7 wall ${i.wallMs}ms`,u?`no state change between them \u2014 ${e.length-1} of these had nothing new to render`:"one per update \u2014 expected for this many changes",i.components?.join(", ")??""].filter(Boolean).join(" \xB7 ")}else i.detail=`${i.sig} \xD7${e.length} ${d?"\u2016 parallel":"\u21BB sequential loop"} \xB7 avg ${i.avgMs}ms \xB7 wall ${i.wallMs}ms`;i.items=e.slice(0,12).map(u=>({start:u.start-q,...u.durationMs!=null?{durationMs:u.durationMs}:{}})),i.memberIds=e.flatMap(u=>u.memberIds??[u.id]),e.some(u=>u.severity==="error")&&(i.severity="error");for(const u of e.slice(1))n.set(u.id,i.id)}if(n.size!==0){o=o.filter(e=>!n.has(e.id));for(const e of o)for(;e.parent&&n.has(e.parent);)e.parent=n.get(e.parent)}},"aggregatePass");W(!0),W(!1);for(const t of o){if(!l(t))continue;const s=t.durationMs??0;if(s<40)continue;const n=o.filter(h=>h.parent===t.id&&(h.durationMs??0)>0).map(h=>[h.start,h.start+(h.durationMs??0)]).sort((h,$)=>h[0]-$[0]);let e=0,i=-1,d=-1;for(const[h,$]of n)h>d?(d>i&&(e+=d-i),i=h,d=$):$>d&&(d=$);d>i&&(e+=d-i);const q=Math.max(0,s-e);(q>=50||q/s>=.7&&s>=40)&&(t.selfMs=q,t.detail=`\u26A0 cpu-heavy: ${q}ms self-time of ${s}ms${t.detail?" \xB7 "+t.detail:""}`)}return{events:o}}x(buildFlowDetail,"buildFlowDetail"),M(buildFlowDetail,"buildFlowDetail");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lensmcp/core",
3
- "version": "1.22.4",
3
+ "version": "1.22.6",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "module": "./index.js",
@@ -19,7 +19,7 @@
19
19
  }
20
20
  },
21
21
  "dependencies": {
22
- "@lensmcp/protocol-types": "1.22.4",
22
+ "@lensmcp/protocol-types": "1.22.6",
23
23
  "tslib": "^2.3.0"
24
24
  },
25
25
  "license": "Apache-2.0",