browsertrack 0.2.2 → 0.2.3
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/dist/{chunk-TWEYRBDU.js → chunk-5NR5K3ER.js} +24 -5
- package/dist/chunk-5NR5K3ER.js.map +1 -0
- package/dist/{chunk-AYSVE6NG.js → chunk-G5CIZSQM.js} +2 -2
- package/dist/{chunk-QRZ57ME3.js → chunk-ONPW7AYL.js} +75 -11
- package/dist/chunk-ONPW7AYL.js.map +1 -0
- package/dist/{chunk-4HRLW6YF.js → chunk-PG4JJDCV.js} +196 -17
- package/dist/chunk-PG4JJDCV.js.map +1 -0
- package/dist/cli/index.js +22 -3
- package/dist/cli/index.js.map +1 -1
- package/dist/client/index.cjs +267 -26
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.js +2 -2
- package/dist/client.iife.js +121 -15
- package/dist/core/index.d.ts +9 -2
- package/dist/core/index.js +3 -1
- package/dist/daemon/index.js +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +6 -4
- package/dist/mcp/index.d.ts +1 -1
- package/dist/mcp/index.js +3 -3
- package/dist/{server-DjV7RWQM.d.ts → server-BRG-RQQP.d.ts} +1 -0
- package/package.json +1 -1
- package/packages/client/src/notes/inspector.ts +210 -19
- package/packages/core/src/selector.ts +110 -15
- package/packages/mcp/src/handlers.ts +7 -0
- package/packages/mcp/src/server.ts +21 -3
- package/test/core/selector.test.ts +133 -0
- package/dist/chunk-4HRLW6YF.js.map +0 -1
- package/dist/chunk-QRZ57ME3.js.map +0 -1
- package/dist/chunk-TWEYRBDU.js.map +0 -1
- /package/dist/{chunk-AYSVE6NG.js.map → chunk-G5CIZSQM.js.map} +0 -0
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
VerificationEngine,
|
|
8
8
|
createDaemon,
|
|
9
9
|
getDaemonConfig
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-G5CIZSQM.js";
|
|
11
11
|
|
|
12
12
|
// packages/mcp/src/tools.ts
|
|
13
13
|
var TOOLS = [
|
|
@@ -275,6 +275,12 @@ var TOOLS = [
|
|
|
275
275
|
|
|
276
276
|
// packages/mcp/src/handlers.ts
|
|
277
277
|
async function sendSessionCommand(ctx, sessionId, command, timeoutMs = 5e3) {
|
|
278
|
+
if (ctx.daemonInitPromise) {
|
|
279
|
+
try {
|
|
280
|
+
await Promise.race([ctx.daemonInitPromise, new Promise((r) => setTimeout(r, 2e3))]);
|
|
281
|
+
} catch {
|
|
282
|
+
}
|
|
283
|
+
}
|
|
278
284
|
if (ctx.sessionManager && ctx.sessionManager.getActiveCount() > 0) {
|
|
279
285
|
const targetSession = sessionId ? ctx.db.getSession(sessionId) : ctx.sessionManager.getAnyActiveSession();
|
|
280
286
|
if (targetSession) {
|
|
@@ -852,13 +858,20 @@ function createMcpServer(options = {}) {
|
|
|
852
858
|
const cliPath = resolveCliPath();
|
|
853
859
|
if (cliPath && fs.existsSync(cliPath)) {
|
|
854
860
|
try {
|
|
861
|
+
const nodePaths = [
|
|
862
|
+
"/usr/local/bin",
|
|
863
|
+
"/opt/homebrew/bin",
|
|
864
|
+
process.env.HOME ? `${process.env.HOME}/.nvm/versions/node/v20.19.5/bin` : "",
|
|
865
|
+
process.env.HOME ? `${process.env.HOME}/.nvm/versions/node/v23.1.0/bin` : ""
|
|
866
|
+
].filter(Boolean);
|
|
867
|
+
const extendedPath = `${process.env.PATH || ""}:${nodePaths.join(":")}`;
|
|
855
868
|
const child = spawn(
|
|
856
869
|
process.execPath,
|
|
857
870
|
[cliPath, "start", "--port", String(config.port), "--host", config.host],
|
|
858
871
|
{
|
|
859
872
|
detached: true,
|
|
860
873
|
stdio: "ignore",
|
|
861
|
-
env: { ...process.env, BROWSERTRACK_DAEMON_DETACHED: "1" }
|
|
874
|
+
env: { ...process.env, PATH: extendedPath, BROWSERTRACK_DAEMON_DETACHED: "1" }
|
|
862
875
|
}
|
|
863
876
|
);
|
|
864
877
|
child.unref();
|
|
@@ -930,7 +943,10 @@ function createMcpServer(options = {}) {
|
|
|
930
943
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
931
944
|
const { name, arguments: args } = request.params;
|
|
932
945
|
try {
|
|
933
|
-
const
|
|
946
|
+
const timeoutPromise = new Promise(
|
|
947
|
+
(_, reject) => setTimeout(() => reject(new Error(`Tool execution timed out after 20s: ${name}`)), 2e4)
|
|
948
|
+
);
|
|
949
|
+
const result = await Promise.race([handleToolCall(name, args || {}, ctx), timeoutPromise]);
|
|
934
950
|
return {
|
|
935
951
|
content: [
|
|
936
952
|
{
|
|
@@ -962,9 +978,12 @@ function createMcpServer(options = {}) {
|
|
|
962
978
|
}
|
|
963
979
|
},
|
|
964
980
|
async startStdio() {
|
|
965
|
-
await ensureDaemon();
|
|
966
981
|
const transport = new StdioServerTransport();
|
|
967
982
|
await server.connect(transport);
|
|
983
|
+
const daemonInitPromise = ensureDaemon().catch((err) => {
|
|
984
|
+
console.error(`[BrowserTrack MCP] Daemon background init note: ${err?.message || err}`);
|
|
985
|
+
});
|
|
986
|
+
ctx.daemonInitPromise = daemonInitPromise;
|
|
968
987
|
}
|
|
969
988
|
};
|
|
970
989
|
}
|
|
@@ -975,4 +994,4 @@ export {
|
|
|
975
994
|
isDaemonRunning,
|
|
976
995
|
createMcpServer
|
|
977
996
|
};
|
|
978
|
-
//# sourceMappingURL=chunk-
|
|
997
|
+
//# sourceMappingURL=chunk-5NR5K3ER.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../packages/mcp/src/tools.ts","../packages/mcp/src/handlers.ts","../packages/mcp/src/server.ts"],"sourcesContent":["import type { Tool } from '@modelcontextprotocol/sdk/types.js';\n\nexport const TOOLS: Tool[] = [\n {\n name: 'list_projects',\n description: 'List all registered projects tracked by BrowserTrack/BrowserDiag',\n inputSchema: {\n type: 'object',\n properties: {},\n },\n },\n {\n name: 'list_sessions',\n description: 'List active and recent browser sessions connected to the local development daemon',\n inputSchema: {\n type: 'object',\n properties: {\n projectId: { type: 'string', description: 'Filter sessions by project ID or name' },\n activeOnly: { type: 'boolean', description: 'Show only currently active WebSocket sessions (default: true)' },\n },\n },\n },\n {\n name: 'list_incidents',\n description: 'List recorded browser runtime errors, unhandled rejections, and console errors grouped into incidents',\n inputSchema: {\n type: 'object',\n properties: {\n projectId: { type: 'string', description: 'Filter by project ID or name' },\n status: {\n type: 'string',\n enum: ['OPEN', 'FIX_ATTEMPTED', 'VERIFYING', 'VERIFIED', 'FAILED', 'INCONCLUSIVE'],\n description: 'Filter by incident status',\n },\n severity: {\n type: 'string',\n enum: ['error', 'warn', 'fatal'],\n description: 'Filter by severity',\n },\n limit: { type: 'number', description: 'Maximum number of incidents to return (default: 20)' },\n },\n },\n },\n {\n name: 'get_incident',\n description: 'Retrieve compact and high-signal debugging context for a specific error incident (stack trace, breadcrumbs, network failures, last interacted element, error screenshot)',\n inputSchema: {\n type: 'object',\n properties: {\n incidentId: { type: 'string', description: 'The unique ID of the incident (e.g. inc_42)' },\n },\n required: ['incidentId'],\n },\n },\n {\n name: 'get_console',\n description: 'Get recent console logs, warnings, and errors from a browser session',\n inputSchema: {\n type: 'object',\n properties: {\n sessionId: { type: 'string', description: 'Browser session ID (optional, defaults to active session)' },\n limit: { type: 'number', description: 'Number of console logs to retrieve (default: 30)' },\n },\n },\n },\n {\n name: 'get_network_failures',\n description: 'Get recent failed HTTP network requests (4xx, 5xx, network errors, timeouts, aborts)',\n inputSchema: {\n type: 'object',\n properties: {\n sessionId: { type: 'string', description: 'Browser session ID (optional)' },\n limit: { type: 'number', description: 'Maximum number of failed requests to return (default: 20)' },\n },\n },\n },\n {\n name: 'get_breadcrumbs',\n description: 'Get the sequence of recent user interactions, navigations, console logs, and network events prior to an error',\n inputSchema: {\n type: 'object',\n properties: {\n sessionId: { type: 'string', description: 'Browser session ID (optional)' },\n incidentId: { type: 'string', description: 'Incident ID to fetch breadcrumbs from (optional)' },\n limit: { type: 'number', description: 'Maximum number of breadcrumbs (default: 50)' },\n },\n },\n },\n {\n name: 'get_page_state',\n description: 'Query the live page state (URL, route, document title, readyState, active element) from the connected browser session',\n inputSchema: {\n type: 'object',\n properties: {\n sessionId: { type: 'string', description: 'Target session ID (optional, defaults to active)' },\n },\n },\n },\n {\n name: 'capture_element',\n description: 'Capture a screenshot of a specific DOM element or the entire visible page in the active browser tab',\n inputSchema: {\n type: 'object',\n properties: {\n selector: { type: 'string', description: 'CSS selector of the element to capture (e.g. [data-testid=\"user-card\"])' },\n sessionId: { type: 'string', description: 'Browser session ID (optional)' },\n },\n },\n },\n {\n name: 'verify_incident',\n description: 'Trigger closed-loop verification of a bug fix: reloads the browser, checks if the incident reoccurs, evaluates optional verification probes, and records before/after screenshots',\n inputSchema: {\n type: 'object',\n properties: {\n incidentId: { type: 'string', description: 'The ID of the incident to verify' },\n route: { type: 'string', description: 'Optional route to navigate to for verification (defaults to incident route)' },\n targetSelector: { type: 'string', description: 'Optional element selector to inspect and capture after-fix screenshot' },\n expect: {\n type: 'array',\n description: 'Optional verification probes to evaluate after reload',\n items: {\n type: 'object',\n properties: {\n type: {\n type: 'string',\n enum: ['element_exists', 'element_visible', 'text_contains', 'route_is', 'no_incident'],\n },\n selector: { type: 'string' },\n text: { type: 'string' },\n route: { type: 'string' },\n },\n required: ['type'],\n },\n },\n observationWindowMs: { type: 'number', description: 'Observation window in milliseconds (default: 2000)' },\n },\n required: ['incidentId'],\n },\n },\n {\n name: 'get_verification',\n description: 'Retrieve the latest verification result and before/after screenshot artifacts for an incident',\n inputSchema: {\n type: 'object',\n properties: {\n incidentId: { type: 'string', description: 'The ID of the incident' },\n },\n required: ['incidentId'],\n },\n },\n {\n name: 'list_notes',\n description: 'List visual development notes / screen annotations left on elements, regions, or pages during development',\n inputSchema: {\n type: 'object',\n properties: {\n projectId: { type: 'string', description: 'Filter notes by project ID or name' },\n scenarioId: { type: 'string', description: 'Filter notes by scenario / flow ID' },\n status: {\n type: 'string',\n enum: ['OPEN', 'IN_PROGRESS', 'VERIFYING', 'RESOLVED', 'FAILED', 'INCONCLUSIVE'],\n description: 'Filter by note status (default: OPEN)',\n },\n limit: { type: 'number', description: 'Maximum number of notes to return (default: 20)' },\n },\n },\n },\n {\n name: 'list_scenarios',\n description: 'List recorded reproduction scenarios and multi-step user interaction flows',\n inputSchema: {\n type: 'object',\n properties: {\n projectId: { type: 'string', description: 'Filter scenarios by project ID or name' },\n status: {\n type: 'string',\n enum: ['OPEN', 'RESOLVED'],\n description: 'Filter by scenario status',\n },\n limit: { type: 'number', description: 'Maximum number of scenarios to return (default: 20)' },\n },\n },\n },\n {\n name: 'get_scenario',\n description: 'Retrieve full chronological step-by-step reproduction flow with selectors, route, screenshots, and action details',\n inputSchema: {\n type: 'object',\n properties: {\n scenarioId: { type: 'string', description: 'The unique ID of the scenario / flow (e.g. scen_123)' },\n },\n required: ['scenarioId'],\n },\n },\n {\n name: 'get_note',\n description: 'Retrieve full debugging context for a visual note (message, route, viewport dimensions, target element selector, DOM context, screenshot file path, project path)',\n inputSchema: {\n type: 'object',\n properties: {\n noteId: { type: 'string', description: 'The unique ID of the visual note (e.g. note_42)' },\n },\n required: ['noteId'],\n },\n },\n {\n name: 'resolve_note',\n description: 'Mark a visual note as RESOLVED once the layout or styling issue has been fixed',\n inputSchema: {\n type: 'object',\n properties: {\n noteId: { type: 'string', description: 'The ID of the visual note to resolve' },\n },\n required: ['noteId'],\n },\n },\n {\n name: 'reopen_note',\n description: 'Reopen a previously resolved visual note',\n inputSchema: {\n type: 'object',\n properties: {\n noteId: { type: 'string', description: 'The ID of the visual note to reopen' },\n },\n required: ['noteId'],\n },\n },\n {\n name: 'verify_note',\n description: 'Run closed-loop layout & visual verification for a note: checks route, element existence/visibility, evaluates viewport overflow probes, captures after-screenshot, and computes geometry diff',\n inputSchema: {\n type: 'object',\n properties: {\n noteId: { type: 'string', description: 'The ID of the visual note to verify' },\n observationWindowMs: { type: 'number', description: 'Observation window in milliseconds (default: 1000)' },\n },\n required: ['noteId'],\n },\n },\n {\n name: 'get_note_verification',\n description: 'Retrieve the latest verification result, before/after screenshot references, and layout geometry diff for a visual note',\n inputSchema: {\n type: 'object',\n properties: {\n noteId: { type: 'string', description: 'The ID of the visual note' },\n },\n required: ['noteId'],\n },\n },\n {\n name: 'capture_note_context',\n description: 'Inspect live DOM context, bounding box, overflow, and styles for a target element in the active browser tab',\n inputSchema: {\n type: 'object',\n properties: {\n selector: { type: 'string', description: 'CSS selector of the target element' },\n sessionId: { type: 'string', description: 'Browser session ID (optional)' },\n },\n required: ['selector'],\n },\n },\n];\n","import type { StorageDB } from '../../daemon/src/storage/db.js';\nimport type { VerificationEngine } from '../../daemon/src/verification/engine.js';\nimport type { NoteVerificationEngine } from '../../daemon/src/notes/verification.js';\nimport type { SessionManager } from '../../daemon/src/session/manager.js';\n\nexport interface McpContext {\n db: StorageDB;\n sessionManager?: SessionManager;\n verificationEngine?: VerificationEngine;\n noteVerificationEngine?: NoteVerificationEngine;\n daemonUrl?: string;\n daemonInitPromise?: Promise<void>;\n}\n\nasync function sendSessionCommand(ctx: McpContext, sessionId: string | undefined, command: any, timeoutMs = 5000): Promise<any> {\n if (ctx.daemonInitPromise) {\n try {\n await Promise.race([ctx.daemonInitPromise, new Promise((r) => setTimeout(r, 2000))]);\n } catch {}\n }\n\n // 1. In-process session manager (if active sockets exist)\n if (ctx.sessionManager && ctx.sessionManager.getActiveCount() > 0) {\n const targetSession = sessionId ? ctx.db.getSession(sessionId) : ctx.sessionManager.getAnyActiveSession();\n if (targetSession) {\n const res = await ctx.sessionManager.sendCommand(targetSession.id, command, timeoutMs);\n if (!res.ok) {\n throw new Error(res.error || res.reason || `Command ${command.type} failed`);\n }\n return res;\n }\n }\n\n // 2. Multi-process proxy to running daemon HTTP server\n if (ctx.daemonUrl) {\n try {\n const resp = await fetch(`${ctx.daemonUrl}/api/command`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ sessionId, command, timeoutMs }),\n signal: AbortSignal.timeout(timeoutMs + 2000),\n });\n if (resp.ok) {\n const res = await resp.json();\n if (!res.ok) {\n throw new Error(res.error || res.reason || `Command ${command.type} failed`);\n }\n return res;\n }\n const errJson = await resp.json().catch(() => ({}));\n if (errJson.error) {\n throw new Error(errJson.error);\n }\n } catch (err: any) {\n if (err.message && !err.message.includes('fetch failed') && !err.message.includes('ECONNREFUSED')) {\n throw err;\n }\n }\n }\n\n throw new Error(\n 'No active browser session connected. Please ensure the BrowserTrack daemon is running (\"browsertrack start\") and your application tab is open in the browser.'\n );\n}\n\nasync function runVerifyIncident(ctx: McpContext, incidentId: string, options: any): Promise<any> {\n if (ctx.verificationEngine && ctx.sessionManager && ctx.sessionManager.getActiveCount() > 0) {\n return await ctx.verificationEngine.verifyIncident(incidentId, options);\n }\n\n if (ctx.daemonUrl) {\n try {\n const timeoutMs = (options?.observationWindowMs || 3000) + 7000;\n const resp = await fetch(`${ctx.daemonUrl}/api/verify/incident`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ incidentId, options }),\n signal: AbortSignal.timeout(timeoutMs),\n });\n if (resp.ok) {\n const data = await resp.json();\n if (data.ok) return data.result;\n throw new Error(data.error);\n }\n } catch (err: any) {\n if (err.message && !err.message.includes('fetch failed') && !err.message.includes('ECONNREFUSED')) {\n throw err;\n }\n }\n }\n\n throw new Error(\n 'Verification failed: No active browser session connected. Please ensure the BrowserTrack daemon is running (\"browsertrack start\") and your application tab is open in the browser.'\n );\n}\n\nasync function runVerifyNote(ctx: McpContext, noteId: string, options: any): Promise<any> {\n if (ctx.noteVerificationEngine && ctx.sessionManager && ctx.sessionManager.getActiveCount() > 0) {\n return await ctx.noteVerificationEngine.verifyNote(noteId, options);\n }\n\n if (ctx.daemonUrl) {\n try {\n const timeoutMs = (options?.observationWindowMs || 3000) + 7000;\n const resp = await fetch(`${ctx.daemonUrl}/api/verify/note`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ noteId, options }),\n signal: AbortSignal.timeout(timeoutMs),\n });\n if (resp.ok) {\n const data = await resp.json();\n if (data.ok) return data.result;\n throw new Error(data.error);\n }\n } catch (err: any) {\n if (err.message && !err.message.includes('fetch failed') && !err.message.includes('ECONNREFUSED')) {\n throw err;\n }\n }\n }\n\n throw new Error(\n 'Verification failed: No active browser session connected. Please ensure the BrowserTrack daemon is running (\"browsertrack start\") and your application tab is open in the browser.'\n );\n}\n\nexport async function handleToolCall(name: string, args: any, ctx: McpContext): Promise<any> {\n const { db, sessionManager, verificationEngine, noteVerificationEngine } = ctx;\n\n switch (name) {\n case 'list_projects': {\n const projects = db.listProjects();\n return {\n projects: projects.map((p) => ({\n id: p.id,\n name: p.name,\n origin: p.origin,\n path: p.path,\n updatedAt: p.updatedAt,\n })),\n };\n }\n\n case 'list_sessions': {\n const activeOnly = args.activeOnly !== false;\n const sessions = db.listSessions(args.projectId, activeOnly);\n return {\n count: sessions.length,\n sessions: sessions.map((s) => ({\n id: s.id,\n projectId: s.projectId,\n origin: s.origin,\n url: s.url,\n title: s.title,\n active: s.active,\n lastSeenAt: s.lastSeenAt,\n })),\n };\n }\n\n case 'list_incidents': {\n const incidents = db.listIncidents({\n projectId: args.projectId,\n status: args.status,\n severity: args.severity,\n limit: args.limit || 20,\n });\n return {\n total: incidents.length,\n incidents: incidents.map((inc) => ({\n id: inc.id,\n type: inc.type,\n severity: inc.severity,\n message: inc.message,\n source: `${inc.source.file}:${inc.source.line}`,\n route: inc.route,\n status: inc.status,\n occurrences: inc.occurrences,\n firstSeen: inc.firstSeen,\n lastSeen: inc.lastSeen,\n })),\n };\n }\n\n case 'get_incident': {\n const incident = db.getIncident(args.incidentId);\n if (!incident) {\n throw new Error(`Incident '${args.incidentId}' not found.`);\n }\n\n // Compact breadcrumbs summary (last 10 most relevant)\n const breadcrumbsTimeline = incident.breadcrumbs.slice(-15).map((b) => {\n const time = new Date(b.timestamp).toISOString().split('T')[1]?.slice(0, 8);\n return `[${time}] ${b.message}`;\n });\n\n return {\n id: incident.id,\n status: incident.status,\n type: incident.type,\n severity: incident.severity,\n message: incident.message,\n source: {\n file: incident.source.file,\n line: incident.source.line,\n column: incident.source.column,\n },\n route: incident.route,\n occurrences: incident.occurrences,\n firstSeen: incident.firstSeen,\n lastSeen: incident.lastSeen,\n stack: incident.stack,\n lastInteractedElement: incident.lastElement\n ? {\n selector: incident.lastElement.selector,\n tag: incident.lastElement.tag,\n visible: incident.lastElement.visible,\n innerText: incident.lastElement.innerText,\n outerHTML: incident.lastElement.outerHTML,\n componentSource: incident.lastElement.componentSource,\n }\n : undefined,\n recentBreadcrumbs: breadcrumbsTimeline,\n networkFailures: incident.networkFailures,\n screenshot: incident.screenshots?.error,\n };\n }\n\n case 'get_console': {\n const limit = args.limit || 30;\n const events = db.getEvents({\n sessionId: args.sessionId,\n eventType: 'console',\n limit,\n });\n\n return {\n logs: events.map((e) => ({\n level: e.payload.level,\n message: e.payload.message,\n timestamp: new Date(e.timestamp).toISOString(),\n route: e.route,\n })),\n };\n }\n\n case 'get_network_failures': {\n const limit = args.limit || 20;\n const events = db.getEvents({\n sessionId: args.sessionId,\n limit: 100,\n });\n\n const failures = events\n .filter((e) => (e.eventType === 'fetch' || e.eventType === 'xhr') && (e.payload.status >= 400 || e.payload.error))\n .slice(0, limit)\n .map((e) => ({\n url: e.payload.url,\n method: e.payload.method,\n status: e.payload.status,\n error: e.payload.error,\n durationMs: e.payload.durationMs,\n timestamp: new Date(e.timestamp).toISOString(),\n }));\n\n return { failures };\n }\n\n case 'get_breadcrumbs': {\n if (args.incidentId) {\n const incident = db.getIncident(args.incidentId);\n if (!incident) {\n throw new Error(`Incident '${args.incidentId}' not found.`);\n }\n return {\n incidentId: args.incidentId,\n breadcrumbs: incident.breadcrumbs.slice(-(args.limit || 50)),\n };\n }\n\n const events = db.getEvents({\n sessionId: args.sessionId,\n limit: args.limit || 50,\n });\n\n return {\n breadcrumbs: events.map((e) => ({\n type: e.eventType,\n message: e.payload.message || `${e.eventType} on ${e.route}`,\n timestamp: new Date(e.timestamp).toISOString(),\n route: e.route,\n })),\n };\n }\n\n case 'get_page_state': {\n const cmdRes = await sendSessionCommand(ctx, args.sessionId, {\n id: `cmd_mcp_${Date.now()}`,\n type: 'get_page_state',\n });\n return cmdRes.result;\n }\n\n case 'capture_element': {\n const cmdRes = await sendSessionCommand(ctx, args.sessionId, {\n id: `cmd_mcp_${Date.now()}`,\n type: 'capture_element',\n params: { selector: args.selector },\n });\n\n return {\n ok: true,\n format: cmdRes.result?.format || 'webp',\n width: cmdRes.result?.width,\n height: cmdRes.result?.height,\n dataUrlPreview: cmdRes.result?.dataUrl ? `${cmdRes.result.dataUrl.slice(0, 100)}...` : undefined,\n };\n }\n\n case 'verify_incident': {\n const res = await runVerifyIncident(ctx, args.incidentId, {\n route: args.route,\n targetSelector: args.targetSelector,\n expect: args.expect,\n observationWindowMs: args.observationWindowMs,\n });\n\n return res;\n }\n\n case 'get_verification': {\n const v = db.getLatestVerification(args.incidentId);\n if (!v) {\n throw new Error(`No verification records found for incident '${args.incidentId}'.`);\n }\n return v;\n }\n\n case 'list_notes': {\n const notes = db.listNotes({\n projectId: args.projectId,\n scenarioId: args.scenarioId,\n status: args.status,\n limit: args.limit || 20,\n });\n\n return {\n total: notes.length,\n notes: notes.map((n) => ({\n id: n.id,\n type: n.type,\n status: n.status,\n route: n.route,\n scenarioId: n.scenarioId,\n stepNumber: n.stepNumber,\n scenarioTitle: n.scenarioTitle,\n viewport: `${n.viewport.width} × ${n.viewport.height}`,\n target: n.target?.selector || n.type,\n message: n.message,\n screenshotAvailable: !!n.screenshots?.original,\n createdAt: n.createdAt,\n })),\n };\n }\n\n case 'list_scenarios': {\n const scenarios = db.listScenarios({\n projectId: args.projectId,\n status: args.status,\n limit: args.limit || 20,\n });\n\n return {\n total: scenarios.length,\n scenarios: scenarios.map((s) => ({\n id: s.id,\n title: s.title,\n stepsCount: s.stepsCount,\n status: s.status,\n route: s.route,\n firstStepAt: s.firstStepAt,\n lastStepAt: s.lastStepAt,\n })),\n };\n }\n\n case 'get_scenario': {\n const scenario = db.getScenario(args.scenarioId);\n if (!scenario) {\n throw new Error(`Scenario '${args.scenarioId}' not found.`);\n }\n\n return {\n id: scenario.id,\n title: scenario.title,\n stepsCount: scenario.stepsCount,\n status: scenario.status,\n steps: scenario.steps.map((step) => ({\n id: step.id,\n stepNumber: step.stepNumber,\n type: step.type,\n message: step.message,\n route: step.route,\n url: step.url,\n targetSelector: step.target?.selector,\n boundingRect: step.target?.boundingRect || step.region,\n elementContext: step.elementContext,\n screenshot: step.screenshots?.original,\n status: step.status,\n createdAt: step.createdAt,\n })),\n createdAt: scenario.createdAt,\n updatedAt: scenario.updatedAt,\n };\n }\n\n case 'get_note': {\n const note = db.getNote(args.noteId);\n if (!note) {\n throw new Error(`Visual note '${args.noteId}' not found.`);\n }\n\n const project = db.getProject(note.projectId);\n\n return {\n id: note.id,\n type: note.type,\n status: note.status,\n message: note.message,\n scenarioId: note.scenarioId,\n stepNumber: note.stepNumber,\n scenarioTitle: note.scenarioTitle,\n route: note.route,\n url: note.url,\n viewport: note.viewport,\n scroll: note.scroll,\n target: note.target,\n elementContext: note.elementContext,\n region: note.region,\n screenshot: {\n available: !!note.screenshots?.original,\n original: note.screenshots?.original,\n after: note.screenshots?.after,\n },\n project: project\n ? {\n id: project.id,\n name: project.name,\n origin: project.origin,\n path: project.path,\n }\n : undefined,\n relatedIncidentId: note.incidentId,\n createdAt: note.createdAt,\n updatedAt: note.updatedAt,\n resolvedAt: note.resolvedAt,\n };\n }\n\n case 'resolve_note': {\n const note = db.getNote(args.noteId);\n if (!note) {\n throw new Error(`Visual note '${args.noteId}' not found.`);\n }\n db.updateNoteStatus(args.noteId, 'RESOLVED');\n return {\n ok: true,\n noteId: args.noteId,\n status: 'RESOLVED',\n resolvedAt: new Date().toISOString(),\n };\n }\n\n case 'reopen_note': {\n const note = db.getNote(args.noteId);\n if (!note) {\n throw new Error(`Visual note '${args.noteId}' not found.`);\n }\n db.updateNoteStatus(args.noteId, 'OPEN');\n return {\n ok: true,\n noteId: args.noteId,\n status: 'OPEN',\n };\n }\n\n case 'verify_note': {\n const res = await runVerifyNote(ctx, args.noteId, {\n observationWindowMs: args.observationWindowMs,\n });\n return res;\n }\n\n case 'get_note_verification': {\n const v = db.getLatestNoteVerification(args.noteId);\n if (!v) {\n throw new Error(`No verification records found for visual note '${args.noteId}'.`);\n }\n return v;\n }\n\n case 'capture_note_context': {\n const queryCmd = await sendSessionCommand(ctx, args.sessionId, {\n id: `cmd_ctx_${Date.now()}`,\n type: 'query_element',\n params: { selector: args.selector },\n });\n\n const overflowCmd = await sendSessionCommand(ctx, args.sessionId, {\n id: `cmd_ovf_${Date.now()}`,\n type: 'check_overflow',\n params: { selector: args.selector },\n });\n\n const styleCmd = await sendSessionCommand(ctx, args.sessionId, {\n id: `cmd_sty_${Date.now()}`,\n type: 'get_element_style',\n params: { selector: args.selector },\n });\n\n return {\n selector: args.selector,\n element: queryCmd.result,\n overflow: overflowCmd.result,\n styles: styleCmd.result?.styles,\n };\n }\n\n default:\n throw new Error(`Unknown MCP tool: ${name}`);\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { spawn } from 'node:child_process';\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';\nimport { getDaemonConfig } from '../../daemon/src/config.js';\nimport { StorageDB } from '../../daemon/src/storage/db.js';\nimport { ScreenshotStore } from '../../daemon/src/storage/screenshot-store.js';\nimport { SessionManager } from '../../daemon/src/session/manager.js';\nimport { NotesEngine } from '../../daemon/src/notes/engine.js';\nimport { NoteVerificationEngine } from '../../daemon/src/notes/verification.js';\nimport { VerificationEngine, createDaemon } from '../../daemon/src/index.js';\nimport type { McpContext } from './handlers.js';\nimport { handleToolCall } from './handlers.js';\nimport { TOOLS } from './tools.js';\n\nexport async function isDaemonRunning(host: string, port: number): Promise<boolean> {\n try {\n const res = await fetch(`http://${host}:${port}/health`, {\n signal: AbortSignal.timeout(600),\n });\n if (res.ok) {\n const data = (await res.json().catch(() => ({}))) as any;\n return data?.name === 'browsertrack';\n }\n } catch {}\n return false;\n}\n\nfunction resolveCliPath(): string | null {\n if (process.argv[1]) {\n const candidate = process.argv[1];\n if (\n candidate.endsWith('cli/index.js') ||\n candidate.endsWith('browsertrack') ||\n candidate.endsWith('bin/browsertrack.js') ||\n candidate.endsWith('dist/cli/index.js')\n ) {\n if (fs.existsSync(candidate)) return candidate;\n }\n }\n\n try {\n const currentDir = path.dirname(fileURLToPath(import.meta.url));\n const paths = [\n path.resolve(currentDir, '../cli/index.js'),\n path.resolve(currentDir, '../../cli/index.js'),\n path.resolve(currentDir, '../../dist/cli/index.js'),\n ];\n for (const p of paths) {\n if (fs.existsSync(p)) return p;\n }\n } catch {}\n\n return null;\n}\n\nfunction acquireBootLock(lockFile: string): boolean {\n try {\n fs.mkdirSync(path.dirname(lockFile), { recursive: true });\n const fd = fs.openSync(lockFile, 'wx');\n fs.writeSync(fd, String(process.pid));\n fs.closeSync(fd);\n return true;\n } catch {\n try {\n const stats = fs.statSync(lockFile);\n // If older than 5 seconds, assume stale lock and break it\n if (Date.now() - stats.mtimeMs > 5000) {\n fs.unlinkSync(lockFile);\n const fd = fs.openSync(lockFile, 'wx');\n fs.writeSync(fd, String(process.pid));\n fs.closeSync(fd);\n return true;\n }\n } catch {}\n return false;\n }\n}\n\nfunction releaseBootLock(lockFile: string): void {\n try {\n if (fs.existsSync(lockFile)) {\n fs.unlinkSync(lockFile);\n }\n } catch {}\n}\n\nexport interface McpServerOptions {\n dbPath?: string;\n context?: Partial<McpContext>;\n autoStartDaemon?: boolean;\n port?: number;\n host?: string;\n detached?: boolean;\n}\n\nexport function createMcpServer(options: McpServerOptions = {}) {\n const config = getDaemonConfig({\n dbPath: options.dbPath,\n port: options.port,\n host: options.host,\n });\n const db = options.context?.db || new StorageDB(config.dbPath);\n const screenshotStore = new ScreenshotStore(config.screenshotsDir);\n const sessionManager = options.context?.sessionManager || new SessionManager(db);\n const verificationEngine = options.context?.verificationEngine || new VerificationEngine(db, sessionManager, screenshotStore);\n const notesEngine = new NotesEngine(db, screenshotStore);\n const noteVerificationEngine = options.context?.noteVerificationEngine || new NoteVerificationEngine(db, sessionManager, notesEngine);\n\n const ctx: McpContext = {\n db,\n sessionManager,\n verificationEngine,\n noteVerificationEngine,\n daemonUrl: `http://${config.host}:${config.port}`,\n ...options.context,\n };\n\n let embeddedDaemon: any = null;\n\n async function ensureDaemon(): Promise<void> {\n if (options.autoStartDaemon === false || options.context?.sessionManager) {\n return;\n }\n\n // 1. Fast path: check if a singleton daemon is already running\n if (await isDaemonRunning(config.host, config.port)) {\n console.error(`[BrowserTrack MCP] Connected to active singleton daemon at http://${config.host}:${config.port}`);\n return;\n }\n\n // 2. Concurrency lock to guarantee only ONE daemon is ever started across multiple IDE sessions\n const lockFile = path.join(config.dataDir, 'daemon_boot.lock');\n const hasLock = acquireBootLock(lockFile);\n\n if (!hasLock) {\n // Another session is currently booting the daemon, wait for it\n for (let i = 0; i < 30; i++) {\n await new Promise((r) => setTimeout(r, 100));\n if (await isDaemonRunning(config.host, config.port)) {\n console.error(`[BrowserTrack MCP] Connected to shared singleton daemon at http://${config.host}:${config.port}`);\n return;\n }\n }\n }\n\n try {\n // 3. Double-check before starting\n if (await isDaemonRunning(config.host, config.port)) {\n console.error(`[BrowserTrack MCP] Connected to shared singleton daemon at http://${config.host}:${config.port}`);\n return;\n }\n\n // 4. Detached background process (singleton daemon that survives across IDE windows/sessions)\n if (options.detached !== false) {\n const cliPath = resolveCliPath();\n if (cliPath && fs.existsSync(cliPath)) {\n try {\n const nodePaths = [\n '/usr/local/bin',\n '/opt/homebrew/bin',\n process.env.HOME ? `${process.env.HOME}/.nvm/versions/node/v20.19.5/bin` : '',\n process.env.HOME ? `${process.env.HOME}/.nvm/versions/node/v23.1.0/bin` : '',\n ].filter(Boolean);\n const extendedPath = `${process.env.PATH || ''}:${nodePaths.join(':')}`;\n\n const child = spawn(\n process.execPath,\n [cliPath, 'start', '--port', String(config.port), '--host', config.host],\n {\n detached: true,\n stdio: 'ignore',\n env: { ...process.env, PATH: extendedPath, BROWSERTRACK_DAEMON_DETACHED: '1' },\n }\n );\n child.unref();\n\n for (let i = 0; i < 30; i++) {\n await new Promise((r) => setTimeout(r, 100));\n if (await isDaemonRunning(config.host, config.port)) {\n console.error(\n `[BrowserTrack MCP] Started singleton background daemon on http://${config.host}:${config.port}`\n );\n return;\n }\n }\n } catch (spawnErr: any) {\n console.error(\n `[BrowserTrack MCP] Detached daemon spawn failed (${spawnErr?.message}), falling back to in-process daemon.`\n );\n }\n }\n }\n\n // 5. In-process fallback (used when detached is false or in test runners)\n embeddedDaemon = createDaemon({\n host: config.host,\n port: config.port,\n dbPath: config.dbPath,\n screenshotsDir: config.screenshotsDir,\n verbose: false,\n });\n await embeddedDaemon.start();\n console.error(`[BrowserTrack MCP] Started singleton daemon on http://${config.host}:${config.port}`);\n\n ctx.db = embeddedDaemon.db;\n ctx.sessionManager = embeddedDaemon.sessionManager;\n ctx.verificationEngine = embeddedDaemon.verificationEngine;\n ctx.noteVerificationEngine = embeddedDaemon.noteVerificationEngine;\n\n const cleanup = () => {\n if (embeddedDaemon) {\n try {\n embeddedDaemon.stop();\n } catch {}\n embeddedDaemon = null;\n }\n };\n\n process.on('exit', cleanup);\n process.on('SIGINT', cleanup);\n process.on('SIGTERM', cleanup);\n } catch (err: any) {\n console.error(\n `[BrowserTrack MCP] Note: Daemon startup skipped (${err?.message}). Running MCP in standalone database mode.`\n );\n } finally {\n if (hasLock) {\n releaseBootLock(lockFile);\n }\n }\n }\n\n const server = new Server(\n {\n name: 'browsertrack-mcp',\n version: '0.1.0',\n },\n {\n capabilities: {\n tools: {},\n },\n }\n );\n\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n return { tools: TOOLS };\n });\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n try {\n // 20-second safety timeout so tool execution never hangs indefinitely\n const timeoutPromise = new Promise((_, reject) =>\n setTimeout(() => reject(new Error(`Tool execution timed out after 20s: ${name}`)), 20000)\n );\n const result = await Promise.race([handleToolCall(name, args || {}, ctx), timeoutPromise]);\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(result, null, 2),\n },\n ],\n };\n } catch (err: any) {\n return {\n isError: true,\n content: [\n {\n type: 'text',\n text: `Error executing tool ${name}: ${err?.message || String(err)}`,\n },\n ],\n };\n }\n });\n\n return {\n server,\n ctx,\n ensureDaemon,\n async stopDaemon() {\n if (embeddedDaemon) {\n await embeddedDaemon.stop();\n embeddedDaemon = null;\n }\n },\n async startStdio() {\n // Connect stdio transport immediately so client handshakes are answered instantly\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n // Start daemon verification / auto-boot asynchronously in background so client handshakes never block\n const daemonInitPromise = ensureDaemon().catch((err) => {\n console.error(`[BrowserTrack MCP] Daemon background init note: ${err?.message || err}`);\n });\n ctx.daemonInitPromise = daemonInitPromise;\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;AAEO,IAAM,QAAgB;AAAA,EAC3B;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa;AAAA,MACX,MAAM;AAAA,MACN,YAAY,CAAC;AAAA,IACf;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa;AAAA,MACX,MAAM;AAAA,MACN,YAAY;AAAA,QACV,WAAW,EAAE,MAAM,UAAU,aAAa,wCAAwC;AAAA,QAClF,YAAY,EAAE,MAAM,WAAW,aAAa,gEAAgE;AAAA,MAC9G;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa;AAAA,MACX,MAAM;AAAA,MACN,YAAY;AAAA,QACV,WAAW,EAAE,MAAM,UAAU,aAAa,+BAA+B;AAAA,QACzE,QAAQ;AAAA,UACN,MAAM;AAAA,UACN,MAAM,CAAC,QAAQ,iBAAiB,aAAa,YAAY,UAAU,cAAc;AAAA,UACjF,aAAa;AAAA,QACf;AAAA,QACA,UAAU;AAAA,UACR,MAAM;AAAA,UACN,MAAM,CAAC,SAAS,QAAQ,OAAO;AAAA,UAC/B,aAAa;AAAA,QACf;AAAA,QACA,OAAO,EAAE,MAAM,UAAU,aAAa,sDAAsD;AAAA,MAC9F;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa;AAAA,MACX,MAAM;AAAA,MACN,YAAY;AAAA,QACV,YAAY,EAAE,MAAM,UAAU,aAAa,8CAA8C;AAAA,MAC3F;AAAA,MACA,UAAU,CAAC,YAAY;AAAA,IACzB;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa;AAAA,MACX,MAAM;AAAA,MACN,YAAY;AAAA,QACV,WAAW,EAAE,MAAM,UAAU,aAAa,4DAA4D;AAAA,QACtG,OAAO,EAAE,MAAM,UAAU,aAAa,mDAAmD;AAAA,MAC3F;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa;AAAA,MACX,MAAM;AAAA,MACN,YAAY;AAAA,QACV,WAAW,EAAE,MAAM,UAAU,aAAa,gCAAgC;AAAA,QAC1E,OAAO,EAAE,MAAM,UAAU,aAAa,4DAA4D;AAAA,MACpG;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa;AAAA,MACX,MAAM;AAAA,MACN,YAAY;AAAA,QACV,WAAW,EAAE,MAAM,UAAU,aAAa,gCAAgC;AAAA,QAC1E,YAAY,EAAE,MAAM,UAAU,aAAa,mDAAmD;AAAA,QAC9F,OAAO,EAAE,MAAM,UAAU,aAAa,8CAA8C;AAAA,MACtF;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa;AAAA,MACX,MAAM;AAAA,MACN,YAAY;AAAA,QACV,WAAW,EAAE,MAAM,UAAU,aAAa,mDAAmD;AAAA,MAC/F;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa;AAAA,MACX,MAAM;AAAA,MACN,YAAY;AAAA,QACV,UAAU,EAAE,MAAM,UAAU,aAAa,0EAA0E;AAAA,QACnH,WAAW,EAAE,MAAM,UAAU,aAAa,gCAAgC;AAAA,MAC5E;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa;AAAA,MACX,MAAM;AAAA,MACN,YAAY;AAAA,QACV,YAAY,EAAE,MAAM,UAAU,aAAa,mCAAmC;AAAA,QAC9E,OAAO,EAAE,MAAM,UAAU,aAAa,8EAA8E;AAAA,QACpH,gBAAgB,EAAE,MAAM,UAAU,aAAa,wEAAwE;AAAA,QACvH,QAAQ;AAAA,UACN,MAAM;AAAA,UACN,aAAa;AAAA,UACb,OAAO;AAAA,YACL,MAAM;AAAA,YACN,YAAY;AAAA,cACV,MAAM;AAAA,gBACJ,MAAM;AAAA,gBACN,MAAM,CAAC,kBAAkB,mBAAmB,iBAAiB,YAAY,aAAa;AAAA,cACxF;AAAA,cACA,UAAU,EAAE,MAAM,SAAS;AAAA,cAC3B,MAAM,EAAE,MAAM,SAAS;AAAA,cACvB,OAAO,EAAE,MAAM,SAAS;AAAA,YAC1B;AAAA,YACA,UAAU,CAAC,MAAM;AAAA,UACnB;AAAA,QACF;AAAA,QACA,qBAAqB,EAAE,MAAM,UAAU,aAAa,qDAAqD;AAAA,MAC3G;AAAA,MACA,UAAU,CAAC,YAAY;AAAA,IACzB;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa;AAAA,MACX,MAAM;AAAA,MACN,YAAY;AAAA,QACV,YAAY,EAAE,MAAM,UAAU,aAAa,yBAAyB;AAAA,MACtE;AAAA,MACA,UAAU,CAAC,YAAY;AAAA,IACzB;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa;AAAA,MACX,MAAM;AAAA,MACN,YAAY;AAAA,QACV,WAAW,EAAE,MAAM,UAAU,aAAa,qCAAqC;AAAA,QAC/E,YAAY,EAAE,MAAM,UAAU,aAAa,qCAAqC;AAAA,QAChF,QAAQ;AAAA,UACN,MAAM;AAAA,UACN,MAAM,CAAC,QAAQ,eAAe,aAAa,YAAY,UAAU,cAAc;AAAA,UAC/E,aAAa;AAAA,QACf;AAAA,QACA,OAAO,EAAE,MAAM,UAAU,aAAa,kDAAkD;AAAA,MAC1F;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa;AAAA,MACX,MAAM;AAAA,MACN,YAAY;AAAA,QACV,WAAW,EAAE,MAAM,UAAU,aAAa,yCAAyC;AAAA,QACnF,QAAQ;AAAA,UACN,MAAM;AAAA,UACN,MAAM,CAAC,QAAQ,UAAU;AAAA,UACzB,aAAa;AAAA,QACf;AAAA,QACA,OAAO,EAAE,MAAM,UAAU,aAAa,sDAAsD;AAAA,MAC9F;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa;AAAA,MACX,MAAM;AAAA,MACN,YAAY;AAAA,QACV,YAAY,EAAE,MAAM,UAAU,aAAa,uDAAuD;AAAA,MACpG;AAAA,MACA,UAAU,CAAC,YAAY;AAAA,IACzB;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa;AAAA,MACX,MAAM;AAAA,MACN,YAAY;AAAA,QACV,QAAQ,EAAE,MAAM,UAAU,aAAa,kDAAkD;AAAA,MAC3F;AAAA,MACA,UAAU,CAAC,QAAQ;AAAA,IACrB;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa;AAAA,MACX,MAAM;AAAA,MACN,YAAY;AAAA,QACV,QAAQ,EAAE,MAAM,UAAU,aAAa,uCAAuC;AAAA,MAChF;AAAA,MACA,UAAU,CAAC,QAAQ;AAAA,IACrB;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa;AAAA,MACX,MAAM;AAAA,MACN,YAAY;AAAA,QACV,QAAQ,EAAE,MAAM,UAAU,aAAa,sCAAsC;AAAA,MAC/E;AAAA,MACA,UAAU,CAAC,QAAQ;AAAA,IACrB;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa;AAAA,MACX,MAAM;AAAA,MACN,YAAY;AAAA,QACV,QAAQ,EAAE,MAAM,UAAU,aAAa,sCAAsC;AAAA,QAC7E,qBAAqB,EAAE,MAAM,UAAU,aAAa,qDAAqD;AAAA,MAC3G;AAAA,MACA,UAAU,CAAC,QAAQ;AAAA,IACrB;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa;AAAA,MACX,MAAM;AAAA,MACN,YAAY;AAAA,QACV,QAAQ,EAAE,MAAM,UAAU,aAAa,4BAA4B;AAAA,MACrE;AAAA,MACA,UAAU,CAAC,QAAQ;AAAA,IACrB;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa;AAAA,MACX,MAAM;AAAA,MACN,YAAY;AAAA,QACV,UAAU,EAAE,MAAM,UAAU,aAAa,qCAAqC;AAAA,QAC9E,WAAW,EAAE,MAAM,UAAU,aAAa,gCAAgC;AAAA,MAC5E;AAAA,MACA,UAAU,CAAC,UAAU;AAAA,IACvB;AAAA,EACF;AACF;;;ACzPA,eAAe,mBAAmB,KAAiB,WAA+B,SAAc,YAAY,KAAoB;AAC9H,MAAI,IAAI,mBAAmB;AACzB,QAAI;AACF,YAAM,QAAQ,KAAK,CAAC,IAAI,mBAAmB,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,GAAI,CAAC,CAAC,CAAC;AAAA,IACrF,QAAQ;AAAA,IAAC;AAAA,EACX;AAGA,MAAI,IAAI,kBAAkB,IAAI,eAAe,eAAe,IAAI,GAAG;AACjE,UAAM,gBAAgB,YAAY,IAAI,GAAG,WAAW,SAAS,IAAI,IAAI,eAAe,oBAAoB;AACxG,QAAI,eAAe;AACjB,YAAM,MAAM,MAAM,IAAI,eAAe,YAAY,cAAc,IAAI,SAAS,SAAS;AACrF,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,IAAI,MAAM,IAAI,SAAS,IAAI,UAAU,WAAW,QAAQ,IAAI,SAAS;AAAA,MAC7E;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAGA,MAAI,IAAI,WAAW;AACjB,QAAI;AACF,YAAM,OAAO,MAAM,MAAM,GAAG,IAAI,SAAS,gBAAgB;AAAA,QACvD,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,WAAW,SAAS,UAAU,CAAC;AAAA,QACtD,QAAQ,YAAY,QAAQ,YAAY,GAAI;AAAA,MAC9C,CAAC;AACD,UAAI,KAAK,IAAI;AACX,cAAM,MAAM,MAAM,KAAK,KAAK;AAC5B,YAAI,CAAC,IAAI,IAAI;AACX,gBAAM,IAAI,MAAM,IAAI,SAAS,IAAI,UAAU,WAAW,QAAQ,IAAI,SAAS;AAAA,QAC7E;AACA,eAAO;AAAA,MACT;AACA,YAAM,UAAU,MAAM,KAAK,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAClD,UAAI,QAAQ,OAAO;AACjB,cAAM,IAAI,MAAM,QAAQ,KAAK;AAAA,MAC/B;AAAA,IACF,SAAS,KAAU;AACjB,UAAI,IAAI,WAAW,CAAC,IAAI,QAAQ,SAAS,cAAc,KAAK,CAAC,IAAI,QAAQ,SAAS,cAAc,GAAG;AACjG,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,IAAI;AAAA,IACR;AAAA,EACF;AACF;AAEA,eAAe,kBAAkB,KAAiB,YAAoB,SAA4B;AAChG,MAAI,IAAI,sBAAsB,IAAI,kBAAkB,IAAI,eAAe,eAAe,IAAI,GAAG;AAC3F,WAAO,MAAM,IAAI,mBAAmB,eAAe,YAAY,OAAO;AAAA,EACxE;AAEA,MAAI,IAAI,WAAW;AACjB,QAAI;AACF,YAAM,aAAa,SAAS,uBAAuB,OAAQ;AAC3D,YAAM,OAAO,MAAM,MAAM,GAAG,IAAI,SAAS,wBAAwB;AAAA,QAC/D,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,YAAY,QAAQ,CAAC;AAAA,QAC5C,QAAQ,YAAY,QAAQ,SAAS;AAAA,MACvC,CAAC;AACD,UAAI,KAAK,IAAI;AACX,cAAM,OAAO,MAAM,KAAK,KAAK;AAC7B,YAAI,KAAK,GAAI,QAAO,KAAK;AACzB,cAAM,IAAI,MAAM,KAAK,KAAK;AAAA,MAC5B;AAAA,IACF,SAAS,KAAU;AACjB,UAAI,IAAI,WAAW,CAAC,IAAI,QAAQ,SAAS,cAAc,KAAK,CAAC,IAAI,QAAQ,SAAS,cAAc,GAAG;AACjG,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,IAAI;AAAA,IACR;AAAA,EACF;AACF;AAEA,eAAe,cAAc,KAAiB,QAAgB,SAA4B;AACxF,MAAI,IAAI,0BAA0B,IAAI,kBAAkB,IAAI,eAAe,eAAe,IAAI,GAAG;AAC/F,WAAO,MAAM,IAAI,uBAAuB,WAAW,QAAQ,OAAO;AAAA,EACpE;AAEA,MAAI,IAAI,WAAW;AACjB,QAAI;AACF,YAAM,aAAa,SAAS,uBAAuB,OAAQ;AAC3D,YAAM,OAAO,MAAM,MAAM,GAAG,IAAI,SAAS,oBAAoB;AAAA,QAC3D,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,QAAQ,QAAQ,CAAC;AAAA,QACxC,QAAQ,YAAY,QAAQ,SAAS;AAAA,MACvC,CAAC;AACD,UAAI,KAAK,IAAI;AACX,cAAM,OAAO,MAAM,KAAK,KAAK;AAC7B,YAAI,KAAK,GAAI,QAAO,KAAK;AACzB,cAAM,IAAI,MAAM,KAAK,KAAK;AAAA,MAC5B;AAAA,IACF,SAAS,KAAU;AACjB,UAAI,IAAI,WAAW,CAAC,IAAI,QAAQ,SAAS,cAAc,KAAK,CAAC,IAAI,QAAQ,SAAS,cAAc,GAAG;AACjG,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,IAAI;AAAA,IACR;AAAA,EACF;AACF;AAEA,eAAsB,eAAe,MAAc,MAAW,KAA+B;AAC3F,QAAM,EAAE,IAAI,gBAAgB,oBAAoB,uBAAuB,IAAI;AAE3E,UAAQ,MAAM;AAAA,IACZ,KAAK,iBAAiB;AACpB,YAAM,WAAW,GAAG,aAAa;AACjC,aAAO;AAAA,QACL,UAAU,SAAS,IAAI,CAAC,OAAO;AAAA,UAC7B,IAAI,EAAE;AAAA,UACN,MAAM,EAAE;AAAA,UACR,QAAQ,EAAE;AAAA,UACV,MAAM,EAAE;AAAA,UACR,WAAW,EAAE;AAAA,QACf,EAAE;AAAA,MACJ;AAAA,IACF;AAAA,IAEA,KAAK,iBAAiB;AACpB,YAAM,aAAa,KAAK,eAAe;AACvC,YAAM,WAAW,GAAG,aAAa,KAAK,WAAW,UAAU;AAC3D,aAAO;AAAA,QACL,OAAO,SAAS;AAAA,QAChB,UAAU,SAAS,IAAI,CAAC,OAAO;AAAA,UAC7B,IAAI,EAAE;AAAA,UACN,WAAW,EAAE;AAAA,UACb,QAAQ,EAAE;AAAA,UACV,KAAK,EAAE;AAAA,UACP,OAAO,EAAE;AAAA,UACT,QAAQ,EAAE;AAAA,UACV,YAAY,EAAE;AAAA,QAChB,EAAE;AAAA,MACJ;AAAA,IACF;AAAA,IAEA,KAAK,kBAAkB;AACrB,YAAM,YAAY,GAAG,cAAc;AAAA,QACjC,WAAW,KAAK;AAAA,QAChB,QAAQ,KAAK;AAAA,QACb,UAAU,KAAK;AAAA,QACf,OAAO,KAAK,SAAS;AAAA,MACvB,CAAC;AACD,aAAO;AAAA,QACL,OAAO,UAAU;AAAA,QACjB,WAAW,UAAU,IAAI,CAAC,SAAS;AAAA,UACjC,IAAI,IAAI;AAAA,UACR,MAAM,IAAI;AAAA,UACV,UAAU,IAAI;AAAA,UACd,SAAS,IAAI;AAAA,UACb,QAAQ,GAAG,IAAI,OAAO,IAAI,IAAI,IAAI,OAAO,IAAI;AAAA,UAC7C,OAAO,IAAI;AAAA,UACX,QAAQ,IAAI;AAAA,UACZ,aAAa,IAAI;AAAA,UACjB,WAAW,IAAI;AAAA,UACf,UAAU,IAAI;AAAA,QAChB,EAAE;AAAA,MACJ;AAAA,IACF;AAAA,IAEA,KAAK,gBAAgB;AACnB,YAAM,WAAW,GAAG,YAAY,KAAK,UAAU;AAC/C,UAAI,CAAC,UAAU;AACb,cAAM,IAAI,MAAM,aAAa,KAAK,UAAU,cAAc;AAAA,MAC5D;AAGA,YAAM,sBAAsB,SAAS,YAAY,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM;AACrE,cAAM,OAAO,IAAI,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE,CAAC,GAAG,MAAM,GAAG,CAAC;AAC1E,eAAO,IAAI,IAAI,KAAK,EAAE,OAAO;AAAA,MAC/B,CAAC;AAED,aAAO;AAAA,QACL,IAAI,SAAS;AAAA,QACb,QAAQ,SAAS;AAAA,QACjB,MAAM,SAAS;AAAA,QACf,UAAU,SAAS;AAAA,QACnB,SAAS,SAAS;AAAA,QAClB,QAAQ;AAAA,UACN,MAAM,SAAS,OAAO;AAAA,UACtB,MAAM,SAAS,OAAO;AAAA,UACtB,QAAQ,SAAS,OAAO;AAAA,QAC1B;AAAA,QACA,OAAO,SAAS;AAAA,QAChB,aAAa,SAAS;AAAA,QACtB,WAAW,SAAS;AAAA,QACpB,UAAU,SAAS;AAAA,QACnB,OAAO,SAAS;AAAA,QAChB,uBAAuB,SAAS,cAC5B;AAAA,UACE,UAAU,SAAS,YAAY;AAAA,UAC/B,KAAK,SAAS,YAAY;AAAA,UAC1B,SAAS,SAAS,YAAY;AAAA,UAC9B,WAAW,SAAS,YAAY;AAAA,UAChC,WAAW,SAAS,YAAY;AAAA,UAChC,iBAAiB,SAAS,YAAY;AAAA,QACxC,IACA;AAAA,QACJ,mBAAmB;AAAA,QACnB,iBAAiB,SAAS;AAAA,QAC1B,YAAY,SAAS,aAAa;AAAA,MACpC;AAAA,IACF;AAAA,IAEA,KAAK,eAAe;AAClB,YAAM,QAAQ,KAAK,SAAS;AAC5B,YAAM,SAAS,GAAG,UAAU;AAAA,QAC1B,WAAW,KAAK;AAAA,QAChB,WAAW;AAAA,QACX;AAAA,MACF,CAAC;AAED,aAAO;AAAA,QACL,MAAM,OAAO,IAAI,CAAC,OAAO;AAAA,UACvB,OAAO,EAAE,QAAQ;AAAA,UACjB,SAAS,EAAE,QAAQ;AAAA,UACnB,WAAW,IAAI,KAAK,EAAE,SAAS,EAAE,YAAY;AAAA,UAC7C,OAAO,EAAE;AAAA,QACX,EAAE;AAAA,MACJ;AAAA,IACF;AAAA,IAEA,KAAK,wBAAwB;AAC3B,YAAM,QAAQ,KAAK,SAAS;AAC5B,YAAM,SAAS,GAAG,UAAU;AAAA,QAC1B,WAAW,KAAK;AAAA,QAChB,OAAO;AAAA,MACT,CAAC;AAED,YAAM,WAAW,OACd,OAAO,CAAC,OAAO,EAAE,cAAc,WAAW,EAAE,cAAc,WAAW,EAAE,QAAQ,UAAU,OAAO,EAAE,QAAQ,MAAM,EAChH,MAAM,GAAG,KAAK,EACd,IAAI,CAAC,OAAO;AAAA,QACX,KAAK,EAAE,QAAQ;AAAA,QACf,QAAQ,EAAE,QAAQ;AAAA,QAClB,QAAQ,EAAE,QAAQ;AAAA,QAClB,OAAO,EAAE,QAAQ;AAAA,QACjB,YAAY,EAAE,QAAQ;AAAA,QACtB,WAAW,IAAI,KAAK,EAAE,SAAS,EAAE,YAAY;AAAA,MAC/C,EAAE;AAEJ,aAAO,EAAE,SAAS;AAAA,IACpB;AAAA,IAEA,KAAK,mBAAmB;AACtB,UAAI,KAAK,YAAY;AACnB,cAAM,WAAW,GAAG,YAAY,KAAK,UAAU;AAC/C,YAAI,CAAC,UAAU;AACb,gBAAM,IAAI,MAAM,aAAa,KAAK,UAAU,cAAc;AAAA,QAC5D;AACA,eAAO;AAAA,UACL,YAAY,KAAK;AAAA,UACjB,aAAa,SAAS,YAAY,MAAM,EAAE,KAAK,SAAS,GAAG;AAAA,QAC7D;AAAA,MACF;AAEA,YAAM,SAAS,GAAG,UAAU;AAAA,QAC1B,WAAW,KAAK;AAAA,QAChB,OAAO,KAAK,SAAS;AAAA,MACvB,CAAC;AAED,aAAO;AAAA,QACL,aAAa,OAAO,IAAI,CAAC,OAAO;AAAA,UAC9B,MAAM,EAAE;AAAA,UACR,SAAS,EAAE,QAAQ,WAAW,GAAG,EAAE,SAAS,OAAO,EAAE,KAAK;AAAA,UAC1D,WAAW,IAAI,KAAK,EAAE,SAAS,EAAE,YAAY;AAAA,UAC7C,OAAO,EAAE;AAAA,QACX,EAAE;AAAA,MACJ;AAAA,IACF;AAAA,IAEA,KAAK,kBAAkB;AACrB,YAAM,SAAS,MAAM,mBAAmB,KAAK,KAAK,WAAW;AAAA,QAC3D,IAAI,WAAW,KAAK,IAAI,CAAC;AAAA,QACzB,MAAM;AAAA,MACR,CAAC;AACD,aAAO,OAAO;AAAA,IAChB;AAAA,IAEA,KAAK,mBAAmB;AACtB,YAAM,SAAS,MAAM,mBAAmB,KAAK,KAAK,WAAW;AAAA,QAC3D,IAAI,WAAW,KAAK,IAAI,CAAC;AAAA,QACzB,MAAM;AAAA,QACN,QAAQ,EAAE,UAAU,KAAK,SAAS;AAAA,MACpC,CAAC;AAED,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,QAAQ,OAAO,QAAQ,UAAU;AAAA,QACjC,OAAO,OAAO,QAAQ;AAAA,QACtB,QAAQ,OAAO,QAAQ;AAAA,QACvB,gBAAgB,OAAO,QAAQ,UAAU,GAAG,OAAO,OAAO,QAAQ,MAAM,GAAG,GAAG,CAAC,QAAQ;AAAA,MACzF;AAAA,IACF;AAAA,IAEA,KAAK,mBAAmB;AACtB,YAAM,MAAM,MAAM,kBAAkB,KAAK,KAAK,YAAY;AAAA,QACxD,OAAO,KAAK;AAAA,QACZ,gBAAgB,KAAK;AAAA,QACrB,QAAQ,KAAK;AAAA,QACb,qBAAqB,KAAK;AAAA,MAC5B,CAAC;AAED,aAAO;AAAA,IACT;AAAA,IAEA,KAAK,oBAAoB;AACvB,YAAM,IAAI,GAAG,sBAAsB,KAAK,UAAU;AAClD,UAAI,CAAC,GAAG;AACN,cAAM,IAAI,MAAM,+CAA+C,KAAK,UAAU,IAAI;AAAA,MACpF;AACA,aAAO;AAAA,IACT;AAAA,IAEA,KAAK,cAAc;AACjB,YAAM,QAAQ,GAAG,UAAU;AAAA,QACzB,WAAW,KAAK;AAAA,QAChB,YAAY,KAAK;AAAA,QACjB,QAAQ,KAAK;AAAA,QACb,OAAO,KAAK,SAAS;AAAA,MACvB,CAAC;AAED,aAAO;AAAA,QACL,OAAO,MAAM;AAAA,QACb,OAAO,MAAM,IAAI,CAAC,OAAO;AAAA,UACvB,IAAI,EAAE;AAAA,UACN,MAAM,EAAE;AAAA,UACR,QAAQ,EAAE;AAAA,UACV,OAAO,EAAE;AAAA,UACT,YAAY,EAAE;AAAA,UACd,YAAY,EAAE;AAAA,UACd,eAAe,EAAE;AAAA,UACjB,UAAU,GAAG,EAAE,SAAS,KAAK,SAAM,EAAE,SAAS,MAAM;AAAA,UACpD,QAAQ,EAAE,QAAQ,YAAY,EAAE;AAAA,UAChC,SAAS,EAAE;AAAA,UACX,qBAAqB,CAAC,CAAC,EAAE,aAAa;AAAA,UACtC,WAAW,EAAE;AAAA,QACf,EAAE;AAAA,MACJ;AAAA,IACF;AAAA,IAEA,KAAK,kBAAkB;AACrB,YAAM,YAAY,GAAG,cAAc;AAAA,QACjC,WAAW,KAAK;AAAA,QAChB,QAAQ,KAAK;AAAA,QACb,OAAO,KAAK,SAAS;AAAA,MACvB,CAAC;AAED,aAAO;AAAA,QACL,OAAO,UAAU;AAAA,QACjB,WAAW,UAAU,IAAI,CAAC,OAAO;AAAA,UAC/B,IAAI,EAAE;AAAA,UACN,OAAO,EAAE;AAAA,UACT,YAAY,EAAE;AAAA,UACd,QAAQ,EAAE;AAAA,UACV,OAAO,EAAE;AAAA,UACT,aAAa,EAAE;AAAA,UACf,YAAY,EAAE;AAAA,QAChB,EAAE;AAAA,MACJ;AAAA,IACF;AAAA,IAEA,KAAK,gBAAgB;AACnB,YAAM,WAAW,GAAG,YAAY,KAAK,UAAU;AAC/C,UAAI,CAAC,UAAU;AACb,cAAM,IAAI,MAAM,aAAa,KAAK,UAAU,cAAc;AAAA,MAC5D;AAEA,aAAO;AAAA,QACL,IAAI,SAAS;AAAA,QACb,OAAO,SAAS;AAAA,QAChB,YAAY,SAAS;AAAA,QACrB,QAAQ,SAAS;AAAA,QACjB,OAAO,SAAS,MAAM,IAAI,CAAC,UAAU;AAAA,UACnC,IAAI,KAAK;AAAA,UACT,YAAY,KAAK;AAAA,UACjB,MAAM,KAAK;AAAA,UACX,SAAS,KAAK;AAAA,UACd,OAAO,KAAK;AAAA,UACZ,KAAK,KAAK;AAAA,UACV,gBAAgB,KAAK,QAAQ;AAAA,UAC7B,cAAc,KAAK,QAAQ,gBAAgB,KAAK;AAAA,UAChD,gBAAgB,KAAK;AAAA,UACrB,YAAY,KAAK,aAAa;AAAA,UAC9B,QAAQ,KAAK;AAAA,UACb,WAAW,KAAK;AAAA,QAClB,EAAE;AAAA,QACF,WAAW,SAAS;AAAA,QACpB,WAAW,SAAS;AAAA,MACtB;AAAA,IACF;AAAA,IAEA,KAAK,YAAY;AACf,YAAM,OAAO,GAAG,QAAQ,KAAK,MAAM;AACnC,UAAI,CAAC,MAAM;AACT,cAAM,IAAI,MAAM,gBAAgB,KAAK,MAAM,cAAc;AAAA,MAC3D;AAEA,YAAM,UAAU,GAAG,WAAW,KAAK,SAAS;AAE5C,aAAO;AAAA,QACL,IAAI,KAAK;AAAA,QACT,MAAM,KAAK;AAAA,QACX,QAAQ,KAAK;AAAA,QACb,SAAS,KAAK;AAAA,QACd,YAAY,KAAK;AAAA,QACjB,YAAY,KAAK;AAAA,QACjB,eAAe,KAAK;AAAA,QACpB,OAAO,KAAK;AAAA,QACZ,KAAK,KAAK;AAAA,QACV,UAAU,KAAK;AAAA,QACf,QAAQ,KAAK;AAAA,QACb,QAAQ,KAAK;AAAA,QACb,gBAAgB,KAAK;AAAA,QACrB,QAAQ,KAAK;AAAA,QACb,YAAY;AAAA,UACV,WAAW,CAAC,CAAC,KAAK,aAAa;AAAA,UAC/B,UAAU,KAAK,aAAa;AAAA,UAC5B,OAAO,KAAK,aAAa;AAAA,QAC3B;AAAA,QACA,SAAS,UACL;AAAA,UACE,IAAI,QAAQ;AAAA,UACZ,MAAM,QAAQ;AAAA,UACd,QAAQ,QAAQ;AAAA,UAChB,MAAM,QAAQ;AAAA,QAChB,IACA;AAAA,QACJ,mBAAmB,KAAK;AAAA,QACxB,WAAW,KAAK;AAAA,QAChB,WAAW,KAAK;AAAA,QAChB,YAAY,KAAK;AAAA,MACnB;AAAA,IACF;AAAA,IAEA,KAAK,gBAAgB;AACnB,YAAM,OAAO,GAAG,QAAQ,KAAK,MAAM;AACnC,UAAI,CAAC,MAAM;AACT,cAAM,IAAI,MAAM,gBAAgB,KAAK,MAAM,cAAc;AAAA,MAC3D;AACA,SAAG,iBAAiB,KAAK,QAAQ,UAAU;AAC3C,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,QAAQ,KAAK;AAAA,QACb,QAAQ;AAAA,QACR,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,MACrC;AAAA,IACF;AAAA,IAEA,KAAK,eAAe;AAClB,YAAM,OAAO,GAAG,QAAQ,KAAK,MAAM;AACnC,UAAI,CAAC,MAAM;AACT,cAAM,IAAI,MAAM,gBAAgB,KAAK,MAAM,cAAc;AAAA,MAC3D;AACA,SAAG,iBAAiB,KAAK,QAAQ,MAAM;AACvC,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,QAAQ,KAAK;AAAA,QACb,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,IAEA,KAAK,eAAe;AAClB,YAAM,MAAM,MAAM,cAAc,KAAK,KAAK,QAAQ;AAAA,QAChD,qBAAqB,KAAK;AAAA,MAC5B,CAAC;AACD,aAAO;AAAA,IACT;AAAA,IAEA,KAAK,yBAAyB;AAC5B,YAAM,IAAI,GAAG,0BAA0B,KAAK,MAAM;AAClD,UAAI,CAAC,GAAG;AACN,cAAM,IAAI,MAAM,kDAAkD,KAAK,MAAM,IAAI;AAAA,MACnF;AACA,aAAO;AAAA,IACT;AAAA,IAEA,KAAK,wBAAwB;AAC3B,YAAM,WAAW,MAAM,mBAAmB,KAAK,KAAK,WAAW;AAAA,QAC7D,IAAI,WAAW,KAAK,IAAI,CAAC;AAAA,QACzB,MAAM;AAAA,QACN,QAAQ,EAAE,UAAU,KAAK,SAAS;AAAA,MACpC,CAAC;AAED,YAAM,cAAc,MAAM,mBAAmB,KAAK,KAAK,WAAW;AAAA,QAChE,IAAI,WAAW,KAAK,IAAI,CAAC;AAAA,QACzB,MAAM;AAAA,QACN,QAAQ,EAAE,UAAU,KAAK,SAAS;AAAA,MACpC,CAAC;AAED,YAAM,WAAW,MAAM,mBAAmB,KAAK,KAAK,WAAW;AAAA,QAC7D,IAAI,WAAW,KAAK,IAAI,CAAC;AAAA,QACzB,MAAM;AAAA,QACN,QAAQ,EAAE,UAAU,KAAK,SAAS;AAAA,MACpC,CAAC;AAED,aAAO;AAAA,QACL,UAAU,KAAK;AAAA,QACf,SAAS,SAAS;AAAA,QAClB,UAAU,YAAY;AAAA,QACtB,QAAQ,SAAS,QAAQ;AAAA,MAC3B;AAAA,IACF;AAAA,IAEA;AACE,YAAM,IAAI,MAAM,qBAAqB,IAAI,EAAE;AAAA,EAC/C;AACF;;;ACphBA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAC9B,SAAS,aAAa;AACtB,SAAS,cAAc;AACvB,SAAS,4BAA4B;AACrC,SAAS,uBAAuB,8BAA8B;AAY9D,eAAsB,gBAAgB,MAAc,MAAgC;AAClF,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,UAAU,IAAI,IAAI,IAAI,WAAW;AAAA,MACvD,QAAQ,YAAY,QAAQ,GAAG;AAAA,IACjC,CAAC;AACD,QAAI,IAAI,IAAI;AACV,YAAM,OAAQ,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC/C,aAAO,MAAM,SAAS;AAAA,IACxB;AAAA,EACF,QAAQ;AAAA,EAAC;AACT,SAAO;AACT;AAEA,SAAS,iBAAgC;AACvC,MAAI,QAAQ,KAAK,CAAC,GAAG;AACnB,UAAM,YAAY,QAAQ,KAAK,CAAC;AAChC,QACE,UAAU,SAAS,cAAc,KACjC,UAAU,SAAS,cAAc,KACjC,UAAU,SAAS,qBAAqB,KACxC,UAAU,SAAS,mBAAmB,GACtC;AACA,UAAI,GAAG,WAAW,SAAS,EAAG,QAAO;AAAA,IACvC;AAAA,EACF;AAEA,MAAI;AACF,UAAM,aAAa,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC;AAC9D,UAAM,QAAQ;AAAA,MACZ,KAAK,QAAQ,YAAY,iBAAiB;AAAA,MAC1C,KAAK,QAAQ,YAAY,oBAAoB;AAAA,MAC7C,KAAK,QAAQ,YAAY,yBAAyB;AAAA,IACpD;AACA,eAAW,KAAK,OAAO;AACrB,UAAI,GAAG,WAAW,CAAC,EAAG,QAAO;AAAA,IAC/B;AAAA,EACF,QAAQ;AAAA,EAAC;AAET,SAAO;AACT;AAEA,SAAS,gBAAgB,UAA2B;AAClD,MAAI;AACF,OAAG,UAAU,KAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AACxD,UAAM,KAAK,GAAG,SAAS,UAAU,IAAI;AACrC,OAAG,UAAU,IAAI,OAAO,QAAQ,GAAG,CAAC;AACpC,OAAG,UAAU,EAAE;AACf,WAAO;AAAA,EACT,QAAQ;AACN,QAAI;AACF,YAAM,QAAQ,GAAG,SAAS,QAAQ;AAElC,UAAI,KAAK,IAAI,IAAI,MAAM,UAAU,KAAM;AACrC,WAAG,WAAW,QAAQ;AACtB,cAAM,KAAK,GAAG,SAAS,UAAU,IAAI;AACrC,WAAG,UAAU,IAAI,OAAO,QAAQ,GAAG,CAAC;AACpC,WAAG,UAAU,EAAE;AACf,eAAO;AAAA,MACT;AAAA,IACF,QAAQ;AAAA,IAAC;AACT,WAAO;AAAA,EACT;AACF;AAEA,SAAS,gBAAgB,UAAwB;AAC/C,MAAI;AACF,QAAI,GAAG,WAAW,QAAQ,GAAG;AAC3B,SAAG,WAAW,QAAQ;AAAA,IACxB;AAAA,EACF,QAAQ;AAAA,EAAC;AACX;AAWO,SAAS,gBAAgB,UAA4B,CAAC,GAAG;AAC9D,QAAM,SAAS,gBAAgB;AAAA,IAC7B,QAAQ,QAAQ;AAAA,IAChB,MAAM,QAAQ;AAAA,IACd,MAAM,QAAQ;AAAA,EAChB,CAAC;AACD,QAAM,KAAK,QAAQ,SAAS,MAAM,IAAI,UAAU,OAAO,MAAM;AAC7D,QAAM,kBAAkB,IAAI,gBAAgB,OAAO,cAAc;AACjE,QAAM,iBAAiB,QAAQ,SAAS,kBAAkB,IAAI,eAAe,EAAE;AAC/E,QAAM,qBAAqB,QAAQ,SAAS,sBAAsB,IAAI,mBAAmB,IAAI,gBAAgB,eAAe;AAC5H,QAAM,cAAc,IAAI,YAAY,IAAI,eAAe;AACvD,QAAM,yBAAyB,QAAQ,SAAS,0BAA0B,IAAI,uBAAuB,IAAI,gBAAgB,WAAW;AAEpI,QAAM,MAAkB;AAAA,IACtB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW,UAAU,OAAO,IAAI,IAAI,OAAO,IAAI;AAAA,IAC/C,GAAG,QAAQ;AAAA,EACb;AAEA,MAAI,iBAAsB;AAE1B,iBAAe,eAA8B;AAC3C,QAAI,QAAQ,oBAAoB,SAAS,QAAQ,SAAS,gBAAgB;AACxE;AAAA,IACF;AAGA,QAAI,MAAM,gBAAgB,OAAO,MAAM,OAAO,IAAI,GAAG;AACnD,cAAQ,MAAM,qEAAqE,OAAO,IAAI,IAAI,OAAO,IAAI,EAAE;AAC/G;AAAA,IACF;AAGA,UAAM,WAAW,KAAK,KAAK,OAAO,SAAS,kBAAkB;AAC7D,UAAM,UAAU,gBAAgB,QAAQ;AAExC,QAAI,CAAC,SAAS;AAEZ,eAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,cAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,GAAG,CAAC;AAC3C,YAAI,MAAM,gBAAgB,OAAO,MAAM,OAAO,IAAI,GAAG;AACnD,kBAAQ,MAAM,qEAAqE,OAAO,IAAI,IAAI,OAAO,IAAI,EAAE;AAC/G;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,QAAI;AAEF,UAAI,MAAM,gBAAgB,OAAO,MAAM,OAAO,IAAI,GAAG;AACnD,gBAAQ,MAAM,qEAAqE,OAAO,IAAI,IAAI,OAAO,IAAI,EAAE;AAC/G;AAAA,MACF;AAGA,UAAI,QAAQ,aAAa,OAAO;AAC9B,cAAM,UAAU,eAAe;AAC/B,YAAI,WAAW,GAAG,WAAW,OAAO,GAAG;AACrC,cAAI;AACF,kBAAM,YAAY;AAAA,cAChB;AAAA,cACA;AAAA,cACA,QAAQ,IAAI,OAAO,GAAG,QAAQ,IAAI,IAAI,qCAAqC;AAAA,cAC3E,QAAQ,IAAI,OAAO,GAAG,QAAQ,IAAI,IAAI,oCAAoC;AAAA,YAC5E,EAAE,OAAO,OAAO;AAChB,kBAAM,eAAe,GAAG,QAAQ,IAAI,QAAQ,EAAE,IAAI,UAAU,KAAK,GAAG,CAAC;AAErE,kBAAM,QAAQ;AAAA,cACZ,QAAQ;AAAA,cACR,CAAC,SAAS,SAAS,UAAU,OAAO,OAAO,IAAI,GAAG,UAAU,OAAO,IAAI;AAAA,cACvE;AAAA,gBACE,UAAU;AAAA,gBACV,OAAO;AAAA,gBACP,KAAK,EAAE,GAAG,QAAQ,KAAK,MAAM,cAAc,8BAA8B,IAAI;AAAA,cAC/E;AAAA,YACF;AACA,kBAAM,MAAM;AAEZ,qBAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,oBAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,GAAG,CAAC;AAC3C,kBAAI,MAAM,gBAAgB,OAAO,MAAM,OAAO,IAAI,GAAG;AACnD,wBAAQ;AAAA,kBACN,oEAAoE,OAAO,IAAI,IAAI,OAAO,IAAI;AAAA,gBAChG;AACA;AAAA,cACF;AAAA,YACF;AAAA,UACF,SAAS,UAAe;AACtB,oBAAQ;AAAA,cACN,oDAAoD,UAAU,OAAO;AAAA,YACvE;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAGA,uBAAiB,aAAa;AAAA,QAC5B,MAAM,OAAO;AAAA,QACb,MAAM,OAAO;AAAA,QACb,QAAQ,OAAO;AAAA,QACf,gBAAgB,OAAO;AAAA,QACvB,SAAS;AAAA,MACX,CAAC;AACD,YAAM,eAAe,MAAM;AAC3B,cAAQ,MAAM,yDAAyD,OAAO,IAAI,IAAI,OAAO,IAAI,EAAE;AAEnG,UAAI,KAAK,eAAe;AACxB,UAAI,iBAAiB,eAAe;AACpC,UAAI,qBAAqB,eAAe;AACxC,UAAI,yBAAyB,eAAe;AAE5C,YAAM,UAAU,MAAM;AACpB,YAAI,gBAAgB;AAClB,cAAI;AACF,2BAAe,KAAK;AAAA,UACtB,QAAQ;AAAA,UAAC;AACT,2BAAiB;AAAA,QACnB;AAAA,MACF;AAEA,cAAQ,GAAG,QAAQ,OAAO;AAC1B,cAAQ,GAAG,UAAU,OAAO;AAC5B,cAAQ,GAAG,WAAW,OAAO;AAAA,IAC/B,SAAS,KAAU;AACjB,cAAQ;AAAA,QACN,oDAAoD,KAAK,OAAO;AAAA,MAClE;AAAA,IACF,UAAE;AACA,UAAI,SAAS;AACX,wBAAgB,QAAQ;AAAA,MAC1B;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAAS,IAAI;AAAA,IACjB;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,cAAc;AAAA,QACZ,OAAO,CAAC;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAEA,SAAO,kBAAkB,wBAAwB,YAAY;AAC3D,WAAO,EAAE,OAAO,MAAM;AAAA,EACxB,CAAC;AAED,SAAO,kBAAkB,uBAAuB,OAAO,YAAY;AACjE,UAAM,EAAE,MAAM,WAAW,KAAK,IAAI,QAAQ;AAC1C,QAAI;AAEF,YAAM,iBAAiB,IAAI;AAAA,QAAQ,CAAC,GAAG,WACrC,WAAW,MAAM,OAAO,IAAI,MAAM,uCAAuC,IAAI,EAAE,CAAC,GAAG,GAAK;AAAA,MAC1F;AACA,YAAM,SAAS,MAAM,QAAQ,KAAK,CAAC,eAAe,MAAM,QAAQ,CAAC,GAAG,GAAG,GAAG,cAAc,CAAC;AACzF,aAAO;AAAA,QACL,SAAS;AAAA,UACP;AAAA,YACE,MAAM;AAAA,YACN,MAAM,KAAK,UAAU,QAAQ,MAAM,CAAC;AAAA,UACtC;AAAA,QACF;AAAA,MACF;AAAA,IACF,SAAS,KAAU;AACjB,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS;AAAA,UACP;AAAA,YACE,MAAM;AAAA,YACN,MAAM,wBAAwB,IAAI,KAAK,KAAK,WAAW,OAAO,GAAG,CAAC;AAAA,UACpE;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM,aAAa;AACjB,UAAI,gBAAgB;AAClB,cAAM,eAAe,KAAK;AAC1B,yBAAiB;AAAA,MACnB;AAAA,IACF;AAAA,IACA,MAAM,aAAa;AAEjB,YAAM,YAAY,IAAI,qBAAqB;AAC3C,YAAM,OAAO,QAAQ,SAAS;AAG9B,YAAM,oBAAoB,aAAa,EAAE,MAAM,CAAC,QAAQ;AACtD,gBAAQ,MAAM,mDAAmD,KAAK,WAAW,GAAG,EAAE;AAAA,MACxF,CAAC;AACD,UAAI,oBAAoB;AAAA,IAC1B;AAAA,EACF;AACF;","names":[]}
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
redactSensitiveData,
|
|
6
6
|
safeJsonParse,
|
|
7
7
|
safeJsonStringify
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-ONPW7AYL.js";
|
|
9
9
|
|
|
10
10
|
// packages/daemon/src/config.ts
|
|
11
11
|
import os from "os";
|
|
@@ -2074,4 +2074,4 @@ export {
|
|
|
2074
2074
|
BrowserTrackDaemon,
|
|
2075
2075
|
createDaemon
|
|
2076
2076
|
};
|
|
2077
|
-
//# sourceMappingURL=chunk-
|
|
2077
|
+
//# sourceMappingURL=chunk-G5CIZSQM.js.map
|
|
@@ -189,7 +189,8 @@ function redactSensitiveData(data, maxDepth = 6, currentDepth = 0) {
|
|
|
189
189
|
function getSemanticSelector(element, options = {}) {
|
|
190
190
|
if (!element || !element.tagName) return "unknown";
|
|
191
191
|
const maxClasses = options.maxClasses ?? 2;
|
|
192
|
-
const
|
|
192
|
+
const maxParentDepth = options.maxParentDepth ?? 3;
|
|
193
|
+
const testId = element.getAttribute("data-testid") || element.getAttribute("data-test") || element.getAttribute("data-cy") || element.getAttribute("data-qa") || element.getAttribute("data-component");
|
|
193
194
|
if (testId) {
|
|
194
195
|
return `[data-testid="${testId}"]`;
|
|
195
196
|
}
|
|
@@ -197,28 +198,90 @@ function getSemanticSelector(element, options = {}) {
|
|
|
197
198
|
if (id && !/^[0-9]+$/.test(id) && !/[0-9a-f]{8}-[0-9a-f]{4}/i.test(id)) {
|
|
198
199
|
return `#${id}`;
|
|
199
200
|
}
|
|
201
|
+
const tag = element.tagName.toLowerCase();
|
|
200
202
|
const ariaLabel = element.getAttribute("aria-label");
|
|
201
|
-
if (ariaLabel && ariaLabel.length <
|
|
202
|
-
return `${
|
|
203
|
+
if (ariaLabel && ariaLabel.length < 40) {
|
|
204
|
+
return `${tag}[aria-label="${ariaLabel}"]`;
|
|
205
|
+
}
|
|
206
|
+
const role = element.getAttribute("role");
|
|
207
|
+
if (role && !["presentation", "none"].includes(role)) {
|
|
208
|
+
const roleSelector = `${tag}[role="${role}"]`;
|
|
209
|
+
if (tag === "button" || tag === "a" || tag === "nav" || tag === "dialog") {
|
|
210
|
+
return roleSelector;
|
|
211
|
+
}
|
|
203
212
|
}
|
|
204
213
|
const nameAttr = element.getAttribute("name");
|
|
205
214
|
if (nameAttr) {
|
|
206
|
-
return `${
|
|
215
|
+
return `${tag}[name="${nameAttr}"]`;
|
|
207
216
|
}
|
|
208
|
-
const
|
|
209
|
-
|
|
217
|
+
const titleAttr = element.getAttribute("title");
|
|
218
|
+
if (titleAttr && titleAttr.length < 30) {
|
|
219
|
+
return `${tag}[title="${titleAttr}"]`;
|
|
220
|
+
}
|
|
221
|
+
const rawClassList = Array.from(element.classList || []);
|
|
222
|
+
const classList = rawClassList.filter(
|
|
223
|
+
(c) => !c.startsWith("css-") && !c.startsWith("_") && !/^[a-z0-9]{5,}$/i.test(c) && // filter dynamic hashed utility classes
|
|
224
|
+
!c.includes(":") && // Tailwind pseudo-class prefixes (hover:, md:, etc.)
|
|
225
|
+
!c.includes("[")
|
|
226
|
+
// Tailwind arbitrary value classes
|
|
227
|
+
).slice(0, maxClasses);
|
|
210
228
|
if (classList.length > 0) {
|
|
211
229
|
const classStr = classList.map((c) => `.${c}`).join("");
|
|
212
230
|
return `${tag}${classStr}`;
|
|
213
231
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
232
|
+
const hasParent = element.parentElement && (typeof document === "undefined" || element.parentElement !== document.body && element.parentElement !== document.documentElement);
|
|
233
|
+
if (hasParent) {
|
|
234
|
+
let suffix = "";
|
|
235
|
+
if (element.parentElement.children && element.parentElement.children.length > 1) {
|
|
236
|
+
const sameTagSiblings = Array.from(element.parentElement.children).filter(
|
|
237
|
+
(child) => child.tagName && child.tagName.toLowerCase() === tag
|
|
238
|
+
);
|
|
239
|
+
if (sameTagSiblings.length > 1) {
|
|
240
|
+
const index = sameTagSiblings.indexOf(element) + 1;
|
|
241
|
+
if (index > 0) {
|
|
242
|
+
suffix = `:nth-of-type(${index})`;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (maxParentDepth > 0) {
|
|
247
|
+
const parentSelector = getSemanticSelector(element.parentElement, {
|
|
248
|
+
maxClasses: 1,
|
|
249
|
+
maxParentDepth: maxParentDepth - 1
|
|
250
|
+
});
|
|
251
|
+
if (parentSelector && parentSelector !== "unknown" && parentSelector !== "body" && parentSelector !== "html") {
|
|
252
|
+
return `${parentSelector} > ${tag}${suffix}`;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (suffix) {
|
|
256
|
+
return `${tag}${suffix}`;
|
|
218
257
|
}
|
|
219
258
|
}
|
|
220
259
|
return tag;
|
|
221
260
|
}
|
|
261
|
+
function resolveMeaningfulTarget(element) {
|
|
262
|
+
if (!element || typeof element !== "object") {
|
|
263
|
+
return element;
|
|
264
|
+
}
|
|
265
|
+
const el = element;
|
|
266
|
+
const isSvg = el.tagName.toLowerCase() === "svg" || el.namespaceURI?.includes("svg");
|
|
267
|
+
if (isSvg || el.closest?.("svg")) {
|
|
268
|
+
const interactiveParent = el.closest?.(
|
|
269
|
+
'button, a, [role="button"], [role="link"], [role="tab"], summary, label, [data-testid], [data-component]'
|
|
270
|
+
);
|
|
271
|
+
if (interactiveParent) return interactiveParent;
|
|
272
|
+
const svgEl = isSvg ? el : el.closest?.("svg");
|
|
273
|
+
if (svgEl) return svgEl;
|
|
274
|
+
}
|
|
275
|
+
const leafTags = ["span", "i", "em", "b", "strong", "small", "mark", "code"];
|
|
276
|
+
const tag = el.tagName.toLowerCase();
|
|
277
|
+
if (leafTags.includes(tag) && !el.id && !el.getAttribute("data-testid") && !el.getAttribute("data-component") && !el.getAttribute("aria-label")) {
|
|
278
|
+
const interactiveParent = el.closest?.(
|
|
279
|
+
'button, a, [role="button"], [role="link"], [role="tab"], [role="menuitem"], summary, label, [data-testid], [data-component]'
|
|
280
|
+
);
|
|
281
|
+
if (interactiveParent) return interactiveParent;
|
|
282
|
+
}
|
|
283
|
+
return el;
|
|
284
|
+
}
|
|
222
285
|
function truncate(str, maxLength = 200) {
|
|
223
286
|
if (!str) return "";
|
|
224
287
|
if (str.length <= maxLength) return str;
|
|
@@ -305,10 +368,11 @@ export {
|
|
|
305
368
|
redactHeaders,
|
|
306
369
|
redactSensitiveData,
|
|
307
370
|
getSemanticSelector,
|
|
371
|
+
resolveMeaningfulTarget,
|
|
308
372
|
truncate,
|
|
309
373
|
safeJsonParse,
|
|
310
374
|
safeJsonStringify,
|
|
311
375
|
safeExecute,
|
|
312
376
|
safeAsync
|
|
313
377
|
};
|
|
314
|
-
//# sourceMappingURL=chunk-
|
|
378
|
+
//# sourceMappingURL=chunk-ONPW7AYL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../packages/core/src/fingerprint.ts","../packages/core/src/redaction.ts","../packages/core/src/selector.ts","../packages/core/src/safety.ts"],"sourcesContent":["/**\n * Error fingerprinting and normalization utilities\n */\n\nexport interface FingerprintInput {\n type: string;\n message: string;\n sourceFile?: string;\n line?: number;\n column?: number;\n stack?: string;\n}\n\n/**\n * Normalizes error messages by stripping dynamic content like UUIDs, timestamps,\n * hex addresses, and file timestamps.\n */\nexport function normalizeErrorMessage(message: string): string {\n if (!message) return 'unknown_error';\n\n return message\n // Normalize string representation\n .trim()\n // Replace hex addresses (e.g. 0x7ffee4b2a8)\n .replace(/0x[0-9a-fA-F]+/g, '0x<HEX>')\n // Replace UUIDs\n .replace(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/g, '<UUID>')\n // Replace timestamp query params like ?t=1719283748 or ?v=1.2.3\n .replace(/\\?[tv]=[\\w.-]+/g, '')\n // Replace ISO timestamps\n .replace(/\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z?/g, '<TIMESTAMP>')\n // Replace numbers in quotes/brackets if likely IDs (e.g., user \"12345\")\n .replace(/#\\d+/g, '#<ID>')\n // Normalize whitespaces\n .replace(/\\s+/g, ' ');\n}\n\n/**\n * Normalizes source filenames by removing origin and cache-busting query strings.\n */\nexport function normalizeSourceFile(filename?: string): string {\n if (!filename) return 'unknown_source';\n\n let cleaned = filename.trim();\n // Strip protocol and origin if present (e.g. http://localhost:5173/src/App.tsx?t=123)\n try {\n if (cleaned.startsWith('http://') || cleaned.startsWith('https://')) {\n const url = new URL(cleaned);\n cleaned = url.pathname;\n }\n } catch {\n // fallback string manipulation\n cleaned = cleaned.replace(/^https?:\\/\\/[^/]+/, '');\n }\n\n // Remove query parameters\n cleaned = cleaned.split('?')[0].split('#')[0];\n\n // Normalize windows path separators\n cleaned = cleaned.replace(/\\\\/g, '/');\n\n return cleaned || 'unknown_source';\n}\n\n/**\n * Extracts source location from stack if not explicitly provided\n */\nexport function extractSourceFromStack(stack?: string): { file: string; line: number; column?: number } | null {\n if (!stack) return null;\n\n const lines = stack.split('\\n');\n for (const line of lines) {\n // Match standard stack line: at functionName (http://localhost:5173/src/main.tsx:15:3) or at http://...:15:3\n const match = line.match(/(?:at\\s+(?:.*?\\s+\\()?)?(https?:\\/\\/[^\\s)]+|file:\\/\\/[^\\s)]+|\\/[^\\s)]+):(\\d+):(\\d+)\\)?/);\n if (match) {\n return {\n file: normalizeSourceFile(match[1]),\n line: parseInt(match[2], 10),\n column: parseInt(match[3], 10),\n };\n }\n }\n return null;\n}\n\n/**\n * Fast synchronous djb2-based hash for universal client/server runtime\n */\nfunction djb2Hash(str: string): string {\n let hash = 5381;\n for (let i = 0; i < str.length; i++) {\n hash = (hash * 33) ^ str.charCodeAt(i);\n }\n // Convert to unsigned 32-bit hex string\n return (hash >>> 0).toString(16).padStart(8, '0');\n}\n\n/**\n * Computes a deterministic fingerprint for an error\n */\nexport function computeFingerprint(input: FingerprintInput): string {\n const normType = (input.type || 'Error').trim().toLowerCase();\n const normMsg = normalizeErrorMessage(input.message);\n\n let sourceFile = normalizeSourceFile(input.sourceFile);\n let line = input.line || 0;\n\n if ((sourceFile === 'unknown_source' || line === 0) && input.stack) {\n const extracted = extractSourceFromStack(input.stack);\n if (extracted) {\n sourceFile = extracted.file;\n line = extracted.line;\n }\n }\n\n const rawKey = `${normType}::${normMsg}::${sourceFile}::${line}`;\n const hash = djb2Hash(rawKey);\n\n return `fp_${hash}`;\n}\n","import { redact as visulimaRedact, standardRules, type Rules } from '@visulima/redact';\n\n/**\n * Security and Redaction Utilities powered by @visulima/redact\n */\n\nexport const REDACTED_PLACEHOLDER = '[REDACTED]';\n\nexport const SENSITIVE_KEY_PATTERNS = [\n /^authorization$/i,\n /^cookie$/i,\n /^set-cookie$/i,\n /password/i,\n /token/i,\n /secret/i,\n /api[-_]?key/i,\n /access[-_]?token/i,\n /refresh[-_]?token/i,\n /credentials/i,\n /private[-_]?key/i,\n /ssn/i,\n /credit[-_]?card/i,\n /cvv/i,\n];\n\nexport const SENSITIVE_QUERY_PARAMS = [\n 'token',\n 'auth',\n 'key',\n 'apikey',\n 'api_key',\n 'secret',\n 'password',\n 'access_token',\n 'refresh_token',\n 'code',\n 'signature',\n];\n\n/**\n * Developer security rules for @visulima/redact\n */\nexport const BROWSER_SECURITY_RULES: Rules = [\n { deep: true, key: 'password', replacement: REDACTED_PLACEHOLDER },\n { deep: true, key: 'secret', replacement: REDACTED_PLACEHOLDER },\n { deep: true, key: 'token', replacement: REDACTED_PLACEHOLDER },\n { deep: true, key: 'authorization', replacement: REDACTED_PLACEHOLDER },\n { deep: true, key: 'cookie', replacement: REDACTED_PLACEHOLDER },\n { deep: true, key: 'set-cookie', replacement: REDACTED_PLACEHOLDER },\n { deep: true, key: 'apikey', replacement: REDACTED_PLACEHOLDER },\n { deep: true, key: 'api_key', replacement: REDACTED_PLACEHOLDER },\n { deep: true, key: 'creditcard', pattern: /(?:\\d[ -]*?){13,16}/, replacement: REDACTED_PLACEHOLDER },\n { deep: true, key: 'cvv', replacement: REDACTED_PLACEHOLDER },\n { deep: true, key: 'ssn', pattern: /\\b\\d{3}-\\d{2}-\\d{4}\\b/, replacement: REDACTED_PLACEHOLDER },\n { deep: true, key: 'awsid', pattern: /\\bAKIA[0-9A-Z]{16}\\b/, replacement: REDACTED_PLACEHOLDER },\n { deep: true, key: 'awskey', pattern: /\\b[0-9a-zA-Z/+]{40}\\b/, replacement: REDACTED_PLACEHOLDER },\n { deep: true, key: 'jwt', pattern: /\\beyJ[0-9a-zA-Z_\\-]*\\.[0-9a-zA-Z_\\-]*\\.[0-9a-zA-Z_\\-]*\\b/, replacement: REDACTED_PLACEHOLDER },\n { deep: true, key: 'slack_token', pattern: /\\bxox[baprs]-[0-9a-zA-Z]{10,48}\\b/, replacement: REDACTED_PLACEHOLDER },\n];\n\nexport { visulimaRedact, standardRules };\nexport type { Rules };\n\n/**\n * Checks if a property name matches any sensitive pattern\n */\nexport function isSensitiveKey(key: string): boolean {\n if (!key) return false;\n const cleaned = key.replace(/[-_]/g, '');\n return SENSITIVE_KEY_PATTERNS.some((pattern) => pattern.test(key) || pattern.test(cleaned));\n}\n\n/**\n * Redacts sensitive query parameters from a URL string\n */\nexport function redactUrl(rawUrl: string): string {\n if (!rawUrl) return rawUrl;\n\n try {\n const isRelative =\n !rawUrl.startsWith('http://') &&\n !rawUrl.startsWith('https://') &&\n !rawUrl.startsWith('ws://') &&\n !rawUrl.startsWith('wss://');\n const base = 'http://localhost';\n const parsed = new URL(rawUrl, base);\n\n let changed = false;\n for (const param of SENSITIVE_QUERY_PARAMS) {\n if (parsed.searchParams.has(param)) {\n parsed.searchParams.set(param, REDACTED_PLACEHOLDER);\n changed = true;\n }\n }\n\n // Also check all query params against isSensitiveKey\n for (const key of Array.from(parsed.searchParams.keys())) {\n if (isSensitiveKey(key)) {\n parsed.searchParams.set(key, REDACTED_PLACEHOLDER);\n changed = true;\n }\n }\n\n if (!changed) return rawUrl;\n\n let result = isRelative ? parsed.pathname + parsed.search + parsed.hash : parsed.toString();\n // Normalize URL-encoded brackets %5BREDACTED%5D back to [REDACTED]\n result = result.replace(/%5BREDACTED%5D/g, REDACTED_PLACEHOLDER);\n return result;\n } catch {\n // If URL parsing fails, perform regex replace\n let safe = rawUrl;\n for (const param of SENSITIVE_QUERY_PARAMS) {\n const reg = new RegExp(`([?&]${param}=)[^&#]+`, 'gi');\n safe = safe.replace(reg, `$1${REDACTED_PLACEHOLDER}`);\n }\n return safe;\n }\n}\n\n/**\n * Redacts sensitive HTTP headers\n */\nexport function redactHeaders(headers: Record<string, string>): Record<string, string> {\n if (!headers || typeof headers !== 'object') return {};\n\n const safe: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n if (isSensitiveKey(key)) {\n safe[key] = REDACTED_PLACEHOLDER;\n } else {\n safe[key] = value;\n }\n }\n return safe;\n}\n\n/**\n * Deeply redacts sensitive keys and payload data from an object or array,\n * leveraging @visulima/redact rules combined with BrowserTrack URL/header sanitizers.\n */\nexport function redactSensitiveData<T = any>(data: T, maxDepth = 6, currentDepth = 0): T {\n if (data === null || data === undefined) return data;\n if (typeof data !== 'object') return data;\n if (currentDepth > maxDepth) return '[DEPTH_EXCEEDED]' as any;\n\n if (Array.isArray(data)) {\n return data.map((item) => redactSensitiveData(item, maxDepth, currentDepth + 1)) as any;\n }\n\n const result: Record<string, any> = {};\n for (const [key, value] of Object.entries(data as Record<string, any>)) {\n if (typeof value === 'object' && value !== null) {\n result[key] = redactSensitiveData(value, maxDepth, currentDepth + 1);\n } else if (isSensitiveKey(key)) {\n result[key] = REDACTED_PLACEHOLDER;\n } else if (typeof value === 'string') {\n // Check if value is a URL containing secrets\n if (value.startsWith('http://') || value.startsWith('https://') || value.includes('?')) {\n result[key] = redactUrl(value);\n } else {\n try {\n result[key] = visulimaRedact(value, BROWSER_SECURITY_RULES);\n } catch {\n result[key] = value;\n }\n }\n } else {\n result[key] = value;\n }\n }\n\n return result as T;\n}\n","/**\n * Semantic element selector generation and inspection\n */\n\nexport interface SelectorOptions {\n maxClasses?: number;\n maxOuterHTMLLength?: number;\n maxInnerTextLength?: number;\n maxParentDepth?: number;\n}\n\n/**\n * Builds a clean, human-readable semantic selector for a DOM element.\n * Prioritizes data-testid/data-component, id, semantic tag + curated class, aria/role, or concise unique parent path.\n */\nexport function getSemanticSelector(element: HTMLElement | Element, options: SelectorOptions = {}): string {\n if (!element || !element.tagName) return 'unknown';\n\n const maxClasses = options.maxClasses ?? 2;\n const maxParentDepth = options.maxParentDepth ?? 3;\n\n // 1. Check for standard test or component attributes\n const testId =\n element.getAttribute('data-testid') ||\n element.getAttribute('data-test') ||\n element.getAttribute('data-cy') ||\n element.getAttribute('data-qa') ||\n element.getAttribute('data-component');\n\n if (testId) {\n return `[data-testid=\"${testId}\"]`;\n }\n\n // 2. Check for meaningful ID (skip dynamic UUID/numeric IDs)\n const id = element.id;\n if (id && !/^[0-9]+$/.test(id) && !/[0-9a-f]{8}-[0-9a-f]{4}/i.test(id)) {\n return `#${id}`;\n }\n\n const tag = element.tagName.toLowerCase();\n\n // 3. Check for aria-label, role, title or name on interactive elements\n const ariaLabel = element.getAttribute('aria-label');\n if (ariaLabel && ariaLabel.length < 40) {\n return `${tag}[aria-label=\"${ariaLabel}\"]`;\n }\n\n const role = element.getAttribute('role');\n if (role && !['presentation', 'none'].includes(role)) {\n const roleSelector = `${tag}[role=\"${role}\"]`;\n if (tag === 'button' || tag === 'a' || tag === 'nav' || tag === 'dialog') {\n return roleSelector;\n }\n }\n\n const nameAttr = element.getAttribute('name');\n if (nameAttr) {\n return `${tag}[name=\"${nameAttr}\"]`;\n }\n\n const titleAttr = element.getAttribute('title');\n if (titleAttr && titleAttr.length < 30) {\n return `${tag}[title=\"${titleAttr}\"]`;\n }\n\n // 4. Tag + curated classes\n const rawClassList = Array.from(element.classList || []);\n const classList = rawClassList\n .filter(\n (c) =>\n !c.startsWith('css-') &&\n !c.startsWith('_') &&\n !/^[a-z0-9]{5,}$/i.test(c) && // filter dynamic hashed utility classes\n !c.includes(':') && // Tailwind pseudo-class prefixes (hover:, md:, etc.)\n !c.includes('[') // Tailwind arbitrary value classes\n )\n .slice(0, maxClasses);\n\n if (classList.length > 0) {\n const classStr = classList.map((c) => `.${c}`).join('');\n return `${tag}${classStr}`;\n }\n\n // 5. If generic element (div, span, p, li) without attributes, try parent context & nth-of-type\n const hasParent =\n element.parentElement &&\n (typeof document === 'undefined' ||\n (element.parentElement !== document.body && element.parentElement !== document.documentElement));\n\n if (hasParent) {\n let suffix = '';\n // Calculate nth-of-type if siblings of same tag exist\n if (element.parentElement.children && element.parentElement.children.length > 1) {\n const sameTagSiblings = Array.from(element.parentElement.children).filter(\n (child: any) => child.tagName && child.tagName.toLowerCase() === tag\n );\n if (sameTagSiblings.length > 1) {\n const index = sameTagSiblings.indexOf(element) + 1;\n if (index > 0) {\n suffix = `:nth-of-type(${index})`;\n }\n }\n }\n\n if (maxParentDepth > 0) {\n const parentSelector = getSemanticSelector(element.parentElement, {\n maxClasses: 1,\n maxParentDepth: maxParentDepth - 1,\n });\n if (parentSelector && parentSelector !== 'unknown' && parentSelector !== 'body' && parentSelector !== 'html') {\n return `${parentSelector} > ${tag}${suffix}`;\n }\n }\n\n if (suffix) {\n return `${tag}${suffix}`;\n }\n }\n\n return tag;\n}\n\n/**\n * If an element under the cursor is an inline or leaf formatting element\n * (e.g. svg, path, i, b, strong, or an inner span without attributes)\n * inside an interactive container or component, resolves to the parent container.\n */\nexport function resolveMeaningfulTarget(element: HTMLElement | Element): HTMLElement {\n if (!element || typeof element !== 'object') {\n return element as HTMLElement;\n }\n\n const el = element as HTMLElement;\n\n // 1. If element is inside an SVG or is an SVG child (path, circle, rect, g, svg)\n const isSvg = el.tagName.toLowerCase() === 'svg' || el.namespaceURI?.includes('svg');\n if (isSvg || el.closest?.('svg')) {\n const interactiveParent = el.closest?.(\n 'button, a, [role=\"button\"], [role=\"link\"], [role=\"tab\"], summary, label, [data-testid], [data-component]'\n ) as HTMLElement | null;\n if (interactiveParent) return interactiveParent;\n const svgEl = isSvg ? el : (el.closest?.('svg') as HTMLElement | null);\n if (svgEl) return svgEl;\n }\n\n // 2. Minor leaf tags (span, i, em, b, strong, small) with no distinct attributes inside interactive element\n const leafTags = ['span', 'i', 'em', 'b', 'strong', 'small', 'mark', 'code'];\n const tag = el.tagName.toLowerCase();\n if (\n leafTags.includes(tag) &&\n !el.id &&\n !el.getAttribute('data-testid') &&\n !el.getAttribute('data-component') &&\n !el.getAttribute('aria-label')\n ) {\n const interactiveParent = el.closest?.(\n 'button, a, [role=\"button\"], [role=\"link\"], [role=\"tab\"], [role=\"menuitem\"], summary, label, [data-testid], [data-component]'\n ) as HTMLElement | null;\n if (interactiveParent) return interactiveParent;\n }\n\n return el;\n}\n\n/**\n * Truncates string to specified max length with ellipsis\n */\nexport function truncate(str: string | undefined | null, maxLength = 200): string {\n if (!str) return '';\n if (str.length <= maxLength) return str;\n return str.slice(0, maxLength) + '...';\n}\n","/**\n * BrowserTrack Safety & Error-Resilience Utilities\n * Provides bulletproof try/catch wrappers, circular-safe JSON serialization, and defensive helpers.\n */\n\n/**\n * Safely parses a JSON string with fallback. Never throws.\n */\nexport function safeJsonParse<T>(raw: any, fallback: T): T {\n if (raw === null || raw === undefined) return fallback;\n if (typeof raw !== 'string') return typeof raw === 'object' ? (raw as T) : fallback;\n try {\n return JSON.parse(raw);\n } catch {\n return fallback;\n }\n}\n\n/**\n * Safely serializes an object to JSON, handling circular references and non-serializable values.\n * Never throws \"TypeError: Converting circular structure to JSON\".\n */\nexport function safeJsonStringify(val: any, fallback = '{}'): string {\n if (val === undefined) return fallback;\n try {\n const seen = new WeakSet();\n return JSON.stringify(val, (key, value) => {\n if (typeof value === 'object' && value !== null) {\n if (seen.has(value)) {\n return '[Circular]';\n }\n seen.add(value);\n }\n if (typeof value === 'bigint') {\n return value.toString();\n }\n return value;\n });\n } catch {\n try {\n return JSON.stringify(String(val));\n } catch {\n return fallback;\n }\n }\n}\n\n/**\n * Executes a synchronous function within an isolated try/catch block.\n * Returns the function result, or fallback if an exception was thrown.\n */\nexport function safeExecute<T>(fn: () => T, fallback: T, onError?: (err: any) => void): T {\n try {\n return fn();\n } catch (err: any) {\n if (onError) {\n try {\n onError(err);\n } catch {}\n }\n return fallback;\n }\n}\n\n/**\n * Executes an asynchronous promise or async function safely without unhandled rejection.\n */\nexport async function safeAsync<T>(\n action: Promise<T> | (() => Promise<T>),\n fallback: T,\n onError?: (err: any) => void\n): Promise<T> {\n try {\n if (typeof action === 'function') {\n return await action();\n }\n return await action;\n } catch (err: any) {\n if (onError) {\n try {\n onError(err);\n } catch {}\n }\n return fallback;\n }\n}\n"],"mappings":"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","names":[]}
|