@lensmcp/vite-plugin 1.18.4 → 1.18.6

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/index.js CHANGED
@@ -1,4 +1 @@
1
- export { lensmcpVitePlugin, handleClientEnvelope } from './lib/plugin.js';
2
- // `envelopeToEvent` moved to @lensmcp/bridge when the bridge was decoupled
3
- // from Vite; re-exported here for backwards compatibility.
4
- export { envelopeToEvent } from '@lensmcp/bridge';
1
+ "use strict";export{lensmcpVitePlugin,handleClientEnvelope}from"./lib/plugin.js";export{envelopeToEvent}from"@lensmcp/bridge";
@@ -1,169 +1,2 @@
1
- import { fingerprint, ulid } from '@lensmcp/core';
2
- const SESSION_ID_PLACEHOLDER = 'pending';
3
- /**
4
- * Translate a single client-runtime envelope into the normalised
5
- * `BaseEvent` consumed by the LensMCP event bus. The Vite plugin
6
- * publishes these; reducers in `@lensmcp/<app>` materialise them into
7
- * `runtime://browser/*` resources.
8
- */
9
- export function envelopeToEvent(env, sessionId = SESSION_ID_PLACEHOLDER) {
10
- // Per-tab identity stamped by the client runtime — keys browser://tabs.
11
- const tab = env.tabId ? { tabId: env.tabId } : {};
12
- switch (env.kind) {
13
- case 'error':
14
- return {
15
- id: ulid(),
16
- sessionId,
17
- timestamp: env.at,
18
- source: 'client-runtime',
19
- category: 'runtime',
20
- severity: 'error',
21
- context: { sessionId, ...tab },
22
- fingerprint: fingerprint({
23
- kind: 'runtime',
24
- identity: `error:${normaliseStackTopFrame(env.stack)}`,
25
- file: env.source,
26
- detail: env.message,
27
- }),
28
- title: env.message,
29
- message: env.stack ?? env.message,
30
- location: env.source
31
- ? { file: env.source, line: env.lineno, column: env.colno }
32
- : undefined,
33
- raw: env,
34
- };
35
- case 'unhandledrejection':
36
- return {
37
- id: ulid(),
38
- sessionId,
39
- timestamp: env.at,
40
- source: 'client-runtime',
41
- category: 'runtime',
42
- severity: 'error',
43
- context: { sessionId, ...tab },
44
- fingerprint: fingerprint({
45
- kind: 'runtime',
46
- identity: `unhandledrejection:${env.reason.slice(0, 80)}`,
47
- }),
48
- title: 'Unhandled promise rejection',
49
- message: env.reason,
50
- raw: env,
51
- };
52
- case 'console': {
53
- const severity = env.level === 'error' ? 'error' : env.level === 'warn' ? 'warning' : 'info';
54
- const text = env.args.map(stringify).join(' ');
55
- return {
56
- id: ulid(),
57
- sessionId,
58
- timestamp: env.at,
59
- source: 'client-runtime',
60
- category: 'runtime',
61
- severity,
62
- context: { sessionId, ...tab },
63
- fingerprint: fingerprint({
64
- kind: 'runtime',
65
- identity: `console:${env.level}:${text.slice(0, 80)}`,
66
- }),
67
- title: `console.${env.level}`,
68
- message: text,
69
- raw: env,
70
- };
71
- }
72
- case 'fetch': {
73
- const failed = env.error !== undefined || (env.status !== undefined && env.status >= 400);
74
- return {
75
- id: ulid(),
76
- sessionId,
77
- timestamp: env.at,
78
- source: 'client-runtime',
79
- category: 'network',
80
- severity: failed ? 'error' : 'info',
81
- context: { sessionId, ...tab },
82
- fingerprint: fingerprint({
83
- kind: 'network',
84
- identity: `${env.method}:${normaliseUrl(env.url)}:${env.status ?? 'err'}`,
85
- }),
86
- title: `${env.method} ${env.url}${env.status !== undefined ? ` → ${env.status}` : ''}`,
87
- message: env.error,
88
- relatedUrls: [env.url],
89
- raw: env,
90
- };
91
- }
92
- case 'route':
93
- return {
94
- id: ulid(),
95
- sessionId,
96
- timestamp: env.at,
97
- source: 'client-runtime',
98
- category: 'runtime',
99
- severity: 'info',
100
- context: { sessionId, url: env.url, ...tab },
101
- fingerprint: fingerprint({ kind: 'runtime', identity: `route:${env.url}` }),
102
- title: `Route → ${env.url}`,
103
- raw: env,
104
- };
105
- case 'ready':
106
- return {
107
- id: ulid(),
108
- sessionId,
109
- timestamp: env.at,
110
- source: 'client-runtime',
111
- category: 'runtime',
112
- severity: 'info',
113
- context: { sessionId, url: env.url, ...tab },
114
- fingerprint: fingerprint({ kind: 'runtime', identity: 'client-ready' }),
115
- title: 'Client runtime ready',
116
- message: env.userAgent,
117
- raw: env,
118
- };
119
- case 'publish':
120
- // Pre-formed publish envelope from in-page instrumentation. Trust
121
- // its source/category (the producer is our own injected code); the
122
- // reducers gate on those, and the context carries the flow id so
123
- // React renders correlate with the originating user action.
124
- return {
125
- id: ulid(),
126
- sessionId,
127
- timestamp: env.at,
128
- source: env.source,
129
- category: env.category,
130
- severity: (env.severity ?? 'info'),
131
- context: { sessionId, ...tab, ...(env.context ?? {}) },
132
- fingerprint: env.fingerprint ?? fingerprint({ kind: 'publish', identity: env.title }),
133
- title: env.title,
134
- message: env.message,
135
- raw: env.raw,
136
- };
137
- }
138
- }
139
- // --- helpers ---
140
- function normaliseStackTopFrame(stack) {
141
- if (!stack)
142
- return 'unknown';
143
- const lines = stack.split('\n').slice(0, 2);
144
- for (const line of lines) {
145
- const m = line.match(/at\s+(\S+)/);
146
- if (m && m[1])
147
- return m[1];
148
- }
149
- return 'unknown';
150
- }
151
- function normaliseUrl(url) {
152
- try {
153
- const u = new URL(url, 'http://x');
154
- return `${u.pathname}`;
155
- }
156
- catch {
157
- return url;
158
- }
159
- }
160
- function stringify(v) {
161
- if (typeof v === 'string')
162
- return v;
163
- try {
164
- return JSON.stringify(v);
165
- }
166
- catch {
167
- return String(v);
168
- }
169
- }
1
+ "use strict";var g=Object.defineProperty;var o=(t,e)=>g(t,"name",{value:e,configurable:!0});var f=Object.defineProperty,a=o((t,e)=>f(t,"name",{value:e,configurable:!0}),"o"),p=Object.defineProperty,u=a((t,e)=>p(t,"name",{value:e,configurable:!0}),"o");import{fingerprint as n,ulid as s}from"@lensmcp/core";const y="pending";export function envelopeToEvent(t,e=y){const r=t.tabId?{tabId:t.tabId}:{};switch(t.kind){case"error":return{id:s(),sessionId:e,timestamp:t.at,source:"client-runtime",category:"runtime",severity:"error",context:{sessionId:e,...r},fingerprint:n({kind:"runtime",identity:`error:${c(t.stack)}`,file:t.source,detail:t.message}),title:t.message,message:t.stack??t.message,location:t.source?{file:t.source,line:t.lineno,column:t.colno}:void 0,raw:t};case"unhandledrejection":return{id:s(),sessionId:e,timestamp:t.at,source:"client-runtime",category:"runtime",severity:"error",context:{sessionId:e,...r},fingerprint:n({kind:"runtime",identity:`unhandledrejection:${t.reason.slice(0,80)}`}),title:"Unhandled promise rejection",message:t.reason,raw:t};case"console":{const i=t.level==="error"?"error":t.level==="warn"?"warning":"info",m=t.args.map(d).join(" ");return{id:s(),sessionId:e,timestamp:t.at,source:"client-runtime",category:"runtime",severity:i,context:{sessionId:e,...r},fingerprint:n({kind:"runtime",identity:`console:${t.level}:${m.slice(0,80)}`}),title:`console.${t.level}`,message:m,raw:t}}case"fetch":{const i=t.error!==void 0||t.status!==void 0&&t.status>=400;return{id:s(),sessionId:e,timestamp:t.at,source:"client-runtime",category:"network",severity:i?"error":"info",context:{sessionId:e,...r},fingerprint:n({kind:"network",identity:`${t.method}:${l(t.url)}:${t.status??"err"}`}),title:`${t.method} ${t.url}${t.status!==void 0?` \u2192 ${t.status}`:""}`,message:t.error,relatedUrls:[t.url],raw:t}}case"route":return{id:s(),sessionId:e,timestamp:t.at,source:"client-runtime",category:"runtime",severity:"info",context:{sessionId:e,url:t.url,...r},fingerprint:n({kind:"runtime",identity:`route:${t.url}`}),title:`Route \u2192 ${t.url}`,raw:t};case"ready":return{id:s(),sessionId:e,timestamp:t.at,source:"client-runtime",category:"runtime",severity:"info",context:{sessionId:e,url:t.url,...r},fingerprint:n({kind:"runtime",identity:"client-ready"}),title:"Client runtime ready",message:t.userAgent,raw:t};case"publish":return{id:s(),sessionId:e,timestamp:t.at,source:t.source,category:t.category,severity:t.severity??"info",context:{sessionId:e,...r,...t.context??{}},fingerprint:t.fingerprint??n({kind:"publish",identity:t.title}),title:t.title,message:t.message,raw:t.raw}}}o(envelopeToEvent,"envelopeToEvent"),a(envelopeToEvent,"envelopeToEvent"),u(envelopeToEvent,"envelopeToEvent");function c(t){if(!t)return"unknown";const e=t.split(`
2
+ `).slice(0,2);for(const r of e){const i=r.match(/at\s+(\S+)/);if(i&&i[1])return i[1]}return"unknown"}o(c,"c"),a(c,"s"),u(c,"normaliseStackTopFrame");function l(t){try{return`${new URL(t,"http://x").pathname}`}catch{return t}}o(l,"l"),a(l,"m"),u(l,"normaliseUrl");function d(t){if(typeof t=="string")return t;try{return JSON.stringify(t)}catch{return String(t)}}o(d,"d"),a(d,"d"),u(d,"stringify");
package/lib/plugin.js CHANGED
@@ -1,307 +1,16 @@
1
- import { fingerprint, ulid } from '@lensmcp/core';
2
- import { createBridgeServer, makePublisher, handleClientEnvelope as forwardClientEnvelope, } from '@lensmcp/bridge';
3
- const CLIENT_VIRTUAL_ID = '/@lensmcp/client-runtime';
4
- const RESOLVED_CLIENT_VIRTUAL_ID = '\0lensmcp-client-runtime';
5
- /**
6
- * Vite plugin that:
7
- * 1. Stands up the LensMCP browser-event bridge (`@lensmcp/bridge`) on an
8
- * ephemeral WebSocket port so the injected @lensmcp/client-runtime can
9
- * post browser events back into the LensMCP event bus.
10
- * 2. Injects a `<script type="module" src="/@lensmcp/client-runtime">`
11
- * into served HTML.
12
- * 3. Resolves that virtual module to the bundled client runtime
13
- * (during Phase 1 — a minimal inline implementation; the real
14
- * package import lands in Phase 2/3).
15
- * 4. Emits HMR + module-graph events for the build reducer.
16
- *
17
- * Since the bridge was decoupled, this plugin is just ONE host adapter for
18
- * it — webpack / Next.js / no-build hosts run the same bridge as a
19
- * standalone sidecar (`lensmcp bridge`) on a fixed port instead.
20
- *
21
- * The plugin is *passive*: it never mutates app state or behaviour.
22
- */
23
- export function lensmcpVitePlugin(options = {}, deps = {}) {
24
- const enabled = options.enabled ?? true;
25
- const wsHost = options.wsHost ?? '127.0.0.1';
26
- const injectClient = options.injectClient ?? true;
27
- let wssPort;
28
- let bridge;
29
- // Bus → env-sink → console publisher, shared by every hook below.
30
- const publishEvent = makePublisher(deps);
31
- const aliasValtio = options.aliasValtio ?? true;
32
- const transformSource = options.transformSource ?? true;
33
- return {
34
- name: '@lensmcp/vite-plugin',
35
- apply: enabled ? undefined : () => false,
36
- enforce: 'pre',
37
- wsPort: () => wssPort,
38
- config() {
39
- if (!enabled || !aliasValtio)
40
- return;
41
- // Zero-config (Phase 8): make `import { proxy } from 'valtio'`
42
- // resolve to the instrumented drop-in, so existing Valtio code
43
- // becomes observable without edits. Dev-only.
44
- //
45
- // EXACT match (`/^valtio$/`) only — a bare-string alias does prefix
46
- // matching, which would also rewrite the drop-in's own
47
- // `valtio/vanilla` + `valtio/react` imports and self-loop.
48
- return {
49
- resolve: {
50
- alias: [
51
- { find: /^valtio$/, replacement: '@lensmcp/valtio-instrumentation' },
52
- ],
53
- },
54
- };
55
- },
56
- async configureServer(server) {
57
- if (!enabled)
58
- return;
59
- // The bridge owns the WebSocket + envelope→event mapping; we just
60
- // host it on an ephemeral port (or `options.wsPort` if pinned) and
61
- // bake that port into the injected runtime at `load()` time.
62
- bridge = await createBridgeServer({
63
- host: wsHost,
64
- port: options.wsPort ?? 0,
65
- bus: deps.bus,
66
- sessionId: deps.sessionId,
67
- });
68
- wssPort = bridge.port;
69
- // Vite native error stream → our bus.
70
- server.ws.on('vite:error', (data) => {
71
- publishEvent(viteErrorToEvent(data, deps.sessionId));
72
- });
73
- },
74
- resolveId(id) {
75
- if (id === CLIENT_VIRTUAL_ID)
76
- return RESOLVED_CLIENT_VIRTUAL_ID;
77
- return null;
78
- },
79
- load(id) {
80
- if (id !== RESOLVED_CLIENT_VIRTUAL_ID)
81
- return null;
82
- const port = wssPort ?? options.wsPort ?? 0;
83
- return buildInlineClientRuntime({ host: wsHost, port });
84
- },
85
- // Zero-config source transform. Runs in our OWN transform hook at
86
- // enforce:'pre', so it's independent of whichever compiler the React
87
- // plugin uses — it runs on the raw JSX/TSX BEFORE @vitejs/plugin-react
88
- // (Babel), @vitejs/plugin-react@6 (oxc, which dropped the `babel` hook)
89
- // or @vitejs/plugin-react-swc (SWC) turn JSX into calls. The LensMCP
90
- // output is plain JSX/TSX (LensmcpRoot/withFlow/traced* + data-* props),
91
- // which every downstream compiler preserves (see swc-coexistence smoke).
92
- // A native SWC/oxc rewrite would only be a perf optimisation.
93
- async transform(code, id) {
94
- if (!enabled || !transformSource)
95
- return null;
96
- const file = id.split('?')[0] ?? id;
97
- if (file.includes('/node_modules/'))
98
- return null;
99
- if (!/\.[jt]sx$/.test(file))
100
- return null;
101
- const result = await runLensmcpBabel(code, file);
102
- if (!result)
103
- return null;
104
- // Babel's source map is a valid Rollup map object at runtime; the
105
- // cast just bridges the nominal type gap.
106
- return { code: result.code, map: result.map };
107
- },
108
- transformIndexHtml() {
109
- if (!injectClient || !enabled)
110
- return;
111
- return [
112
- {
113
- tag: 'script',
114
- attrs: { type: 'module', src: CLIENT_VIRTUAL_ID },
115
- injectTo: 'head-prepend',
116
- },
117
- ];
118
- },
119
- handleHotUpdate(ctx) {
120
- if (!enabled)
121
- return;
122
- const affected = ctx.modules
123
- .map((m) => m.file ?? m.id ?? null)
124
- .filter((f) => !!f);
125
- publishEvent({
126
- id: ulid(),
127
- sessionId: deps.sessionId ?? 'pending',
128
- timestamp: Date.now(),
129
- source: 'vite',
130
- category: 'build',
131
- severity: 'info',
132
- context: { sessionId: deps.sessionId ?? 'pending' },
133
- fingerprint: fingerprint({
134
- kind: 'hmr',
135
- identity: ctx.file,
136
- }),
137
- title: `HMR ${ctx.file}`,
138
- // The changed file, structured. The build reducer retracts bundler
139
- // failures for exactly the files an update touched, so it needs them
140
- // as data — it previously had to scrape the `HMR ` title prefix.
141
- location: { file: ctx.file },
142
- relatedFiles: affected,
143
- raw: { kind: 'hmr', file: ctx.file },
144
- });
145
- },
146
- generateBundle(_opts, bundle) {
147
- if (!enabled)
148
- return;
149
- const chunks = [];
150
- let totalBytes = 0;
151
- for (const file of Object.values(bundle)) {
152
- const item = file;
153
- const name = item.fileName;
154
- let sizeBytes;
155
- if (item.type === 'chunk') {
156
- sizeBytes = (item.code ?? '').length;
157
- }
158
- else {
159
- const src = item.source;
160
- sizeBytes =
161
- typeof src === 'string' ? src.length : src instanceof Uint8Array ? src.byteLength : 0;
162
- }
163
- totalBytes += sizeBytes;
164
- const modules = [];
165
- if (item.type === 'chunk' && item.modules) {
166
- for (const [id, mod] of Object.entries(item.modules)) {
167
- const ml = mod.renderedLength ?? mod.originalLength ?? 0;
168
- modules.push({ id, sizeBytes: ml });
169
- }
170
- modules.sort((a, b) => b.sizeBytes - a.sizeBytes);
171
- }
172
- chunks.push({ name, sizeBytes, modules });
173
- }
174
- const report = { timestamp: Date.now(), totalBytes, chunks };
175
- publishEvent({
176
- id: ulid(),
177
- sessionId: deps.sessionId ?? 'pending',
178
- timestamp: Date.now(),
179
- source: 'rollup',
180
- category: 'build',
181
- severity: 'info',
182
- context: { sessionId: deps.sessionId ?? 'pending' },
183
- fingerprint: fingerprint({ kind: 'bundle', identity: 'report' }),
184
- title: `Bundle report: ${chunks.length} chunks, ${totalBytes} bytes`,
185
- raw: { kind: 'bundle-report', report },
186
- });
187
- },
188
- async closeBundle() {
189
- if (bridge) {
190
- await bridge.close();
191
- bridge = undefined;
192
- }
193
- },
194
- };
195
- }
196
- // --- helpers ---
197
- /**
198
- * Run the LensMCP Babel transform on one module's source. Lazily loads
199
- * `@babel/core` + the plugin so they're only touched in dev when a
200
- * `.jsx`/`.tsx` file is transformed. Returns null on parse error (let
201
- * Vite proceed with the untransformed source).
202
- */
203
- async function runLensmcpBabel(code, filename) {
204
- try {
205
- const [{ transformSync }, { lensmcpBabelPlugin }] = await Promise.all([
206
- import('@babel/core'),
207
- import('@lensmcp/react-instrumentation'),
208
- ]);
209
- const result = transformSync(code, {
210
- filename,
211
- babelrc: false,
212
- configFile: false,
213
- sourceMaps: true,
214
- parserOpts: { plugins: ['jsx', 'typescript'] },
215
- plugins: [[lensmcpBabelPlugin, { rootDir: process.cwd(), production: false }]],
216
- code: true,
217
- });
218
- if (!result?.code)
219
- return null;
220
- return { code: result.code, map: result.map };
221
- }
222
- catch {
223
- return null;
224
- }
225
- }
226
- /**
227
- * Parse one raw WebSocket message from the injected client runtime and
228
- * publish the resulting event. Kept as a public export for backwards
229
- * compatibility — it now delegates to `@lensmcp/bridge`. Exercised over a
230
- * real WebSocket without booting Vite (see the react-bridge smoke).
231
- */
232
- export function handleClientEnvelope(raw, deps) {
233
- forwardClientEnvelope(raw, makePublisher(deps), deps.sessionId);
234
- }
235
- /**
236
- * Vite's `ErrorPayload.err` carries the offending module as `loc.file` (when
237
- * the plugin reported a position) or `id` (the resolved module id, which may
238
- * carry a `?t=` cache-buster). Both are stripped to a bare path.
239
- */
240
- function errorLocation(err) {
241
- const raw = err?.loc?.file ?? err?.id;
242
- if (typeof raw !== 'string' || raw.length === 0)
243
- return undefined;
244
- const file = raw.split('?')[0] || raw;
245
- return {
246
- file,
247
- ...(typeof err?.loc?.line === 'number' ? { line: err.loc.line } : {}),
248
- ...(typeof err?.loc?.column === 'number' ? { column: err.loc.column } : {}),
249
- };
250
- }
251
- function viteErrorToEvent(data, sessionId) {
252
- const message = data.err?.message ?? 'Vite error';
253
- const stack = data.err?.stack;
254
- const loc = errorLocation(data.err);
255
- return {
256
- id: ulid(),
257
- sessionId: sessionId ?? 'pending',
258
- timestamp: Date.now(),
259
- source: 'vite',
260
- category: 'build',
261
- severity: 'error',
262
- context: { sessionId: sessionId ?? 'pending' },
263
- // The FILE is part of the identity. Without it two different modules
264
- // failing with the same message ("Failed to resolve import") collapsed
265
- // into ONE entry, so retracting either would have silenced both — and a
266
- // file-scoped clear could not be safe. It is also what lets a later HMR
267
- // update for that file retract this error at all: until now nothing in
268
- // this repo ever removed a build failure, so a fixed Vite error was
269
- // reported for the life of the session.
270
- fingerprint: fingerprint({
271
- kind: 'vite',
272
- identity: data.type ?? message,
273
- ...(loc ? { file: loc.file } : {}),
274
- }),
275
- title: message,
276
- message: stack,
277
- ...(loc ? { location: loc } : {}),
278
- raw: {
279
- kind: 'build-diagnostic',
280
- ...(loc ? { file: loc.file } : {}),
281
- ...(data.err?.plugin ? { plugin: data.err.plugin } : {}),
282
- },
283
- };
284
- }
285
- /**
286
- * Minimal browser-side runtime that mirrors what `@lensmcp/client-runtime`
287
- * will eventually own. Inlined here so Phase 1 has no cross-build
288
- * coupling. Future: replace with a real import of the published
289
- * package's browser bundle.
290
- */
291
- function buildInlineClientRuntime(opts) {
292
- return `// @lensmcp/client-runtime (inlined by @lensmcp/vite-plugin)
1
+ "use strict";var x=Object.defineProperty;var l=(e,t)=>x(e,"name",{value:t,configurable:!0});var P=Object.defineProperty,u=l((e,t)=>P(e,"name",{value:t,configurable:!0}),"l");import{fingerprint as v,ulid as b}from"@lensmcp/core";import{createBridgeServer as D,makePublisher as S,handleClientEnvelope as N}from"@lensmcp/bridge";const _="/@lensmcp/client-runtime",E="\0lensmcp-client-runtime";export function lensmcpVitePlugin(e={},t={}){const r=e.enabled??!0,p=e.wsHost??"127.0.0.1",o=e.injectClient??!0;let g,f;const y=S(t),L=e.aliasValtio??!0,T=e.transformSource??!0;return{name:"@lensmcp/vite-plugin",apply:r?void 0:()=>!1,enforce:"pre",wsPort:u(()=>g,"wsPort"),config(){if(!(!r||!L))return{resolve:{alias:[{find:/^valtio$/,replacement:"@lensmcp/valtio-instrumentation"}]}}},async configureServer(n){r&&(f=await D({host:p,port:e.wsPort??0,bus:t.bus,sessionId:t.sessionId}),g=f.port,n.ws.on("vite:error",s=>{y(C(s,t.sessionId))}))},resolveId(n){return n===_?E:null},load(n){if(n!==E)return null;const s=g??e.wsPort??0;return R({host:p,port:s})},async transform(n,s){if(!r||!T)return null;const i=s.split("?")[0]??s;if(i.includes("/node_modules/")||!/\.[jt]sx$/.test(i))return null;const c=await k(n,i);return c?{code:c.code,map:c.map}:null},transformIndexHtml(){if(!(!o||!r))return[{tag:"script",attrs:{type:"module",src:_},injectTo:"head-prepend"}]},handleHotUpdate(n){if(!r)return;const s=n.modules.map(i=>i.file??i.id??null).filter(i=>!!i);y({id:b(),sessionId:t.sessionId??"pending",timestamp:Date.now(),source:"vite",category:"build",severity:"info",context:{sessionId:t.sessionId??"pending"},fingerprint:v({kind:"hmr",identity:n.file}),title:`HMR ${n.file}`,location:{file:n.file},relatedFiles:s,raw:{kind:"hmr",file:n.file}})},generateBundle(n,s){if(!r)return;const i=[];let c=0;for(const A of Object.values(s)){const d=A,B=d.fileName;let m;if(d.type==="chunk")m=(d.code??"").length;else{const a=d.source;m=typeof a=="string"?a.length:a instanceof Uint8Array?a.byteLength:0}c+=m;const w=[];if(d.type==="chunk"&&d.modules){for(const[a,h]of Object.entries(d.modules)){const F=h.renderedLength??h.originalLength??0;w.push({id:a,sizeBytes:F})}w.sort((a,h)=>h.sizeBytes-a.sizeBytes)}i.push({name:B,sizeBytes:m,modules:w})}const O={timestamp:Date.now(),totalBytes:c,chunks:i};y({id:b(),sessionId:t.sessionId??"pending",timestamp:Date.now(),source:"rollup",category:"build",severity:"info",context:{sessionId:t.sessionId??"pending"},fingerprint:v({kind:"bundle",identity:"report"}),title:`Bundle report: ${i.length} chunks, ${c} bytes`,raw:{kind:"bundle-report",report:O}})},async closeBundle(){f&&(await f.close(),f=void 0)}}}l(lensmcpVitePlugin,"lensmcpVitePlugin"),u(lensmcpVitePlugin,"lensmcpVitePlugin");async function k(e,t){try{const[{transformSync:r},{lensmcpBabelPlugin:p}]=await Promise.all([import("@babel/core"),import("@lensmcp/react-instrumentation")]),o=r(e,{filename:t,babelrc:!1,configFile:!1,sourceMaps:!0,parserOpts:{plugins:["jsx","typescript"]},plugins:[[p,{rootDir:process.cwd(),production:!1}]],code:!0});return o?.code?{code:o.code,map:o.map}:null}catch{return null}}l(k,"B"),u(k,"runLensmcpBabel");export function handleClientEnvelope(e,t){N(e,S(t),t.sessionId)}l(handleClientEnvelope,"handleClientEnvelope"),u(handleClientEnvelope,"handleClientEnvelope");function I(e){const t=e?.loc?.file??e?.id;return typeof t!="string"||t.length===0?void 0:{file:t.split("?")[0]||t,...typeof e?.loc?.line=="number"?{line:e.loc.line}:{},...typeof e?.loc?.column=="number"?{column:e.loc.column}:{}}}l(I,"F"),u(I,"errorLocation");function C(e,t){const r=e.err?.message??"Vite error",p=e.err?.stack,o=I(e.err);return{id:b(),sessionId:t??"pending",timestamp:Date.now(),source:"vite",category:"build",severity:"error",context:{sessionId:t??"pending"},fingerprint:v({kind:"vite",identity:e.type??r,...o?{file:o.file}:{}}),title:r,message:p,...o?{location:o}:{},raw:{kind:"build-diagnostic",...o?{file:o.file}:{},...e.err?.plugin?{plugin:e.err.plugin}:{}}}}l(C,"D"),u(C,"viteErrorToEvent");function R(e){return`// @lensmcp/client-runtime (inlined by @lensmcp/vite-plugin)
293
2
  (() => {
294
3
  if (typeof window === 'undefined') return;
295
4
  if (window.__LENSMCP_CLIENT__) return;
296
5
  window.__LENSMCP_CLIENT__ = true;
297
6
 
298
- // React DevTools hook shim/patch this script is the FIRST module script in
7
+ // React DevTools hook shim/patch \u2014 this script is the FIRST module script in
299
8
  // the document, so it runs BEFORE react-dom evaluates. react-dom only calls
300
9
  // onCommitFiberRoot when a hook exists at its module init, so installing (or
301
10
  // patching) it here is what gives @lensmcp/react-instrumentation per-commit
302
11
  // fiber roots for per-component render attribution. When another hook is
303
12
  // already present (React DevTools extension, react-refresh preamble) we
304
- // CHAIN its onCommitFiberRoot never replace it.
13
+ // CHAIN its onCommitFiberRoot \u2014 never replace it.
305
14
  (() => {
306
15
  try {
307
16
  const subs = new Set();
@@ -330,16 +39,16 @@ function buildInlineClientRuntime(opts) {
330
39
  setStrictMode() {},
331
40
  };
332
41
  }
333
- } catch (e) { /* strictly passive never break the host page */ }
42
+ } catch (e) { /* strictly passive \u2014 never break the host page */ }
334
43
  })();
335
44
 
336
- const WS_URL = 'ws://${opts.host}:${opts.port}';
45
+ const WS_URL = 'ws://${e.host}:${e.port}';
337
46
  let ws = null;
338
47
  let queue = [];
339
48
  let openOnce = false;
340
49
 
341
50
  // Per-TAB identity: sessionStorage is scoped to the tab (and survives
342
- // reloads in it), so each browser tab gets a stable id this is what
51
+ // reloads in it), so each browser tab gets a stable id \u2014 this is what
343
52
  // lets browser://tabs show every open tab instead of one 'default'.
344
53
  const TAB_ID = (() => {
345
54
  try {
@@ -363,7 +72,7 @@ function buildInlineClientRuntime(opts) {
363
72
  send({ kind: 'ready', userAgent: navigator.userAgent, url: location.href });
364
73
  });
365
74
  ws.addEventListener('close', () => { if (openOnce) setTimeout(open, 1000); });
366
- ws.addEventListener('error', () => { /* ignore close handler reconnects */ });
75
+ ws.addEventListener('error', () => { /* ignore \u2014 close handler reconnects */ });
367
76
  }
368
77
  open();
369
78
 
@@ -394,10 +103,10 @@ function buildInlineClientRuntime(opts) {
394
103
  const MAX_STACK = 4096;
395
104
  function clipStr(s, n) {
396
105
  if (typeof s !== 'string' || s.length <= n) return s;
397
- return s.slice(0, n) + '[lensmcp truncated: ' + (s.length - n) + ' more chars]';
106
+ return s.slice(0, n) + '\u2026[lensmcp truncated: ' + (s.length - n) + ' more chars]';
398
107
  }
399
108
  // A rejected non-Error ({ status: 401 }, a Response) used to be flattened by
400
- // String(reason) into "[object Object]" the reason was captured and told
109
+ // String(reason) into "[object Object]" \u2014 the reason was captured and told
401
110
  // you nothing.
402
111
  function describeReason(r) {
403
112
  if (r === null || r === undefined) return String(r);
@@ -410,7 +119,7 @@ function buildInlineClientRuntime(opts) {
410
119
  // This listener is registered in the CAPTURE phase, which means it also
411
120
  // receives subresource load failures (<img>, <script>, <link>, <video>).
412
121
  // Those arrive as a plain Event, NOT an ErrorEvent: no message, no
413
- // filename, no lineno the only identity is the target element and its
122
+ // filename, no lineno \u2014 the only identity is the target element and its
414
123
  // URL. Reading only the ErrorEvent fields (as this did) serialised every
415
124
  // one of them to a bare { kind:'error', at, tabId } and lost the lot.
416
125
  const t = e.target;
@@ -460,7 +169,7 @@ function buildInlineClientRuntime(opts) {
460
169
  const start = Date.now();
461
170
  // flow-fetch (@lensmcp/react-instrumentation) wraps fetch OUTSIDE this
462
171
  // wrapper and publishes flow-correlated network events for the same
463
- // request sending ours too double-counts every fetch in the timeline.
172
+ // request \u2014 sending ours too double-counts every fetch in the timeline.
464
173
  const flowFetchActive = !!window.__LENSMCP_FLOW_FETCH__;
465
174
  try {
466
175
  const res = await origFetch(input, init);
@@ -487,5 +196,4 @@ function buildInlineClientRuntime(opts) {
487
196
  try { return JSON.parse(JSON.stringify(v)); } catch { return String(v); }
488
197
  }
489
198
  })();
490
- `;
491
- }
199
+ `}l(R,"N"),u(R,"buildInlineClientRuntime");
package/lib/types.js CHANGED
@@ -1 +1 @@
1
- export {};
1
+ "use strict";export{};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lensmcp/vite-plugin",
3
- "version": "1.18.4",
3
+ "version": "1.18.6",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "module": "./index.js",
@@ -15,11 +15,11 @@
15
15
  },
16
16
  "dependencies": {
17
17
  "@babel/core": "^7.0.0",
18
- "@lensmcp/bridge": "1.18.4",
19
- "@lensmcp/core": "1.18.4",
20
- "@lensmcp/protocol-types": "1.18.4",
21
- "@lensmcp/react-instrumentation": "1.18.4",
22
- "@lensmcp/valtio-instrumentation": "1.18.4",
18
+ "@lensmcp/bridge": "1.18.6",
19
+ "@lensmcp/core": "1.18.6",
20
+ "@lensmcp/protocol-types": "1.18.6",
21
+ "@lensmcp/react-instrumentation": "1.18.6",
22
+ "@lensmcp/valtio-instrumentation": "1.18.6",
23
23
  "tslib": "^2.3.0"
24
24
  },
25
25
  "peerDependencies": {