@lensmcp/nest-instrumentation 1.25.32 → 1.25.33
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-start.d.ts +28 -0
- package/lib/flow-start.js +1 -0
- package/lib/trace-interceptor.js +2 -2
- package/package.json +4 -4
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A request context's START signal: how a request the gateway routed with no flow is given one when its work turns out
|
|
3
|
+
* to matter.
|
|
4
|
+
*
|
|
5
|
+
* A request that arrives with no flow starts `api:<request id>` (`@lensmcp/core` headless-flow.ts) — unless the gateway
|
|
6
|
+
* routed it (`x-internal-token`). The gateway starts no flow for a call between the cluster's own services that carries
|
|
7
|
+
* none: that is a service's background chatter (a timer polling a directory every few seconds), and a flow for each
|
|
8
|
+
* poll would bury the flows a person opens. On FoodGuard (2026-10-27) those were ~5,600 routed requests in three hours,
|
|
9
|
+
* beside 189 flows.
|
|
10
|
+
*
|
|
11
|
+
* But chatter sometimes does the work a reader asks about. Engine's memory consolidation is a `setInterval`: it asks
|
|
12
|
+
* knowledge to remember a summary, and knowledge embeds it — an AI call, paid for, filed under a request id and no
|
|
13
|
+
* flow. Before east-west hops carried the caller's flow (tap-egress `hopHeaders`), every processor and knowledge AI
|
|
14
|
+
* call landed that way: 1,392 of them, none in a flow.
|
|
15
|
+
*
|
|
16
|
+
* So such a request carries a START signal. The context is opened here (`TraceInterceptor`), but the work that decides
|
|
17
|
+
* it matters is seen by `@lensmcp/node-instrumentation`'s taps (an AI provider's call), and that package depends on
|
|
18
|
+
* this one. The seam is the context object itself, the way `context-end.ts` crosses: the interceptor attaches a starter
|
|
19
|
+
* under a REGISTERED symbol, and a tap holding the live context calls it. The starter gives the request the flow the
|
|
20
|
+
* pod would have — `api:<request id>` — and says what started it (its route, stamped at the request's start), once.
|
|
21
|
+
* What the request does from then on carries the flow: its AI call, its later queries, spans and hops, its
|
|
22
|
+
* `server-request`. What it did before does not.
|
|
23
|
+
*/
|
|
24
|
+
export declare const FLOW_START: unique symbol;
|
|
25
|
+
/** Starts the context's flow, once; answers the flow it is in. */
|
|
26
|
+
export type FlowStart = () => string | undefined;
|
|
27
|
+
/** Give `ctx` a start signal. Not enumerable: a spread or a JSON of the context never carries it. */
|
|
28
|
+
export declare function attachFlowStart(ctx: object, start: FlowStart): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var a=Object.defineProperty;var r=(t,e)=>a(t,"name",{value:e,configurable:!0});var o=Object.defineProperty,c=r((t,e)=>o(t,"name",{value:e,configurable:!0}),"e");export const FLOW_START=Symbol.for("lensmcp.flowStart");export function attachFlowStart(t,e){Object.defineProperty(t,FLOW_START,{value:e})}r(attachFlowStart,"attachFlowStart"),c(attachFlowStart,"attachFlowStart");
|
package/lib/trace-interceptor.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var re=Object.defineProperty;var u=(t,e)=>re(t,"name",{value:e,configurable:!0});var se=Object.defineProperty,i=u((t,e)=>se(t,"name",{value:e,configurable:!0}),"a");import{__decorate as oe}from"tslib";import{Injectable as ne}from"@nestjs/common";import{Observable as z}from"rxjs";import{catchError as ae,tap as ie}from"rxjs/operators";import{fingerprint as $,headlessFlowId as Q,headlessFlowsEnabled as X,isProbePath as J,LENS_REQUEST_ID_HEADER as de,requestFacts as ue,responseFacts as ce,shapeOf as K,ulid as v}from"@lensmcp/core";import{activeMemoryOptions as fe,onFlowStart as le,onFlowSettled as pe}from"@lensmcp/memory-tracker";import{LENSMCP_CONTEXT_STORAGE as me}from"./context.js";import{attachContextEnd as Ie}from"./context-end.js";import{attachFlowStart as ge}from"./flow-start.js";import{activeOptions as q}from"./state.js";const we=Symbol.for("lensmcp.exchangeId");let T=class{static{u(this,"S")}static{i(this,"TraceInterceptor")}intercept(t,e){const o=q();if(!o.trace.requests)return e.handle();const d=t.switchToHttp(),r=d.getRequest(),s=d.getResponse(),n=h(r.headers?.[de])??h(r.headers?.["x-request-id"])??v(),c=h(r.headers?.["x-lensmcp-flow-id"]),f=h(r.headers?.["x-internal-token"])!==void 0,m=c??(!f&&X()&&!J(r.url)?Q(n):void 0),a={sessionId:o.sessionId,requestId:n,parentRequestId:h(r.headers?.["x-lensmcp-parent-request-id"]),flowId:m,originNodeId:h(r.headers?.["x-lensmcp-origin-node-id"]),traceparent:h(r.headers?.traceparent),runId:v(),dbCallCount:0},I=r[we],l=(r.method??"GET").toUpperCase(),p=r.route?.path??r.url??"<unknown>",D=t.getClass?.()?.name,M=t.getHandler?.()?.name,y=Date.now();m&&!c&&N(a,l,p,y),!m&&f&&X()&&!J(r.url)&&ge(a,()=>(a.flowId||(a.flowId=Q(n),N(a,l,p,y)),a.flowId));const _=o.memory.mode!=="off";_&&le(n);const A=i(()=>{if(!_)return;const g=fe()?.settleMs??1e3;setTimeout(()=>pe(n,a.flowId),g).unref?.()},"settleAndCheck"),W=Ie(a);let E;typeof s?.once=="function"&&ye()&&s.once("finish",()=>V(o.projectName,a,l,p,r,s,E));let H=!1,S=typeof s?.once!="function";const P=i(()=>{H&&S&&W()},"endIfOver");if(!S){const g=i(()=>{S=!0,P()},"responseOut");s.once?.("finish",g),s.once?.("close",g)}return new z(g=>{let F;return me.run(a,()=>{F=e.handle().pipe(ie({next:i(w=>{E??={value:w},g.next(w)},"next"),complete:i(()=>{x(o.projectName,a,l,p,s.statusCode??200,y,void 0,I,k(s)),O(a,D,M,y,"ok"),A(),g.complete()},"complete")}),ae(w=>{const B=w?.getResponse?.();B!==void 0&&(E??={value:B});const L=j(w);if(L!==void 0||typeof s.once!="function")x(o.projectName,a,l,p,L??R(s),y,w,I,k(s));else{let U=!1;const G=i(ee=>{if(U)return;U=!0;const te=ee&&s.statusCode&&s.statusCode>=400?s.statusCode:R(s);x(o.projectName,a,l,p,te,y,w,I,k(s))},"settle");s.once("finish",()=>G(!0)),s.once("close",()=>G(!1))}return O(a,D,M,y,"error"),A(),g.error(w),new z})).subscribe()}),()=>{F?.unsubscribe(),H=!0,P()}})}};T=oe([ne()],T);export{T as TraceInterceptor};const b=new Map,he=6e4,ye=i(()=>process.env.LENSMCP_HTTP_SHAPES!=="0","shapesOn");function V(t,e,o,d,r,s,n){try{const c=s.statusCode??200,f=`${o} ${d} ${Math.floor(c/100)}`,m=Date.now();if(m-(b.get(f)??0)<he)return;b.set(f,m),b.size>2e3&&b.delete(b.keys().next().value);const a=r.headers??{},I=r.body!==void 0&&r.body!==null&&typeof r.body=="object"&&!Buffer.isBuffer(r.body)?K(r.body):void 0,l=n&&n.value!==void 0&&typeof n.value=="object"&&n.value!==null&&!Buffer.isBuffer(n.value)&&typeof n.value.pipe!="function"?K(n.value):void 0,p={id:v(),sessionId:e.sessionId,timestamp:m,source:"nestjs",category:"backend",severity:"info",context:{sessionId:e.sessionId,flowId:e.flowId,requestId:e.requestId,...e.runId?{runId:e.runId}:{}},fingerprint:$({kind:"http-exchange",identity:f}),title:`${o} ${d} \u2192 ${c} (its shape)`,raw:{kind:"http-exchange",method:o,route:d,status:c,request:{...ue(a,r.query,r.params),...I?{body:I}:{}},response:{...ce(s.getHeaders?.()??{},typeof a.origin=="string"?a.origin:void 0),...l?{body:l}:{}}}};C(p,t),q().emit(p)}catch{}}u(V,"ye"),i(V,"recordExchange");function j(t){const e=t;if(typeof e?.getStatus=="function")return e.getStatus();if(typeof e?.status=="number"&&e.status>=100)return e.status;if(typeof e?.statusCode=="number"&&e.statusCode>=100)return e.statusCode}u(j,"Q"),i(j,"ownStatus");const ve=new Set(["TypeError","ReferenceError","SyntaxError","RangeError","EvalError","URIError"]);export function requestOutcome(t,e){return e instanceof Error&&ve.has(e.name)&&j(e)===void 0?"fault":t<400?"ok":t<500?"refused":t===500?"fault":"declared"}u(requestOutcome,"requestOutcome"),i(requestOutcome,"requestOutcome");function Y(t){return t==="ok"?"info":t==="refused"?"warning":"error"}u(Y,"we"),i(Y,"severityOf");function R(t){return t.statusCode&&t.statusCode>=400?t.statusCode:500}u(R,"V"),i(R,"inferStatus");function h(t){if(typeof t=="string")return t;if(Array.isArray(t)&&typeof t[0]=="string")return t[0]}u(h,"h"),i(h,"stringHeader");function N(t,e,o,d){const r=q(),s=`request:${e} ${o}`,n={id:v(),sessionId:t.sessionId,timestamp:d,source:"nestjs",category:"backend",severity:"info",context:{sessionId:t.sessionId,flowId:t.flowId,requestId:t.requestId,originType:"request",originNodeId:s},fingerprint:$({kind:"flow-origin",identity:`${e}:${o}`}),title:`${e} ${o}`,raw:{kind:"flow-origin",originNodeId:s,method:e,route:o}};C(n,r.projectName),r.emit(n)}u(N,"Y"),i(N,"emitRequestOrigin");function O(t,e,o,d,r){if(!e||!o)return;const s=q(),n=Date.now()-d,c=`${e}.${o}`,f={id:v(),sessionId:t.sessionId,timestamp:Date.now(),source:"nestjs",category:"backend",severity:r==="error"?"error":"info",context:{sessionId:t.sessionId,flowId:t.flowId,requestId:t.requestId,...t.runId?{runId:t.runId}:{}},fingerprint:$({kind:"method-call",identity:c}),title:`${c} (${n}ms)${r==="error"?" \u2717":""}`,raw:{kind:"span",span:{name:c,role:"controller",flowId:t.flowId,requestId:t.requestId,startTime:d,endTime:Date.now(),durationMs:n,status:r}}};C(f,q().projectName),s.emit(f)}u(O,"J"),i(O,"emitControllerSpan");function Z(t){if(!(t instanceof Error))return{message:String(t)};const e=t.stack?t.stack.split(`
|
|
2
2
|
`).slice(0,30).join(`
|
|
3
|
-
`):void 0;return{...t.name?{name:t.name}:{},message:t.message,...e?{stack:e}:{}}}u(
|
|
3
|
+
`):void 0;return{...t.name?{name:t.name}:{},message:t.message,...e?{stack:e}:{}}}u(Z,"he"),i(Z,"errorDetail");function C(t,e){if(!e)return;const o=t.raw;o&&o.project===void 0&&(o.project=e)}u(C,"b"),i(C,"stampProject");function k(t){try{const e=t.getHeaders?.()["content-type"];return typeof e=="string"?e.split(";")[0]?.trim().toLowerCase():void 0}catch{return}}u(k,"D"),i(k,"responseContentType");function x(t,e,o,d,r,s,n,c,f){const m=q(),a=Date.now()-s,I=requestOutcome(r,n),l={id:v(),sessionId:e.sessionId,timestamp:Date.now(),source:"nestjs",category:"backend",severity:Y(I),context:{sessionId:e.sessionId,flowId:e.flowId,requestId:e.requestId,...e.runId?{runId:e.runId}:{},...e.parentRequestId?{parentRequestId:e.parentRequestId}:{},originNodeId:e.originNodeId,traceparent:e.traceparent},fingerprint:$({kind:"server-request",identity:`${o}:${d}:${Math.floor(r/100)}xx`}),title:`${o} ${d} \u2192 ${r} (${a}ms)`,message:n instanceof Error?n.message:void 0,raw:{kind:"server-request",request:{method:o,route:d,status:r,outcome:I,durationMs:a,startedAt:s,...f?{contentType:f,streaming:f==="text/event-stream"}:{},endedAt:Date.now(),flowId:e.flowId,requestId:e.requestId,...e.parentRequestId?{parentRequestId:e.parentRequestId}:{},...typeof c=="string"?{exchangeId:c}:{},...e.runId?{runId:e.runId}:{},...e.dbCallCount||e.redisCallCount?{calls:{...e.dbCallCount?{db:e.dbCallCount}:{},...e.redisCallCount?{cache:e.redisCallCount}:{}}}:{},...n?{error:Z(n)}:{}}}},p={...l,id:v(),raw:{kind:"span",span:{name:`nest.request ${o} ${d}`,traceId:e.traceparent,flowId:e.flowId,requestId:e.requestId,startTime:s,endTime:Date.now(),durationMs:a,status:I==="ok"?"ok":"error"}}};C(l,t),C(p,t),m.emit(l),m.emit(p)}u(x,"O"),i(x,"emitRequest");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lensmcp/nest-instrumentation",
|
|
3
|
-
"version": "1.25.
|
|
3
|
+
"version": "1.25.33",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@lensmcp/core": "1.25.
|
|
18
|
-
"@lensmcp/memory-tracker": "1.25.
|
|
19
|
-
"@lensmcp/protocol-types": "1.25.
|
|
17
|
+
"@lensmcp/core": "1.25.33",
|
|
18
|
+
"@lensmcp/memory-tracker": "1.25.33",
|
|
19
|
+
"@lensmcp/protocol-types": "1.25.33",
|
|
20
20
|
"tslib": "^2.3.0"
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|