@lensmcp/react-instrumentation 1.16.6 → 1.16.8

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.
@@ -1 +1 @@
1
- {"version":3,"file":"publish.d.ts","sourceRoot":"","sources":["../../src/lib/publish.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD,MAAM,MAAM,eAAe,GACvB,QAAQ,GACR,OAAO,GACP,SAAS,GACT,SAAS,GACT,QAAQ,GACR,OAAO,CAAC;AAEZ,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,OAAO,GAAG,QAAQ,GAAG,gBAAgB,GAAG,QAAQ,CAAC;IACzD,QAAQ,EAAE,eAAe,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kFAAkF;IAClF,OAAO,CAAC,EAAE,OAAO,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;QACrB,cAAc,EAAE,MAAM,CAAC;QACvB,KAAK,EAAE,MAAM,CAAC;QACd,GAAG,EAAE,MAAM,CAAC;KACb,CAAC,CAAC;IACH,GAAG,EAAE,mBAAmB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpD;AAED,KAAK,SAAS,GAAG,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;AAElD,OAAO,CAAC,MAAM,CAAC;IACb,IAAI,mBAAmB,EAAE,SAAS,GAAG,SAAS,CAAC;CAChD;AAmBD,wBAAgB,gBAAgB,CAAC,IAAI,CAAC,EAAE,QAAQ,GAAG,IAAI,CAGtD;AAMD,gFAAgF;AAChF,wBAAgB,UAAU,IAAI,QAAQ,GAAG,SAAS,CAEjD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,QAAQ,CAgBnG;AAED,wBAAgB,YAAY,CAAC,EAAE,EAAE,SAAS,GAAG,SAAS,GAAG,IAAI,CAc5D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAQ3D;AAED,wBAAgB,WAAW,IAAI,QAAQ,GAAG,SAAS,CAElD;AAKD;;;;;;;;;;;;GAYG;AACH,wBAAgB,QAAQ,CAAC,KAAK,SAAS,OAAO,EAAE,EAAE,OAAO,EACvD,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,EACpC,IAAI,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAO,GACxD,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,CAgC7B;AAQD,wBAAgB,OAAO,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI,CA6BlD;AAED,sDAAsD;AACtD,wBAAgB,UAAU,IAAI,eAAe,EAAE,CAE9C"}
1
+ {"version":3,"file":"publish.d.ts","sourceRoot":"","sources":["../../src/lib/publish.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD,MAAM,MAAM,eAAe,GACvB,QAAQ,GACR,OAAO,GACP,SAAS,GACT,SAAS,GACT,QAAQ,GACR,OAAO,CAAC;AAEZ,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,OAAO,GAAG,QAAQ,GAAG,gBAAgB,GAAG,QAAQ,CAAC;IACzD,QAAQ,EAAE,eAAe,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kFAAkF;IAClF,OAAO,CAAC,EAAE,OAAO,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;QACrB,cAAc,EAAE,MAAM,CAAC;QACvB,KAAK,EAAE,MAAM,CAAC;QACd,GAAG,EAAE,MAAM,CAAC;KACb,CAAC,CAAC;IACH,GAAG,EAAE,mBAAmB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpD;AAED,KAAK,SAAS,GAAG,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;AAElD,OAAO,CAAC,MAAM,CAAC;IACb,IAAI,mBAAmB,EAAE,SAAS,GAAG,SAAS,CAAC;CAChD;AAmBD,wBAAgB,gBAAgB,CAAC,IAAI,CAAC,EAAE,QAAQ,GAAG,IAAI,CAGtD;AAMD,gFAAgF;AAChF,wBAAgB,UAAU,IAAI,QAAQ,GAAG,SAAS,CAEjD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,QAAQ,CAgBnG;AAED,wBAAgB,YAAY,CAAC,EAAE,EAAE,SAAS,GAAG,SAAS,GAAG,IAAI,CAc5D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAQ3D;AAED,wBAAgB,WAAW,IAAI,QAAQ,GAAG,SAAS,CAElD;AAqCD;;;;;;;;;;;;GAYG;AACH,wBAAgB,QAAQ,CAAC,KAAK,SAAS,OAAO,EAAE,EAAE,OAAO,EACvD,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,EACpC,IAAI,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAO,GACxD,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,CA4D7B;AAQD,wBAAgB,OAAO,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI,CA6BlD;AAED,sDAAsD;AACtD,wBAAgB,UAAU,IAAI,eAAe,EAAE,CAE9C"}
package/lib/publish.js CHANGED
@@ -89,6 +89,41 @@ export function currentFlow() {
89
89
  }
90
90
  /** Marks a handler as already flow-wrapped so layered wrapping is a no-op. */
91
91
  const FLOWED = Symbol.for('lensmcp.flowed');
92
+ /**
93
+ * INPUT-CHANGE taming (the "an AI streamed text into an input → hundreds of flows" fix). Two rules,
94
+ * applied ONLY to `originType: 'input-change'` (clicks/submits keep one-flow-per-invocation):
95
+ *
96
+ * 1. A PROGRAMMATIC change (the event arg is untrusted — `isTrusted === false`) is a CONSEQUENCE,
97
+ * never a user intent: it must not mint a flow. The handler runs as-is and its effects inherit the
98
+ * ambient flow (the click/submit that started the stream), which is the true causality; a live
99
+ * ambient window is extended so a long stream keeps attributing to its trigger.
100
+ * 2. A TRUSTED burst on the SAME element (typing) COALESCES: invocations within a sliding window reuse
101
+ * the burst's flow and publish NO second origin — one editing burst = one flow, not one per keystroke.
102
+ */
103
+ const INPUT_COALESCE_MS = 2000;
104
+ const inputBursts = new Map();
105
+ /** The event's trust, wherever React put it (SyntheticEvent.nativeEvent or a raw DOM event). */
106
+ function eventTrust(arg) {
107
+ if (arg == null || typeof arg !== 'object')
108
+ return undefined;
109
+ const ev = arg;
110
+ const nested = ev.nativeEvent;
111
+ if (nested != null && typeof nested === 'object') {
112
+ const trusted = nested.isTrusted;
113
+ if (typeof trusted === 'boolean')
114
+ return trusted;
115
+ }
116
+ if (typeof ev.isTrusted === 'boolean')
117
+ return ev.isTrusted;
118
+ return undefined;
119
+ }
120
+ function pruneInputBursts(now) {
121
+ if (inputBursts.size <= 64)
122
+ return;
123
+ for (const [key, burst] of inputBursts)
124
+ if (now >= burst.until)
125
+ inputBursts.delete(key);
126
+ }
92
127
  /**
93
128
  * Wrap a React event handler so every call runs inside a fresh flow.
94
129
  * The flowId is generated per invocation; `originType` defaults to
@@ -108,25 +143,52 @@ export function withFlow(handler, spec = {}) {
108
143
  const existing = handler;
109
144
  if (existing[FLOWED])
110
145
  return handler;
146
+ const publishOrigin = () => {
147
+ // The origin event — published inside the flow so it carries the
148
+ // flowId. It guarantees the flow materialises in the reducer (which
149
+ // keys on the first event with a flowId) even when every downstream
150
+ // effect lands asynchronously, outside the synchronous flow window.
151
+ publish({
152
+ source: 'react',
153
+ category: 'runtime',
154
+ severity: 'info',
155
+ title: spec.originType ?? 'user-click',
156
+ fingerprint: `flow-origin:${spec.originNodeId ?? 'unknown'}`,
157
+ raw: { kind: 'flow-origin', originNodeId: spec.originNodeId },
158
+ });
159
+ };
111
160
  const flowed = function flowed(...args) {
161
+ if (spec.originType === 'input-change') {
162
+ // Rule 1 — a programmatic (untrusted) input change never mints a flow; it inherits the ambient
163
+ // one (the interaction that caused it). Keep a LIVE window alive so a long stream stays attributed.
164
+ if (eventTrust(args[0]) === false) {
165
+ if (currentFlow() == null && windowedFlow() != null)
166
+ extendFlowWindow();
167
+ return handler(...args);
168
+ }
169
+ // Rule 2 — a trusted burst on the same element coalesces into ONE flow (sliding window).
170
+ const key = spec.originNodeId ?? 'input';
171
+ const now = Date.now();
172
+ const burst = inputBursts.get(key);
173
+ if (burst && now < burst.until) {
174
+ burst.until = now + INPUT_COALESCE_MS;
175
+ return runInFlow(burst.spec, () => handler(...args)); // same flow, no second origin event
176
+ }
177
+ const burstSpec = { flowId: newFlowId(), originType: 'input-change', originNodeId: spec.originNodeId };
178
+ inputBursts.set(key, { spec: burstSpec, until: now + INPUT_COALESCE_MS });
179
+ pruneInputBursts(now);
180
+ return runInFlow(burstSpec, () => {
181
+ publishOrigin();
182
+ return handler(...args);
183
+ });
184
+ }
112
185
  const flowId = newFlowId();
113
186
  return runInFlow({
114
187
  flowId,
115
188
  originType: spec.originType ?? 'user-click',
116
189
  originNodeId: spec.originNodeId,
117
190
  }, () => {
118
- // The origin event — published inside the flow so it carries the
119
- // flowId. It guarantees the flow materialises in the reducer (which
120
- // keys on the first event with a flowId) even when every downstream
121
- // effect lands asynchronously, outside the synchronous flow window.
122
- publish({
123
- source: 'react',
124
- category: 'runtime',
125
- severity: 'info',
126
- title: spec.originType ?? 'user-click',
127
- fingerprint: `flow-origin:${spec.originNodeId ?? 'unknown'}`,
128
- raw: { kind: 'flow-origin', originNodeId: spec.originNodeId },
129
- });
191
+ publishOrigin();
130
192
  return handler(...args);
131
193
  });
132
194
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lensmcp/react-instrumentation",
3
- "version": "1.16.6",
3
+ "version": "1.16.8",
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.16.6",
22
+ "@lensmcp/protocol-types": "1.16.8",
23
23
  "tslib": "^2.3.0"
24
24
  },
25
25
  "peerDependencies": {