@lensmcp/core 1.25.5 → 1.25.7
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/flow-categories.js +1 -1
- package/lib/flow-segments.js +1 -1
- package/lib/http-exchange.d.ts +11 -2
- package/lib/http-exchange.js +1 -1
- package/lib/redact.js +1 -1
- package/lib/resource-store.d.ts +19 -0
- package/lib/resource-store.js +1 -1
- package/lib/thresholds.d.ts +5 -0
- package/lib/thresholds.js +1 -1
- package/package.json +2 -2
package/lib/flow-categories.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var M=Object.defineProperty;var c=(e,t)=>M(e,"name",{value:t,configurable:!0});var T=Object.defineProperty,s=c((e,t)=>T(e,"name",{value:t,configurable:!0}),"s");export const CATEGORY_ORDER=["user-action","page-load","navigation","api","ai","background","test","system","other"];const p=new Map;export function globMatch(e,t){let o=p.get(e);return o||(o=new RegExp(`^${e.replace(/[.+^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*").replace(/\?/g,".")}$`),p.size>500&&p.clear(),p.set(e,o)),o.test(t)}c(globMatch,"globMatch"),s(globMatch,"globMatch");const F=s(e=>/^[A-Z]+ /.test(e),"hasMethod");function w(e,t){return t.some(o=>o.route?F(e)?globMatch(e,`${o.method??"GET"} ${o.route}`):globMatch(e,o.route):!1)}c(w,"$"),s(w,"routeMatches");function m(e,t){return!([e.flowId,e.originType,e.origin,e.route,e.project].filter(o=>o!==void 0).length===0||e.flowId!==void 0&&!globMatch(e.flowId,t.flowId)||e.originType!==void 0&&e.originType!==t.origin.type||e.origin!==void 0&&!(t.origin.nodeId!==void 0&&globMatch(e.origin,t.origin.nodeId))||e.route!==void 0&&!w(e.route,t.segments)||e.project!==void 0&&!t.segments.some(o=>o.projects.some(n=>globMatch(e.project,n))))}c(m,"b"),s(m,"ruleMatches");const y=s(e=>typeof e=="object"&&e!==null&&!Array.isArray(e),"isObj"),g=s(e=>typeof e=="string"&&e!==""?e:void 0,"str"),f=s(e=>y(e.raw)?g(e.raw.kind)??"":"","kindOf"),k=s((e,t)=>{const o=Number(e.timestamp)||0,n=Number(t.timestamp)||0;return o<n||o===n&&e.id<t.id},"earlier");function $(e){let t,o;for(const a of e)f(a)==="flow-origin"&&(!t||k(a,t))&&(t=a),g(a.context?.originType)&&(!o||k(a,o))&&(o=a);const n=t??o;if(!n)return{};const r=y(n.raw)?n.raw:{},i=g(n.context?.originType)??(t?g(n.title):void 0),u=g(r.originNodeId)??g(n.context?.originNodeId);return{...i?{type:i}:{},...u?{nodeId:u}:{}}}c($,"O"),s($,"originOf");const S=/^(verify|e2e|test|smoke|load)[-_:]/i,z=s((e,t)=>S.test(e)||t!==void 0&&/^(ui:)?(tools|scripts?|e2e)\//.test(t),"isTestFlow"),x={"page-load":"page-load","user-click":"user-action","form-submit":"user-action","input-change":"user-action","double-click":"user-action","context-menu":"user-action","key-press":"user-action","route-load":"navigation","route-change":"navigation",navigation:"navigation","ai-turn":"ai",request:"api",timer:"background",cron:"background",interval:"background",queue:"background",job:"background","websocket-message":"background","hmr-update":"system"},A=new Set(["page-load","user-action","navigation"]),E=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;export function templatePath(e){return e.replace(/[?#].*$/,"").split("/").map(t=>E.test(t)||/^[0-9a-f]{16,}$/i.test(t)||/^\d{2,}$/.test(t)?":id":t).join("/")}c(templatePath,"templatePath"),s(templatePath,"templatePath");function I(e){const t=/^ui:(?:.*\/)?([^/:]+?)(?:\.[a-z]+)?:\d+:([A-Za-z]+)$/.exec(e);return t?`${t[1]} \xB7 ${t[2]}`:e}c(I,"q"),s(I,"componentOf");export function scriptFamily(e){let t=e.replace(/[-_:][0-9a-f]{6,}(?:-[0-9a-f]+)*$/i,"");return t=t.replace(/[-_:](?=[a-z0-9]*\d)[a-z0-9]{6,}$/i,""),t=t.replace(/[-_:]\d+$/,""),t||e}c(scriptFamily,"scriptFamily"),s(scriptFamily,"scriptFamily");function j(e,t,o,n){const r=t.nodeId,i=r?/^(page|route|ambient):(.*)$/.exec(r):null;if(i)return`${i[1]}:${templatePath(i[2])}`;if(t.type&&x[t.type]==="user-action"&&r)return I(r);if(e==="test")return scriptFamily(o.flowId);const u=o.segments.find(l=>l.kind==="request"&&l.route);if(u?.route)return`${u.method??"GET"} ${templatePath(u.route)}`;const a=n.find(l=>f(l)==="ai-call");return a&&y(a.raw)?`ai \xB7 ${g(a.raw.model)??g(a.raw.provider)??"call"}`:r?templatePath(r):void 0}c(j,"R"),s(j,"subjectOf");const _=10,B=1e3,N=3,R=500,C=1e3;function q(e,t){const o=new Set,n=t.segments.filter(i=>i.kind==="request");t.segments.some(i=>i.counts.errors>0)&&o.add("has-error"),n.some(i=>i.durationMs>=B)&&o.add("slow"),n.length>=_&&o.add("fan-out"),new Set(t.segments.flatMap(i=>i.projects)).size>=3&&o.add("multi-service"),e.some(i=>f(i)==="queue-enqueued")&&o.add("queue"),e.some(i=>f(i)==="ai-call"||i.source==="ai")&&o.add("ai"),t.stats.bookkeeping>=R&&t.stats.bookkeeping*2>=t.stats.events&&o.add("storm"),t.stats.events>=C&&o.add("large");const r=new Map;for(const i of n)(!i.part||i.part.index===1)&&r.set(i.label,(r.get(i.label)??0)+1);return[...r.values()].some(i=>i>=N)&&o.add("repeated-requests"),[...o].sort()}c(q,"G"),s(q,"tagsOf");export function classifyFlow(e,t,o=[]){const n=$(e),r={flowId:t.flowId,origin:n,segments:t.segments},i=q(e,t),u=s((d,O,b)=>{const h=j(d,n,t,e);return{category:d,decidedBy:O,...b!==void 0?{ruleIndex:b}:{},tags:i,origin:n,...h?{subject:h}:{}}},"done"),a=o.findIndex(d=>d.category&&m(d,r));if(a!==-1)return u(o[a].category,"rule",a);if(z(t.flowId,n.nodeId))return u("test","test");const l=n.type?x[n.type]:void 0;if(l)return u(l,"origin");if(i.includes("ai"))return u("ai","shape");const v=t.segments.filter(d=>d.kind==="request");return v.length>0?u(v.every(d=>d.requestId?.startsWith("job:"))?"background":"api","shape"):t.segments.length>0&&t.segments.every(d=>d.kind==="ambient")?u("system","shape"):u("other","shape")}c(classifyFlow,"classifyFlow"),s(classifyFlow,"classifyFlow");export function categoryOfSegment(e,t,o=[]){const n=o.find(r=>r.category&&(r.route!==void 0||r.project!==void 0)&&r.flowId===void 0&&r.origin===void 0&&r.originType===void 0&&m(r,{flowId:e.flowId,origin:t.origin,segments:[e]}));return n?n.category:e.kind==="request"?e.requestId?.startsWith("job:")?"background":"api":e.kind==="ambient"?"system":e.kind==="origin"&&A.has(t.category)?t.category:"ui"}c(categoryOfSegment,"categoryOfSegment"),s(categoryOfSegment,"categoryOfSegment");export function groupFlows(e){return groupByCategory(e).map(t=>{const o=new Map;for(const r of t.items){const i=o.get(r.subject);i?i.push(r):o.set(r.subject,[r])}const n=[...o.entries()].sort((r,i)=>(r[0]===void 0?1:0)-(i[0]===void 0?1:0)||i[1].length-r[1].length||String(r[0]).localeCompare(String(i[0]))).map(([r,i])=>({subject:r,count:i.length,items:i}));return{...t,subjects:n}})}c(groupFlows,"groupFlows"),s(groupFlows,"groupFlows");export function groupByCategory(e){const t=new Map;for(const n of e){const r=t.get(n.category);r?r.push(n):t.set(n.category,[n])}const o=s(n=>{const r=CATEGORY_ORDER.indexOf(n);return r===-1?CATEGORY_ORDER.length:r},"rank");return[...t.entries()].sort((n,r)=>o(n[0])-o(r[0])||(n[0]<r[0]?-1:n[0]>r[0]?1:0)).map(([n,r])=>({category:n,count:r.length,items:r}))}c(groupByCategory,"groupByCategory"),s(groupByCategory,"groupByCategory");
|
package/lib/flow-segments.js
CHANGED
|
@@ -1 +1 @@
|
|
|
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");
|
|
1
|
+
"use strict";var Y=Object.defineProperty;var b=(e,t)=>Y(e,"name",{value:t,configurable:!0});var Z=Object.defineProperty,m=b((e,t)=>Z(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"]),SQL_ACCOUNT_KIND="db-request-account";const ee=new Set(["react","valtio","client-runtime","chrome","visual"]),S=m(e=>typeof e=="object"&&e!==null&&!Array.isArray(e),"isObj"),v=m(e=>typeof e=="string"&&e!==""?e:void 0,"str"),M=m(e=>typeof e=="number"&&Number.isFinite(e)?e:void 0,"num"),W=new WeakMap;function F(e){let t=W.get(e);return t||(t=G(e),W.set(e,t)),t}b(F,"U"),m(F,"itemOf");function G(e){const t=S(e.raw)?e.raw:{},s=S(t.request)?t.request:{},o=S(t.span)?t.span:{},i=e.context??{},n=v(t.kind)??"",p=n===SQL_ACCOUNT_KIND,g=(p?M(t.lastEndedAt):void 0)??M(e.timestamp)??0,q=M(s.startedAt)??M(o.startTime)??M(t.startedAt)??(p?M(t.firstStartedAt):void 0),I=M(s.endedAt)??M(o.endTime)??(q!==void 0&&M(t.durationMs)!==void 0?q+t.durationMs:void 0),w=Math.min(g,q??g),$=Math.max(g,I??g),a=S(t.render)?t.render:{},u=S(t.update)?t.update:{},j=n==="render"?v(a.componentName):n==="state-update"?v(u.path)?.split(".").slice(0,2).join("."):void 0,d=v(t.queue),c=v(t.jobId),f=String(e.source),r=v(t.project)??v(i.projectName),l=n==="fetch-start"||n==="fetch-done"||n==="gateway-request";return{event:e,id:e.id,ts:g,from:w,to:$,kind:n,source:f,requestId:v(i.requestId),parentRequestId:v(i.parentRequestId),queueKey:d&&c?`${d}#${c}`:void 0,browser:ee.has(f)||f==="memory"&&r===void 0,bookkeeping:FLOW_BOOKKEEPING_KINDS.has(n),project:r,subject:j,route:l?T(v(t.url)):n==="server-request"?v(s.route):void 0,method:l?v(t.method):n==="server-request"?v(s.method):void 0}}b(G,"L"),m(G,"readItem");function T(e){if(!e)return;const t=e.search(/[?#]/),s=t===-1?e:e.slice(0,t),o=/^[a-z][a-z0-9+.-]*:\/\/[^/]*(\/.*)?$/i.exec(s);return o?o[1]??"/":s}b(T,"B"),m(T,"pathOnly");const te=m(e=>e.split(".")[0],"rootOf"),_=m((e,t)=>e.reduce((s,o)=>Math.min(s,t(o)),1/0),"minOf"),B=m((e,t)=>e.reduce((s,o)=>Math.max(s,t(o)),-1/0),"maxOf"),O=m((e,t)=>e.ts-t.ts||(e.id<t.id?-1:e.id>t.id?1:0),"compare"),K=m(e=>e.requestId&&!e.requestId.startsWith("job:")?te(e.requestId):void 0,"directFamilyOf");function U(e){if(e.requestId?.startsWith("job:"))return e.requestId.slice(4);if(e.queueKey&&e.kind!=="queue-enqueued"&&!e.requestId)return e.queueKey}b(U,"D"),m(U,"jobKeyOf");function D(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,p)=>n.from-p.from||O(n,p));if(s.length<2)return[e];const o=[];for(const n of s){const p=o[o.length-1];p&&n.from<=p.to+t?p.to=Math.max(p.to,n.to):o.push({from:n.from,to:n.to})}if(o.length<2)return[e];const i=o.map(()=>[]);for(const n of e)i[t>=0?z(o,n.ts,t):R(o,n.ts,t)].push(n);return i.filter(n=>n.length>0)}b(D,"z"),m(D,"occurrencesOf");function R(e,t,s){const o=e.findIndex(n=>t>=n.from-s&&t<=n.to+s);if(o!==-1)return o;let i=0;for(let n=0;n<e.length;n+=1)e[n].from<=t&&(i=n);return i}b(R,"P"),m(R,"spanScan");function z(e,t,s){let o=0,i=e.length;for(;o<i;){const n=o+i>>1;e[n].to+s<t?o=n+1:i=n}if(o<e.length&&t>=e[o].from-s)return o;for(o=0,i=e.length;o<i;){const n=o+i>>1;e[n].from<=t?o=n+1:i=n}return Math.max(0,o-1)}b(z,"Q"),m(z,"spanAt");const C={debug:0,info:1,warning:2,error:3,fatal:4};function A(e,t){const s=[];let o=[];for(const i of e){const n=o[o.length-1];n&&i.ts-n.ts>=t&&(s.push(o),o=[]),o.push(i)}return o.length&&s.push(o),s}b(A,"R"),m(A,"runs");export function segmentFlow(e,t={}){const s={...DEFAULT_SEGMENT_OPTIONS};for(const[r,l]of Object.entries(t))typeof l=="number"&&Number.isFinite(l)&&(s[r]=l);const o=new Set,i=[];for(const r of e)o.has(r.id)||(o.add(r.id),i.push(F(r)));i.sort(O);const n=v(i[0]?.event.context?.flowId)??v(i[0]?.event.raw?.flowId)??"",p=new Map;for(const r of i)r.kind==="queue-enqueued"&&r.queueKey&&r.requestId&&!r.requestId.startsWith("job:")&&!p.has(r.queueKey)&&p.set(r.queueKey,r);const g=new Map,q=new Map,I=[];for(const r of i){const l=U(r),h=K(r);if(l!==void 0){const k=q.get(l);k?k.push(r):q.set(l,[r])}else if(h!==void 0){const k=g.get(h);k?k.push(r):g.set(h,[r])}else I.push(r)}const w=[],$=new Map;for(const[r,l]of g)D(l,s.occurrenceSlackMs).forEach((h,k)=>{const x={id:`${n}~r:${r}${k===0?"":`@${k+1}`}`,kind:"request",items:h,requestId:r};if(w.push(x),p.size)for(const y of h)y.kind==="queue-enqueued"&&$.set(y,x)});for(const[r,l]of q){const h=p.get(r),k=h?$.get(h):void 0;k?k.items.push(...l):w.push({id:`${n}~r:job:${r}`,kind:"request",items:l,requestId:`job:${r}`})}const a=i.filter(r=>r.kind==="fetch-done"&&K(r)!==void 0),u=new Map;for(const r of w)r.requestId&&u.set(r.requestId,[...u.get(r.requestId)??[],r]);const j=m((r,l)=>{const h=u.get(r);if(h)return h.find(k=>k.items.some(x=>x.kind==="fetch-done"&&x.ts===l))??h[h.length-1]},"owningUnit"),d=I.filter(r=>r.browser);for(const r of A(d,s.burstGapMs)){const l=r[0].ts,h=a.filter(y=>y.ts<=l&&l-y.ts<=s.attachWindowMs),k=[...new Set(h.map(y=>K(y)))];if(k.length===1){const y=j(k[0],h[h.length-1].ts);if(y){y.items.push(...r);continue}}const x=r.some(y=>y.kind==="flow-origin");w.push({id:`${n}~${x?"o":"u"}:${r[0].id}`,kind:x?"origin":"ui",items:r,...k.length>1?{after:k.sort()}:{}})}for(const r of A(I.filter(l=>!l.browser),s.phaseGapMs))w.push({id:`${n}~a:${r[0].id}`,kind:"ambient",items:r});const c=L(n,i.length,w,s),f=N(i);return f?{...c,sql:f}:c}b(segmentFlow,"segmentFlow"),m(segmentFlow,"segmentFlow");function L(e,t,s,o){for(const a of s)a.items.sort(O);const i=s.map(a=>({unit:a,from:_(a.items,u=>u.from),to:B(a.items,u=>u.to)}));i.sort((a,u)=>a.from-u.from||(a.unit.id<u.unit.id?-1:1));const n=new Map;let p=0,g=-1/0;for(const a of i)(p===0||a.from-g>=o.phaseGapMs)&&(p+=1),g=Math.max(g,a.to),n.set(a.unit,p);const q=[];for(const a of i){const u=P(a.unit.items,o.maxSegmentEvents),j=a.unit.kind==="request"?N(a.unit.items):void 0;u.forEach((d,c)=>{const f=Q(e,a.unit,d,n.get(a.unit),u.length>1?{index:c+1,total:u.length}:void 0);q.push(j&&c===0?{...f,sql:j}:f)})}const I={};for(const a of q)for(const u of a.eventIds)I[u]=a.id;const w=[];for(const a of q){let u=w[a.phase-1];u||(u={index:a.phase,start:a.start,end:a.end,segmentIds:[]},w[a.phase-1]=u),u.start=Math.min(u.start,a.start),u.end=Math.max(u.end,a.end),u.segmentIds.push(a.id)}const $=q.reduce((a,u)=>a+u.counts.signal,0);return{flowId:e,segments:q,phases:w.filter(Boolean),assignment:I,stats:{events:t,signal:$,bookkeeping:t-$,segments:q.length,phases:w.filter(Boolean).length,largestSegment:q.reduce((a,u)=>Math.max(a,u.counts.total),0)}}}b(L,"V"),m(L,"assemble");function P(e,t){if(e.filter(n=>!n.bookkeeping).length<=t)return[e];const s=[];let o=[],i=0;for(const n of e)!n.bookkeeping&&i===t&&(s.push(o),o=[],i=0),o.push(n),n.bookkeeping||(i+=1);return o.length&&s.push(o),s}b(P,"X"),m(P,"chunk");function Q(e,t,s,o,i){const n={total:s.length,signal:0,bookkeeping:0,errors:0,warnings:0},p=new Set;let g="debug";for(const u of s){u.bookkeeping?n.bookkeeping+=1:n.signal+=1;const j=String(u.event.severity);j==="error"||j==="fatal"?n.errors+=1:j==="warning"&&(n.warnings+=1),(C[j]??1)>(C[g]??0)&&(g=j),u.project&&p.add(u.project)}const q=t.kind==="ui"||t.kind==="origin"?J(s):[],I=_(s,u=>u.from),w=B(s,u=>u.to),$=H(t.items),a=t.id;return{id:i&&i.index>1?`${a}#${i.index}`:a,flowId:e,kind:t.kind,label:V(t,s,$,i,q),phase:o,start:I,end:w,durationMs:w-I,eventIds:s.map(u=>u.id),counts:n,projects:[...p].sort(),...$.route?{route:$.route}:{},...$.method?{method:$.method}:{},...t.requestId?{requestId:t.requestId}:{},...t.after?{after:t.after}:{},...q.length?{hot:q}:{},...i?{part:i}:{},worstSeverity:g}}b(Q,"Y"),m(Q,"toSegment");function H(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()}:{}}}b(H,"H"),m(H,"routeOf");function J(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,o)=>o[1]-s[1]||(s[0]<o[0]?-1:1)).slice(0,3).map(([s,o])=>o>1?`${s} \xD7${o}`:s)}b(J,"J"),m(J,"hotOf");function V(e,t,s,o,i){const n=o?` (part ${o.index}/${o.total})`:"";if(e.kind==="request"){if(s.route)return`${s.method?`${s.method} `:""}${s.route}${n}`;const p=t.find(g=>g.queueKey);return p?`job ${p.queueKey}${n}`:`request ${e.requestId??""}${n}`}return e.kind==="origin"?`${t.find(p=>p.kind==="flow-origin")?.event.title||"start"}${i.length?` \xB7 ${i.join(", ")}`:""}${n}`:e.kind==="ui"?`UI update${i.length?` \xB7 ${i.join(", ")}`:""}${n}`:`background${n}`}b(V,"Z"),m(V,"labelOf");const X=50;export function sqlAccountingOf(e){return E(e,!1)}b(sqlAccountingOf,"sqlAccountingOf"),m(sqlAccountingOf,"sqlAccountingOf");function N(e){return E(e.map(t=>t.event),!0)}b(N,"T"),m(N,"accountSqlOfItems");function E(e,t){const s=new Map,o=new Map,i=t?void 0:new Set;for(const d of e){if(i){if(i.has(d.id))continue;i.add(d.id)}const c=S(d.raw)?d.raw:{},f=v(c.kind),r=v(d.context?.requestId);if(!r)continue;const l=v(c.project)??v(d.context?.projectName);if(f==="db-query"&&d.source==="db"){const h=o.get(r)??{count:0,workMs:0,...l?{project:l}:{}};h.count+=1,h.workMs+=M(c.durationMs)??0,o.set(r,h)}else if(f===SQL_ACCOUNT_KIND){const h=v(c.accountId)??d.id,k=M(c.seq)??0,x=s.get(h);if(x&&x.seq>=k)continue;s.set(h,{requestId:r,...l?{project:l}:{},seq:k,basis:c.basis==="end"?"end":"settled",statements:M(c.statements)??0,failed:M(c.failed)??0,workMs:M(c.workMs)??0,recorded:M(c.recorded)??0,suppressed:M(c.suppressed)??0,unwritten:M(c.unwritten)??0})}}const n=new Map;for(const d of s.values()){const c=n.get(d.requestId);if(!c){n.set(d.requestId,{...d});continue}d.basis==="settled"&&(c.basis="settled"),c.statements+=d.statements,c.failed+=d.failed,c.workMs+=d.workMs,c.recorded+=d.recorded,c.suppressed+=d.suppressed,c.unwritten+=d.unwritten}const p=[...new Set([...o.keys(),...n.keys()])].sort();if(p.length===0)return;const g={statements:0,failed:0,workMs:0,recorded:0,suppressed:0,unwritten:0},q={statements:0,workMs:0},I=[];for(const d of p){const c=o.get(d),f=n.get(d),r=c?.count??0;q.statements+=r,q.workMs+=c?.workMs??0;const l=!f||r>f.statements?"unverified":r===f.statements?"complete":"incomplete";f&&l!=="unverified"&&(g.statements+=f.statements,g.failed+=f.failed,g.workMs+=f.workMs,g.recorded+=f.recorded,g.suppressed+=f.suppressed,g.unwritten+=f.unwritten);const h=c?.project??f?.project;I.push({requestId:d,...h?{project:h}:{},status:l,observed:r,observedWorkMs:c?.workMs??0,...f?{basis:f.basis,statements:f.statements,failed:f.failed,workMs:f.workMs,recorded:f.recorded,suppressed:f.suppressed,unwritten:f.unwritten}:{}})}const w=m(d=>I.filter(c=>c.status===d).length,"tally"),$=w("unverified"),a=w("incomplete"),u=m(d=>(d.statements??0)-d.observed,"missing"),j=I.filter(d=>d.status!=="complete").sort((d,c)=>(d.status==="incomplete"?0:1)-(c.status==="incomplete"?0:1)||u(c)-u(d)||(d.requestId<c.requestId?-1:d.requestId>c.requestId?1:0));return{status:$>0?"unverified":a>0?"incomplete":"complete",requests:I.length,complete:w("complete"),incomplete:a,unverified:$,settled:I.filter(d=>d.status!=="unverified"&&d.basis==="settled").length,observed:q,accounted:g,gaps:j.slice(0,X),gapsTruncated:j.length>X}}b(E,"W"),m(E,"accountSql");
|
package/lib/http-exchange.d.ts
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import type { HttpExchange } from '@lensmcp/protocol-types';
|
|
2
2
|
export type { HttpExchange, HttpExchangeBody, HttpExchangeStreamEvent } from '@lensmcp/protocol-types';
|
|
3
3
|
export declare const HTTP_EXCHANGE_DIR = "http";
|
|
4
|
+
/**
|
|
5
|
+
* Where an exchange is kept, each place pruned on its own budget: a third-party call (`http/`), a request a service
|
|
6
|
+
* served (`http/in/`), and a call between the cluster's own services through the gateway (`http/hops/`, a host under
|
|
7
|
+
* `.localhost`). One budget for all let FoodGuard's ~2,000 service-to-service calls an hour push its OpenAI calls out.
|
|
8
|
+
*/
|
|
9
|
+
export type HttpExchangePlace = 'calls' | 'in' | 'hops';
|
|
10
|
+
export declare const HTTP_EXCHANGE_PLACES: readonly HttpExchangePlace[];
|
|
11
|
+
/** The place an exchange belongs in. */
|
|
12
|
+
export declare function exchangePlace(x: Pick<HttpExchange, 'side' | 'host'>): HttpExchangePlace;
|
|
4
13
|
export declare const isHttpExchangeId: (value: unknown) => value is string;
|
|
5
|
-
export declare function httpExchangeDir(busFile: string): string;
|
|
6
|
-
export declare function httpExchangeFile(busFile: string, id: string): string;
|
|
14
|
+
export declare function httpExchangeDir(busFile: string, place?: HttpExchangePlace): string;
|
|
15
|
+
export declare function httpExchangeFile(busFile: string, id: string, place?: HttpExchangePlace): string;
|
|
7
16
|
/** One exchange, or `undefined` — a malformed id, a file never written or rotated away, or unreadable JSON. */
|
|
8
17
|
export declare function readHttpExchange(busFile: string, id: string): HttpExchange | undefined;
|
package/lib/http-exchange.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var i=Object.defineProperty;var n=(t,e)=>i(t,"name",{value:e,configurable:!0});var s=Object.defineProperty,a=n((t,e)=>s(t,"name",{value:e,configurable:!0}),"r");import*as h from"node:fs";import{dirname as p,join as o}from"node:path";export const HTTP_EXCHANGE_DIR="http",HTTP_EXCHANGE_PLACES=["calls","in","hops"];export function exchangePlace(t){return t.side==="ingress"?"in":/(^|\.)localhost$/i.test(t.host)?"hops":"calls"}n(exchangePlace,"exchangePlace"),a(exchangePlace,"exchangePlace");const l=/^hx_[0-9a-z]{8,40}$/;export const isHttpExchangeId=a(t=>typeof t=="string"&&l.test(t),"isHttpExchangeId");export function httpExchangeDir(t,e="calls"){const r=o(p(t),HTTP_EXCHANGE_DIR);return e==="calls"?r:o(r,e)}n(httpExchangeDir,"httpExchangeDir"),a(httpExchangeDir,"httpExchangeDir");export function httpExchangeFile(t,e,r="calls"){if(!isHttpExchangeId(e))throw new Error("invalid http exchange id");return o(httpExchangeDir(t,r),`${e}.json`)}n(httpExchangeFile,"httpExchangeFile"),a(httpExchangeFile,"httpExchangeFile");export function readHttpExchange(t,e){if(isHttpExchangeId(e))for(const r of HTTP_EXCHANGE_PLACES)try{const c=JSON.parse(h.readFileSync(httpExchangeFile(t,e,r),"utf8"));if(c&&c.schemaVersion===1&&c.id===e)return c}catch{}}n(readHttpExchange,"readHttpExchange"),a(readHttpExchange,"readHttpExchange");
|
package/lib/redact.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var y=Object.defineProperty;var u=(e,r)=>y(e,"name",{value:r,configurable:!0});var E=Object.defineProperty,l=u((e,r)=>E(e,"name",{value:r,configurable:!0}),"a");export const REDACTED="REDACTED",MAX_TITLE_CHARS=300,MAX_MESSAGE_CHARS=4096,MAX_URL_CHARS=1024;const z=["token","secret","password","passwd","apikey","credential","assertion","signature"],_=new Set(["key","auth","authorization","jwt","code","sig","pwd","pass","session","sid","bearer","otp","cookie","set-cookie"]),R=/^(?:(?:prompt|completion|total|input|output|cache|cached|cacheread|cachewrite|reasoning|thinking|max|min|remaining|used|billed)tokens|tokens?(?:count|used|limit|remaining))$/;export function isSecretParam(e){const r=d.get(e);if(r!==void 0)return r;const s=A(e);return e.length<=64&&d.size<I&&d.set(e,s),s}u(isSecretParam,"isSecretParam"),l(isSecretParam,"isSecretParam");const I=4096,d=new Map;function A(e){const r=e.toLowerCase();if(R.test(r.replace(/[-_.]+/g,"")))return!1;if(z.some(s=>r.includes(s)))return!0;for(const s of r.split(/[-_.]+/))if(s&&_.has(s))return!0;return!1}u(A,"M"),l(A,"judgeParam");const v=/^([a-z][a-z0-9+.-]*:\/\/)[^/?#@\s]*@/i,P=/([?&#])([^=&#\s]+)=([^&#\s]*)/g;export function redactUrlParams(e){return e&&e.replace(v,`$1${REDACTED}@`).replace(P,(r,s,t)=>isSecretParam(t)?`${s}${t}=${REDACTED}`:r)}u(redactUrlParams,"redactUrlParams"),l(redactUrlParams,"redactUrlParams");export function redactUrl(e){return e&&clip(redactUrlParams(e),1024)}u(redactUrl,"redactUrl"),l(redactUrl,"redactUrl");const C=/\beyJ[A-Za-z0-9_-]{6,}\.[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]*/g,D=/\b(bearer|basic)\s+[A-Za-z0-9._~+/=-]{8,}/gi,N=/\b(authorization|x-api-key|api-key|cookie|set-cookie|proxy-authorization)\b(\s*[:=]\s*)(["']?)((?:(?:bearer|basic)\s+)?[^\s"',;]{4,})\3/gi,M=/[@=:]|eyJ|bearer|basic/i;export function redactText(e){return M.test(e)?redactUrlParams(e).replace(N,(r,s,t,c)=>`${s}${t}${c}${REDACTED}${c}`).replace(C,REDACTED).replace(D,(r,s)=>`${s} ${REDACTED}`):e}u(redactText,"redactText"),l(redactText,"redactText");export function clip(e,r){return e.length<=r?e:`${e.slice(0,r)}\u2026[lensmcp truncated: ${e.length-r} more chars]`}u(clip,"clip"),l(clip,"clip");export function safeText(e,r=4096){if(e!==void 0)return clip(redactText(e),r)}u(safeText,"safeText"),l(safeText,"safeText");export function safeTitle(e){return clip(redactText(e),300)}u(safeTitle,"safeTitle"),l(safeTitle,"safeTitle");const O=6,U=64,Z=/^TS\d{1,5}$/,j=l((e,r)=>e==="code"&&typeof r=="string"&&Z.test(r),"isDiagnosticCode");export function redactDeep(e,r=0,s=O){if(typeof e=="string")return redactText(e);if(e===null||typeof e!="object"||r>=s)return e;if(Array.isArray(e)){let n;for(let o=0;o<e.length;o++){if(o>=U){n&&n.push(e[o]);continue}const i=e[o],f=redactDeep(i,r+1,s);n?n.push(f):f!==i&&(n=e.slice(0,o),n.push(f))}return n??e}const t=e,c=Object.keys(t);let a;for(let n=0;n<c.length;n++){const o=c[n],i=t[o],f=isSecretParam(o)&&i!==void 0&&i!==null&&!j(o,i)?REDACTED:redactDeep(i,r+1,s);if(a)a[o]=f;else if(f!==i){a={};for(let p=0;p<n;p++)a[c[p]]=t[c[p]];a[o]=f}}return a??e}u(redactDeep,"redactDeep"),l(redactDeep,"redactDeep");const h=String.raw`(?:"(?:[^"]|"")+"|[A-Za-z_][\w$]*)`,m=String.raw`${h}(?:\s*\.\s*${h})*`,L=String.raw`(?:=|<>|!=|<=|>=|<|>|(?:NOT\s+)?I?LIKE|IS\s+(?:NOT\s+)?DISTINCT\s+FROM)`,H=new RegExp(String.raw`(${m})\s*${L}\s*(?:[A-Za-z_]\w*\s*\(\s*)?\$(\d+)`,"gi"),X=new RegExp(String.raw`(${m})\s+(?:NOT\s+)?IN\s*\(([^()]*)\)`,"gi"),q=new RegExp(String.raw`\bINSERT\s+INTO\s+${m}\s*\(([^()]*)\)\s*VALUES\s*`,"gi"),F=new RegExp(h,"g"),w=/\$(\d+)/g,S=16384,G=256,J=4096,g=new Map,$=l(e=>{const r=e.match(F)??[],s=r[r.length-1]??"";return s.startsWith('"')?s.slice(1,-1).replace(/""/g,'"'):s},"lastIdent");function b(e,r){const s=[];let t=r;for(;;){for(;t<e.length&&/\s/.test(e[t]);)t++;if(e[t]!=="(")return s;const c=[];let a=t+1,n=0,o=!1;for(t+=1;t<e.length;t++){const i=e[t];if(o){i==="'"&&(o=!1);continue}if(i==="'")o=!0;else if(i==="(")n++;else if(i===")"){if(n===0)break;n--}else i===","&&n===0&&(c.push(e.slice(a,t)),a=t+1)}if(t>=e.length)return s;for(c.push(e.slice(a,t)),s.push(c),t+=1;t<e.length&&/\s/.test(e[t]);)t++;if(e[t]!==",")return s;t+=1}}u(b,"G"),l(b,"valuesTuples");function T(e){const r=e.length<=J,s=r?g.get(e):void 0;if(s)return s;const t=e.length>S?e.slice(0,S):e,c=new Set,a=l((n,o)=>{o&&isSecretParam(o)&&c.add(Number(n))},"bind");for(const n of t.matchAll(H))a(n[2],$(n[1]));for(const n of t.matchAll(X)){const o=$(n[1]);for(const i of n[2].matchAll(w))a(i[1],o)}for(const n of t.matchAll(q)){const o=n[1].split(",").map(i=>$(i.trim()));for(const i of b(t,(n.index??0)+n[0].length))i.forEach((f,p)=>{const x=o[p];if(x)for(const k of f.matchAll(w))a(k[1],x)})}if(r){if(g.size>=G){const n=g.keys().next().value;n!==void 0&&g.delete(n)}g.set(e,c)}return c}u(T,"F"),l(T,"secretPlaceholders");export function redactSqlParams(e,r){if(r.length===0||!e.includes("$"))return r;const s=T(e);if(s.size===0)return r;let t=!1;const c=r.map((a,n)=>a==null||!s.has(n+1)?a:(t=!0,REDACTED));return t?c:r}u(redactSqlParams,"redactSqlParams"),l(redactSqlParams,"redactSqlParams");
|
package/lib/resource-store.d.ts
CHANGED
|
@@ -18,6 +18,17 @@ export interface ResourceStoreOptions {
|
|
|
18
18
|
* Reads are never delayed: `read` produces the resource fresh. Only the revision and the notification wait.
|
|
19
19
|
*/
|
|
20
20
|
coalesceMs?: number;
|
|
21
|
+
/**
|
|
22
|
+
* Space a resource's recomputes by what each one COSTS: at least this many times its last recompute (0, the default:
|
|
23
|
+
* `coalesceMs` alone). At 20, a resource that takes 50 ms to produce and serialise is recomputed at most once a
|
|
24
|
+
* second, one that takes 2 ms keeps `coalesceMs` — so no resource can take more than ~1/20 of a core however busy the
|
|
25
|
+
* workspace. FoodGuard, 2026-10-20: `agent://current-status` (which reads every signal) and `routes://lint` (which
|
|
26
|
+
* details every route) were recomputed every 100 ms of a 400-event-a-second stream, about a sixth of the dashboard's
|
|
27
|
+
* CPU, only to learn whether their JSON had changed.
|
|
28
|
+
*/
|
|
29
|
+
costFactor?: number;
|
|
30
|
+
/** The most cost spacing may hold a resource back (default 30 s). */
|
|
31
|
+
maxGapMs?: number;
|
|
21
32
|
}
|
|
22
33
|
/**
|
|
23
34
|
* The resource store. URIs are registered once; reading is cheap; the
|
|
@@ -28,9 +39,13 @@ export declare class ResourceStore {
|
|
|
28
39
|
private readonly entries;
|
|
29
40
|
private readonly subs;
|
|
30
41
|
private readonly coalesceMs;
|
|
42
|
+
private readonly costFactor;
|
|
43
|
+
private readonly maxGapMs;
|
|
31
44
|
/** A recompute already scheduled per URI — every mark until it runs rides on it. */
|
|
32
45
|
private readonly queued;
|
|
33
46
|
private readonly lastRun;
|
|
47
|
+
/** What the last recompute of each URI took (produce + serialise), in ms. */
|
|
48
|
+
private readonly lastCost;
|
|
34
49
|
constructor(opts?: ResourceStoreOptions);
|
|
35
50
|
register(uri: string, produce: ResourceProducer): Unsubscribe;
|
|
36
51
|
read(uri: string): Promise<ResourceValue>;
|
|
@@ -41,6 +56,10 @@ export declare class ResourceStore {
|
|
|
41
56
|
* change.
|
|
42
57
|
*/
|
|
43
58
|
markDirty(uri: string): Promise<void>;
|
|
59
|
+
/** How long after its last recompute a URI may be recomputed again. */
|
|
60
|
+
private gapFor;
|
|
61
|
+
/** What the last recompute of a URI took, in ms (produce + serialise) — undefined before the first. */
|
|
62
|
+
cost(uri: string): number | undefined;
|
|
44
63
|
private recompute;
|
|
45
64
|
revision(uri: string): number | undefined;
|
|
46
65
|
subscribe(uri: string, handler: ResourceUpdatedHandler): Unsubscribe;
|
package/lib/resource-store.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var u=Object.defineProperty;var a=(o,e)=>u(o,"name",{value:e,configurable:!0});var l=Object.defineProperty,h=a((o,e)=>l(o,"name",{value:e,configurable:!0}),"a");export class ResourceStore{static{a(this,"ResourceStore")}static{h(this,"ResourceStore")}constructor(e={}){this.entries=new Map,this.subs=new Map,this.queued=new Map,this.lastRun=new Map,this.lastCost=new Map,this.coalesceMs=Math.max(0,e.coalesceMs??0),this.costFactor=Math.max(0,e.costFactor??0),this.maxGapMs=Math.max(0,e.maxGapMs??3e4)}register(e,s){if(this.entries.has(e))throw new Error(`Resource already registered: ${e}`);return this.entries.set(e,{uri:e,produce:s,revision:0}),()=>{this.entries.delete(e),this.subs.delete(e),this.lastRun.delete(e),this.lastCost.delete(e)}}async read(e){const s=this.entries.get(e);if(!s)throw new Error(`Unknown resource: ${e}`);return s.produce()}list(){return[...this.entries.keys()]}markDirty(e){if(this.coalesceMs===0&&this.costFactor===0)return this.recompute(e);if(!this.entries.has(e))return Promise.resolve();const s=this.queued.get(e);if(s)return s;const t=Math.max(0,(this.lastRun.get(e)??Number.NEGATIVE_INFINITY)+this.gapFor(e)-Date.now()),n=new Promise(r=>{const i=h(()=>{this.queued.delete(e),this.lastRun.set(e,Date.now()),this.recompute(e).then(r,()=>r())},"go");t===0?setImmediate(i):setTimeout(i,t).unref?.()});return this.queued.set(e,n),n}gapFor(e){const s=this.lastCost.get(e)??0;return Math.max(this.coalesceMs,Math.min(this.maxGapMs,this.costFactor*s))}cost(e){return this.lastCost.get(e)}async recompute(e){const s=this.entries.get(e);if(!s)return;const t=performance.now(),n=await s.produce(),r=JSON.stringify(n);if(this.lastCost.set(e,performance.now()-t),r===s.lastJson)return;s.lastJson=r,s.revision+=1;const i=this.subs.get(e);if(i)for(const c of i)try{c(e,s.revision)}catch{}}revision(e){return this.entries.get(e)?.revision}subscribe(e,s){let t=this.subs.get(e);return t||(t=new Set,this.subs.set(e,t)),t.add(s),()=>{t?.delete(s)}}}
|
package/lib/thresholds.d.ts
CHANGED
|
@@ -15,6 +15,11 @@ export interface LensmcpThresholds {
|
|
|
15
15
|
renderStorm: number;
|
|
16
16
|
/** Server route durationMs at/above which it's "slow". Default 500. */
|
|
17
17
|
slowRouteMs: number;
|
|
18
|
+
/**
|
|
19
|
+
* A page visit's ready (ms from its navigation; a hard load from the app's entry) at/above which the page is
|
|
20
|
+
* "slow" — the Pages screen's Slow tab and the page review's budget. Default 1000.
|
|
21
|
+
*/
|
|
22
|
+
slowPageMs: number;
|
|
18
23
|
/** React render durationMs at/above which it's "slow". Default 16. */
|
|
19
24
|
slowRenderMs: number;
|
|
20
25
|
/** DB query durationMs at/above which its query shape is "slow" (`db://slow-queries`). Default 100. */
|
package/lib/thresholds.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var n=Object.defineProperty;var r=(e,s)=>n(e,"name",{value:s,configurable:!0});var l=Object.defineProperty,u=r((e,s)=>l(e,"name",{value:s,configurable:!0}),"n");export const DEFAULT_THRESHOLDS={dbInLoop:5,nPlusOne:3,renderStorm:5,slowRouteMs:500,slowRenderMs:16,slowQueryMs:100,duplicateRequestPeak:3,slowEgressMs:1e3};export function resolveThresholds(e){const s={...DEFAULT_THRESHOLDS};if(e)for(const t of Object.keys(DEFAULT_THRESHOLDS)){const o=e[t];typeof o=="number"&&Number.isFinite(o)&&o>=0&&(s[t]=o)}return s}r(resolveThresholds,"resolveThresholds"),u(resolveThresholds,"resolveThresholds");
|
|
1
|
+
"use strict";var n=Object.defineProperty;var r=(e,s)=>n(e,"name",{value:s,configurable:!0});var l=Object.defineProperty,u=r((e,s)=>l(e,"name",{value:s,configurable:!0}),"n");export const DEFAULT_THRESHOLDS={dbInLoop:5,nPlusOne:3,renderStorm:5,slowRouteMs:500,slowPageMs:1e3,slowRenderMs:16,slowQueryMs:100,duplicateRequestPeak:3,slowEgressMs:1e3};export function resolveThresholds(e){const s={...DEFAULT_THRESHOLDS};if(e)for(const t of Object.keys(DEFAULT_THRESHOLDS)){const o=e[t];typeof o=="number"&&Number.isFinite(o)&&o>=0&&(s[t]=o)}return s}r(resolveThresholds,"resolveThresholds"),u(resolveThresholds,"resolveThresholds");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lensmcp/core",
|
|
3
|
-
"version": "1.25.
|
|
3
|
+
"version": "1.25.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
}
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
|
-
"@lensmcp/protocol-types": "1.25.
|
|
67
|
+
"@lensmcp/protocol-types": "1.25.7",
|
|
68
68
|
"tslib": "^2.3.0"
|
|
69
69
|
},
|
|
70
70
|
"license": "Apache-2.0",
|