@lensmcp/react-instrumentation 1.21.5 → 1.22.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/README.md CHANGED
@@ -24,7 +24,8 @@ If you can't run the global transform, the runtime is usable directly:
24
24
  import { createRoot } from 'react-dom/client';
25
25
  import { LensmcpRoot, installFlowFetch } from '@lensmcp/react-instrumentation';
26
26
 
27
- // Wrap browser fetches so they carry the active flow's headers.
27
+ // Wrap browser fetches so they carry the active flow's headers. Same-origin and your own
28
+ // machine only — name any other backend you own with `propagateTo`.
28
29
  installFlowFetch();
29
30
 
30
31
  createRoot(document.getElementById('root')!).render(
@@ -72,7 +73,7 @@ Exports from the package entry (`@lensmcp/react-instrumentation`):
72
73
  | `tracedUseEffect` / `tracedUseMemo` / `tracedUseCallback` | Drop-in hook replacements that emit `effect-run`/`hook-run` events; the transform rewrites bare hooks to these. |
73
74
  | `TraceSlot` / `TraceSlotProps` | Wrapper that emits `slot-content-changed` when its primary child's type changes. |
74
75
  | `withTraceComponent` / `WithTraceOptions` | HOC for opt-in render tracing when the global transform isn't available. |
75
- | `installFlowFetch` / `FlowFetchOptions` | Monkeypatches `globalThis.fetch` to inject flow headers and publish network legs; returns an uninstall function. |
76
+ | `installFlowFetch` / `FlowFetchOptions` | Monkeypatches `globalThis.fetch` to inject flow headers and publish network legs; returns an uninstall function. Headers go to the page's origin, loopback, and origins named in `propagateTo` / `window.__LENSMCP_FLOW_PROPAGATE__` — never to a third party, since `originNodeId` is a source path. |
76
77
  | `withFlow` | Wraps an event handler so each call runs inside a fresh correlated flow (idempotent; passes non-functions through). |
77
78
  | `runInFlow` / `beginBackgroundFlow` / `currentFlow` / `activeFlow` / `extendFlowWindow` | Flow-stack and causality-window primitives for starting/inspecting/extending flows manually. |
78
79
  | `publish` / `setPublisher` / `drainQueue` | The publish channel: emit an event, install the transport, or drain the buffered queue (tests). |
package/index.d.ts CHANGED
@@ -7,7 +7,7 @@ export { installFlowFetch, type FlowFetchOptions } from './lib/flow-fetch.js';
7
7
  export { useComponentIdentity } from './lib/component-identity.js';
8
8
  export type { ComponentIdentity } from './lib/component-identity.js';
9
9
  export { installFiberRenderPublisher, resetFiberRenderPublisherForTests } from './lib/fiber-renders.js';
10
- export { publish, setPublisher, drainQueue, runInFlow, currentFlow, activeFlow, extendFlowWindow, beginBackgroundFlow, withFlow, type PublishEnvelope, type PublishCategory, type FlowSpec, } from './lib/publish.js';
10
+ export { publish, publishLazy, setPublisher, drainQueue, queuedByteCount, runInFlow, currentFlow, activeFlow, extendFlowWindow, beginBackgroundFlow, withFlow, type PublishEnvelope, type PublishCategory, type FlowSpec, } from './lib/publish.js';
11
11
  export { componentLogicalId, hookLogicalId, slotLogicalId, makeInstanceId, depsHash, resetIdentityState, } from './lib/identity.js';
12
12
  export type { RenderRecord, EffectRecord, HookRecord, SlotChangeRecord, ComponentMountRecord, ReactPublishPayload, } from './lib/types.js';
13
13
  export { default as lensmcpBabelPlugin, type LensmcpBabelOptions } from './lib/babel-plugin.js';
package/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";export{LensmcpRoot}from"./lib/lensmcp-root.js";export{LensmcpContext,useLensmcpContext}from"./lib/context.js";export{tracedUseEffect,tracedUseMemo,tracedUseCallback}from"./lib/traced-hooks.js";export{TraceSlot}from"./lib/trace-slot.js";export{withTraceComponent}from"./lib/hoc.js";export{installFlowFetch}from"./lib/flow-fetch.js";export{useComponentIdentity}from"./lib/component-identity.js";export{installFiberRenderPublisher,resetFiberRenderPublisherForTests}from"./lib/fiber-renders.js";export{publish,setPublisher,drainQueue,runInFlow,currentFlow,activeFlow,extendFlowWindow,beginBackgroundFlow,withFlow}from"./lib/publish.js";export{componentLogicalId,hookLogicalId,slotLogicalId,makeInstanceId,depsHash,resetIdentityState}from"./lib/identity.js";export{default as lensmcpBabelPlugin}from"./lib/babel-plugin.js";
1
+ "use strict";export{LensmcpRoot}from"./lib/lensmcp-root.js";export{LensmcpContext,useLensmcpContext}from"./lib/context.js";export{tracedUseEffect,tracedUseMemo,tracedUseCallback}from"./lib/traced-hooks.js";export{TraceSlot}from"./lib/trace-slot.js";export{withTraceComponent}from"./lib/hoc.js";export{installFlowFetch}from"./lib/flow-fetch.js";export{useComponentIdentity}from"./lib/component-identity.js";export{installFiberRenderPublisher,resetFiberRenderPublisherForTests}from"./lib/fiber-renders.js";export{publish,publishLazy,setPublisher,drainQueue,queuedByteCount,runInFlow,currentFlow,activeFlow,extendFlowWindow,beginBackgroundFlow,withFlow}from"./lib/publish.js";export{componentLogicalId,hookLogicalId,slotLogicalId,makeInstanceId,depsHash,resetIdentityState}from"./lib/identity.js";export{default as lensmcpBabelPlugin}from"./lib/babel-plugin.js";
@@ -37,6 +37,23 @@ export interface LensmcpBabelOptions {
37
37
  /** Which JSX `on*` attributes start a flow. Default: the intent set below. */
38
38
  flowEvents?: string[];
39
39
  }
40
+ /**
41
+ * The path stamped into `data-agent-source` and into a flow's `originNodeId`.
42
+ *
43
+ * issues/codex-security/39: this used to be `file.slice(rootDir.length)` when the file was under the
44
+ * root and **the raw absolute path otherwise** — so a monorepo app (every file outside the Vite root
45
+ * is) shipped `/Users/<name>/work/acme/libs/ui/Button.tsx` into the DOM and, until the same finding's
46
+ * origin gate landed in `flow-fetch.ts`, into third-party request headers. The username and the
47
+ * machine's directory layout are not ours to publish.
48
+ *
49
+ * `startsWith` was also the usual prefix trap: with a root of `/w/app`, `/w/app-secrets/x.tsx`
50
+ * "started with" it and came out as `-secrets/x.tsx`. Comparing whole SEGMENTS is what closes that.
51
+ *
52
+ * Deliberately hand-rolled rather than `node:path`: this module is re-exported from the package
53
+ * barrel, which `@lensmcp/vite-plugin` bundles into the browser, and a `node:` import there is a
54
+ * build failure for every consumer.
55
+ */
56
+ export declare function displaySourcePath(file: string, rootDir: string): string;
40
57
  interface State extends PluginPass {
41
58
  opts: LensmcpBabelOptions;
42
59
  fileIgnored?: boolean;
@@ -1 +1 @@
1
- "use strict";var w=Object.defineProperty;var d=(o,e)=>w(o,"name",{value:e,configurable:!0});var j=Object.defineProperty,c=d((o,e)=>j(o,"name",{value:e,configurable:!0}),"a");const h="@lensmcp/react-instrumentation",b={useEffect:"tracedUseEffect",useMemo:"tracedUseMemo",useCallback:"tracedUseCallback"},C=["onClick","onDoubleClick","onContextMenu","onSubmit","onChange"],J={onSubmit:"form-submit",onChange:"input-change"};export default function v(o){const e=o.types;return{name:"@lensmcp/react-instrumentation/babel-plugin",visitor:{Program:{enter(t,n){if(n.needHelpers=new Set,n.needRoot=!1,n.hookIndex=0,n.flowEvents=new Set(n.opts.flowEvents??C),n.rootVars=new Set,n.opts.production??process.env.NODE_ENV==="production"){n.fileIgnored=!0;return}(t.node.body[0]?.leadingComments??t.node.directives.flatMap(a=>a.leadingComments??[])??[]).some(a=>a.value.includes("@lensmcp-ignore"))&&(n.fileIgnored=!0);const r=n.file?.opts?.filename,i=n.opts.rootDir??process.cwd();r&&r.startsWith(i)?n.relativePath=r.slice(i.length).replace(/^\/+/,""):r&&(n.relativePath=r)},exit(t,n){if(n.fileIgnored)return;const r=[...n.needHelpers??[]];if(n.needRoot&&r.push("LensmcpRoot","installFlowFetch"),r.length===0)return;const i=x(t.node.body,h,e),a=r.filter(s=>!i.has(s));if(a.length>0&&t.node.body.unshift(e.importDeclaration(a.map(s=>e.importSpecifier(e.identifier(s),e.identifier(s))),e.stringLiteral(h))),n.needRoot){const s=t.node.body.findIndex(l=>!e.isImportDeclaration(l)),p=e.expressionStatement(e.callExpression(e.identifier("installFlowFetch"),[]));t.node.body.splice(s<0?t.node.body.length:s,0,p)}}},VariableDeclarator(t,n){n.fileIgnored||n.opts.autoWrapRoot!==!1&&e.isIdentifier(t.node.id)&&t.node.init&&f(t.node.init,e)&&n.rootVars.add(t.node.id.name)},CallExpression(t,n){if(n.fileIgnored)return;const r=t.node.callee;if(n.opts.autoWrapHooks!==!1&&e.isIdentifier(r)&&b[r.name]){const i=b[r.name],a=`${n.relativePath??"(anon)"}:${t.node.loc?.start.line??0}:${n.hookIndex++}`;t.node.callee=e.identifier(i),t.node.arguments.unshift(e.stringLiteral(a)),n.needHelpers.add(i);return}if(n.opts.autoWrapRoot!==!1&&e.isMemberExpression(r)&&e.isIdentifier(r.property,{name:"render"})&&(f(r.object,e)||e.isIdentifier(r.object)&&n.rootVars?.has(r.object.name))&&t.node.arguments.length>=1){const i=t.node.arguments[0];i&&!y(i,e)&&(t.node.arguments[0]=e.jsxElement(e.jsxOpeningElement(e.jsxIdentifier("LensmcpRoot"),[],!1),e.jsxClosingElement(e.jsxIdentifier("LensmcpRoot")),[e.jsxExpressionContainer(i)],!1),n.needRoot=!0)}},JSXOpeningElement(t,n){if(n.fileIgnored)return;const r=t.node.attributes;if(u(r,"data-agent-component")||u(r,"data-lensmcp-ignore")||(t.parent.leadingComments??t.node.leadingComments??[]).some(s=>s.value.includes("@lensmcp-ignore")))return;if(n.opts.autoFlowHandlers!==!1){const s=t.node.loc?.start.line??0,p=n.relativePath??"(anon)";for(const l of t.node.attributes){if(l.type!=="JSXAttribute"||l.name.type!=="JSXIdentifier"||!n.flowEvents.has(l.name.name)||!l.value||l.value.type!=="JSXExpressionContainer")continue;const m=l.value.expression;if(m.type==="JSXEmptyExpression"||I(m,e))continue;const g=l.name.name;l.value.expression=e.callExpression(e.identifier("withFlow"),[m,e.objectExpression([e.objectProperty(e.identifier("originType"),e.stringLiteral(J[g]??"user-click")),e.objectProperty(e.identifier("originNodeId"),e.stringLiteral(`ui:${p}:${s}:${g}`))])]),n.needHelpers.add("withFlow")}}const i=S(t.node.name);if(!i||E(i))return;const a=[e.jsxAttribute(e.jsxIdentifier("data-agent-component"),e.stringLiteral(i))];if(n.opts.includeSource!==!1){const s=t.node.loc?.start.line??0,p=n.relativePath??"(anon)";a.push(e.jsxAttribute(e.jsxIdentifier("data-agent-source"),e.stringLiteral(`${p}:${s}`)))}t.node.attributes.push(...a)}}}}d(v,"y"),c(v,"lensmcpBabelPlugin");function u(o,e){return o.some(t=>t.type==="JSXAttribute"&&t.name.type==="JSXIdentifier"&&t.name.name===e)}d(u,"b"),c(u,"hasAttr");function x(o,e,t){const n=new Set;for(const r of o)if(t.isImportDeclaration(r)&&r.source.value===e)for(const i of r.specifiers)t.isImportSpecifier(i)&&t.isIdentifier(i.imported)&&n.add(i.imported.name);return n}d(x,"h"),c(x,"collectImported");function f(o,e){if(!e.isCallExpression(o))return!1;const t=o.callee,n=new Set(["createRoot","hydrateRoot"]);return e.isIdentifier(t)?n.has(t.name):e.isMemberExpression(t)&&e.isIdentifier(t.property)?n.has(t.property.name):!1}d(f,"I"),c(f,"isRootFactoryCall");function I(o,e){return e.isCallExpression(o)&&e.isIdentifier(o.callee,{name:"withFlow"})}d(I,"v"),c(I,"isAlreadyWithFlow");function y(o,e){return e.isJSXElement(o)&&e.isJSXIdentifier(o.openingElement.name,{name:"LensmcpRoot"})}d(y,"C"),c(y,"isAlreadyLensmcpRoot");const R=new Set(["Fragment","StrictMode","Suspense","SuspenseList","Profiler"]);function E(o){const e=o.split(".").pop()??o;return R.has(e)?!0:e.endsWith("Provider")||e.endsWith("Consumer")}d(E,"L"),c(E,"isUnstampableElement");function S(o){switch(o.type){case"JSXIdentifier":return o.name;case"JSXMemberExpression":{const e=[];let t=o;for(;t.type==="JSXMemberExpression";)e.unshift(t.property.name),t=t.object;return t.type==="JSXIdentifier"&&e.unshift(t.name),e.join(".")}case"JSXNamespacedName":return`${o.namespace.name}:${o.name.name}`;default:return}}d(S,"P"),c(S,"describeElementName");
1
+ "use strict";var v=Object.defineProperty;var a=(o,e)=>v(o,"name",{value:e,configurable:!0});var j=Object.defineProperty,d=a((o,e)=>j(o,"name",{value:e,configurable:!0}),"c");const X=4;export function displaySourcePath(o,e){const n=d(p=>p.replace(/\\/g,"/").replace(/\/+$/,"").split("/"),"segments"),t=n(o),i=n(e);let r=0;for(;r<t.length&&r<i.length&&t[r]===i[r];)r++;const l=i.length-r;if(r===0||l>4)return`<external>/${t[t.length-1]??"unknown"}`;const s=t.slice(r);return s.length===0?"<external>/unknown":[...new Array(l).fill(".."),...s].join("/")}a(displaySourcePath,"displaySourcePath"),d(displaySourcePath,"displaySourcePath");const h="@lensmcp/react-instrumentation",b={useEffect:"tracedUseEffect",useMemo:"tracedUseMemo",useCallback:"tracedUseCallback"},C=["onClick","onDoubleClick","onContextMenu","onSubmit","onChange"],R={onSubmit:"form-submit",onChange:"input-change"};export default function w(o){const e=o.types;return{name:"@lensmcp/react-instrumentation/babel-plugin",visitor:{Program:{enter(n,t){if(t.needHelpers=new Set,t.needRoot=!1,t.hookIndex=0,t.flowEvents=new Set(t.opts.flowEvents??C),t.rootVars=new Set,t.opts.production??process.env.NODE_ENV==="production"){t.fileIgnored=!0;return}(n.node.body[0]?.leadingComments??n.node.directives.flatMap(r=>r.leadingComments??[])??[]).some(r=>r.value.includes("@lensmcp-ignore"))&&(t.fileIgnored=!0);const i=t.file?.opts?.filename;i&&(t.relativePath=displaySourcePath(i,t.opts.rootDir??process.cwd()))},exit(n,t){if(t.fileIgnored)return;const i=[...t.needHelpers??[]];if(t.needRoot&&i.push("LensmcpRoot","installFlowFetch"),i.length===0)return;const r=x(n.node.body,h,e),l=i.filter(s=>!r.has(s));if(l.length>0&&n.node.body.unshift(e.importDeclaration(l.map(s=>e.importSpecifier(e.identifier(s),e.identifier(s))),e.stringLiteral(h))),t.needRoot){const s=n.node.body.findIndex(c=>!e.isImportDeclaration(c)),p=e.expressionStatement(e.callExpression(e.identifier("installFlowFetch"),[]));n.node.body.splice(s<0?n.node.body.length:s,0,p)}}},VariableDeclarator(n,t){t.fileIgnored||t.opts.autoWrapRoot!==!1&&e.isIdentifier(n.node.id)&&n.node.init&&f(n.node.init,e)&&t.rootVars.add(n.node.id.name)},CallExpression(n,t){if(t.fileIgnored)return;const i=n.node.callee;if(t.opts.autoWrapHooks!==!1&&e.isIdentifier(i)&&b[i.name]){const r=b[i.name],l=`${t.relativePath??"(anon)"}:${n.node.loc?.start.line??0}:${t.hookIndex++}`;n.node.callee=e.identifier(r),n.node.arguments.unshift(e.stringLiteral(l)),t.needHelpers.add(r);return}if(t.opts.autoWrapRoot!==!1&&e.isMemberExpression(i)&&e.isIdentifier(i.property,{name:"render"})&&(f(i.object,e)||e.isIdentifier(i.object)&&t.rootVars?.has(i.object.name))&&n.node.arguments.length>=1){const r=n.node.arguments[0];r&&!I(r,e)&&(n.node.arguments[0]=e.jsxElement(e.jsxOpeningElement(e.jsxIdentifier("LensmcpRoot"),[],!1),e.jsxClosingElement(e.jsxIdentifier("LensmcpRoot")),[e.jsxExpressionContainer(r)],!1),t.needRoot=!0)}},JSXOpeningElement(n,t){if(t.fileIgnored)return;const i=n.node.attributes;if(m(i,"data-agent-component")||m(i,"data-lensmcp-ignore")||(n.parent.leadingComments??n.node.leadingComments??[]).some(s=>s.value.includes("@lensmcp-ignore")))return;if(t.opts.autoFlowHandlers!==!1){const s=n.node.loc?.start.line??0,p=t.relativePath??"(anon)";for(const c of n.node.attributes){if(c.type!=="JSXAttribute"||c.name.type!=="JSXIdentifier"||!t.flowEvents.has(c.name.name)||!c.value||c.value.type!=="JSXExpressionContainer")continue;const u=c.value.expression;if(u.type==="JSXEmptyExpression"||y(u,e))continue;const g=c.name.name;c.value.expression=e.callExpression(e.identifier("withFlow"),[u,e.objectExpression([e.objectProperty(e.identifier("originType"),e.stringLiteral(R[g]??"user-click")),e.objectProperty(e.identifier("originNodeId"),e.stringLiteral(`ui:${p}:${s}:${g}`))])]),t.needHelpers.add("withFlow")}}const r=E(n.node.name);if(!r||S(r))return;const l=[e.jsxAttribute(e.jsxIdentifier("data-agent-component"),e.stringLiteral(r))];if(t.opts.includeSource!==!1){const s=n.node.loc?.start.line??0,p=t.relativePath??"(anon)";l.push(e.jsxAttribute(e.jsxIdentifier("data-agent-source"),e.stringLiteral(`${p}:${s}`)))}n.node.attributes.push(...l)}}}}a(w,"y"),d(w,"lensmcpBabelPlugin");function m(o,e){return o.some(n=>n.type==="JSXAttribute"&&n.name.type==="JSXIdentifier"&&n.name.name===e)}a(m,"S"),d(m,"hasAttr");function x(o,e,n){const t=new Set;for(const i of o)if(n.isImportDeclaration(i)&&i.source.value===e)for(const r of i.specifiers)n.isImportSpecifier(r)&&n.isIdentifier(r.imported)&&t.add(r.imported.name);return t}a(x,"h"),d(x,"collectImported");function f(o,e){if(!e.isCallExpression(o))return!1;const n=o.callee,t=new Set(["createRoot","hydrateRoot"]);return e.isIdentifier(n)?t.has(n.name):e.isMemberExpression(n)&&e.isIdentifier(n.property)?t.has(n.property.name):!1}a(f,"b"),d(f,"isRootFactoryCall");function y(o,e){return e.isCallExpression(o)&&e.isIdentifier(o.callee,{name:"withFlow"})}a(y,"C"),d(y,"isAlreadyWithFlow");function I(o,e){return e.isJSXElement(o)&&e.isJSXIdentifier(o.openingElement.name,{name:"LensmcpRoot"})}a(I,"j"),d(I,"isAlreadyLensmcpRoot");const J=new Set(["Fragment","StrictMode","Suspense","SuspenseList","Profiler"]);function S(o){const e=o.split(".").pop()??o;return J.has(e)?!0:e.endsWith("Provider")||e.endsWith("Consumer")}a(S,"P"),d(S,"isUnstampableElement");function E(o){switch(o.type){case"JSXIdentifier":return o.name;case"JSXMemberExpression":{const e=[];let n=o;for(;n.type==="JSXMemberExpression";)e.unshift(n.property.name),n=n.object;return n.type==="JSXIdentifier"&&e.unshift(n.name),e.join(".")}case"JSXNamespacedName":return`${o.namespace.name}:${o.name.name}`;default:return}}a(E,"A"),d(E,"describeElementName");
@@ -1 +1 @@
1
- "use strict";var M=Object.defineProperty;var d=(r,e)=>M(r,"name",{value:e,configurable:!0});var P=Object.defineProperty,u=d((r,e)=>P(r,"name",{value:e,configurable:!0}),"d");import{makeInstanceId as $}from"./identity.js";import{publish as f}from"./publish.js";const k=1,R=0,D=1,F=11,O=14,T=15,_=15e3,b=25,y=15,j=8;let h=!1,m=!1;function w(r){const e=r.type;if(!(e==null||typeof e=="string"))switch(r.tag){case R:case D:return e.displayName||e.name||void 0;case F:return e.displayName||e.render?.displayName||e.render?.name||void 0;case O:case T:return e.displayName||e.type?.displayName||e.type?.name||e.name||void 0;default:return}}d(w,"k"),u(w,"componentNameOf");function N(r){const e=r.alternate?.memoizedProps,o=r.memoizedProps;if(!e||!o||e===o)return[];const t=[];for(const n of Object.keys(o))if(n!=="children"&&!Object.is(e[n],o[n])&&(t.push(n),t.length>=j))break;return t}d(N,"C"),u(N,"changedPropKeys");function v(r){const e=[];let o=0;const t=[];for(r.current.child&&t.push({node:r.current.child,parentName:void 0});t.length>0;){const{node:n,parentName:s}=t.pop();if(o+=1,o>_)break;let c=s;const i=w(n),a=(n.flags&k)!==0;if(i&&a){const l=n.alternate==null,p=[];if(!l){const g=N(n);g.length>0&&p.push({type:"props",changedKeys:g}),s&&p.push({type:"parent-render",parentRenderId:s}),p.length===0&&p.push({type:"react-state",hookIndex:-1})}e.push({name:i,phase:l?"mount":"update",durationMs:typeof n.actualDuration=="number"?n.actualDuration:0,why:p,parentName:s}),c=i}n.sibling&&t.push({node:n.sibling,parentName:s}),n.child&&t.push({node:n.child,parentName:c})}return e}d(v,"D"),u(v,"collectCommitRenders");function I(r,e){const o=r.length>b?[...r].sort((t,n)=>n.durationMs-t.durationMs).slice(0,b):r;for(const t of o){const n=`react:component:(fiber):${t.name}`,{instanceId:s}=$(n),c={id:s,componentInstanceId:s,componentLogicalId:n,componentName:t.name,rendererId:"fiber",phase:t.phase,actualDurationMs:t.durationMs,baseDurationMs:t.durationMs,startTime:e,commitTime:e,why:t.why};f({source:"react",category:"render",severity:"info",title:`render ${t.name} (${t.phase})`,fingerprint:`react-render:${n}`,raw:{kind:"render",render:c}})}r.length>o.length&&f({source:"react",category:"render",severity:"debug",title:`render commit truncated: ${r.length-o.length} of ${r.length} renders dropped (per-commit cap)`,fingerprint:"react-render:commit-truncated",raw:{kind:"render-commit-truncated",total:r.length,published:o.length}})}d(I,"P"),u(I,"publishRecords");export function installFiberRenderPublisher(){if(h||m||typeof window>"u")return;const r=window.__LENSMCP_FIBER__;if(!r||typeof r.subscribe!="function")return;h=!0;let e=y,o=0;const t=new Map;let n;const s=u(()=>{if(n=void 0,e=y,o===0)return;const c=[...t.entries()].sort((i,a)=>a[1]-i[1]).slice(0,5);f({source:"react",category:"render",severity:"warning",title:`render storm: ${o} commits/s over budget \u2014 top: ${c.map(([i,a])=>`${i}\xD7${a}`).join(", ")}`,fingerprint:"react-render:storm",raw:{kind:"render-storm",commits:o,components:Object.fromEntries(c)}}),o=0,t.clear()},"flushStorm");r.subscribe((c,i)=>{if(!m)try{if(!i||!i.current)return;const a=v(i);if(a.length===0)return;if(e>0)e-=1,I(a,Date.now());else{o+=1;for(const l of a)t.set(l.name,(t.get(l.name)??0)+1)}n||(n=setTimeout(s,1e3))}catch{m=!0}})}d(installFiberRenderPublisher,"installFiberRenderPublisher"),u(installFiberRenderPublisher,"installFiberRenderPublisher");export function resetFiberRenderPublisherForTests(){h=!1,m=!1}d(resetFiberRenderPublisherForTests,"resetFiberRenderPublisherForTests"),u(resetFiberRenderPublisherForTests,"resetFiberRenderPublisherForTests");
1
+ "use strict";var R=Object.defineProperty;var d=(r,t)=>R(r,"name",{value:t,configurable:!0});var E=Object.defineProperty,l=d((r,t)=>E(r,"name",{value:t,configurable:!0}),"d");import{makeInstanceId as M}from"./identity.js";import{publish as h}from"./publish.js";const $=1,k=0,D=1,S=11,_=14,T=15,C=15e3,y=25,b=15,F=8,L=(typeof process<"u"?process.env?.LENSMCP_RENDER_INCLUDE_INTERNALS:void 0)==="1",O=new Set(["Insertion","EmotionGlobal","EmotionCssPropInternal","GlobalStyles","ThemeProvider","CacheProvider"]);function N(r){return!!(O.has(r)||r.startsWith("Styled(")&&/^Styled\([a-z][a-zA-Z0-9-]*\)$/.test(r)||r==="lensmcp-root"||r.startsWith("Lensmcp"))}d(N,"P"),l(N,"isInternalRender");let f=!1,m=!1;function v(r){const t=r.type;if(!(t==null||typeof t=="string"))switch(r.tag){case k:case D:return t.displayName||t.name||void 0;case S:return t.displayName||t.render?.displayName||t.render?.name||void 0;case _:case T:return t.displayName||t.type?.displayName||t.type?.name||t.name||void 0;default:return}}d(v,"A"),l(v,"componentNameOf");function w(r){const t=r.alternate?.memoizedProps,n=r.memoizedProps;if(!t||!n||t===n)return[];const o=[];for(const e of Object.keys(n))if(e!=="children"&&!Object.is(t[e],n[e])&&(o.push(e),o.length>=F))break;return o}d(w,"O"),l(w,"changedPropKeys");function I(r){const t=[];let n=0;const o=[];for(r.current.child&&o.push({node:r.current.child,parentName:void 0});o.length>0;){const{node:e,parentName:s}=o.pop();if(n+=1,n>C)break;let i=s;const a=v(e),c=(e.flags&$)!==0;if(a&&c){const u=e.alternate==null,p=[];if(!u){const g=w(e);g.length>0&&p.push({type:"props",changedKeys:g}),s&&p.push({type:"parent-render",parentRenderId:s}),p.length===0&&p.push({type:"react-state",hookIndex:-1})}t.push({name:a,phase:u?"mount":"update",durationMs:typeof e.actualDuration=="number"?e.actualDuration:0,why:p,parentName:s}),i=a}e.sibling&&o.push({node:e.sibling,parentName:s}),e.child&&o.push({node:e.child,parentName:i})}return t}d(I,"k"),l(I,"collectCommitRenders");function P(r,t){const n=L?r:r.filter(e=>!N(e.name));if(n.length===0)return;const o=n.length>y?[...n].sort((e,s)=>s.durationMs-e.durationMs).slice(0,y):n;for(const e of o){const s=`react:component:(fiber):${e.name}`,{instanceId:i}=M(s),a={id:i,componentInstanceId:i,componentLogicalId:s,componentName:e.name,rendererId:"fiber",phase:e.phase,actualDurationMs:e.durationMs,baseDurationMs:e.durationMs,startTime:t,commitTime:t,why:e.why};h({source:"react",category:"render",severity:"info",title:`render ${e.name} (${e.phase})`,fingerprint:`react-render:${s}`,raw:{kind:"render",render:a}})}n.length>o.length&&h({source:"react",category:"render",severity:"debug",title:`render commit truncated: ${n.length-o.length} of ${n.length} renders dropped (per-commit cap)`,fingerprint:"react-render:commit-truncated",raw:{kind:"render-commit-truncated",total:n.length,published:o.length}})}d(P,"L"),l(P,"publishRecords");export function installFiberRenderPublisher(){if(f||m||typeof window>"u")return;const r=window.__LENSMCP_FIBER__;if(!r||typeof r.subscribe!="function")return;f=!0;let t=b,n=0;const o=new Map;let e;const s=l(()=>{if(e=void 0,t=b,n===0)return;const i=[...o.entries()].sort((a,c)=>c[1]-a[1]).slice(0,5);h({source:"react",category:"render",severity:"warning",title:`render storm: ${n} commits/s over budget \u2014 top: ${i.map(([a,c])=>`${a}\xD7${c}`).join(", ")}`,fingerprint:"react-render:storm",raw:{kind:"render-storm",commits:n,components:Object.fromEntries(i)}}),n=0,o.clear()},"flushStorm");r.subscribe((i,a)=>{if(!m)try{if(!a||!a.current)return;const c=I(a);if(c.length===0)return;if(t>0)t-=1,P(c,Date.now());else{n+=1;for(const u of c)o.set(u.name,(o.get(u.name)??0)+1)}e||(e=setTimeout(s,1e3))}catch{m=!0}})}d(installFiberRenderPublisher,"installFiberRenderPublisher"),l(installFiberRenderPublisher,"installFiberRenderPublisher");export function resetFiberRenderPublisherForTests(){f=!1,m=!1}d(resetFiberRenderPublisherForTests,"resetFiberRenderPublisherForTests"),l(resetFiberRenderPublisherForTests,"resetFiberRenderPublisherForTests");
@@ -3,5 +3,33 @@ export interface FlowFetchOptions {
3
3
  flowHeader?: string;
4
4
  /** Header carrying the origin node id. Default `x-lensmcp-origin-node-id`. */
5
5
  originHeader?: string;
6
+ /**
7
+ * Extra origins allowed to receive the flow headers, beyond the page's own
8
+ * origin and the developer's own machine (see `mayPropagateTo`). Give the
9
+ * origin of a backend YOU run — `['https://api.mycompany.dev']`.
10
+ *
11
+ * The babel transform injects `installFlowFetch()` with no arguments, so a
12
+ * zero-config app cannot reach this; set `globalThis.__LENSMCP_FLOW_PROPAGATE__`
13
+ * (an array, or a comma-separated string) before the root module instead.
14
+ */
15
+ propagateTo?: string[];
6
16
  }
17
+ /**
18
+ * `x-lensmcp-origin-node-id` is a SOURCE PATH — `ui:src/features/billing/CardForm.tsx:118:click`.
19
+ * The patched fetch used to stamp it on every request a flow made, so an app that talks to
20
+ * analytics, a CDN, a payment SDK or any other third party handed that party a running map of the
21
+ * private repository: file layout, feature names, and which line of which component the user is on.
22
+ * The flow id rides along and correlates those disclosures into a session.
23
+ *
24
+ * So the headers are OURS to send only where the answer comes back to us:
25
+ *
26
+ * - the page's own origin (which is also where every relative URL resolves), and
27
+ * - the developer's own machine — a dev backend on `localhost:3000` behind a Vite proxy, or
28
+ * reached directly, is the same trust boundary as the source tree this instrumentation reads;
29
+ * this whole transform is dev-only.
30
+ *
31
+ * Anything else needs `propagateTo` / `__LENSMCP_FLOW_PROPAGATE__` said out loud. A URL that will
32
+ * not parse is not ours by definition.
33
+ */
34
+ export declare function mayPropagateTo(rawUrl: string, extraOrigins?: readonly string[]): boolean;
7
35
  export declare function installFlowFetch(options?: FlowFetchOptions): () => void;
package/lib/flow-fetch.js CHANGED
@@ -1 +1 @@
1
- "use strict";var $=Object.defineProperty;var l=(o,e)=>$(o,"name",{value:e,configurable:!0});var m=Object.defineProperty,f=l((o,e)=>m(o,"name",{value:e,configurable:!0}),"f");import{activeFlow as T,beginBackgroundFlow as b,extendFlowWindow as g,publish as h}from"./publish.js";let w=!1;export function installFlowFetch(o={}){const e=globalThis;if(typeof e.fetch!="function")return()=>{};!w&&typeof location<"u"&&typeof document<"u"&&(w=!0,b({originType:"page-load",originNodeId:`page:${location.pathname}`}));const c=o.flowHeader??"x-lensmcp-flow-id",d=o.originHeader??"x-lensmcp-origin-node-id",r=e.fetch.bind(globalThis);globalThis.__LENSMCP_FLOW_FETCH__=!0;const n=f(((i,s)=>{const t=T();if(!t?.flowId)return r(i,s);const y=typeof i=="object"&&i!==null&&"headers"in i?i.headers:void 0,a=new Headers(s?.headers??y??void 0);a.has(c)||a.set(c,t.flowId),t.originNodeId&&!a.has(d)&&a.set(d,t.originNodeId);const u=r(i,{...s,headers:a});return p(t,i,s,u),u}),"patched");return e.fetch=n,()=>{e.fetch=r}}l(installFlowFetch,"installFlowFetch"),f(installFlowFetch,"installFlowFetch");function p(o,e,c,d){const r=typeof e=="string"?e:e instanceof URL?e.href:e.url??String(e),n=c?.method??(typeof e=="object"&&e!==null&&"method"in e?e.method??"GET":"GET"),i=Date.now(),s={flowId:o.flowId,...o.originType?{originType:o.originType}:{},...o.originNodeId?{originNodeId:o.originNodeId}:{}};h({source:"react",category:"network",severity:"info",title:`fetch ${n} ${r}`,fingerprint:`flow-fetch:${n}:${r}`,raw:{kind:"fetch-start",url:r,method:n}}),d.then(t=>{g(o),h({source:"react",category:"network",severity:t.ok?"info":"error",title:`fetch ${n} ${r} \u2192 ${t.status}${t.statusText?` ${t.statusText}`:""}`,fingerprint:`flow-fetch:${n}:${r}:${t.status}`,context:s,raw:{kind:"fetch-done",url:r,method:n,status:t.status,statusText:t.statusText,ok:t.ok,startedAt:i,durationMs:Date.now()-i}})},t=>{g(o),h({source:"react",category:"network",severity:"error",title:`fetch ${n} ${r} failed`,message:t instanceof Error?t.message:String(t),fingerprint:`flow-fetch:${n}:${r}:error`,context:s,raw:{kind:"fetch-error",url:r,method:n,startedAt:i,durationMs:Date.now()-i}})})}l(p,"m"),f(p,"publishFetchLeg");
1
+ "use strict";var k=Object.defineProperty;var s=(t,e)=>k(t,"name",{value:e,configurable:!0});var x=Object.defineProperty,c=s((t,e)=>x(t,"name",{value:e,configurable:!0}),"s");import{activeFlow as L,beginBackgroundFlow as F,extendFlowWindow as m,publish as g}from"./publish.js";export function mayPropagateTo(t,e=[]){const f=typeof location<"u"?location.href:void 0;let a;try{a=new URL(t,f)}catch{return!1}if(typeof location<"u"&&a.origin===location.origin||T(a.hostname))return!0;for(const n of e){let r;try{r=new URL(n).origin}catch{continue}if(r===a.origin)return!0}return!1}s(mayPropagateTo,"mayPropagateTo"),c(mayPropagateTo,"mayPropagateTo");const v=new Set(["localhost","[::1]","::1"]);function T(t){const e=t.toLowerCase();return v.has(e)||/^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(e)}s(T,"x"),c(T,"isLoopbackHost");function $(){const t=globalThis.__LENSMCP_FLOW_PROPAGATE__;return Array.isArray(t)?t.filter(e=>typeof e=="string"):typeof t=="string"?t.split(",").map(e=>e.trim()).filter(Boolean):[]}s($,"k"),c($,"globalPropagateList");function h(t){return typeof t=="string"?t:t instanceof URL?t.href:t.url??String(t)}s(h,"y"),c(h,"urlOf");let b=!1;export function installFlowFetch(t={}){const e=globalThis;if(typeof e.fetch!="function")return()=>{};!b&&typeof location<"u"&&typeof document<"u"&&(b=!0,F({originType:"page-load",originNodeId:`page:${location.pathname}`}));const f=t.flowHeader??"x-lensmcp-flow-id",a=t.originHeader??"x-lensmcp-origin-node-id",n=t.propagateTo??[],r=e.fetch.bind(globalThis);globalThis.__LENSMCP_FLOW_FETCH__=!0;const l=c(((i,o)=>{const d=L();if(!d?.flowId)return r(i,o);if(!mayPropagateTo(h(i),[...n,...$()])){const y=r(i,o);return p(d,i,o,y),y}const _=typeof i=="object"&&i!==null&&"headers"in i?i.headers:void 0,u=new Headers(o?.headers??_??void 0);u.has(f)||u.set(f,d.flowId),d.originNodeId&&!u.has(a)&&u.set(a,d.originNodeId);const w=r(i,{...o,headers:u});return p(d,i,o,w),w}),"patched");return e.fetch=l,()=>{e.fetch=r}}s(installFlowFetch,"installFlowFetch"),c(installFlowFetch,"installFlowFetch");function p(t,e,f,a){const n=h(e),r=f?.method??(typeof e=="object"&&e!==null&&"method"in e?e.method??"GET":"GET"),l=Date.now(),i={flowId:t.flowId,...t.originType?{originType:t.originType}:{},...t.originNodeId?{originNodeId:t.originNodeId}:{}};g({source:"react",category:"network",severity:"info",title:`fetch ${r} ${n}`,fingerprint:`flow-fetch:${r}:${n}`,raw:{kind:"fetch-start",url:n,method:r}}),a.then(o=>{m(t),g({source:"react",category:"network",severity:o.ok||o.status===304?"info":"error",title:`fetch ${r} ${n} \u2192 ${o.status}${o.statusText?` ${o.statusText}`:""}`,fingerprint:`flow-fetch:${r}:${n}:${o.status}`,context:i,raw:{kind:"fetch-done",url:n,method:r,status:o.status,statusText:o.statusText,ok:o.ok,startedAt:l,durationMs:Date.now()-l}})},o=>{m(t),g({source:"react",category:"network",severity:"error",title:`fetch ${r} ${n} failed`,message:o instanceof Error?o.message:String(o),fingerprint:`flow-fetch:${r}:${n}:error`,context:i,raw:{kind:"fetch-error",url:n,method:r,startedAt:l,durationMs:Date.now()-l}})})}s(p,"$"),c(p,"publishFetchLeg");
package/lib/publish.d.ts CHANGED
@@ -1,13 +1,3 @@
1
- /**
2
- * Single publish channel + module-level "current flow" stack. The
3
- * `@lensmcp/client-runtime` injected into the served HTML sets
4
- * `window.__LENSMCP_PUBLISH__` to a function that takes a partial event
5
- * and forwards it to the LensMCP bus (via WebSocket in dev, or via the
6
- * FrontMCP server transport in production).
7
- *
8
- * If the channel isn't installed we silently no-op — this keeps the
9
- * library safe to import from any React tree (tests, SSR, etc).
10
- */
11
1
  import type { ReactPublishPayload } from './types.js';
12
2
  export type PublishCategory = 'render' | 'state' | 'runtime' | 'network' | 'visual' | 'trace';
13
3
  export interface FlowSpec {
@@ -38,6 +28,8 @@ type PublishFn = (event: PublishEnvelope) => void;
38
28
  declare global {
39
29
  var __LENSMCP_PUBLISH__: PublishFn | undefined;
40
30
  }
31
+ /** Test seam: what the queue is currently holding, in estimated bytes. */
32
+ export declare function queuedByteCount(): number;
41
33
  export declare function extendFlowWindow(spec?: FlowSpec): void;
42
34
  /** The flow that owns work happening NOW: sync stack first, else the window. */
43
35
  export declare function activeFlow(): FlowSpec | undefined;
@@ -83,6 +75,16 @@ export declare function withFlow<TArgs extends unknown[], TResult>(handler: (...
83
75
  originNodeId?: string;
84
76
  }): (...args: TArgs) => TResult;
85
77
  export declare function publish(env: PublishEnvelope): void;
78
+ /**
79
+ * Publish only if the fingerprint's budget allows — and BUILD the payload only
80
+ * then.
81
+ *
82
+ * Use this instead of {@link publish} wherever assembling the envelope is itself
83
+ * expensive. The fingerprint has to be derivable cheaply (an id and a path), which
84
+ * is the whole trick: the decision is made from data you already have, before any
85
+ * clone or walk happens.
86
+ */
87
+ export declare function publishLazy(fingerprint: string, build: () => PublishEnvelope): void;
86
88
  /** Test hook — only useful in unit tests / smokes. */
87
89
  export declare function drainQueue(): PublishEnvelope[];
88
90
  export {};
package/lib/publish.js CHANGED
@@ -1 +1 @@
1
- "use strict";var F=Object.defineProperty;var i=(e,o)=>F(e,"name",{value:o,configurable:!0});var v=Object.defineProperty,n=i((e,o)=>v(e,"name",{value:o,configurable:!0}),"t");const u=[],B=200,g=[],S=4e3;let l;export function extendFlowWindow(e){const o=e??currentFlow()??l?.spec;o&&(l={spec:o,until:Date.now()+4e3})}i(extendFlowWindow,"extendFlowWindow"),n(extendFlowWindow,"extendFlowWindow");function p(){return l&&Date.now()<l.until?l.spec:void 0}i(p,"w"),n(p,"windowedFlow");export function activeFlow(){return currentFlow()??p()}i(activeFlow,"activeFlow"),n(activeFlow,"activeFlow");export function beginBackgroundFlow(e){const o={flowId:w(),originType:e.originType,...e.originNodeId?{originNodeId:e.originNodeId}:{}};return l={spec:o,until:Date.now()+4e3},publish({source:"react",category:"runtime",severity:"info",title:e.originType,fingerprint:`flow-origin:${e.originNodeId??e.originType}`,raw:{kind:"flow-origin",originNodeId:e.originNodeId}}),o}i(beginBackgroundFlow,"beginBackgroundFlow"),n(beginBackgroundFlow,"beginBackgroundFlow");export function setPublisher(e){if(globalThis.__LENSMCP_PUBLISH__=e,e)for(;u.length;){const o=u.shift();if(o)try{e(o)}catch{}}}i(setPublisher,"setPublisher"),n(setPublisher,"setPublisher");export function runInFlow(e,o){g.push(e),l={spec:e,until:Date.now()+4e3};try{return o()}finally{g.pop()}}i(runInFlow,"runInFlow"),n(runInFlow,"runInFlow");export function currentFlow(){return g[g.length-1]}i(currentFlow,"currentFlow"),n(currentFlow,"currentFlow");const N=Symbol.for("lensmcp.flowed"),h=2e3,c=new Map;function T(e){if(e==null||typeof e!="object")return;const o=e,t=o.nativeEvent;if(t!=null&&typeof t=="object"){const r=t.isTrusted;if(typeof r=="boolean")return r}if(typeof o.isTrusted=="boolean")return o.isTrusted}i(T,"S"),n(T,"eventTrust");function b(e){if(!(c.size<=64))for(const[o,t]of c)e>=t.until&&c.delete(o)}i(b,"F"),n(b,"pruneInputBursts");export function withFlow(e,o={}){if(typeof e!="function"||e[N])return e;const t=n(()=>{publish({source:"react",category:"runtime",severity:"info",title:o.originType??"user-click",fingerprint:`flow-origin:${o.originNodeId??"unknown"}`,raw:{kind:"flow-origin",originNodeId:o.originNodeId}})},"publishOrigin"),r=n(function(...d){if(o.originType==="input-change"){if(T(d[0])===!1)return currentFlow()==null&&p()!=null&&extendFlowWindow(),e(...d);const I=o.originNodeId??"input",s=Date.now(),f=c.get(I);if(f&&s<f.until)return f.until=s+h,runInFlow(f.spec,()=>e(...d));const y={flowId:w(),originType:"input-change",originNodeId:o.originNodeId};return c.set(I,{spec:y,until:s+h}),b(s),runInFlow(y,()=>(t(),e(...d)))}const x=w();return runInFlow({flowId:x,originType:o.originType??"user-click",originNodeId:o.originNodeId},()=>(t(),e(...d)))},"flowed");return r[N]=!0,r}i(withFlow,"withFlow"),n(withFlow,"withFlow");let a=0;function w(){return a=(a+1)%65535,`flow:${Date.now().toString(36)}:${a.toString(36)}`}i(w,"g"),n(w,"newFlowId");export function publish(e){const o=currentFlow()??(e.context?.flowId?void 0:p()),t=o==null?e:{...e,context:{...o.flowId?{flowId:o.flowId}:{},...o.originType?{originType:o.originType}:{},...o.originNodeId?{originNodeId:o.originNodeId}:{},...o.causedByNodeId?{causedByNodeId:o.causedByNodeId}:{},...e.context}},r=globalThis.__LENSMCP_PUBLISH__;if(r)try{r(t);return}catch{}u.length>=200&&u.shift(),u.push(t)}i(publish,"publish"),n(publish,"publish");export function drainQueue(){return u.splice(0,u.length)}i(drainQueue,"drainQueue"),n(drainQueue,"drainQueue");
1
+ "use strict";var k=Object.defineProperty;var n=(e,o)=>k(e,"name",{value:o,configurable:!0});var E=Object.defineProperty,t=n((e,o)=>E(e,"name",{value:o,configurable:!0}),"t");import{createEventFloodGuard as L,estimateJsonBytes as P}from"@lensmcp/protocol-types";const u=[],D=200,T=(()=>{const e=typeof process<"u"?process.env?.LENSMCP_PUBLISH_QUEUE_BYTES:void 0,o=Number(e);return Number.isFinite(o)&&o>0?Math.floor(o):1024*1024})(),g=[];let s=0;function x(e){const o=P(e,T);for(u.push(e),g.push(o),s+=o;u.length>D||s>T&&u.length>1;)u.shift(),s-=g.shift()??0;u.length===0&&(s=0)}n(x,"m"),t(x,"enqueue");function F(){const e=u.shift();return s-=g.shift()??0,u.length===0&&(s=0),e}n(F,"L"),t(F,"dequeue");export function queuedByteCount(){return s}n(queuedByteCount,"queuedByteCount"),t(queuedByteCount,"queuedByteCount");const a=[],I=4e3;let l;export function extendFlowWindow(e){const o=e??currentFlow()??l?.spec;o&&(l={spec:o,until:Date.now()+I})}n(extendFlowWindow,"extendFlowWindow"),t(extendFlowWindow,"extendFlowWindow");function w(){return l&&Date.now()<l.until?l.spec:void 0}n(w,"y"),t(w,"windowedFlow");export function activeFlow(){return currentFlow()??w()}n(activeFlow,"activeFlow"),t(activeFlow,"activeFlow");export function beginBackgroundFlow(e){const o={flowId:y(),originType:e.originType,...e.originNodeId?{originNodeId:e.originNodeId}:{}};return l={spec:o,until:Date.now()+I},publish({source:"react",category:"runtime",severity:"info",title:e.originType,fingerprint:`flow-origin:${e.originNodeId??e.originType}`,raw:{kind:"flow-origin",originNodeId:e.originNodeId}}),o}n(beginBackgroundFlow,"beginBackgroundFlow"),t(beginBackgroundFlow,"beginBackgroundFlow");export function setPublisher(e){if(globalThis.__LENSMCP_PUBLISH__=e,e)for(;u.length;){const o=F();if(o)try{e(o)}catch{}}}n(setPublisher,"setPublisher"),t(setPublisher,"setPublisher");export function runInFlow(e,o){a.push(e),l={spec:e,until:Date.now()+I};try{return o()}finally{a.pop()}}n(runInFlow,"runInFlow"),t(runInFlow,"runInFlow");export function currentFlow(){return a[a.length-1]}n(currentFlow,"currentFlow"),t(currentFlow,"currentFlow");const v=Symbol.for("lensmcp.flowed"),B=2e3,d=new Map;function _(e){if(e==null||typeof e!="object")return;const o=e,i=o.nativeEvent;if(i!=null&&typeof i=="object"){const r=i.isTrusted;if(typeof r=="boolean")return r}if(typeof o.isTrusted=="boolean")return o.isTrusted}n(_,"k"),t(_,"eventTrust");function S(e){if(!(d.size<=64))for(const[o,i]of d)e>=i.until&&d.delete(o)}n(S,"M"),t(S,"pruneInputBursts");export function withFlow(e,o={}){if(typeof e!="function"||e[v])return e;const i=t(()=>{publish({source:"react",category:"runtime",severity:"info",title:o.originType??"user-click",fingerprint:`flow-origin:${o.originNodeId??"unknown"}`,raw:{kind:"flow-origin",originNodeId:o.originNodeId}})},"publishOrigin"),r=t(function(...c){if(o.originType==="input-change"){if(_(c[0])===!1)return currentFlow()==null&&w()!=null&&extendFlowWindow(),e(...c);const N=o.originNodeId??"input",f=Date.now(),p=d.get(N);if(p&&f<p.until)return p.until=f+B,runInFlow(p.spec,()=>e(...c));const b={flowId:y(),originType:"input-change",originNodeId:o.originNodeId};return d.set(N,{spec:b,until:f+B}),S(f),runInFlow(b,()=>(i(),e(...c)))}const m=y();return runInFlow({flowId:m,originType:o.originType??"user-click",originNodeId:o.originNodeId},()=>(i(),e(...c)))},"flowed");return r[v]=!0,r}n(withFlow,"withFlow"),t(withFlow,"withFlow");let h=0;function y(){return h=(h+1)%65535,`flow:${Date.now().toString(36)}:${h.toString(36)}`}n(y,"a"),t(y,"newFlowId");export function publish(e){const o=currentFlow()??(e.context?.flowId?void 0:w()),i=o==null?e:{...e,context:{...o.flowId?{flowId:o.flowId}:{},...o.originType?{originType:o.originType}:{},...o.originNodeId?{originNodeId:o.originNodeId}:{},...o.causedByNodeId?{causedByNodeId:o.causedByNodeId}:{},...e.context}},r=globalThis.__LENSMCP_PUBLISH__;if(r)try{r(i);return}catch{}x(i)}n(publish,"publish"),t(publish,"publish");const M=L({burst:30,perSec:5});export function publishLazy(e,o){const i=M.admit(e);if(!i.ok)return;const r=o();if(i.suppressed>0&&r.raw!==null&&typeof r.raw=="object"){publish({...r,raw:{...r.raw,floodSuppressed:i.suppressed}});return}publish(r)}n(publishLazy,"publishLazy"),t(publishLazy,"publishLazy");export function drainQueue(){return g.length=0,s=0,u.splice(0,u.length)}n(drainQueue,"drainQueue"),t(drainQueue,"drainQueue");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lensmcp/react-instrumentation",
3
- "version": "1.21.5",
3
+ "version": "1.22.0",
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.21.5",
22
+ "@lensmcp/protocol-types": "1.22.0",
23
23
  "tslib": "^2.3.0"
24
24
  },
25
25
  "peerDependencies": {