@lensmcp/react-instrumentation 1.16.6 → 1.16.9
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/publish.d.ts.map +1 -1
- package/lib/publish.js +74 -12
- package/package.json +2 -2
package/lib/publish.d.ts.map
CHANGED
|
@@ -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;
|
|
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
|
-
|
|
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.
|
|
3
|
+
"version": "1.16.9",
|
|
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.
|
|
22
|
+
"@lensmcp/protocol-types": "1.16.9",
|
|
23
23
|
"tslib": "^2.3.0"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|