@lensmcp/client-runtime 1.18.4 → 1.18.7

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/auto.js CHANGED
@@ -1,16 +1 @@
1
- /**
2
- * Side-effecting no-build entry.
3
- *
4
- * import '@lensmcp/client-runtime/auto';
5
- *
6
- * Installs the browser capture layer immediately, resolving the bridge WS
7
- * endpoint at runtime (global → `<meta>` → `ws://<host>:5747`). Pair it with a
8
- * standalone bridge (`lensmcp bridge`) for hosts with no LensMCP build plugin:
9
- * webpack, Next.js, or no bundler at all.
10
- *
11
- * For explicit control over the URL or reconnect behaviour, import
12
- * `{ installLensmcp }` and call it yourself instead of importing this entry.
13
- */
14
- import { installLensmcp } from './lib/auto.js';
15
- installLensmcp();
16
- export { installLensmcp, resolveLensmcpWsUrl } from './lib/auto.js';
1
+ "use strict";import{installLensmcp as s}from"./lib/auto.js";s();export{installLensmcp,resolveLensmcpWsUrl}from"./lib/auto.js";
package/index.js CHANGED
@@ -1,2 +1 @@
1
- export { installClientRuntime } from './lib/install.js';
2
- export { installLensmcp, resolveLensmcpWsUrl, } from './lib/auto.js';
1
+ "use strict";export{installClientRuntime}from"./lib/install.js";export{installLensmcp,resolveLensmcpWsUrl}from"./lib/auto.js";
package/lib/auto.js CHANGED
@@ -1,72 +1 @@
1
- /**
2
- * Runtime WS-endpoint resolution for the no-build path.
3
- *
4
- * The Vite plugin bakes the bridge WS port into the injected runtime at
5
- * build/serve time. Without a bundler there's nothing to bake it in, so the
6
- * endpoint is resolved at runtime from (in order):
7
- *
8
- * 1. an explicit `wsUrl` argument,
9
- * 2. `globalThis.__LENSMCP_WS_URL__` (a full `ws://…` URL),
10
- * 3. `<meta name="lensmcp-ws" content="ws://…">`,
11
- * 4. `ws://<host>:<port>` assembled from
12
- * `__LENSMCP_WS_HOST__` / `<meta name="lensmcp-ws-host">` / `location.hostname`
13
- * and `__LENSMCP_WS_PORT__` / `<meta name="lensmcp-ws-port">` / the default.
14
- *
15
- * This is what lets `import '@lensmcp/client-runtime/auto'` work in any host
16
- * (webpack, Next.js, plain `<script type=module>`, no build at all).
17
- */
18
- import { installClientRuntime } from './install.js';
19
- // Must match `@lensmcp/bridge`'s DEFAULT_WS_PORT. The bridge is a Node-only
20
- // package (it imports `ws`/`node:net`) and can't be pulled into a browser
21
- // bundle, so the literal is duplicated here on purpose — keep them in sync.
22
- const DEFAULT_WS_PORT = 5747;
23
- const DEFAULT_WS_HOST = '127.0.0.1';
24
- /** Resolve the bridge WebSocket URL from the runtime environment. */
25
- export function resolveLensmcpWsUrl(override) {
26
- if (override)
27
- return override;
28
- const g = globalThis;
29
- const globalUrl = g['__LENSMCP_WS_URL__'];
30
- if (typeof globalUrl === 'string' && globalUrl.length > 0)
31
- return globalUrl;
32
- const metaUrl = readMeta('lensmcp-ws');
33
- if (metaUrl)
34
- return metaUrl;
35
- const globalPort = g['__LENSMCP_WS_PORT__'];
36
- const port = typeof globalPort === 'number' || typeof globalPort === 'string'
37
- ? String(globalPort)
38
- : readMeta('lensmcp-ws-port') ?? String(DEFAULT_WS_PORT);
39
- const globalHost = g['__LENSMCP_WS_HOST__'];
40
- const host = typeof globalHost === 'string' && globalHost.length > 0
41
- ? globalHost
42
- : readMeta('lensmcp-ws-host') ??
43
- (typeof location !== 'undefined' && location.hostname ? location.hostname : DEFAULT_WS_HOST);
44
- return `ws://${host}:${port}`;
45
- }
46
- /**
47
- * Resolve the bridge WS endpoint and install the capture layer. The no-build
48
- * seam: any app can call this (or `import '@lensmcp/client-runtime/auto'`)
49
- * without a bundler plugin. No-op outside a browser (SSR-safe), and
50
- * idempotent (the underlying runtime guards against double-install).
51
- */
52
- export function installLensmcp(opts = {}) {
53
- if (typeof window === 'undefined')
54
- return;
55
- const options = { wsUrl: resolveLensmcpWsUrl(opts.wsUrl) };
56
- if (opts.reconnectIntervalMs !== undefined) {
57
- options.reconnectIntervalMs = opts.reconnectIntervalMs;
58
- }
59
- installClientRuntime(options);
60
- }
61
- function readMeta(name) {
62
- if (typeof document === 'undefined')
63
- return undefined;
64
- try {
65
- const el = document.querySelector(`meta[name="${name}"]`);
66
- const content = el?.getAttribute('content');
67
- return content && content.length > 0 ? content : undefined;
68
- }
69
- catch {
70
- return undefined;
71
- }
72
- }
1
+ "use strict";var u=Object.defineProperty;var n=(e,t)=>u(e,"name",{value:t,configurable:!0});var f=Object.defineProperty,i=n((e,t)=>f(e,"name",{value:t,configurable:!0}),"o");import{installClientRuntime as p}from"./install.js";const _=5747,m="127.0.0.1";export function resolveLensmcpWsUrl(e){if(e)return e;const t=globalThis,o=t.__LENSMCP_WS_URL__;if(typeof o=="string"&&o.length>0)return o;const l=r("lensmcp-ws");if(l)return l;const s=t.__LENSMCP_WS_PORT__,a=typeof s=="number"||typeof s=="string"?String(s):r("lensmcp-ws-port")??String(_),c=t.__LENSMCP_WS_HOST__;return`ws://${typeof c=="string"&&c.length>0?c:r("lensmcp-ws-host")??(typeof location<"u"&&location.hostname?location.hostname:m)}:${a}`}n(resolveLensmcpWsUrl,"resolveLensmcpWsUrl"),i(resolveLensmcpWsUrl,"resolveLensmcpWsUrl");export function installLensmcp(e={}){if(typeof window>"u")return;const t={wsUrl:resolveLensmcpWsUrl(e.wsUrl)};e.reconnectIntervalMs!==void 0&&(t.reconnectIntervalMs=e.reconnectIntervalMs),p(t)}n(installLensmcp,"installLensmcp"),i(installLensmcp,"installLensmcp");function r(e){if(!(typeof document>"u"))try{const t=document.querySelector(`meta[name="${e}"]`)?.getAttribute("content");return t&&t.length>0?t:void 0}catch{return}}n(r,"c"),i(r,"readMeta");
package/lib/install.js CHANGED
@@ -1,218 +1 @@
1
- /**
2
- * @lensmcp/client-runtime — the browser-side capture layer.
3
- *
4
- * Vite (via `@lensmcp/vite-plugin`) injects this into served HTML. It
5
- * connects to a localhost WebSocket the plugin spawns, and emits one
6
- * `ClientEnvelope` per observed event. The runtime is **strictly
7
- * passive**: it never mutates app state, never swallows errors, never
8
- * changes scheduling.
9
- *
10
- * Phase 1 scope (mirror of the inline runtime in `@lensmcp/vite-plugin`,
11
- * promoted out so the plugin can resolve it as a real package import
12
- * in Phase 2):
13
- * • `window.error` / `unhandledrejection`
14
- * • `console.{log,info,warn,error,debug}` wrapping that preserves original
15
- * • `fetch` wrapping (timing + status, no body inspection)
16
- * • History API + popstate route changes
17
- * • `ready` event on first connect
18
- *
19
- * Each envelope is timestamped on send.
20
- */
21
- const FLAG = '__LENSMCP_CLIENT__';
22
- export function installClientRuntime(opts) {
23
- if (typeof window === 'undefined')
24
- return;
25
- if (window[FLAG])
26
- return;
27
- window[FLAG] = true;
28
- const wsUrl = opts.wsUrl;
29
- const reconnect = opts.reconnectIntervalMs ?? 1000;
30
- let ws = null;
31
- let openOnce = false;
32
- const queue = [];
33
- const send = (env) => {
34
- env.at = Date.now();
35
- if (ws && ws.readyState === 1 /* OPEN */) {
36
- try {
37
- ws.send(JSON.stringify(env));
38
- }
39
- catch { /* swallow */ }
40
- }
41
- else {
42
- queue.push(env);
43
- }
44
- };
45
- const open = () => {
46
- try {
47
- ws = new WebSocket(wsUrl);
48
- }
49
- catch {
50
- setTimeout(open, reconnect);
51
- return;
52
- }
53
- ws.addEventListener('open', () => {
54
- openOnce = true;
55
- while (queue.length) {
56
- try {
57
- ws.send(JSON.stringify(queue.shift()));
58
- }
59
- catch { /* swallow */ }
60
- }
61
- send({ kind: 'ready', userAgent: navigator.userAgent, url: location.href });
62
- });
63
- ws.addEventListener('close', () => {
64
- if (openOnce)
65
- setTimeout(open, reconnect);
66
- });
67
- ws.addEventListener('error', () => { });
68
- };
69
- open();
70
- // Bridge for in-page instrumentation (@lensmcp/react-instrumentation,
71
- // @lensmcp/valtio-instrumentation): their publish() output rides the
72
- // same WS as a `publish` envelope so React/Valtio events reach the bus.
73
- window.__LENSMCP_PUBLISH__ = (envelope) => {
74
- send({ kind: 'publish', ...envelope });
75
- };
76
- // Errors
77
- window.addEventListener('error', (e) => {
78
- // Registered in the CAPTURE phase, so this also receives subresource
79
- // load failures (<img>, <script>, <link>, <video>). Those are a plain
80
- // Event, not an ErrorEvent — no message, no filename, no lineno; the
81
- // only identity is the target element and its URL. Reading just the
82
- // ErrorEvent fields (as this did) reduced every one of them to a bare
83
- // `{ kind: 'error', at, tabId }` with nothing to diagnose.
84
- const target = e.target;
85
- if (target && target !== window && target.tagName && !e.message) {
86
- send({
87
- kind: 'resource-error',
88
- tagName: target.tagName.toLowerCase(),
89
- url: target.currentSrc || target.src || target.href || undefined,
90
- });
91
- return;
92
- }
93
- const err = e.error;
94
- send({
95
- kind: 'error',
96
- message: e.message,
97
- name: err?.name,
98
- stack: clipStack(err?.stack),
99
- source: e.filename,
100
- lineno: e.lineno,
101
- colno: e.colno,
102
- });
103
- }, true);
104
- window.addEventListener('unhandledrejection', (e) => {
105
- const reason = e.reason;
106
- send({
107
- kind: 'unhandledrejection',
108
- reason: clipStack(describeReason(reason)),
109
- name: typeof reason === 'object' && reason ? reason.name : undefined,
110
- stack: typeof reason === 'object' && reason ? clipStack(reason.stack) : undefined,
111
- });
112
- });
113
- const levels = ['log', 'info', 'warn', 'error', 'debug'];
114
- for (const level of levels) {
115
- const orig = console[level];
116
- console[level] = function (...args) {
117
- try {
118
- send({ kind: 'console', level, args: args.map(safe) });
119
- }
120
- catch { /* swallow */ }
121
- orig.apply(console, args);
122
- };
123
- }
124
- // Fetch
125
- const origFetch = window.fetch.bind(window);
126
- window.fetch = async function (input, init) {
127
- const method = init?.method ??
128
- (typeof input === 'object' && input && 'method' in input ? input.method : 'GET');
129
- const url = typeof input === 'string'
130
- ? input
131
- : input instanceof URL
132
- ? input.toString()
133
- : input.url;
134
- const start = Date.now();
135
- try {
136
- const res = await origFetch(input, init);
137
- send({
138
- kind: 'fetch',
139
- method,
140
- url,
141
- status: res.status,
142
- statusText: res.statusText,
143
- ok: res.ok,
144
- durationMs: Date.now() - start,
145
- });
146
- return res;
147
- }
148
- catch (err) {
149
- const e = err;
150
- send({ kind: 'fetch', method, url, error: e.message ?? String(e), durationMs: Date.now() - start });
151
- throw err;
152
- }
153
- };
154
- // History
155
- const origPush = history.pushState.bind(history);
156
- const origReplace = history.replaceState.bind(history);
157
- const notify = (from) => send({ kind: 'route', url: location.href, from });
158
- history.pushState = function (...args) {
159
- const from = location.href;
160
- const r = origPush(...args);
161
- notify(from);
162
- return r;
163
- };
164
- history.replaceState = function (...args) {
165
- const from = location.href;
166
- const r = origReplace(...args);
167
- notify(from);
168
- return r;
169
- };
170
- window.addEventListener('popstate', () => notify());
171
- }
172
- /**
173
- * Bound a stack at CAPTURE so a deep React tree can't push a multi-hundred-KB
174
- * frame down the socket. The bridge bounds again (it must — a hand-rolled
175
- * client is not obliged to do this), and the marker matches the one
176
- * `@lensmcp/storage` uses for `raw`, so both truncations read the same.
177
- */
178
- const MAX_STACK_CHARS = 4096;
179
- function clipStack(value) {
180
- if (value === undefined || value.length <= MAX_STACK_CHARS)
181
- return value;
182
- return `${value.slice(0, MAX_STACK_CHARS)}…[lensmcp truncated: ${value.length - MAX_STACK_CHARS} more chars]`;
183
- }
184
- /**
185
- * A rejected non-Error — `{ status: 401 }`, a `Response` — used to be
186
- * flattened by `String(reason)` into `"[object Object]"`: captured, and
187
- * completely uninformative.
188
- */
189
- function describeReason(reason) {
190
- if (reason === null || reason === undefined)
191
- return String(reason);
192
- if (typeof reason === 'string')
193
- return reason;
194
- if (typeof reason.message === 'string' && reason.message)
195
- return reason.message;
196
- try {
197
- const json = JSON.stringify(reason);
198
- if (json && json !== '{}' && json !== 'null')
199
- return json;
200
- }
201
- catch {
202
- /* circular / non-serialisable — fall through */
203
- }
204
- return String(reason);
205
- }
206
- function safe(v) {
207
- if (v === null)
208
- return null;
209
- const t = typeof v;
210
- if (t === 'string' || t === 'number' || t === 'boolean')
211
- return v;
212
- try {
213
- return JSON.parse(JSON.stringify(v));
214
- }
215
- catch {
216
- return String(v);
217
- }
218
- }
1
+ "use strict";var C=Object.defineProperty;var u=(t,r)=>C(t,"name",{value:r,configurable:!0});var j=Object.defineProperty,c=u((t,r)=>j(t,"name",{value:r,configurable:!0}),"c");const k="__LENSMCP_CLIENT__";export function installClientRuntime(t){if(typeof window>"u"||window[k])return;window[k]=!0;const r=t.wsUrl,w=t.reconnectIntervalMs??1e3;let i=null,y=!1;const l=[],s=c(n=>{if(n.at=Date.now(),i&&i.readyState===1)try{i.send(JSON.stringify(n))}catch{}else l.push(n)},"send"),f=c(()=>{try{i=new WebSocket(r)}catch{setTimeout(f,w);return}i.addEventListener("open",()=>{for(y=!0;l.length;)try{i.send(JSON.stringify(l.shift()))}catch{}s({kind:"ready",userAgent:navigator.userAgent,url:location.href})}),i.addEventListener("close",()=>{y&&setTimeout(f,w)}),i.addEventListener("error",()=>{})},"open");f(),window.__LENSMCP_PUBLISH__=n=>{s({kind:"publish",...n})},window.addEventListener("error",n=>{const e=n.target;if(e&&e!==window&&e.tagName&&!n.message){s({kind:"resource-error",tagName:e.tagName.toLowerCase(),url:e.currentSrc||e.src||e.href||void 0});return}const o=n.error;s({kind:"error",message:n.message,name:o?.name,stack:d(o?.stack),source:n.filename,lineno:n.lineno,colno:n.colno})},!0),window.addEventListener("unhandledrejection",n=>{const e=n.reason;s({kind:"unhandledrejection",reason:d(v(e)),name:typeof e=="object"&&e?e.name:void 0,stack:typeof e=="object"&&e?d(e.stack):void 0})});const L=["log","info","warn","error","debug"];for(const n of L){const e=console[n];console[n]=function(...o){try{s({kind:"console",level:n,args:o.map(b)})}catch{}e.apply(console,o)}}const N=window.fetch.bind(window);window.fetch=async function(n,e){const o=e?.method??(typeof n=="object"&&n&&"method"in n?n.method:"GET"),m=typeof n=="string"?n:n instanceof URL?n.toString():n.url,p=Date.now();try{const a=await N(n,e);return s({kind:"fetch",method:o,url:m,status:a.status,statusText:a.statusText,ok:a.ok,durationMs:Date.now()-p}),a}catch(a){const S=a;throw s({kind:"fetch",method:o,url:m,error:S.message??String(S),durationMs:Date.now()-p}),a}};const E=history.pushState.bind(history),_=history.replaceState.bind(history),h=c(n=>s({kind:"route",url:location.href,from:n}),"notify");history.pushState=function(...n){const e=location.href,o=E(...n);return h(e),o},history.replaceState=function(...n){const e=location.href,o=_(...n);return h(e),o},window.addEventListener("popstate",()=>h())}u(installClientRuntime,"installClientRuntime"),c(installClientRuntime,"installClientRuntime");const g=4096;function d(t){return t===void 0||t.length<=g?t:`${t.slice(0,g)}\u2026[lensmcp truncated: ${t.length-g} more chars]`}u(d,"h"),c(d,"clipStack");function v(t){if(t==null)return String(t);if(typeof t=="string")return t;if(typeof t.message=="string"&&t.message)return t.message;try{const r=JSON.stringify(t);if(r&&r!=="{}"&&r!=="null")return r}catch{}return String(t)}u(v,"E"),c(v,"describeReason");function b(t){if(t===null)return null;const r=typeof t;if(r==="string"||r==="number"||r==="boolean")return t;try{return JSON.parse(JSON.stringify(t))}catch{return String(t)}}u(b,"C"),c(b,"safe");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lensmcp/client-runtime",
3
- "version": "1.18.4",
3
+ "version": "1.18.7",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "module": "./index.js",