browsertrack 0.2.1 → 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.
Files changed (55) hide show
  1. package/AGENTS.md +9 -6
  2. package/README.md +2 -0
  3. package/dist/{chunk-464D4U2U.js → chunk-5NR5K3ER.js} +299 -44
  4. package/dist/chunk-5NR5K3ER.js.map +1 -0
  5. package/dist/{chunk-3HOXPTM2.js → chunk-G5CIZSQM.js} +808 -53
  6. package/dist/chunk-G5CIZSQM.js.map +1 -0
  7. package/dist/{chunk-6VA7GBAO.js → chunk-ONPW7AYL.js} +144 -12
  8. package/dist/chunk-ONPW7AYL.js.map +1 -0
  9. package/dist/{chunk-INXDWPJW.js → chunk-PG4JJDCV.js} +353 -119
  10. package/dist/chunk-PG4JJDCV.js.map +1 -0
  11. package/dist/cli/index.js +1058 -546
  12. package/dist/cli/index.js.map +1 -1
  13. package/dist/client/index.cjs +449 -128
  14. package/dist/client/index.d.ts +2 -0
  15. package/dist/client/index.js +2 -2
  16. package/dist/client.iife.js +121 -15
  17. package/dist/core/index.d.ts +32 -2
  18. package/dist/core/index.js +11 -1
  19. package/dist/daemon/index.d.ts +2 -2
  20. package/dist/daemon/index.js +6 -8
  21. package/dist/index.d.ts +2 -2
  22. package/dist/index.js +16 -7
  23. package/dist/mcp/index.d.ts +1 -1
  24. package/dist/mcp/index.js +7 -4
  25. package/dist/{server-DiVmTrIR.d.ts → server-BRG-RQQP.d.ts} +10 -1
  26. package/docs/cli.md +4 -1
  27. package/docs/getting-started.md +60 -6
  28. package/docs/mcp-reference.md +42 -0
  29. package/package.json +1 -1
  30. package/packages/cli/src/index.ts +247 -151
  31. package/packages/client/src/interceptors/navigation.ts +38 -26
  32. package/packages/client/src/interceptors/network.ts +22 -17
  33. package/packages/client/src/notes/inspector.ts +289 -65
  34. package/packages/client/src/source/resolver.ts +9 -3
  35. package/packages/client/src/transport/websocket.ts +23 -18
  36. package/packages/core/src/index.ts +1 -0
  37. package/packages/core/src/safety.ts +86 -0
  38. package/packages/core/src/selector.ts +110 -15
  39. package/packages/daemon/src/server/daemon.ts +7 -1
  40. package/packages/daemon/src/server/http.ts +125 -5
  41. package/packages/daemon/src/server/ws.ts +33 -29
  42. package/packages/daemon/src/storage/db.ts +57 -35
  43. package/packages/mcp/src/handlers.ts +121 -45
  44. package/packages/mcp/src/server.ts +221 -3
  45. package/test/core/safety.test.ts +106 -0
  46. package/test/core/selector.test.ts +133 -0
  47. package/test/daemon/storage.test.ts +36 -0
  48. package/test/e2e/daemon-mcp-e2e.test.ts +10 -0
  49. package/test/mcp/auto-start.test.ts +87 -0
  50. package/dist/chunk-3HOXPTM2.js.map +0 -1
  51. package/dist/chunk-464D4U2U.js.map +0 -1
  52. package/dist/chunk-6VA7GBAO.js.map +0 -1
  53. package/dist/chunk-7OCOQGDN.js +0 -635
  54. package/dist/chunk-7OCOQGDN.js.map +0 -1
  55. package/dist/chunk-INXDWPJW.js.map +0 -1
@@ -0,0 +1,106 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { safeAsync, safeExecute, safeJsonParse, safeJsonStringify } from '../../packages/core/src/safety.js';
3
+
4
+ describe('BrowserTrack Core Safety Utilities', () => {
5
+ describe('safeJsonParse', () => {
6
+ it('should parse valid JSON correctly', () => {
7
+ const result = safeJsonParse('{"foo":"bar","num":42}', {});
8
+ expect(result).toEqual({ foo: 'bar', num: 42 });
9
+ });
10
+
11
+ it('should return fallback on malformed JSON without throwing', () => {
12
+ const fallback = { status: 'fallback' };
13
+ const result = safeJsonParse('{ broken json: true', fallback);
14
+ expect(result).toBe(fallback);
15
+ });
16
+
17
+ it('should handle null, undefined, or empty string gracefully', () => {
18
+ expect(safeJsonParse(null, 'default')).toBe('default');
19
+ expect(safeJsonParse(undefined, 'default')).toBe('default');
20
+ expect(safeJsonParse('', 'default')).toBe('default');
21
+ });
22
+
23
+ it('should return raw object if input is already parsed object', () => {
24
+ const obj = { already: 'object' };
25
+ expect(safeJsonParse(obj, {})).toBe(obj);
26
+ });
27
+ });
28
+
29
+ describe('safeJsonStringify', () => {
30
+ it('should serialize simple objects into valid JSON', () => {
31
+ const json = safeJsonStringify({ name: 'BrowserTrack', active: true });
32
+ expect(JSON.parse(json)).toEqual({ name: 'BrowserTrack', active: true });
33
+ });
34
+
35
+ it('should serialize circular structures without throwing', () => {
36
+ const circular: any = { name: 'circular-node' };
37
+ circular.self = circular;
38
+ circular.child = { parent: circular };
39
+
40
+ let json = '';
41
+ expect(() => {
42
+ json = safeJsonStringify(circular);
43
+ }).not.toThrow();
44
+
45
+ expect(json).toContain('[Circular]');
46
+ const parsed = JSON.parse(json);
47
+ expect(parsed.name).toBe('circular-node');
48
+ expect(parsed.self).toBe('[Circular]');
49
+ expect(parsed.child.parent).toBe('[Circular]');
50
+ });
51
+
52
+ it('should serialize BigInt without throwing', () => {
53
+ const obj = { id: BigInt(9007199254740991) };
54
+ expect(() => safeJsonStringify(obj)).not.toThrow();
55
+ expect(safeJsonStringify(obj)).toContain('9007199254740991');
56
+ });
57
+
58
+ it('should handle undefined gracefully', () => {
59
+ expect(safeJsonStringify(undefined, 'empty')).toBe('empty');
60
+ });
61
+ });
62
+
63
+ describe('safeExecute', () => {
64
+ it('should return function result on success', () => {
65
+ const res = safeExecute(() => 10 + 20, 0);
66
+ expect(res).toBe(30);
67
+ });
68
+
69
+ it('should return fallback on error and notify onError callback', () => {
70
+ const errorHandler = vi.fn();
71
+ const res = safeExecute(
72
+ () => {
73
+ throw new Error('Test crash');
74
+ },
75
+ -1,
76
+ errorHandler
77
+ );
78
+
79
+ expect(res).toBe(-1);
80
+ expect(errorHandler).toHaveBeenCalledTimes(1);
81
+ expect(errorHandler.mock.calls[0][0].message).toBe('Test crash');
82
+ });
83
+ });
84
+
85
+ describe('safeAsync', () => {
86
+ it('should resolve async function correctly', async () => {
87
+ const res = await safeAsync(async () => 'resolved_val', 'fallback');
88
+ expect(res).toBe('resolved_val');
89
+ });
90
+
91
+ it('should catch rejection without throwing and return fallback', async () => {
92
+ const errorHandler = vi.fn();
93
+ const res = await safeAsync(
94
+ async () => {
95
+ throw new Error('Async boom');
96
+ },
97
+ 'fallback',
98
+ errorHandler
99
+ );
100
+
101
+ expect(res).toBe('fallback');
102
+ expect(errorHandler).toHaveBeenCalledTimes(1);
103
+ expect(errorHandler.mock.calls[0][0].message).toBe('Async boom');
104
+ });
105
+ });
106
+ });
@@ -0,0 +1,133 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { getSemanticSelector, resolveMeaningfulTarget } from '../../packages/core/src/index.js';
3
+
4
+ function createMockElement(options: {
5
+ tagName: string;
6
+ id?: string;
7
+ classList?: string[];
8
+ attributes?: Record<string, string>;
9
+ parentElement?: any;
10
+ children?: any[];
11
+ closest?: (sel: string) => any;
12
+ }): any {
13
+ const attrs = options.attributes || {};
14
+ const el: any = {
15
+ tagName: options.tagName.toUpperCase(),
16
+ id: options.id || '',
17
+ classList: options.classList || [],
18
+ parentElement: options.parentElement || null,
19
+ children: options.children || [],
20
+ getAttribute: (name: string) => attrs[name] || null,
21
+ closest: options.closest || (() => null),
22
+ };
23
+ return el;
24
+ }
25
+
26
+ describe('Selector & Target Resolution', () => {
27
+ it('prioritizes data-testid and data-component attributes', () => {
28
+ const el = createMockElement({
29
+ tagName: 'div',
30
+ attributes: { 'data-testid': 'submit-btn' },
31
+ });
32
+ expect(getSemanticSelector(el)).toBe('[data-testid="submit-btn"]');
33
+
34
+ const elComp = createMockElement({
35
+ tagName: 'div',
36
+ attributes: { 'data-component': 'UserProfile' },
37
+ });
38
+ expect(getSemanticSelector(elComp)).toBe('[data-testid="UserProfile"]');
39
+ });
40
+
41
+ it('uses clean IDs and skips uuid/numeric hashes', () => {
42
+ const el = createMockElement({
43
+ tagName: 'div',
44
+ id: 'navbar-header',
45
+ });
46
+ expect(getSemanticSelector(el)).toBe('#navbar-header');
47
+
48
+ const numericEl = createMockElement({
49
+ tagName: 'div',
50
+ id: '123456',
51
+ });
52
+ expect(getSemanticSelector(numericEl)).toBe('div');
53
+ });
54
+
55
+ it('handles aria-label, role, and title on elements', () => {
56
+ const btn = createMockElement({
57
+ tagName: 'button',
58
+ attributes: { 'aria-label': 'Close dialog' },
59
+ });
60
+ expect(getSemanticSelector(btn)).toBe('button[aria-label="Close dialog"]');
61
+
62
+ const nav = createMockElement({
63
+ tagName: 'nav',
64
+ attributes: { role: 'navigation' },
65
+ });
66
+ expect(getSemanticSelector(nav)).toBe('nav[role="navigation"]');
67
+
68
+ const iconBtn = createMockElement({
69
+ tagName: 'button',
70
+ attributes: { title: 'Refresh page' },
71
+ });
72
+ expect(getSemanticSelector(iconBtn)).toBe('button[title="Refresh page"]');
73
+ });
74
+
75
+ it('filters out dynamic hashed utility classes and retains semantic classes', () => {
76
+ const el = createMockElement({
77
+ tagName: 'div',
78
+ classList: ['css-98741', 'card-item', '_scoped123', 'md:flex', 'p-[12px]', 'primary-box'],
79
+ });
80
+ expect(getSemanticSelector(el)).toBe('div.card-item.primary-box');
81
+ });
82
+
83
+ it('generates nth-of-type for identical siblings', () => {
84
+ const parent = createMockElement({
85
+ tagName: 'div',
86
+ id: 'sidebar',
87
+ });
88
+ const child1 = createMockElement({
89
+ tagName: 'div',
90
+ parentElement: parent,
91
+ });
92
+ const child2 = createMockElement({
93
+ tagName: 'div',
94
+ parentElement: parent,
95
+ });
96
+ parent.children = [child1, child2];
97
+
98
+ expect(getSemanticSelector(child2)).toBe('#sidebar > div:nth-of-type(2)');
99
+ });
100
+
101
+ it('resolves meaningful targets from leaf SVG or formatting tags', () => {
102
+ const button = createMockElement({
103
+ tagName: 'button',
104
+ id: 'login-button',
105
+ });
106
+
107
+ const span = createMockElement({
108
+ tagName: 'span',
109
+ closest: (sel: string) => (sel.includes('button') ? button : null),
110
+ });
111
+
112
+ expect(resolveMeaningfulTarget(span)).toBe(button);
113
+
114
+ const link = createMockElement({
115
+ tagName: 'a',
116
+ id: 'home-link',
117
+ });
118
+
119
+ const svg = createMockElement({
120
+ tagName: 'svg',
121
+ closest: (sel: string) => (sel.includes('a') ? link : null),
122
+ });
123
+
124
+ expect(resolveMeaningfulTarget(svg)).toBe(link);
125
+
126
+ // Standalone div returns itself
127
+ const card = createMockElement({
128
+ tagName: 'div',
129
+ id: 'info-card',
130
+ });
131
+ expect(resolveMeaningfulTarget(card)).toBe(card);
132
+ });
133
+ });
@@ -120,4 +120,40 @@ describe('StorageDB (SQLite)', () => {
120
120
  const updated = db.getIncident('inc_1');
121
121
  expect(updated?.occurrences).toBe(2);
122
122
  });
123
+
124
+ it('should seamlessly migrate a legacy database without scenario columns', () => {
125
+ const legacyDbPath = path.join(os.tmpdir(), `bt_legacy_${Date.now()}.db`);
126
+ try {
127
+ // Create legacy database with old notes schema lacking scenario_id
128
+ const Database = (db as any).db.constructor;
129
+ const legacyRaw = new Database(legacyDbPath);
130
+ legacyRaw.exec(`
131
+ CREATE TABLE notes (
132
+ id TEXT PRIMARY KEY,
133
+ project_id TEXT,
134
+ session_id TEXT,
135
+ type TEXT,
136
+ message TEXT,
137
+ route TEXT,
138
+ url TEXT,
139
+ status TEXT DEFAULT 'OPEN',
140
+ created_at TEXT,
141
+ updated_at TEXT
142
+ );
143
+ `);
144
+ legacyRaw.close();
145
+
146
+ // Opening with StorageDB should not throw "no such column: scenario_id"
147
+ const migratedDb = new StorageDB(legacyDbPath);
148
+ const cols = (migratedDb as any).db.prepare('PRAGMA table_info(notes)').all().map((c: any) => c.name);
149
+ expect(cols).toContain('scenario_id');
150
+ expect(cols).toContain('step_number');
151
+ expect(cols).toContain('scenario_title');
152
+ migratedDb.close();
153
+ } finally {
154
+ if (fs.existsSync(legacyDbPath)) {
155
+ fs.unlinkSync(legacyDbPath);
156
+ }
157
+ }
158
+ });
123
159
  });
@@ -149,5 +149,15 @@ describe('BrowserTrack Full E2E Lifecycle (Daemon + WebSocket Client + MCP)', ()
149
149
 
150
150
  expect(pageState.url).toBe('http://localhost:5173/products/123');
151
151
  expect(pageState.title).toBe('Product Details');
152
+
153
+ // 6. Test Multi-process Standalone MCP Tool Call (no in-process session manager, bridges over HTTP to daemon)
154
+ const remotePageState = await handleToolCall(
155
+ 'get_page_state',
156
+ { sessionId: receivedSessionId },
157
+ { db: daemon.db, daemonUrl: `http://127.0.0.1:${testPort}` }
158
+ );
159
+
160
+ expect(remotePageState.url).toBe('http://localhost:5173/products/123');
161
+ expect(remotePageState.title).toBe('Product Details');
152
162
  });
153
163
  });
@@ -0,0 +1,87 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
5
+ import { createMcpServer, isDaemonRunning } from '../../packages/mcp/src/server.js';
6
+
7
+ describe('BrowserTrack MCP Daemon Auto-Start', () => {
8
+ let tempDir: string;
9
+ const testPort = 7338;
10
+ let serverInstance: ReturnType<typeof createMcpServer> | null = null;
11
+
12
+ beforeEach(() => {
13
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'bt_mcp_autostart_'));
14
+ });
15
+
16
+ afterEach(async () => {
17
+ if (serverInstance) {
18
+ await serverInstance.stopDaemon();
19
+ serverInstance = null;
20
+ }
21
+ fs.rmSync(tempDir, { recursive: true, force: true });
22
+ });
23
+
24
+ it('should auto-start background HTTP/WS daemon if offline when ensureDaemon is invoked', async () => {
25
+ const dbPath = path.join(tempDir, 'autostart.db');
26
+
27
+ // 1. Initially, daemon should not be running on testPort
28
+ const initiallyRunning = await isDaemonRunning('127.0.0.1', testPort);
29
+ expect(initiallyRunning).toBe(false);
30
+
31
+ // 2. Create MCP server configured with autoStartDaemon
32
+ serverInstance = createMcpServer({
33
+ dbPath,
34
+ port: testPort,
35
+ host: '127.0.0.1',
36
+ autoStartDaemon: true,
37
+ detached: false,
38
+ });
39
+
40
+ // 3. Trigger ensureDaemon (which runs prior to stdio connection)
41
+ await serverInstance.ensureDaemon();
42
+
43
+ // 4. Verify daemon is now online and responds to /health
44
+ const isNowRunning = await isDaemonRunning('127.0.0.1', testPort);
45
+ expect(isNowRunning).toBe(true);
46
+
47
+ const resp = await fetch(`http://127.0.0.1:${testPort}/health`);
48
+ expect(resp.ok).toBe(true);
49
+ const health = await resp.json();
50
+ expect(health.name).toBe('browsertrack');
51
+ expect(health.status).toBe('ok');
52
+ });
53
+
54
+ it('should reuse the existing singleton daemon without spawning a duplicate when another session starts', async () => {
55
+ const dbPath = path.join(tempDir, 'singleton.db');
56
+
57
+ // 1. Session 1 starts the daemon
58
+ serverInstance = createMcpServer({
59
+ dbPath,
60
+ port: testPort,
61
+ host: '127.0.0.1',
62
+ autoStartDaemon: true,
63
+ detached: false,
64
+ });
65
+ await serverInstance.ensureDaemon();
66
+ expect(await isDaemonRunning('127.0.0.1', testPort)).toBe(true);
67
+
68
+ // 2. Session 2 initializes on the same port/host
69
+ const session2 = createMcpServer({
70
+ dbPath,
71
+ port: testPort,
72
+ host: '127.0.0.1',
73
+ autoStartDaemon: true,
74
+ detached: false,
75
+ });
76
+
77
+ // Session 2 calls ensureDaemon - must detect existing singleton daemon without throwing
78
+ await expect(session2.ensureDaemon()).resolves.not.toThrow();
79
+
80
+ // Verify single daemon is intact and healthy
81
+ const resp = await fetch(`http://127.0.0.1:${testPort}/health`);
82
+ expect(resp.ok).toBe(true);
83
+ const health = await resp.json();
84
+ expect(health.name).toBe('browsertrack');
85
+ expect(health.status).toBe('ok');
86
+ });
87
+ });
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../packages/daemon/src/config.ts","../packages/daemon/src/storage/db.ts","../packages/daemon/src/storage/screenshot-store.ts","../packages/daemon/src/session/manager.ts","../packages/daemon/src/notes/engine.ts","../packages/daemon/src/notes/verification.ts","../packages/daemon/src/verification/engine.ts"],"sourcesContent":["import os from 'node:os';\nimport path from 'node:path';\n\nexport interface DaemonConfig {\n host: string;\n port: number;\n dataDir: string;\n dbPath: string;\n screenshotsDir: string;\n maxEventsPerSession: number;\n verbose: boolean;\n}\n\nexport function getDaemonConfig(overrides: Partial<DaemonConfig> = {}): DaemonConfig {\n const homeDir = os.homedir();\n const dataDir = overrides.dataDir || path.join(homeDir, '.browsertrack');\n const dbPath = overrides.dbPath || path.join(dataDir, 'browsertrack.db');\n const screenshotsDir = overrides.screenshotsDir || path.join(dataDir, 'projects');\n\n return {\n host: overrides.host || '127.0.0.1',\n port: overrides.port || 7331,\n dataDir,\n dbPath,\n screenshotsDir,\n maxEventsPerSession: overrides.maxEventsPerSession || 1000,\n verbose: !!overrides.verbose,\n };\n}\n","import Database from 'better-sqlite3';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport type {\n Breadcrumb,\n ElementSummary,\n Incident,\n IncidentOccurrence,\n IncidentSeverity,\n IncidentStatus,\n NetworkEvent,\n Project,\n Session,\n VerificationResult,\n VisualNote,\n NoteStatus,\n NoteVerificationResult,\n ScenarioOverview,\n ScenarioDetail,\n} from '../../../core/src/index.js';\n\nexport class StorageDB {\n private db: Database.Database;\n\n constructor(dbPath: string) {\n const dir = path.dirname(dbPath);\n fs.mkdirSync(dir, { recursive: true });\n\n this.db = new Database(dbPath);\n this.db.pragma('journal_mode = WAL');\n this.db.pragma('synchronous = NORMAL');\n this.initTables();\n }\n\n private initTables(): void {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS projects (\n id TEXT PRIMARY KEY,\n name TEXT UNIQUE,\n origin TEXT,\n path TEXT,\n created_at TEXT,\n updated_at TEXT\n );\n\n CREATE TABLE IF NOT EXISTS sessions (\n id TEXT PRIMARY KEY,\n project_id TEXT,\n origin TEXT,\n url TEXT,\n title TEXT,\n user_agent TEXT,\n connected_at TEXT,\n last_seen_at TEXT,\n active INTEGER DEFAULT 1\n );\n\n CREATE TABLE IF NOT EXISTS events (\n id TEXT PRIMARY KEY,\n session_id TEXT,\n event_type TEXT,\n payload TEXT,\n timestamp INTEGER,\n route TEXT,\n url TEXT\n );\n\n CREATE TABLE IF NOT EXISTS incidents (\n id TEXT PRIMARY KEY,\n project_id TEXT,\n session_id TEXT,\n type TEXT,\n severity TEXT,\n message TEXT,\n source_file TEXT,\n source_line INTEGER,\n source_col INTEGER,\n fingerprint TEXT UNIQUE,\n route TEXT,\n first_seen TEXT,\n last_seen TEXT,\n occurrences INTEGER DEFAULT 1,\n status TEXT DEFAULT 'OPEN',\n stack TEXT,\n breadcrumbs TEXT,\n network_failures TEXT,\n last_element TEXT,\n screenshot_path TEXT\n );\n\n CREATE TABLE IF NOT EXISTS incident_occurrences (\n id TEXT PRIMARY KEY,\n incident_id TEXT,\n session_id TEXT,\n timestamp TEXT,\n route TEXT,\n url TEXT,\n stack TEXT,\n breadcrumbs TEXT,\n last_element TEXT\n );\n\n CREATE TABLE IF NOT EXISTS screenshots (\n id TEXT PRIMARY KEY,\n incident_id TEXT,\n file_path TEXT,\n format TEXT,\n width INTEGER,\n height INTEGER,\n created_at TEXT\n );\n\n CREATE TABLE IF NOT EXISTS verifications (\n id TEXT PRIMARY KEY,\n incident_id TEXT,\n status TEXT,\n checks TEXT,\n before_screenshot TEXT,\n after_screenshot TEXT,\n message TEXT,\n created_at TEXT\n );\n\n CREATE TABLE IF NOT EXISTS notes (\n id TEXT PRIMARY KEY,\n project_id TEXT,\n session_id TEXT,\n type TEXT,\n message TEXT,\n route TEXT,\n url TEXT,\n viewport_json TEXT,\n scroll_json TEXT,\n target_json TEXT,\n element_context_json TEXT,\n region_json TEXT,\n screenshot_path TEXT,\n incident_id TEXT,\n scenario_id TEXT,\n step_number INTEGER,\n scenario_title TEXT,\n status TEXT DEFAULT 'OPEN',\n created_at TEXT,\n updated_at TEXT,\n resolved_at TEXT\n );\n\n CREATE TABLE IF NOT EXISTS note_verifications (\n id TEXT PRIMARY KEY,\n note_id TEXT,\n status TEXT,\n checks TEXT,\n geometry_diff TEXT,\n before_screenshot TEXT,\n after_screenshot TEXT,\n message TEXT,\n created_at TEXT\n );\n\n CREATE INDEX IF NOT EXISTS idx_events_session ON events(session_id, timestamp);\n CREATE INDEX IF NOT EXISTS idx_incidents_project ON incidents(project_id, status);\n CREATE INDEX IF NOT EXISTS idx_incidents_fp ON incidents(fingerprint);\n CREATE INDEX IF NOT EXISTS idx_notes_project ON notes(project_id, status);\n CREATE INDEX IF NOT EXISTS idx_notes_scenario ON notes(scenario_id, step_number);\n `);\n\n // Schema migrations for scenario fields\n try {\n this.db.exec('ALTER TABLE notes ADD COLUMN scenario_id TEXT;');\n } catch {}\n try {\n this.db.exec('ALTER TABLE notes ADD COLUMN step_number INTEGER;');\n } catch {}\n try {\n this.db.exec('ALTER TABLE notes ADD COLUMN scenario_title TEXT;');\n } catch {}\n }\n\n // --- PROJECTS ---\n public upsertProject(project: { id: string; name: string; origin: string; path?: string }): Project {\n const now = new Date().toISOString();\n const existing = this.db.prepare('SELECT * FROM projects WHERE name = ? OR origin = ?').get(project.name, project.origin) as any;\n\n if (existing) {\n this.db\n .prepare('UPDATE projects SET name = ?, origin = ?, path = COALESCE(?, path), updated_at = ? WHERE id = ?')\n .run(project.name, project.origin, project.path || null, now, existing.id);\n return {\n id: existing.id,\n name: project.name,\n origin: project.origin,\n path: project.path || existing.path,\n createdAt: existing.created_at,\n updatedAt: now,\n };\n }\n\n this.db\n .prepare('INSERT INTO projects (id, name, origin, path, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)')\n .run(project.id, project.name, project.origin, project.path || null, now, now);\n\n return {\n id: project.id,\n name: project.name,\n origin: project.origin,\n path: project.path,\n createdAt: now,\n updatedAt: now,\n };\n }\n\n public getProject(idOrName: string): Project | null {\n const row = this.db.prepare('SELECT * FROM projects WHERE id = ? OR name = ?').get(idOrName, idOrName) as any;\n if (!row) return null;\n return {\n id: row.id,\n name: row.name,\n origin: row.origin,\n path: row.path,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n }\n\n public getProjectByOrigin(origin: string): Project | null {\n const row = this.db.prepare('SELECT * FROM projects WHERE origin = ?').get(origin) as any;\n if (!row) return null;\n return {\n id: row.id,\n name: row.name,\n origin: row.origin,\n path: row.path,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n }\n\n public listProjects(): Project[] {\n const rows = this.db.prepare('SELECT * FROM projects ORDER BY updated_at DESC').all() as any[];\n return rows.map((row) => ({\n id: row.id,\n name: row.name,\n origin: row.origin,\n path: row.path,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n }));\n }\n\n // --- SESSIONS ---\n public upsertSession(session: Session): void {\n const existing = this.db.prepare('SELECT id FROM sessions WHERE id = ?').get(session.id);\n if (existing) {\n this.db\n .prepare('UPDATE sessions SET url = ?, title = ?, last_seen_at = ?, active = ? WHERE id = ?')\n .run(session.url, session.title, session.lastSeenAt, session.active ? 1 : 0, session.id);\n } else {\n this.db\n .prepare(\n 'INSERT INTO sessions (id, project_id, origin, url, title, user_agent, connected_at, last_seen_at, active) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)'\n )\n .run(\n session.id,\n session.projectId,\n session.origin,\n session.url,\n session.title,\n session.userAgent,\n session.connectedAt,\n session.lastSeenAt,\n session.active ? 1 : 0\n );\n }\n }\n\n public getSession(id: string): Session | null {\n const row = this.db.prepare('SELECT * FROM sessions WHERE id = ?').get(id) as any;\n if (!row) return null;\n return {\n id: row.id,\n projectId: row.project_id,\n origin: row.origin,\n url: row.url,\n title: row.title,\n userAgent: row.user_agent,\n connectedAt: row.connected_at,\n lastSeenAt: row.last_seen_at,\n active: row.active === 1,\n };\n }\n\n public listSessions(projectId?: string, activeOnly = false): Session[] {\n let sql = 'SELECT * FROM sessions WHERE 1=1';\n const params: any[] = [];\n\n if (projectId) {\n sql += ' AND project_id = ?';\n params.push(projectId);\n }\n if (activeOnly) {\n sql += ' AND active = 1';\n }\n sql += ' ORDER BY last_seen_at DESC';\n\n const rows = this.db.prepare(sql).all(...params) as any[];\n return rows.map((row) => ({\n id: row.id,\n projectId: row.project_id,\n origin: row.origin,\n url: row.url,\n title: row.title,\n userAgent: row.user_agent,\n connectedAt: row.connected_at,\n lastSeenAt: row.last_seen_at,\n active: row.active === 1,\n }));\n }\n\n public deactivateSession(id: string): void {\n this.db.prepare('UPDATE sessions SET active = 0, last_seen_at = ? WHERE id = ?').run(new Date().toISOString(), id);\n }\n\n // --- EVENTS & RETENTION ---\n public insertEvent(event: {\n id: string;\n sessionId: string;\n eventType: string;\n payload: any;\n timestamp: number;\n route?: string;\n url?: string;\n }): void {\n this.db\n .prepare('INSERT INTO events (id, session_id, event_type, payload, timestamp, route, url) VALUES (?, ?, ?, ?, ?, ?, ?)')\n .run(\n event.id,\n event.sessionId,\n event.eventType,\n JSON.stringify(event.payload),\n event.timestamp,\n event.route || '',\n event.url || ''\n );\n }\n\n public pruneSessionEvents(sessionId: string, maxEvents = 1000): void {\n this.db\n .prepare(\n `DELETE FROM events \n WHERE session_id = ? AND id NOT IN (\n SELECT id FROM events WHERE session_id = ? ORDER BY timestamp DESC LIMIT ?\n )`\n )\n .run(sessionId, sessionId, maxEvents);\n }\n\n public getEvents(options: { sessionId?: string; eventType?: string; limit?: number }): any[] {\n let sql = 'SELECT * FROM events WHERE 1=1';\n const params: any[] = [];\n\n if (options.sessionId) {\n sql += ' AND session_id = ?';\n params.push(options.sessionId);\n }\n if (options.eventType) {\n sql += ' AND event_type = ?';\n params.push(options.eventType);\n }\n\n sql += ' ORDER BY timestamp DESC LIMIT ?';\n params.push(options.limit || 50);\n\n const rows = this.db.prepare(sql).all(...params) as any[];\n return rows.map((r) => ({\n id: r.id,\n sessionId: r.session_id,\n eventType: r.event_type,\n payload: JSON.parse(r.payload),\n timestamp: r.timestamp,\n route: r.route,\n url: r.url,\n }));\n }\n\n // --- INCIDENTS ---\n public findIncidentByFingerprint(fingerprint: string): Incident | null {\n const row = this.db.prepare('SELECT * FROM incidents WHERE fingerprint = ?').get(fingerprint) as any;\n if (!row) return null;\n return this.mapIncidentRow(row);\n }\n\n public getIncident(id: string): Incident | null {\n const row = this.db.prepare('SELECT * FROM incidents WHERE id = ?').get(id) as any;\n if (!row) return null;\n return this.mapIncidentRow(row);\n }\n\n public listIncidents(options: { projectId?: string; status?: IncidentStatus; severity?: IncidentSeverity; limit?: number } = {}): Incident[] {\n let sql = 'SELECT * FROM incidents WHERE 1=1';\n const params: any[] = [];\n\n if (options.projectId) {\n sql += ' AND project_id = ?';\n params.push(options.projectId);\n }\n if (options.status) {\n sql += ' AND status = ?';\n params.push(options.status);\n }\n if (options.severity) {\n sql += ' AND severity = ?';\n params.push(options.severity);\n }\n\n sql += ' ORDER BY last_seen DESC LIMIT ?';\n params.push(options.limit || 50);\n\n const rows = this.db.prepare(sql).all(...params) as any[];\n return rows.map((r) => this.mapIncidentRow(r));\n }\n\n public insertIncident(incident: Incident): void {\n this.db\n .prepare(\n `INSERT INTO incidents (\n id, project_id, session_id, type, severity, message, source_file, source_line, source_col,\n fingerprint, route, first_seen, last_seen, occurrences, status, stack, breadcrumbs,\n network_failures, last_element, screenshot_path\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`\n )\n .run(\n incident.id,\n incident.projectId,\n incident.sessionId,\n incident.type,\n incident.severity,\n incident.message,\n incident.source.file,\n incident.source.line,\n incident.source.column || null,\n incident.fingerprint,\n incident.route,\n incident.firstSeen,\n incident.lastSeen,\n incident.occurrences,\n incident.status,\n incident.stack || null,\n JSON.stringify(incident.breadcrumbs || []),\n JSON.stringify(incident.networkFailures || []),\n incident.lastElement ? JSON.stringify(incident.lastElement) : null,\n incident.screenshots?.error || null\n );\n }\n\n public updateIncidentOccurrence(\n incidentId: string,\n update: {\n sessionId: string;\n lastSeen: string;\n occurrences: number;\n route: string;\n breadcrumbs: Breadcrumb[];\n lastElement?: ElementSummary;\n stack?: string;\n }\n ): void {\n this.db\n .prepare(\n `UPDATE incidents \n SET session_id = ?, last_seen = ?, occurrences = ?, route = ?, breadcrumbs = ?, last_element = COALESCE(?, last_element), stack = COALESCE(?, stack)\n WHERE id = ?`\n )\n .run(\n update.sessionId,\n update.lastSeen,\n update.occurrences,\n update.route,\n JSON.stringify(update.breadcrumbs || []),\n update.lastElement ? JSON.stringify(update.lastElement) : null,\n update.stack || null,\n incidentId\n );\n }\n\n public updateIncidentStatus(id: string, status: IncidentStatus): void {\n this.db.prepare('UPDATE incidents SET status = ? WHERE id = ?').run(status, id);\n }\n\n public insertIncidentOccurrence(occurrence: IncidentOccurrence): void {\n this.db\n .prepare(\n `INSERT INTO incident_occurrences (id, incident_id, session_id, timestamp, route, url, stack, breadcrumbs, last_element)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`\n )\n .run(\n occurrence.id,\n occurrence.incidentId,\n occurrence.sessionId,\n occurrence.timestamp,\n occurrence.route,\n occurrence.url,\n occurrence.stack || null,\n JSON.stringify(occurrence.breadcrumbs || []),\n occurrence.lastElement ? JSON.stringify(occurrence.lastElement) : null\n );\n }\n\n // --- VERIFICATIONS ---\n public insertVerification(v: {\n id: string;\n incidentId: string;\n status: IncidentStatus;\n checks: any[];\n beforeScreenshot?: string;\n afterScreenshot?: string;\n message?: string;\n createdAt: string;\n }): void {\n this.db\n .prepare(\n `INSERT INTO verifications (id, incident_id, status, checks, before_screenshot, after_screenshot, message, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)`\n )\n .run(\n v.id,\n v.incidentId,\n v.status,\n JSON.stringify(v.checks),\n v.beforeScreenshot || null,\n v.afterScreenshot || null,\n v.message || null,\n v.createdAt\n );\n }\n\n public getLatestVerification(incidentId: string): VerificationResult | null {\n const row = this.db.prepare('SELECT * FROM verifications WHERE incident_id = ? ORDER BY created_at DESC LIMIT 1').get(incidentId) as any;\n if (!row) return null;\n return {\n incidentId: row.incident_id,\n status: row.status as IncidentStatus,\n checks: JSON.parse(row.checks || '[]'),\n screenshots: {\n before: row.before_screenshot || undefined,\n after: row.after_screenshot || undefined,\n },\n timestamp: row.created_at,\n message: row.message || undefined,\n };\n }\n\n // --- VISUAL NOTES ---\n public insertNote(note: VisualNote): void {\n this.db\n .prepare(\n `INSERT INTO notes (\n id, project_id, session_id, type, message, route, url,\n viewport_json, scroll_json, target_json, element_context_json, region_json,\n screenshot_path, incident_id, scenario_id, step_number, scenario_title, status, created_at, updated_at, resolved_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`\n )\n .run(\n note.id,\n note.projectId,\n note.sessionId,\n note.type,\n note.message,\n note.route,\n note.url,\n JSON.stringify(note.viewport),\n JSON.stringify(note.scroll),\n note.target ? JSON.stringify(note.target) : null,\n note.elementContext ? JSON.stringify(note.elementContext) : null,\n note.region ? JSON.stringify(note.region) : null,\n note.screenshots?.original || null,\n note.incidentId || null,\n note.scenarioId || null,\n note.stepNumber ?? null,\n note.scenarioTitle || null,\n note.status,\n note.createdAt,\n note.updatedAt,\n note.resolvedAt || null\n );\n }\n\n public getNote(id: string): VisualNote | null {\n const row = this.db.prepare('SELECT * FROM notes WHERE id = ?').get(id) as any;\n if (!row) return null;\n return this.mapNoteRow(row);\n }\n\n public listNotes(options: { projectId?: string; status?: NoteStatus; route?: string; scenarioId?: string; limit?: number } = {}): VisualNote[] {\n let sql = 'SELECT * FROM notes WHERE 1=1';\n const params: any[] = [];\n\n if (options.projectId) {\n sql += ' AND project_id = ?';\n params.push(options.projectId);\n }\n if (options.status) {\n sql += ' AND status = ?';\n params.push(options.status);\n }\n if (options.route) {\n sql += ' AND route = ?';\n params.push(options.route);\n }\n if (options.scenarioId) {\n sql += ' AND scenario_id = ?';\n params.push(options.scenarioId);\n }\n\n sql += ' ORDER BY created_at DESC LIMIT ?';\n params.push(options.limit || 50);\n\n const rows = this.db.prepare(sql).all(...params) as any[];\n return rows.map((r) => this.mapNoteRow(r));\n }\n\n public deleteNote(id: string): void {\n this.db.prepare('DELETE FROM note_verifications WHERE note_id = ?').run(id);\n this.db.prepare('DELETE FROM notes WHERE id = ?').run(id);\n }\n\n // --- SCENARIOS (MULTI-STEP FLOWS) ---\n public listScenarios(options: { projectId?: string; status?: NoteStatus; limit?: number } = {}): ScenarioOverview[] {\n let sql = `\n SELECT \n scenario_id, \n project_id, \n COALESCE(MAX(scenario_title), 'Scenario') as title,\n COUNT(id) as steps_count,\n CASE WHEN SUM(CASE WHEN status != 'RESOLVED' THEN 1 ELSE 0 END) = 0 THEN 'RESOLVED' ELSE 'OPEN' END as status,\n MIN(route) as route,\n MIN(created_at) as first_step_at,\n MAX(created_at) as last_step_at\n FROM notes\n WHERE scenario_id IS NOT NULL\n `;\n const params: any[] = [];\n if (options.projectId) {\n sql += ' AND (project_id = ? OR project_id IS NULL)';\n params.push(options.projectId);\n }\n sql += ' GROUP BY scenario_id';\n\n if (options.status) {\n if (options.status === 'RESOLVED') {\n sql += \" HAVING SUM(CASE WHEN status != 'RESOLVED' THEN 1 ELSE 0 END) = 0\";\n } else {\n sql += \" HAVING SUM(CASE WHEN status != 'RESOLVED' THEN 1 ELSE 0 END) > 0\";\n }\n }\n\n sql += ' ORDER BY last_step_at DESC LIMIT ?';\n params.push(options.limit || 50);\n\n const rows = this.db.prepare(sql).all(...params) as any[];\n return rows.map((r) => ({\n id: r.scenario_id,\n projectId: r.project_id,\n title: r.title,\n stepsCount: Number(r.steps_count),\n status: r.status as NoteStatus,\n route: r.route || '/',\n firstStepAt: r.first_step_at,\n lastStepAt: r.last_step_at,\n }));\n }\n\n public getScenario(scenarioId: string): ScenarioDetail | null {\n const rows = this.db\n .prepare('SELECT * FROM notes WHERE scenario_id = ? ORDER BY COALESCE(step_number, 999999) ASC, created_at ASC')\n .all(scenarioId) as any[];\n\n if (!rows || rows.length === 0) return null;\n\n const steps = rows.map((r) => this.mapNoteRow(r));\n const title = steps.find((s) => s.scenarioTitle)?.scenarioTitle || `Scenario ${scenarioId}`;\n const allResolved = steps.every((s) => s.status === 'RESOLVED');\n\n return {\n id: scenarioId,\n projectId: steps[0].projectId,\n title,\n stepsCount: steps.length,\n status: allResolved ? 'RESOLVED' : 'OPEN',\n steps,\n createdAt: steps[0].createdAt,\n updatedAt: steps[steps.length - 1].updatedAt,\n };\n }\n\n public deleteScenario(scenarioId: string): void {\n const notes = this.db.prepare('SELECT id FROM notes WHERE scenario_id = ?').all(scenarioId) as any[];\n for (const n of notes) {\n this.deleteNote(n.id);\n }\n }\n\n public updateNoteStatus(id: string, status: NoteStatus): void {\n const now = new Date().toISOString();\n const resolvedAt = status === 'RESOLVED' ? now : null;\n this.db\n .prepare('UPDATE notes SET status = ?, updated_at = ?, resolved_at = COALESCE(?, resolved_at) WHERE id = ?')\n .run(status, now, resolvedAt, id);\n }\n\n public updateNote(id: string, updates: Partial<VisualNote>): void {\n const now = new Date().toISOString();\n const current = this.getNote(id);\n if (!current) return;\n\n this.db\n .prepare(\n `UPDATE notes \n SET message = COALESCE(?, message), status = COALESCE(?, status),\n screenshot_path = COALESCE(?, screenshot_path), updated_at = ?\n WHERE id = ?`\n )\n .run(\n updates.message || null,\n updates.status || null,\n updates.screenshots?.original || updates.screenshots?.after || null,\n now,\n id\n );\n }\n\n public insertNoteVerification(v: NoteVerificationResult): void {\n this.db\n .prepare(\n `INSERT INTO note_verifications (\n id, note_id, status, checks, geometry_diff, before_screenshot, after_screenshot, message, created_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`\n )\n .run(\n `nver_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,\n v.noteId,\n v.status,\n JSON.stringify(v.checks),\n v.geometryDiff ? JSON.stringify(v.geometryDiff) : null,\n v.screenshots?.before || null,\n v.screenshots?.after || null,\n v.message || null,\n v.timestamp\n );\n }\n\n public getLatestNoteVerification(noteId: string): NoteVerificationResult | null {\n const row = this.db.prepare('SELECT * FROM note_verifications WHERE note_id = ? ORDER BY created_at DESC LIMIT 1').get(noteId) as any;\n if (!row) return null;\n return {\n noteId: row.note_id,\n status: row.status as NoteStatus,\n checks: JSON.parse(row.checks || '[]'),\n geometryDiff: row.geometry_diff ? JSON.parse(row.geometry_diff) : undefined,\n screenshots: {\n before: row.before_screenshot || undefined,\n after: row.after_screenshot || undefined,\n },\n timestamp: row.created_at,\n message: row.message || undefined,\n };\n }\n\n public clearAll(): void {\n this.db.exec(`\n DELETE FROM events;\n DELETE FROM incident_occurrences;\n DELETE FROM incidents;\n DELETE FROM screenshots;\n DELETE FROM verifications;\n DELETE FROM notes;\n DELETE FROM note_verifications;\n DELETE FROM sessions;\n `);\n }\n\n private mapIncidentRow(row: any): Incident {\n return {\n id: row.id,\n projectId: row.project_id,\n sessionId: row.session_id,\n type: row.type,\n severity: row.severity as IncidentSeverity,\n message: row.message,\n source: {\n file: row.source_file,\n line: row.source_line,\n column: row.source_col || undefined,\n },\n fingerprint: row.fingerprint,\n route: row.route,\n firstSeen: row.first_seen,\n lastSeen: row.last_seen,\n occurrences: row.occurrences,\n status: row.status as IncidentStatus,\n stack: row.stack || undefined,\n breadcrumbs: JSON.parse(row.breadcrumbs || '[]'),\n networkFailures: JSON.parse(row.network_failures || '[]'),\n lastElement: row.last_element ? JSON.parse(row.last_element) : undefined,\n screenshots: row.screenshot_path\n ? {\n error: row.screenshot_path,\n }\n : undefined,\n };\n }\n\n private mapNoteRow(row: any): VisualNote {\n return {\n id: row.id,\n projectId: row.project_id,\n sessionId: row.session_id,\n type: row.type,\n message: row.message,\n route: row.route,\n url: row.url,\n viewport: JSON.parse(row.viewport_json || '{}'),\n scroll: JSON.parse(row.scroll_json || '{}'),\n target: row.target_json ? JSON.parse(row.target_json) : undefined,\n elementContext: row.element_context_json ? JSON.parse(row.element_context_json) : undefined,\n region: row.region_json ? JSON.parse(row.region_json) : undefined,\n status: row.status as NoteStatus,\n incidentId: row.incident_id || undefined,\n scenarioId: row.scenario_id || undefined,\n stepNumber: row.step_number != null ? Number(row.step_number) : undefined,\n scenarioTitle: row.scenario_title || undefined,\n screenshots: row.screenshot_path\n ? {\n original: row.screenshot_path,\n }\n : undefined,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n resolvedAt: row.resolved_at || undefined,\n };\n }\n\n public close(): void {\n this.db.close();\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nexport class ScreenshotStore {\n private baseDir: string;\n\n constructor(baseDir: string) {\n this.baseDir = baseDir;\n fs.mkdirSync(this.baseDir, { recursive: true });\n }\n\n public saveScreenshot(projectId: string, incidentId: string, name: string, dataUrl: string): { filePath: string; format: string } | null {\n try {\n if (!dataUrl || !dataUrl.startsWith('data:image/')) return null;\n\n const match = dataUrl.match(/^data:image\\/([a-zA-Z0-9+]+);base64,(.+)$/);\n if (!match) return null;\n\n let format = match[1].toLowerCase();\n if (format === 'jpeg') format = 'jpg';\n const base64Data = match[2];\n const buffer = Buffer.from(base64Data, 'base64');\n\n const targetDir = path.join(this.baseDir, projectId || 'default', 'incidents', incidentId);\n fs.mkdirSync(targetDir, { recursive: true });\n\n const fileName = `${name}.${format}`;\n const filePath = path.join(targetDir, fileName);\n\n fs.writeFileSync(filePath, buffer);\n\n return { filePath, format };\n } catch {\n return null;\n }\n }\n\n public getScreenshotPath(projectId: string, incidentId: string, name: string): string | null {\n const targetDir = path.join(this.baseDir, projectId || 'default', 'incidents', incidentId);\n if (!fs.existsSync(targetDir)) return null;\n\n const files = fs.readdirSync(targetDir);\n const match = files.find((f) => f.startsWith(`${name}.`));\n if (match) {\n return path.join(targetDir, match);\n }\n return null;\n }\n}\n","import type { WebSocket } from 'ws';\nimport type { ClientCommand, CommandResponse, Session } from '../../../core/src/index.js';\nimport type { StorageDB } from '../storage/db.js';\n\ninterface ActiveSessionSocket {\n sessionId: string;\n ws: WebSocket;\n origin: string;\n projectId: string;\n pendingCommands: Map<\n string,\n {\n resolve: (res: CommandResponse) => void;\n reject: (err: Error) => void;\n timer: any;\n }\n >;\n}\n\nexport class SessionManager {\n private activeSockets: Map<string, ActiveSessionSocket> = new Map();\n private db: StorageDB;\n\n constructor(db: StorageDB) {\n this.db = db;\n }\n\n public registerSocket(sessionId: string, ws: WebSocket, origin: string, projectId: string): void {\n this.activeSockets.set(sessionId, {\n sessionId,\n ws,\n origin,\n projectId,\n pendingCommands: new Map(),\n });\n }\n\n public unregisterSocket(sessionId: string): void {\n const active = this.activeSockets.get(sessionId);\n if (active) {\n for (const pending of active.pendingCommands.values()) {\n clearTimeout(pending.timer);\n pending.reject(new Error('Session disconnected before command completed'));\n }\n active.pendingCommands.clear();\n this.activeSockets.delete(sessionId);\n this.db.deactivateSession(sessionId);\n }\n }\n\n public handleCommandResponse(sessionId: string, response: CommandResponse): void {\n const active = this.activeSockets.get(sessionId);\n if (!active) return;\n\n const pending = active.pendingCommands.get(response.id);\n if (pending) {\n clearTimeout(pending.timer);\n active.pendingCommands.delete(response.id);\n pending.resolve(response);\n }\n }\n\n public async sendCommand<T = any>(sessionId: string, command: ClientCommand<T>, timeoutMs = 5000): Promise<CommandResponse> {\n const active = this.activeSockets.get(sessionId);\n if (!active || active.ws.readyState !== 1 /* WebSocket.OPEN */) {\n return {\n id: command.id,\n ok: false,\n error: `Session ${sessionId} is not actively connected`,\n };\n }\n\n return new Promise<CommandResponse>((resolve, reject) => {\n const timer = setTimeout(() => {\n active.pendingCommands.delete(command.id);\n resolve({\n id: command.id,\n ok: false,\n error: `Command ${command.type} timed out after ${timeoutMs}ms`,\n });\n }, timeoutMs);\n\n active.pendingCommands.set(command.id, {\n resolve,\n reject,\n timer,\n });\n\n try {\n active.ws.send(\n JSON.stringify({\n type: 'command',\n command,\n })\n );\n } catch (err: any) {\n clearTimeout(timer);\n active.pendingCommands.delete(command.id);\n resolve({\n id: command.id,\n ok: false,\n error: err?.message || 'Failed to send command over WebSocket',\n });\n }\n });\n }\n\n public getActiveSessionForProject(projectId: string): Session | null {\n for (const active of this.activeSockets.values()) {\n if (active.projectId === projectId) {\n return this.db.getSession(active.sessionId);\n }\n }\n return null;\n }\n\n public getAnyActiveSession(): Session | null {\n const first = this.activeSockets.keys().next().value;\n if (!first) return null;\n return this.db.getSession(first);\n }\n\n public getActiveCount(): number {\n return this.activeSockets.size;\n }\n\n public broadcastToProject(projectId: string, message: any): void {\n const raw = typeof message === 'string' ? message : JSON.stringify(message);\n for (const active of this.activeSockets.values()) {\n if (active.projectId === projectId && active.ws.readyState === 1 /* OPEN */) {\n try {\n active.ws.send(raw);\n } catch {}\n }\n }\n }\n\n public sendToSession(sessionId: string, message: any): void {\n const active = this.activeSockets.get(sessionId);\n if (active && active.ws.readyState === 1) {\n try {\n const raw = typeof message === 'string' ? message : JSON.stringify(message);\n active.ws.send(raw);\n } catch {}\n }\n }\n}\n","import crypto from 'node:crypto';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport type { VisualNote } from '../../../core/src/index.js';\nimport type { StorageDB } from '../storage/db.js';\nimport type { ScreenshotStore } from '../storage/screenshot-store.js';\n\nexport class NotesEngine {\n private db: StorageDB;\n private screenshotStore: ScreenshotStore;\n\n constructor(db: StorageDB, screenshotStore: ScreenshotStore) {\n this.db = db;\n this.screenshotStore = screenshotStore;\n }\n\n public createNoteFromClient(payload: {\n sessionId: string;\n noteType?: 'element' | 'region' | 'page';\n message: string;\n route?: string;\n url?: string;\n viewport?: { width: number; height: number; devicePixelRatio: number };\n scroll?: { scrollX: number; scrollY: number };\n target?: any;\n elementContext?: any;\n region?: any;\n screenshot?: string;\n incidentId?: string;\n scenarioId?: string;\n stepNumber?: number;\n scenarioTitle?: string;\n }): VisualNote {\n const session = this.db.getSession(payload.sessionId);\n const projectId = session?.projectId || 'default';\n const noteId = `note_${crypto.randomUUID().slice(0, 8)}`;\n const now = new Date().toISOString();\n\n let screenshotPath: string | undefined;\n if (payload.screenshot) {\n const saved = this.saveNoteScreenshot(projectId, noteId, 'original', payload.screenshot);\n if (saved) {\n screenshotPath = saved;\n }\n }\n\n const note: VisualNote = {\n id: noteId,\n projectId,\n sessionId: payload.sessionId,\n type: payload.noteType || 'element',\n message: payload.message,\n route: payload.route || '/',\n url: payload.url || '',\n viewport: payload.viewport || { width: 1280, height: 800, devicePixelRatio: 1 },\n scroll: payload.scroll || { scrollX: 0, scrollY: 0 },\n target: payload.target,\n elementContext: payload.elementContext,\n region: payload.region,\n status: 'OPEN',\n incidentId: payload.incidentId,\n scenarioId: payload.scenarioId,\n stepNumber: payload.stepNumber,\n scenarioTitle: payload.scenarioTitle,\n screenshots: screenshotPath ? { original: screenshotPath } : undefined,\n createdAt: now,\n updatedAt: now,\n };\n\n this.db.insertNote(note);\n return note;\n }\n\n public saveNoteScreenshot(projectId: string, noteId: string, name: string, dataUrl: string): string | null {\n try {\n if (!dataUrl || !dataUrl.startsWith('data:image/')) return null;\n\n const match = dataUrl.match(/^data:image\\/([a-zA-Z0-9+]+);base64,(.+)$/);\n if (!match) return null;\n\n let format = match[1].toLowerCase();\n if (format === 'jpeg') format = 'jpg';\n const base64Data = match[2];\n const buffer = Buffer.from(base64Data, 'base64');\n\n const targetDir = path.join((this.screenshotStore as any).baseDir, projectId || 'default', 'notes', noteId);\n fs.mkdirSync(targetDir, { recursive: true });\n\n const fileName = `${name}.${format}`;\n const filePath = path.join(targetDir, fileName);\n\n fs.writeFileSync(filePath, buffer);\n return filePath;\n } catch {\n return null;\n }\n }\n}\n","import crypto from 'node:crypto';\nimport type { DOMRectJson, NoteStatus, NoteVerificationResult, OverflowCheckResult, VisualNote } from '../../../core/src/index.js';\nimport type { NotesEngine } from './engine.js';\nimport type { SessionManager } from '../session/manager.js';\nimport type { StorageDB } from '../storage/db.js';\n\nexport class NoteVerificationEngine {\n private db: StorageDB;\n private sessionManager: SessionManager;\n private notesEngine: NotesEngine;\n\n constructor(db: StorageDB, sessionManager: SessionManager, notesEngine: NotesEngine) {\n this.db = db;\n this.sessionManager = sessionManager;\n this.notesEngine = notesEngine;\n }\n\n public async verifyNote(noteId: string, options: { observationWindowMs?: number } = {}): Promise<NoteVerificationResult> {\n const note = this.db.getNote(noteId);\n if (!note) {\n return {\n noteId,\n status: 'FAILED',\n checks: [{ type: 'note_exists', passed: false, details: `Note ${noteId} not found` }],\n timestamp: new Date().toISOString(),\n message: `Note ${noteId} not found in database`,\n };\n }\n\n // 1. Find active session\n let session = this.sessionManager.getActiveSessionForProject(note.projectId);\n if (!session) {\n session = this.sessionManager.getAnyActiveSession();\n }\n\n if (!session) {\n const result: NoteVerificationResult = {\n noteId,\n status: 'INCONCLUSIVE',\n checks: [],\n timestamp: new Date().toISOString(),\n message: 'No active browser session currently connected to verify visual note.',\n };\n this.recordNoteVerification(result, note);\n return result;\n }\n\n // 2. Set status to VERIFYING\n this.db.updateNoteStatus(noteId, 'VERIFYING');\n\n // 3. Navigation check / route sync\n const pageStateCmd = await this.sessionManager.sendCommand(session.id, {\n id: `cmd_${crypto.randomUUID().slice(0, 8)}`,\n type: 'get_page_state',\n });\n\n const currentRoute = pageStateCmd.result?.route || '/';\n const isRouteMatch = currentRoute === note.route || currentRoute.startsWith(note.route);\n\n if (!isRouteMatch && note.route) {\n await this.sessionManager.sendCommand(session.id, {\n id: `cmd_${crypto.randomUUID().slice(0, 8)}`,\n type: 'navigate',\n params: { url: note.route },\n });\n // Wait for navigation observation window\n await new Promise((r) => setTimeout(r, options.observationWindowMs || 1000));\n }\n\n const checks: Array<{ type: string; passed: boolean; details?: string }> = [];\n\n checks.push({\n type: 'route_loaded',\n passed: true,\n details: `Route ${note.route || '/'} loaded`,\n });\n\n // 4. Target element checks\n const targetSelector = note.target?.selector || (note.type === 'page' ? 'body' : 'body');\n let elementExists = true;\n let elementVisible = true;\n let currentRect: DOMRectJson | undefined;\n let overflowResult: OverflowCheckResult | undefined;\n\n if (note.type === 'element' && targetSelector) {\n const queryCmd = await this.sessionManager.sendCommand(session.id, {\n id: `cmd_${crypto.randomUUID().slice(0, 8)}`,\n type: 'query_element',\n params: { selector: targetSelector },\n });\n\n elementExists = queryCmd.ok && !!queryCmd.result?.exists;\n elementVisible = queryCmd.ok && !!queryCmd.result?.visible;\n currentRect = queryCmd.result?.boundingRect;\n\n checks.push({\n type: 'element_exists',\n passed: elementExists,\n details: elementExists ? `Element ${targetSelector} exists in DOM` : `Element ${targetSelector} not found`,\n });\n\n checks.push({\n type: 'element_visible',\n passed: elementVisible,\n details: elementVisible ? `Element ${targetSelector} is visible` : `Element ${targetSelector} is hidden`,\n });\n\n // 5. Layout Overflow Probe\n const overflowCmd = await this.sessionManager.sendCommand(session.id, {\n id: `cmd_${crypto.randomUUID().slice(0, 8)}`,\n type: 'check_overflow',\n params: { selector: targetSelector },\n });\n\n if (overflowCmd.ok && overflowCmd.result) {\n const ovf = overflowCmd.result as OverflowCheckResult;\n overflowResult = ovf;\n const isOverflowingNow = !!ovf.overflow;\n\n checks.push({\n type: 'no_viewport_overflow',\n passed: !isOverflowingNow,\n details: !isOverflowingNow\n ? `Element fits within viewport (width: ${ovf.viewportWidth}px, rect right: ${ovf.rect?.right}px)`\n : `Element overflows viewport by ${ovf.overflowRightPx}px (rect right: ${ovf.rect?.right}px, viewport: ${ovf.viewportWidth}px)`,\n });\n }\n }\n\n // 6. Capture \"after\" screenshot\n let afterScreenshotPath: string | undefined;\n const captureCmd = await this.sessionManager.sendCommand(session.id, {\n id: `cmd_${crypto.randomUUID().slice(0, 8)}`,\n type: 'capture_element',\n params: { selector: targetSelector },\n });\n\n if (captureCmd.ok && captureCmd.result?.dataUrl) {\n const saved = this.notesEngine.saveNoteScreenshot(note.projectId, note.id, 'after', captureCmd.result.dataUrl);\n if (saved) {\n afterScreenshotPath = saved;\n }\n }\n\n // 7. Calculate Geometry Difference\n const beforeRect = note.target?.boundingRect;\n const overflowFixed = overflowResult ? !overflowResult.overflow : undefined;\n\n const geometryDiff = {\n before: beforeRect,\n current: currentRect || (overflowResult?.rect as DOMRectJson),\n viewportWidth: overflowResult?.viewportWidth || note.viewport.width,\n overflowFixed,\n overflowPx: overflowResult?.overflowRightPx || 0,\n };\n\n // 8. Verdict\n const anyFailed = checks.some((c) => !c.passed);\n let status: NoteStatus = 'VERIFIED';\n\n if (anyFailed) {\n status = 'FAILED';\n } else {\n status = 'VERIFIED';\n }\n\n const verificationResult: NoteVerificationResult = {\n noteId,\n status,\n checks,\n geometryDiff,\n screenshots: {\n before: note.screenshots?.original,\n after: afterScreenshotPath,\n },\n timestamp: new Date().toISOString(),\n message:\n status === 'VERIFIED'\n ? 'Visual note verified: target element is present, visible, and layout checks (including viewport overflow) passed.'\n : 'Visual note verification failed: target element missing, hidden, or still overflowing.',\n };\n\n this.recordNoteVerification(verificationResult, note);\n return verificationResult;\n }\n\n private recordNoteVerification(result: NoteVerificationResult, note: VisualNote): void {\n this.db.updateNoteStatus(note.id, result.status);\n this.db.insertNoteVerification(result);\n }\n}\n","import crypto from 'node:crypto';\nimport type {\n Incident,\n IncidentStatus,\n ProbeResult,\n VerificationProbe,\n VerificationRecipe,\n VerificationResult,\n} from '../../../core/src/index.js';\nimport type { SessionManager } from '../session/manager.js';\nimport type { StorageDB } from '../storage/db.js';\nimport type { ScreenshotStore } from '../storage/screenshot-store.js';\n\nexport class VerificationEngine {\n private db: StorageDB;\n private sessionManager: SessionManager;\n private screenshotStore: ScreenshotStore;\n\n constructor(db: StorageDB, sessionManager: SessionManager, screenshotStore: ScreenshotStore) {\n this.db = db;\n this.sessionManager = sessionManager;\n this.screenshotStore = screenshotStore;\n }\n\n public async verifyIncident(incidentId: string, recipe?: VerificationRecipe): Promise<VerificationResult> {\n const incident = this.db.getIncident(incidentId);\n if (!incident) {\n return {\n incidentId,\n status: 'FAILED',\n checks: [{ type: 'no_incident', passed: false, details: `Incident ${incidentId} not found` }],\n timestamp: new Date().toISOString(),\n message: `Incident ${incidentId} not found in database`,\n };\n }\n\n // 1. Find active session\n let session = this.sessionManager.getActiveSessionForProject(incident.projectId);\n if (!session) {\n session = this.sessionManager.getAnyActiveSession();\n }\n\n if (!session) {\n const result: VerificationResult = {\n incidentId,\n status: 'INCONCLUSIVE',\n checks: [],\n timestamp: new Date().toISOString(),\n message: 'No active browser session currently connected to verify the fix.',\n };\n this.recordVerification(result, incident);\n return result;\n }\n\n // 2. Set status to VERIFYING and record baseline\n this.db.updateIncidentStatus(incidentId, 'VERIFYING');\n const baselineOccurrences = incident.occurrences;\n\n // 3. Initiate browser reload or navigation\n const targetRoute = recipe?.route || incident.route;\n const observationMs = recipe?.observationWindowMs || 2000;\n\n if (recipe?.route && recipe.route !== incident.route) {\n await this.sessionManager.sendCommand(session.id, {\n id: `cmd_${crypto.randomUUID().slice(0, 8)}`,\n type: 'navigate',\n params: { url: recipe.route },\n });\n } else {\n await this.sessionManager.sendCommand(session.id, {\n id: `cmd_${crypto.randomUUID().slice(0, 8)}`,\n type: 'reload',\n params: { force: true },\n });\n }\n\n // 4. Observation window\n await new Promise((resolve) => setTimeout(resolve, observationMs));\n\n // 5. Check if incident re-occurred\n const freshIncident = this.db.getIncident(incidentId);\n const hasReoccurred = freshIncident ? freshIncident.occurrences > baselineOccurrences : false;\n\n const checks: ProbeResult[] = [];\n checks.push({\n type: 'no_incident',\n passed: !hasReoccurred,\n details: hasReoccurred\n ? `Incident reoccurred (${freshIncident?.occurrences} occurrences vs baseline ${baselineOccurrences})`\n : 'No recurring error observed during verification window',\n });\n\n // 6. Run additional probes\n const probes = recipe?.expect || [];\n for (const probe of probes) {\n const probeRes = await this.evaluateProbe(session.id, probe);\n checks.push(probeRes);\n }\n\n // 7. Check target element or last element for after-screenshot\n let afterScreenshotPath: string | undefined;\n const targetSelector = recipe?.targetSelector || incident.lastElement?.selector;\n\n if (targetSelector) {\n const captureCmd = await this.sessionManager.sendCommand(session.id, {\n id: `cmd_${crypto.randomUUID().slice(0, 8)}`,\n type: 'capture_element',\n params: { selector: targetSelector },\n });\n\n if (captureCmd.ok && captureCmd.result?.dataUrl) {\n const saved = this.screenshotStore.saveScreenshot(\n incident.projectId,\n incident.id,\n 'verified',\n captureCmd.result.dataUrl\n );\n if (saved) {\n afterScreenshotPath = saved.filePath;\n }\n }\n }\n\n // 8. Calculate verdict\n let verdict: IncidentStatus = 'VERIFIED';\n const anyCheckFailed = checks.some((c) => !c.passed);\n\n if (anyCheckFailed || hasReoccurred) {\n verdict = 'FAILED';\n } else {\n // Check if incident required an explicit interaction (like button click) that wasn't covered by probes\n const wasInteractionError = incident.breadcrumbs.some((b) => b.type === 'click' || b.type === 'submit');\n if (wasInteractionError && probes.length === 0) {\n verdict = 'INCONCLUSIVE';\n }\n }\n\n const verificationResult: VerificationResult = {\n incidentId,\n status: verdict,\n checks,\n screenshots: {\n before: incident.screenshots?.error,\n after: afterScreenshotPath,\n },\n timestamp: new Date().toISOString(),\n message:\n verdict === 'VERIFIED'\n ? 'Fix verified: Error did not reoccur and all checks passed.'\n : verdict === 'FAILED'\n ? 'Verification failed: Error reoccurred or probe expectation was not met.'\n : 'Verification inconclusive: Error did not appear on reload, but specific interaction may need manual verification or custom probes.',\n };\n\n this.recordVerification(verificationResult, incident);\n return verificationResult;\n }\n\n private async evaluateProbe(sessionId: string, probe: VerificationProbe): Promise<ProbeResult> {\n try {\n switch (probe.type) {\n case 'element_exists': {\n if (!probe.selector) {\n return { type: probe.type, passed: false, details: 'Missing selector in probe' };\n }\n const res = await this.sessionManager.sendCommand(sessionId, {\n id: `cmd_${crypto.randomUUID().slice(0, 8)}`,\n type: 'query_element',\n params: { selector: probe.selector },\n });\n const exists = res.ok && !!res.result?.exists;\n return {\n type: probe.type,\n passed: exists,\n details: exists ? `Element ${probe.selector} exists in DOM` : `Element ${probe.selector} does not exist`,\n };\n }\n\n case 'element_visible': {\n if (!probe.selector) {\n return { type: probe.type, passed: false, details: 'Missing selector in probe' };\n }\n const res = await this.sessionManager.sendCommand(sessionId, {\n id: `cmd_${crypto.randomUUID().slice(0, 8)}`,\n type: 'query_element',\n params: { selector: probe.selector },\n });\n const visible = res.ok && !!res.result?.exists && !!res.result?.visible;\n return {\n type: probe.type,\n passed: visible,\n details: visible ? `Element ${probe.selector} is visible` : `Element ${probe.selector} is not visible`,\n };\n }\n\n case 'text_contains': {\n if (!probe.selector || !probe.text) {\n return { type: probe.type, passed: false, details: 'Missing selector or text in probe' };\n }\n const res = await this.sessionManager.sendCommand(sessionId, {\n id: `cmd_${crypto.randomUUID().slice(0, 8)}`,\n type: 'query_element',\n params: { selector: probe.selector },\n });\n const innerText = res.result?.innerText || '';\n const contains = res.ok && innerText.includes(probe.text);\n return {\n type: probe.type,\n passed: contains,\n details: contains\n ? `Element ${probe.selector} contains text \"${probe.text}\"`\n : `Element text \"${innerText}\" did not contain \"${probe.text}\"`,\n };\n }\n\n case 'route_is': {\n if (!probe.route) {\n return { type: probe.type, passed: false, details: 'Missing route in probe' };\n }\n const res = await this.sessionManager.sendCommand(sessionId, {\n id: `cmd_${crypto.randomUUID().slice(0, 8)}`,\n type: 'get_page_state',\n });\n const currentRoute = res.result?.route || '';\n const matches = res.ok && (currentRoute === probe.route || currentRoute.startsWith(probe.route));\n return {\n type: probe.type,\n passed: matches,\n details: matches\n ? `Current route matches ${probe.route}`\n : `Current route is \"${currentRoute}\", expected \"${probe.route}\"`,\n };\n }\n\n default:\n return {\n type: probe.type,\n passed: true,\n details: `Probe ${probe.type} evaluated`,\n };\n }\n } catch (err: any) {\n return {\n type: probe.type,\n passed: false,\n details: err?.message || 'Probe execution error',\n };\n }\n }\n\n private recordVerification(result: VerificationResult, incident: Incident): void {\n this.db.updateIncidentStatus(incident.id, result.status);\n this.db.insertVerification({\n id: `ver_${crypto.randomUUID().slice(0, 8)}`,\n incidentId: incident.id,\n status: result.status,\n checks: result.checks,\n beforeScreenshot: result.screenshots?.before,\n afterScreenshot: result.screenshots?.after,\n message: result.message,\n createdAt: result.timestamp,\n });\n }\n}\n"],"mappings":";AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AAYV,SAAS,gBAAgB,YAAmC,CAAC,GAAiB;AACnF,QAAM,UAAU,GAAG,QAAQ;AAC3B,QAAM,UAAU,UAAU,WAAW,KAAK,KAAK,SAAS,eAAe;AACvE,QAAM,SAAS,UAAU,UAAU,KAAK,KAAK,SAAS,iBAAiB;AACvE,QAAM,iBAAiB,UAAU,kBAAkB,KAAK,KAAK,SAAS,UAAU;AAEhF,SAAO;AAAA,IACL,MAAM,UAAU,QAAQ;AAAA,IACxB,MAAM,UAAU,QAAQ;AAAA,IACxB;AAAA,IACA;AAAA,IACA;AAAA,IACA,qBAAqB,UAAU,uBAAuB;AAAA,IACtD,SAAS,CAAC,CAAC,UAAU;AAAA,EACvB;AACF;;;AC5BA,OAAO,cAAc;AACrB,OAAO,QAAQ;AACf,OAAOA,WAAU;AAmBV,IAAM,YAAN,MAAgB;AAAA,EACb;AAAA,EAER,YAAY,QAAgB;AAC1B,UAAM,MAAMA,MAAK,QAAQ,MAAM;AAC/B,OAAG,UAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAErC,SAAK,KAAK,IAAI,SAAS,MAAM;AAC7B,SAAK,GAAG,OAAO,oBAAoB;AACnC,SAAK,GAAG,OAAO,sBAAsB;AACrC,SAAK,WAAW;AAAA,EAClB;AAAA,EAEQ,aAAmB;AACzB,SAAK,GAAG,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAiIZ;AAGD,QAAI;AACF,WAAK,GAAG,KAAK,gDAAgD;AAAA,IAC/D,QAAQ;AAAA,IAAC;AACT,QAAI;AACF,WAAK,GAAG,KAAK,mDAAmD;AAAA,IAClE,QAAQ;AAAA,IAAC;AACT,QAAI;AACF,WAAK,GAAG,KAAK,mDAAmD;AAAA,IAClE,QAAQ;AAAA,IAAC;AAAA,EACX;AAAA;AAAA,EAGO,cAAc,SAA+E;AAClG,UAAM,OAAM,oBAAI,KAAK,GAAE,YAAY;AACnC,UAAM,WAAW,KAAK,GAAG,QAAQ,qDAAqD,EAAE,IAAI,QAAQ,MAAM,QAAQ,MAAM;AAExH,QAAI,UAAU;AACZ,WAAK,GACF,QAAQ,iGAAiG,EACzG,IAAI,QAAQ,MAAM,QAAQ,QAAQ,QAAQ,QAAQ,MAAM,KAAK,SAAS,EAAE;AAC3E,aAAO;AAAA,QACL,IAAI,SAAS;AAAA,QACb,MAAM,QAAQ;AAAA,QACd,QAAQ,QAAQ;AAAA,QAChB,MAAM,QAAQ,QAAQ,SAAS;AAAA,QAC/B,WAAW,SAAS;AAAA,QACpB,WAAW;AAAA,MACb;AAAA,IACF;AAEA,SAAK,GACF,QAAQ,iGAAiG,EACzG,IAAI,QAAQ,IAAI,QAAQ,MAAM,QAAQ,QAAQ,QAAQ,QAAQ,MAAM,KAAK,GAAG;AAE/E,WAAO;AAAA,MACL,IAAI,QAAQ;AAAA,MACZ,MAAM,QAAQ;AAAA,MACd,QAAQ,QAAQ;AAAA,MAChB,MAAM,QAAQ;AAAA,MACd,WAAW;AAAA,MACX,WAAW;AAAA,IACb;AAAA,EACF;AAAA,EAEO,WAAW,UAAkC;AAClD,UAAM,MAAM,KAAK,GAAG,QAAQ,iDAAiD,EAAE,IAAI,UAAU,QAAQ;AACrG,QAAI,CAAC,IAAK,QAAO;AACjB,WAAO;AAAA,MACL,IAAI,IAAI;AAAA,MACR,MAAM,IAAI;AAAA,MACV,QAAQ,IAAI;AAAA,MACZ,MAAM,IAAI;AAAA,MACV,WAAW,IAAI;AAAA,MACf,WAAW,IAAI;AAAA,IACjB;AAAA,EACF;AAAA,EAEO,mBAAmB,QAAgC;AACxD,UAAM,MAAM,KAAK,GAAG,QAAQ,yCAAyC,EAAE,IAAI,MAAM;AACjF,QAAI,CAAC,IAAK,QAAO;AACjB,WAAO;AAAA,MACL,IAAI,IAAI;AAAA,MACR,MAAM,IAAI;AAAA,MACV,QAAQ,IAAI;AAAA,MACZ,MAAM,IAAI;AAAA,MACV,WAAW,IAAI;AAAA,MACf,WAAW,IAAI;AAAA,IACjB;AAAA,EACF;AAAA,EAEO,eAA0B;AAC/B,UAAM,OAAO,KAAK,GAAG,QAAQ,iDAAiD,EAAE,IAAI;AACpF,WAAO,KAAK,IAAI,CAAC,SAAS;AAAA,MACxB,IAAI,IAAI;AAAA,MACR,MAAM,IAAI;AAAA,MACV,QAAQ,IAAI;AAAA,MACZ,MAAM,IAAI;AAAA,MACV,WAAW,IAAI;AAAA,MACf,WAAW,IAAI;AAAA,IACjB,EAAE;AAAA,EACJ;AAAA;AAAA,EAGO,cAAc,SAAwB;AAC3C,UAAM,WAAW,KAAK,GAAG,QAAQ,sCAAsC,EAAE,IAAI,QAAQ,EAAE;AACvF,QAAI,UAAU;AACZ,WAAK,GACF,QAAQ,mFAAmF,EAC3F,IAAI,QAAQ,KAAK,QAAQ,OAAO,QAAQ,YAAY,QAAQ,SAAS,IAAI,GAAG,QAAQ,EAAE;AAAA,IAC3F,OAAO;AACL,WAAK,GACF;AAAA,QACC;AAAA,MACF,EACC;AAAA,QACC,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,QAAQ,SAAS,IAAI;AAAA,MACvB;AAAA,IACJ;AAAA,EACF;AAAA,EAEO,WAAW,IAA4B;AAC5C,UAAM,MAAM,KAAK,GAAG,QAAQ,qCAAqC,EAAE,IAAI,EAAE;AACzE,QAAI,CAAC,IAAK,QAAO;AACjB,WAAO;AAAA,MACL,IAAI,IAAI;AAAA,MACR,WAAW,IAAI;AAAA,MACf,QAAQ,IAAI;AAAA,MACZ,KAAK,IAAI;AAAA,MACT,OAAO,IAAI;AAAA,MACX,WAAW,IAAI;AAAA,MACf,aAAa,IAAI;AAAA,MACjB,YAAY,IAAI;AAAA,MAChB,QAAQ,IAAI,WAAW;AAAA,IACzB;AAAA,EACF;AAAA,EAEO,aAAa,WAAoB,aAAa,OAAkB;AACrE,QAAI,MAAM;AACV,UAAM,SAAgB,CAAC;AAEvB,QAAI,WAAW;AACb,aAAO;AACP,aAAO,KAAK,SAAS;AAAA,IACvB;AACA,QAAI,YAAY;AACd,aAAO;AAAA,IACT;AACA,WAAO;AAEP,UAAM,OAAO,KAAK,GAAG,QAAQ,GAAG,EAAE,IAAI,GAAG,MAAM;AAC/C,WAAO,KAAK,IAAI,CAAC,SAAS;AAAA,MACxB,IAAI,IAAI;AAAA,MACR,WAAW,IAAI;AAAA,MACf,QAAQ,IAAI;AAAA,MACZ,KAAK,IAAI;AAAA,MACT,OAAO,IAAI;AAAA,MACX,WAAW,IAAI;AAAA,MACf,aAAa,IAAI;AAAA,MACjB,YAAY,IAAI;AAAA,MAChB,QAAQ,IAAI,WAAW;AAAA,IACzB,EAAE;AAAA,EACJ;AAAA,EAEO,kBAAkB,IAAkB;AACzC,SAAK,GAAG,QAAQ,+DAA+D,EAAE,KAAI,oBAAI,KAAK,GAAE,YAAY,GAAG,EAAE;AAAA,EACnH;AAAA;AAAA,EAGO,YAAY,OAQV;AACP,SAAK,GACF,QAAQ,8GAA8G,EACtH;AAAA,MACC,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,MACN,KAAK,UAAU,MAAM,OAAO;AAAA,MAC5B,MAAM;AAAA,MACN,MAAM,SAAS;AAAA,MACf,MAAM,OAAO;AAAA,IACf;AAAA,EACJ;AAAA,EAEO,mBAAmB,WAAmB,YAAY,KAAY;AACnE,SAAK,GACF;AAAA,MACC;AAAA;AAAA;AAAA;AAAA,IAIF,EACC,IAAI,WAAW,WAAW,SAAS;AAAA,EACxC;AAAA,EAEO,UAAU,SAA4E;AAC3F,QAAI,MAAM;AACV,UAAM,SAAgB,CAAC;AAEvB,QAAI,QAAQ,WAAW;AACrB,aAAO;AACP,aAAO,KAAK,QAAQ,SAAS;AAAA,IAC/B;AACA,QAAI,QAAQ,WAAW;AACrB,aAAO;AACP,aAAO,KAAK,QAAQ,SAAS;AAAA,IAC/B;AAEA,WAAO;AACP,WAAO,KAAK,QAAQ,SAAS,EAAE;AAE/B,UAAM,OAAO,KAAK,GAAG,QAAQ,GAAG,EAAE,IAAI,GAAG,MAAM;AAC/C,WAAO,KAAK,IAAI,CAAC,OAAO;AAAA,MACtB,IAAI,EAAE;AAAA,MACN,WAAW,EAAE;AAAA,MACb,WAAW,EAAE;AAAA,MACb,SAAS,KAAK,MAAM,EAAE,OAAO;AAAA,MAC7B,WAAW,EAAE;AAAA,MACb,OAAO,EAAE;AAAA,MACT,KAAK,EAAE;AAAA,IACT,EAAE;AAAA,EACJ;AAAA;AAAA,EAGO,0BAA0B,aAAsC;AACrE,UAAM,MAAM,KAAK,GAAG,QAAQ,+CAA+C,EAAE,IAAI,WAAW;AAC5F,QAAI,CAAC,IAAK,QAAO;AACjB,WAAO,KAAK,eAAe,GAAG;AAAA,EAChC;AAAA,EAEO,YAAY,IAA6B;AAC9C,UAAM,MAAM,KAAK,GAAG,QAAQ,sCAAsC,EAAE,IAAI,EAAE;AAC1E,QAAI,CAAC,IAAK,QAAO;AACjB,WAAO,KAAK,eAAe,GAAG;AAAA,EAChC;AAAA,EAEO,cAAc,UAAwG,CAAC,GAAe;AAC3I,QAAI,MAAM;AACV,UAAM,SAAgB,CAAC;AAEvB,QAAI,QAAQ,WAAW;AACrB,aAAO;AACP,aAAO,KAAK,QAAQ,SAAS;AAAA,IAC/B;AACA,QAAI,QAAQ,QAAQ;AAClB,aAAO;AACP,aAAO,KAAK,QAAQ,MAAM;AAAA,IAC5B;AACA,QAAI,QAAQ,UAAU;AACpB,aAAO;AACP,aAAO,KAAK,QAAQ,QAAQ;AAAA,IAC9B;AAEA,WAAO;AACP,WAAO,KAAK,QAAQ,SAAS,EAAE;AAE/B,UAAM,OAAO,KAAK,GAAG,QAAQ,GAAG,EAAE,IAAI,GAAG,MAAM;AAC/C,WAAO,KAAK,IAAI,CAAC,MAAM,KAAK,eAAe,CAAC,CAAC;AAAA,EAC/C;AAAA,EAEO,eAAe,UAA0B;AAC9C,SAAK,GACF;AAAA,MACC;AAAA;AAAA;AAAA;AAAA;AAAA,IAKF,EACC;AAAA,MACC,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS,OAAO;AAAA,MAChB,SAAS,OAAO;AAAA,MAChB,SAAS,OAAO,UAAU;AAAA,MAC1B,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS,SAAS;AAAA,MAClB,KAAK,UAAU,SAAS,eAAe,CAAC,CAAC;AAAA,MACzC,KAAK,UAAU,SAAS,mBAAmB,CAAC,CAAC;AAAA,MAC7C,SAAS,cAAc,KAAK,UAAU,SAAS,WAAW,IAAI;AAAA,MAC9D,SAAS,aAAa,SAAS;AAAA,IACjC;AAAA,EACJ;AAAA,EAEO,yBACL,YACA,QASM;AACN,SAAK,GACF;AAAA,MACC;AAAA;AAAA;AAAA,IAGF,EACC;AAAA,MACC,OAAO;AAAA,MACP,OAAO;AAAA,MACP,OAAO;AAAA,MACP,OAAO;AAAA,MACP,KAAK,UAAU,OAAO,eAAe,CAAC,CAAC;AAAA,MACvC,OAAO,cAAc,KAAK,UAAU,OAAO,WAAW,IAAI;AAAA,MAC1D,OAAO,SAAS;AAAA,MAChB;AAAA,IACF;AAAA,EACJ;AAAA,EAEO,qBAAqB,IAAY,QAA8B;AACpE,SAAK,GAAG,QAAQ,8CAA8C,EAAE,IAAI,QAAQ,EAAE;AAAA,EAChF;AAAA,EAEO,yBAAyB,YAAsC;AACpE,SAAK,GACF;AAAA,MACC;AAAA;AAAA,IAEF,EACC;AAAA,MACC,WAAW;AAAA,MACX,WAAW;AAAA,MACX,WAAW;AAAA,MACX,WAAW;AAAA,MACX,WAAW;AAAA,MACX,WAAW;AAAA,MACX,WAAW,SAAS;AAAA,MACpB,KAAK,UAAU,WAAW,eAAe,CAAC,CAAC;AAAA,MAC3C,WAAW,cAAc,KAAK,UAAU,WAAW,WAAW,IAAI;AAAA,IACpE;AAAA,EACJ;AAAA;AAAA,EAGO,mBAAmB,GASjB;AACP,SAAK,GACF;AAAA,MACC;AAAA;AAAA,IAEF,EACC;AAAA,MACC,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,KAAK,UAAU,EAAE,MAAM;AAAA,MACvB,EAAE,oBAAoB;AAAA,MACtB,EAAE,mBAAmB;AAAA,MACrB,EAAE,WAAW;AAAA,MACb,EAAE;AAAA,IACJ;AAAA,EACJ;AAAA,EAEO,sBAAsB,YAA+C;AAC1E,UAAM,MAAM,KAAK,GAAG,QAAQ,oFAAoF,EAAE,IAAI,UAAU;AAChI,QAAI,CAAC,IAAK,QAAO;AACjB,WAAO;AAAA,MACL,YAAY,IAAI;AAAA,MAChB,QAAQ,IAAI;AAAA,MACZ,QAAQ,KAAK,MAAM,IAAI,UAAU,IAAI;AAAA,MACrC,aAAa;AAAA,QACX,QAAQ,IAAI,qBAAqB;AAAA,QACjC,OAAO,IAAI,oBAAoB;AAAA,MACjC;AAAA,MACA,WAAW,IAAI;AAAA,MACf,SAAS,IAAI,WAAW;AAAA,IAC1B;AAAA,EACF;AAAA;AAAA,EAGO,WAAW,MAAwB;AACxC,SAAK,GACF;AAAA,MACC;AAAA;AAAA;AAAA;AAAA;AAAA,IAKF,EACC;AAAA,MACC,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK,UAAU,KAAK,QAAQ;AAAA,MAC5B,KAAK,UAAU,KAAK,MAAM;AAAA,MAC1B,KAAK,SAAS,KAAK,UAAU,KAAK,MAAM,IAAI;AAAA,MAC5C,KAAK,iBAAiB,KAAK,UAAU,KAAK,cAAc,IAAI;AAAA,MAC5D,KAAK,SAAS,KAAK,UAAU,KAAK,MAAM,IAAI;AAAA,MAC5C,KAAK,aAAa,YAAY;AAAA,MAC9B,KAAK,cAAc;AAAA,MACnB,KAAK,cAAc;AAAA,MACnB,KAAK,cAAc;AAAA,MACnB,KAAK,iBAAiB;AAAA,MACtB,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK,cAAc;AAAA,IACrB;AAAA,EACJ;AAAA,EAEO,QAAQ,IAA+B;AAC5C,UAAM,MAAM,KAAK,GAAG,QAAQ,kCAAkC,EAAE,IAAI,EAAE;AACtE,QAAI,CAAC,IAAK,QAAO;AACjB,WAAO,KAAK,WAAW,GAAG;AAAA,EAC5B;AAAA,EAEO,UAAU,UAA4G,CAAC,GAAiB;AAC7I,QAAI,MAAM;AACV,UAAM,SAAgB,CAAC;AAEvB,QAAI,QAAQ,WAAW;AACrB,aAAO;AACP,aAAO,KAAK,QAAQ,SAAS;AAAA,IAC/B;AACA,QAAI,QAAQ,QAAQ;AAClB,aAAO;AACP,aAAO,KAAK,QAAQ,MAAM;AAAA,IAC5B;AACA,QAAI,QAAQ,OAAO;AACjB,aAAO;AACP,aAAO,KAAK,QAAQ,KAAK;AAAA,IAC3B;AACA,QAAI,QAAQ,YAAY;AACtB,aAAO;AACP,aAAO,KAAK,QAAQ,UAAU;AAAA,IAChC;AAEA,WAAO;AACP,WAAO,KAAK,QAAQ,SAAS,EAAE;AAE/B,UAAM,OAAO,KAAK,GAAG,QAAQ,GAAG,EAAE,IAAI,GAAG,MAAM;AAC/C,WAAO,KAAK,IAAI,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC;AAAA,EAC3C;AAAA,EAEO,WAAW,IAAkB;AAClC,SAAK,GAAG,QAAQ,kDAAkD,EAAE,IAAI,EAAE;AAC1E,SAAK,GAAG,QAAQ,gCAAgC,EAAE,IAAI,EAAE;AAAA,EAC1D;AAAA;AAAA,EAGO,cAAc,UAAuE,CAAC,GAAuB;AAClH,QAAI,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAaV,UAAM,SAAgB,CAAC;AACvB,QAAI,QAAQ,WAAW;AACrB,aAAO;AACP,aAAO,KAAK,QAAQ,SAAS;AAAA,IAC/B;AACA,WAAO;AAEP,QAAI,QAAQ,QAAQ;AAClB,UAAI,QAAQ,WAAW,YAAY;AACjC,eAAO;AAAA,MACT,OAAO;AACL,eAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO;AACP,WAAO,KAAK,QAAQ,SAAS,EAAE;AAE/B,UAAM,OAAO,KAAK,GAAG,QAAQ,GAAG,EAAE,IAAI,GAAG,MAAM;AAC/C,WAAO,KAAK,IAAI,CAAC,OAAO;AAAA,MACtB,IAAI,EAAE;AAAA,MACN,WAAW,EAAE;AAAA,MACb,OAAO,EAAE;AAAA,MACT,YAAY,OAAO,EAAE,WAAW;AAAA,MAChC,QAAQ,EAAE;AAAA,MACV,OAAO,EAAE,SAAS;AAAA,MAClB,aAAa,EAAE;AAAA,MACf,YAAY,EAAE;AAAA,IAChB,EAAE;AAAA,EACJ;AAAA,EAEO,YAAY,YAA2C;AAC5D,UAAM,OAAO,KAAK,GACf,QAAQ,sGAAsG,EAC9G,IAAI,UAAU;AAEjB,QAAI,CAAC,QAAQ,KAAK,WAAW,EAAG,QAAO;AAEvC,UAAM,QAAQ,KAAK,IAAI,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC;AAChD,UAAM,QAAQ,MAAM,KAAK,CAAC,MAAM,EAAE,aAAa,GAAG,iBAAiB,YAAY,UAAU;AACzF,UAAM,cAAc,MAAM,MAAM,CAAC,MAAM,EAAE,WAAW,UAAU;AAE9D,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,WAAW,MAAM,CAAC,EAAE;AAAA,MACpB;AAAA,MACA,YAAY,MAAM;AAAA,MAClB,QAAQ,cAAc,aAAa;AAAA,MACnC;AAAA,MACA,WAAW,MAAM,CAAC,EAAE;AAAA,MACpB,WAAW,MAAM,MAAM,SAAS,CAAC,EAAE;AAAA,IACrC;AAAA,EACF;AAAA,EAEO,eAAe,YAA0B;AAC9C,UAAM,QAAQ,KAAK,GAAG,QAAQ,4CAA4C,EAAE,IAAI,UAAU;AAC1F,eAAW,KAAK,OAAO;AACrB,WAAK,WAAW,EAAE,EAAE;AAAA,IACtB;AAAA,EACF;AAAA,EAEO,iBAAiB,IAAY,QAA0B;AAC5D,UAAM,OAAM,oBAAI,KAAK,GAAE,YAAY;AACnC,UAAM,aAAa,WAAW,aAAa,MAAM;AACjD,SAAK,GACF,QAAQ,kGAAkG,EAC1G,IAAI,QAAQ,KAAK,YAAY,EAAE;AAAA,EACpC;AAAA,EAEO,WAAW,IAAY,SAAoC;AAChE,UAAM,OAAM,oBAAI,KAAK,GAAE,YAAY;AACnC,UAAM,UAAU,KAAK,QAAQ,EAAE;AAC/B,QAAI,CAAC,QAAS;AAEd,SAAK,GACF;AAAA,MACC;AAAA;AAAA;AAAA;AAAA,IAIF,EACC;AAAA,MACC,QAAQ,WAAW;AAAA,MACnB,QAAQ,UAAU;AAAA,MAClB,QAAQ,aAAa,YAAY,QAAQ,aAAa,SAAS;AAAA,MAC/D;AAAA,MACA;AAAA,IACF;AAAA,EACJ;AAAA,EAEO,uBAAuB,GAAiC;AAC7D,SAAK,GACF;AAAA,MACC;AAAA;AAAA;AAAA,IAGF,EACC;AAAA,MACC,QAAQ,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAAA,MAC5D,EAAE;AAAA,MACF,EAAE;AAAA,MACF,KAAK,UAAU,EAAE,MAAM;AAAA,MACvB,EAAE,eAAe,KAAK,UAAU,EAAE,YAAY,IAAI;AAAA,MAClD,EAAE,aAAa,UAAU;AAAA,MACzB,EAAE,aAAa,SAAS;AAAA,MACxB,EAAE,WAAW;AAAA,MACb,EAAE;AAAA,IACJ;AAAA,EACJ;AAAA,EAEO,0BAA0B,QAA+C;AAC9E,UAAM,MAAM,KAAK,GAAG,QAAQ,qFAAqF,EAAE,IAAI,MAAM;AAC7H,QAAI,CAAC,IAAK,QAAO;AACjB,WAAO;AAAA,MACL,QAAQ,IAAI;AAAA,MACZ,QAAQ,IAAI;AAAA,MACZ,QAAQ,KAAK,MAAM,IAAI,UAAU,IAAI;AAAA,MACrC,cAAc,IAAI,gBAAgB,KAAK,MAAM,IAAI,aAAa,IAAI;AAAA,MAClE,aAAa;AAAA,QACX,QAAQ,IAAI,qBAAqB;AAAA,QACjC,OAAO,IAAI,oBAAoB;AAAA,MACjC;AAAA,MACA,WAAW,IAAI;AAAA,MACf,SAAS,IAAI,WAAW;AAAA,IAC1B;AAAA,EACF;AAAA,EAEO,WAAiB;AACtB,SAAK,GAAG,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KASZ;AAAA,EACH;AAAA,EAEQ,eAAe,KAAoB;AACzC,WAAO;AAAA,MACL,IAAI,IAAI;AAAA,MACR,WAAW,IAAI;AAAA,MACf,WAAW,IAAI;AAAA,MACf,MAAM,IAAI;AAAA,MACV,UAAU,IAAI;AAAA,MACd,SAAS,IAAI;AAAA,MACb,QAAQ;AAAA,QACN,MAAM,IAAI;AAAA,QACV,MAAM,IAAI;AAAA,QACV,QAAQ,IAAI,cAAc;AAAA,MAC5B;AAAA,MACA,aAAa,IAAI;AAAA,MACjB,OAAO,IAAI;AAAA,MACX,WAAW,IAAI;AAAA,MACf,UAAU,IAAI;AAAA,MACd,aAAa,IAAI;AAAA,MACjB,QAAQ,IAAI;AAAA,MACZ,OAAO,IAAI,SAAS;AAAA,MACpB,aAAa,KAAK,MAAM,IAAI,eAAe,IAAI;AAAA,MAC/C,iBAAiB,KAAK,MAAM,IAAI,oBAAoB,IAAI;AAAA,MACxD,aAAa,IAAI,eAAe,KAAK,MAAM,IAAI,YAAY,IAAI;AAAA,MAC/D,aAAa,IAAI,kBACb;AAAA,QACE,OAAO,IAAI;AAAA,MACb,IACA;AAAA,IACN;AAAA,EACF;AAAA,EAEQ,WAAW,KAAsB;AACvC,WAAO;AAAA,MACL,IAAI,IAAI;AAAA,MACR,WAAW,IAAI;AAAA,MACf,WAAW,IAAI;AAAA,MACf,MAAM,IAAI;AAAA,MACV,SAAS,IAAI;AAAA,MACb,OAAO,IAAI;AAAA,MACX,KAAK,IAAI;AAAA,MACT,UAAU,KAAK,MAAM,IAAI,iBAAiB,IAAI;AAAA,MAC9C,QAAQ,KAAK,MAAM,IAAI,eAAe,IAAI;AAAA,MAC1C,QAAQ,IAAI,cAAc,KAAK,MAAM,IAAI,WAAW,IAAI;AAAA,MACxD,gBAAgB,IAAI,uBAAuB,KAAK,MAAM,IAAI,oBAAoB,IAAI;AAAA,MAClF,QAAQ,IAAI,cAAc,KAAK,MAAM,IAAI,WAAW,IAAI;AAAA,MACxD,QAAQ,IAAI;AAAA,MACZ,YAAY,IAAI,eAAe;AAAA,MAC/B,YAAY,IAAI,eAAe;AAAA,MAC/B,YAAY,IAAI,eAAe,OAAO,OAAO,IAAI,WAAW,IAAI;AAAA,MAChE,eAAe,IAAI,kBAAkB;AAAA,MACrC,aAAa,IAAI,kBACb;AAAA,QACE,UAAU,IAAI;AAAA,MAChB,IACA;AAAA,MACJ,WAAW,IAAI;AAAA,MACf,WAAW,IAAI;AAAA,MACf,YAAY,IAAI,eAAe;AAAA,IACjC;AAAA,EACF;AAAA,EAEO,QAAc;AACnB,SAAK,GAAG,MAAM;AAAA,EAChB;AACF;;;AC50BA,OAAOC,SAAQ;AACf,OAAOC,WAAU;AAEV,IAAM,kBAAN,MAAsB;AAAA,EACnB;AAAA,EAER,YAAY,SAAiB;AAC3B,SAAK,UAAU;AACf,IAAAD,IAAG,UAAU,KAAK,SAAS,EAAE,WAAW,KAAK,CAAC;AAAA,EAChD;AAAA,EAEO,eAAe,WAAmB,YAAoB,MAAc,SAA8D;AACvI,QAAI;AACF,UAAI,CAAC,WAAW,CAAC,QAAQ,WAAW,aAAa,EAAG,QAAO;AAE3D,YAAM,QAAQ,QAAQ,MAAM,2CAA2C;AACvE,UAAI,CAAC,MAAO,QAAO;AAEnB,UAAI,SAAS,MAAM,CAAC,EAAE,YAAY;AAClC,UAAI,WAAW,OAAQ,UAAS;AAChC,YAAM,aAAa,MAAM,CAAC;AAC1B,YAAM,SAAS,OAAO,KAAK,YAAY,QAAQ;AAE/C,YAAM,YAAYC,MAAK,KAAK,KAAK,SAAS,aAAa,WAAW,aAAa,UAAU;AACzF,MAAAD,IAAG,UAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AAE3C,YAAM,WAAW,GAAG,IAAI,IAAI,MAAM;AAClC,YAAM,WAAWC,MAAK,KAAK,WAAW,QAAQ;AAE9C,MAAAD,IAAG,cAAc,UAAU,MAAM;AAEjC,aAAO,EAAE,UAAU,OAAO;AAAA,IAC5B,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEO,kBAAkB,WAAmB,YAAoB,MAA6B;AAC3F,UAAM,YAAYC,MAAK,KAAK,KAAK,SAAS,aAAa,WAAW,aAAa,UAAU;AACzF,QAAI,CAACD,IAAG,WAAW,SAAS,EAAG,QAAO;AAEtC,UAAM,QAAQA,IAAG,YAAY,SAAS;AACtC,UAAM,QAAQ,MAAM,KAAK,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,GAAG,CAAC;AACxD,QAAI,OAAO;AACT,aAAOC,MAAK,KAAK,WAAW,KAAK;AAAA,IACnC;AACA,WAAO;AAAA,EACT;AACF;;;AC7BO,IAAM,iBAAN,MAAqB;AAAA,EAClB,gBAAkD,oBAAI,IAAI;AAAA,EAC1D;AAAA,EAER,YAAY,IAAe;AACzB,SAAK,KAAK;AAAA,EACZ;AAAA,EAEO,eAAe,WAAmB,IAAe,QAAgB,WAAyB;AAC/F,SAAK,cAAc,IAAI,WAAW;AAAA,MAChC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,iBAAiB,oBAAI,IAAI;AAAA,IAC3B,CAAC;AAAA,EACH;AAAA,EAEO,iBAAiB,WAAyB;AAC/C,UAAM,SAAS,KAAK,cAAc,IAAI,SAAS;AAC/C,QAAI,QAAQ;AACV,iBAAW,WAAW,OAAO,gBAAgB,OAAO,GAAG;AACrD,qBAAa,QAAQ,KAAK;AAC1B,gBAAQ,OAAO,IAAI,MAAM,+CAA+C,CAAC;AAAA,MAC3E;AACA,aAAO,gBAAgB,MAAM;AAC7B,WAAK,cAAc,OAAO,SAAS;AACnC,WAAK,GAAG,kBAAkB,SAAS;AAAA,IACrC;AAAA,EACF;AAAA,EAEO,sBAAsB,WAAmB,UAAiC;AAC/E,UAAM,SAAS,KAAK,cAAc,IAAI,SAAS;AAC/C,QAAI,CAAC,OAAQ;AAEb,UAAM,UAAU,OAAO,gBAAgB,IAAI,SAAS,EAAE;AACtD,QAAI,SAAS;AACX,mBAAa,QAAQ,KAAK;AAC1B,aAAO,gBAAgB,OAAO,SAAS,EAAE;AACzC,cAAQ,QAAQ,QAAQ;AAAA,IAC1B;AAAA,EACF;AAAA,EAEA,MAAa,YAAqB,WAAmB,SAA2B,YAAY,KAAgC;AAC1H,UAAM,SAAS,KAAK,cAAc,IAAI,SAAS;AAC/C,QAAI,CAAC,UAAU,OAAO,GAAG,eAAe,GAAwB;AAC9D,aAAO;AAAA,QACL,IAAI,QAAQ;AAAA,QACZ,IAAI;AAAA,QACJ,OAAO,WAAW,SAAS;AAAA,MAC7B;AAAA,IACF;AAEA,WAAO,IAAI,QAAyB,CAAC,SAAS,WAAW;AACvD,YAAM,QAAQ,WAAW,MAAM;AAC7B,eAAO,gBAAgB,OAAO,QAAQ,EAAE;AACxC,gBAAQ;AAAA,UACN,IAAI,QAAQ;AAAA,UACZ,IAAI;AAAA,UACJ,OAAO,WAAW,QAAQ,IAAI,oBAAoB,SAAS;AAAA,QAC7D,CAAC;AAAA,MACH,GAAG,SAAS;AAEZ,aAAO,gBAAgB,IAAI,QAAQ,IAAI;AAAA,QACrC;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAED,UAAI;AACF,eAAO,GAAG;AAAA,UACR,KAAK,UAAU;AAAA,YACb,MAAM;AAAA,YACN;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF,SAAS,KAAU;AACjB,qBAAa,KAAK;AAClB,eAAO,gBAAgB,OAAO,QAAQ,EAAE;AACxC,gBAAQ;AAAA,UACN,IAAI,QAAQ;AAAA,UACZ,IAAI;AAAA,UACJ,OAAO,KAAK,WAAW;AAAA,QACzB,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEO,2BAA2B,WAAmC;AACnE,eAAW,UAAU,KAAK,cAAc,OAAO,GAAG;AAChD,UAAI,OAAO,cAAc,WAAW;AAClC,eAAO,KAAK,GAAG,WAAW,OAAO,SAAS;AAAA,MAC5C;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEO,sBAAsC;AAC3C,UAAM,QAAQ,KAAK,cAAc,KAAK,EAAE,KAAK,EAAE;AAC/C,QAAI,CAAC,MAAO,QAAO;AACnB,WAAO,KAAK,GAAG,WAAW,KAAK;AAAA,EACjC;AAAA,EAEO,iBAAyB;AAC9B,WAAO,KAAK,cAAc;AAAA,EAC5B;AAAA,EAEO,mBAAmB,WAAmB,SAAoB;AAC/D,UAAM,MAAM,OAAO,YAAY,WAAW,UAAU,KAAK,UAAU,OAAO;AAC1E,eAAW,UAAU,KAAK,cAAc,OAAO,GAAG;AAChD,UAAI,OAAO,cAAc,aAAa,OAAO,GAAG,eAAe,GAAc;AAC3E,YAAI;AACF,iBAAO,GAAG,KAAK,GAAG;AAAA,QACpB,QAAQ;AAAA,QAAC;AAAA,MACX;AAAA,IACF;AAAA,EACF;AAAA,EAEO,cAAc,WAAmB,SAAoB;AAC1D,UAAM,SAAS,KAAK,cAAc,IAAI,SAAS;AAC/C,QAAI,UAAU,OAAO,GAAG,eAAe,GAAG;AACxC,UAAI;AACF,cAAM,MAAM,OAAO,YAAY,WAAW,UAAU,KAAK,UAAU,OAAO;AAC1E,eAAO,GAAG,KAAK,GAAG;AAAA,MACpB,QAAQ;AAAA,MAAC;AAAA,IACX;AAAA,EACF;AACF;;;AClJA,OAAO,YAAY;AACnB,OAAOC,WAAU;AACjB,OAAOC,SAAQ;AAKR,IAAM,cAAN,MAAkB;AAAA,EACf;AAAA,EACA;AAAA,EAER,YAAY,IAAe,iBAAkC;AAC3D,SAAK,KAAK;AACV,SAAK,kBAAkB;AAAA,EACzB;AAAA,EAEO,qBAAqB,SAgBb;AACb,UAAM,UAAU,KAAK,GAAG,WAAW,QAAQ,SAAS;AACpD,UAAM,YAAY,SAAS,aAAa;AACxC,UAAM,SAAS,QAAQ,OAAO,WAAW,EAAE,MAAM,GAAG,CAAC,CAAC;AACtD,UAAM,OAAM,oBAAI,KAAK,GAAE,YAAY;AAEnC,QAAI;AACJ,QAAI,QAAQ,YAAY;AACtB,YAAM,QAAQ,KAAK,mBAAmB,WAAW,QAAQ,YAAY,QAAQ,UAAU;AACvF,UAAI,OAAO;AACT,yBAAiB;AAAA,MACnB;AAAA,IACF;AAEA,UAAM,OAAmB;AAAA,MACvB,IAAI;AAAA,MACJ;AAAA,MACA,WAAW,QAAQ;AAAA,MACnB,MAAM,QAAQ,YAAY;AAAA,MAC1B,SAAS,QAAQ;AAAA,MACjB,OAAO,QAAQ,SAAS;AAAA,MACxB,KAAK,QAAQ,OAAO;AAAA,MACpB,UAAU,QAAQ,YAAY,EAAE,OAAO,MAAM,QAAQ,KAAK,kBAAkB,EAAE;AAAA,MAC9E,QAAQ,QAAQ,UAAU,EAAE,SAAS,GAAG,SAAS,EAAE;AAAA,MACnD,QAAQ,QAAQ;AAAA,MAChB,gBAAgB,QAAQ;AAAA,MACxB,QAAQ,QAAQ;AAAA,MAChB,QAAQ;AAAA,MACR,YAAY,QAAQ;AAAA,MACpB,YAAY,QAAQ;AAAA,MACpB,YAAY,QAAQ;AAAA,MACpB,eAAe,QAAQ;AAAA,MACvB,aAAa,iBAAiB,EAAE,UAAU,eAAe,IAAI;AAAA,MAC7D,WAAW;AAAA,MACX,WAAW;AAAA,IACb;AAEA,SAAK,GAAG,WAAW,IAAI;AACvB,WAAO;AAAA,EACT;AAAA,EAEO,mBAAmB,WAAmB,QAAgB,MAAc,SAAgC;AACzG,QAAI;AACF,UAAI,CAAC,WAAW,CAAC,QAAQ,WAAW,aAAa,EAAG,QAAO;AAE3D,YAAM,QAAQ,QAAQ,MAAM,2CAA2C;AACvE,UAAI,CAAC,MAAO,QAAO;AAEnB,UAAI,SAAS,MAAM,CAAC,EAAE,YAAY;AAClC,UAAI,WAAW,OAAQ,UAAS;AAChC,YAAM,aAAa,MAAM,CAAC;AAC1B,YAAM,SAAS,OAAO,KAAK,YAAY,QAAQ;AAE/C,YAAM,YAAYD,MAAK,KAAM,KAAK,gBAAwB,SAAS,aAAa,WAAW,SAAS,MAAM;AAC1G,MAAAC,IAAG,UAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AAE3C,YAAM,WAAW,GAAG,IAAI,IAAI,MAAM;AAClC,YAAM,WAAWD,MAAK,KAAK,WAAW,QAAQ;AAE9C,MAAAC,IAAG,cAAc,UAAU,MAAM;AACjC,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;ACjGA,OAAOC,aAAY;AAMZ,IAAM,yBAAN,MAA6B;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,IAAe,gBAAgC,aAA0B;AACnF,SAAK,KAAK;AACV,SAAK,iBAAiB;AACtB,SAAK,cAAc;AAAA,EACrB;AAAA,EAEA,MAAa,WAAW,QAAgB,UAA4C,CAAC,GAAoC;AACvH,UAAM,OAAO,KAAK,GAAG,QAAQ,MAAM;AACnC,QAAI,CAAC,MAAM;AACT,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,QACR,QAAQ,CAAC,EAAE,MAAM,eAAe,QAAQ,OAAO,SAAS,QAAQ,MAAM,aAAa,CAAC;AAAA,QACpF,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,QAClC,SAAS,QAAQ,MAAM;AAAA,MACzB;AAAA,IACF;AAGA,QAAI,UAAU,KAAK,eAAe,2BAA2B,KAAK,SAAS;AAC3E,QAAI,CAAC,SAAS;AACZ,gBAAU,KAAK,eAAe,oBAAoB;AAAA,IACpD;AAEA,QAAI,CAAC,SAAS;AACZ,YAAM,SAAiC;AAAA,QACrC;AAAA,QACA,QAAQ;AAAA,QACR,QAAQ,CAAC;AAAA,QACT,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,QAClC,SAAS;AAAA,MACX;AACA,WAAK,uBAAuB,QAAQ,IAAI;AACxC,aAAO;AAAA,IACT;AAGA,SAAK,GAAG,iBAAiB,QAAQ,WAAW;AAG5C,UAAM,eAAe,MAAM,KAAK,eAAe,YAAY,QAAQ,IAAI;AAAA,MACrE,IAAI,OAAOA,QAAO,WAAW,EAAE,MAAM,GAAG,CAAC,CAAC;AAAA,MAC1C,MAAM;AAAA,IACR,CAAC;AAED,UAAM,eAAe,aAAa,QAAQ,SAAS;AACnD,UAAM,eAAe,iBAAiB,KAAK,SAAS,aAAa,WAAW,KAAK,KAAK;AAEtF,QAAI,CAAC,gBAAgB,KAAK,OAAO;AAC/B,YAAM,KAAK,eAAe,YAAY,QAAQ,IAAI;AAAA,QAChD,IAAI,OAAOA,QAAO,WAAW,EAAE,MAAM,GAAG,CAAC,CAAC;AAAA,QAC1C,MAAM;AAAA,QACN,QAAQ,EAAE,KAAK,KAAK,MAAM;AAAA,MAC5B,CAAC;AAED,YAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,QAAQ,uBAAuB,GAAI,CAAC;AAAA,IAC7E;AAEA,UAAM,SAAqE,CAAC;AAE5E,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,SAAS,SAAS,KAAK,SAAS,GAAG;AAAA,IACrC,CAAC;AAGD,UAAM,iBAAiB,KAAK,QAAQ,aAAa,KAAK,SAAS,SAAS,SAAS;AACjF,QAAI,gBAAgB;AACpB,QAAI,iBAAiB;AACrB,QAAI;AACJ,QAAI;AAEJ,QAAI,KAAK,SAAS,aAAa,gBAAgB;AAC7C,YAAM,WAAW,MAAM,KAAK,eAAe,YAAY,QAAQ,IAAI;AAAA,QACjE,IAAI,OAAOA,QAAO,WAAW,EAAE,MAAM,GAAG,CAAC,CAAC;AAAA,QAC1C,MAAM;AAAA,QACN,QAAQ,EAAE,UAAU,eAAe;AAAA,MACrC,CAAC;AAED,sBAAgB,SAAS,MAAM,CAAC,CAAC,SAAS,QAAQ;AAClD,uBAAiB,SAAS,MAAM,CAAC,CAAC,SAAS,QAAQ;AACnD,oBAAc,SAAS,QAAQ;AAE/B,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS,gBAAgB,WAAW,cAAc,mBAAmB,WAAW,cAAc;AAAA,MAChG,CAAC;AAED,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS,iBAAiB,WAAW,cAAc,gBAAgB,WAAW,cAAc;AAAA,MAC9F,CAAC;AAGD,YAAM,cAAc,MAAM,KAAK,eAAe,YAAY,QAAQ,IAAI;AAAA,QACpE,IAAI,OAAOA,QAAO,WAAW,EAAE,MAAM,GAAG,CAAC,CAAC;AAAA,QAC1C,MAAM;AAAA,QACN,QAAQ,EAAE,UAAU,eAAe;AAAA,MACrC,CAAC;AAED,UAAI,YAAY,MAAM,YAAY,QAAQ;AACxC,cAAM,MAAM,YAAY;AACxB,yBAAiB;AACjB,cAAM,mBAAmB,CAAC,CAAC,IAAI;AAE/B,eAAO,KAAK;AAAA,UACV,MAAM;AAAA,UACN,QAAQ,CAAC;AAAA,UACT,SAAS,CAAC,mBACN,wCAAwC,IAAI,aAAa,mBAAmB,IAAI,MAAM,KAAK,QAC3F,iCAAiC,IAAI,eAAe,mBAAmB,IAAI,MAAM,KAAK,iBAAiB,IAAI,aAAa;AAAA,QAC9H,CAAC;AAAA,MACH;AAAA,IACF;AAGA,QAAI;AACJ,UAAM,aAAa,MAAM,KAAK,eAAe,YAAY,QAAQ,IAAI;AAAA,MACnE,IAAI,OAAOA,QAAO,WAAW,EAAE,MAAM,GAAG,CAAC,CAAC;AAAA,MAC1C,MAAM;AAAA,MACN,QAAQ,EAAE,UAAU,eAAe;AAAA,IACrC,CAAC;AAED,QAAI,WAAW,MAAM,WAAW,QAAQ,SAAS;AAC/C,YAAM,QAAQ,KAAK,YAAY,mBAAmB,KAAK,WAAW,KAAK,IAAI,SAAS,WAAW,OAAO,OAAO;AAC7G,UAAI,OAAO;AACT,8BAAsB;AAAA,MACxB;AAAA,IACF;AAGA,UAAM,aAAa,KAAK,QAAQ;AAChC,UAAM,gBAAgB,iBAAiB,CAAC,eAAe,WAAW;AAElE,UAAM,eAAe;AAAA,MACnB,QAAQ;AAAA,MACR,SAAS,eAAgB,gBAAgB;AAAA,MACzC,eAAe,gBAAgB,iBAAiB,KAAK,SAAS;AAAA,MAC9D;AAAA,MACA,YAAY,gBAAgB,mBAAmB;AAAA,IACjD;AAGA,UAAM,YAAY,OAAO,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM;AAC9C,QAAI,SAAqB;AAEzB,QAAI,WAAW;AACb,eAAS;AAAA,IACX,OAAO;AACL,eAAS;AAAA,IACX;AAEA,UAAM,qBAA6C;AAAA,MACjD;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,aAAa;AAAA,QACX,QAAQ,KAAK,aAAa;AAAA,QAC1B,OAAO;AAAA,MACT;AAAA,MACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,SACE,WAAW,aACP,sHACA;AAAA,IACR;AAEA,SAAK,uBAAuB,oBAAoB,IAAI;AACpD,WAAO;AAAA,EACT;AAAA,EAEQ,uBAAuB,QAAgC,MAAwB;AACrF,SAAK,GAAG,iBAAiB,KAAK,IAAI,OAAO,MAAM;AAC/C,SAAK,GAAG,uBAAuB,MAAM;AAAA,EACvC;AACF;;;AC9LA,OAAOC,aAAY;AAaZ,IAAM,qBAAN,MAAyB;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,IAAe,gBAAgC,iBAAkC;AAC3F,SAAK,KAAK;AACV,SAAK,iBAAiB;AACtB,SAAK,kBAAkB;AAAA,EACzB;AAAA,EAEA,MAAa,eAAe,YAAoB,QAA0D;AACxG,UAAM,WAAW,KAAK,GAAG,YAAY,UAAU;AAC/C,QAAI,CAAC,UAAU;AACb,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,QACR,QAAQ,CAAC,EAAE,MAAM,eAAe,QAAQ,OAAO,SAAS,YAAY,UAAU,aAAa,CAAC;AAAA,QAC5F,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,QAClC,SAAS,YAAY,UAAU;AAAA,MACjC;AAAA,IACF;AAGA,QAAI,UAAU,KAAK,eAAe,2BAA2B,SAAS,SAAS;AAC/E,QAAI,CAAC,SAAS;AACZ,gBAAU,KAAK,eAAe,oBAAoB;AAAA,IACpD;AAEA,QAAI,CAAC,SAAS;AACZ,YAAM,SAA6B;AAAA,QACjC;AAAA,QACA,QAAQ;AAAA,QACR,QAAQ,CAAC;AAAA,QACT,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,QAClC,SAAS;AAAA,MACX;AACA,WAAK,mBAAmB,QAAQ,QAAQ;AACxC,aAAO;AAAA,IACT;AAGA,SAAK,GAAG,qBAAqB,YAAY,WAAW;AACpD,UAAM,sBAAsB,SAAS;AAGrC,UAAM,cAAc,QAAQ,SAAS,SAAS;AAC9C,UAAM,gBAAgB,QAAQ,uBAAuB;AAErD,QAAI,QAAQ,SAAS,OAAO,UAAU,SAAS,OAAO;AACpD,YAAM,KAAK,eAAe,YAAY,QAAQ,IAAI;AAAA,QAChD,IAAI,OAAOA,QAAO,WAAW,EAAE,MAAM,GAAG,CAAC,CAAC;AAAA,QAC1C,MAAM;AAAA,QACN,QAAQ,EAAE,KAAK,OAAO,MAAM;AAAA,MAC9B,CAAC;AAAA,IACH,OAAO;AACL,YAAM,KAAK,eAAe,YAAY,QAAQ,IAAI;AAAA,QAChD,IAAI,OAAOA,QAAO,WAAW,EAAE,MAAM,GAAG,CAAC,CAAC;AAAA,QAC1C,MAAM;AAAA,QACN,QAAQ,EAAE,OAAO,KAAK;AAAA,MACxB,CAAC;AAAA,IACH;AAGA,UAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,aAAa,CAAC;AAGjE,UAAM,gBAAgB,KAAK,GAAG,YAAY,UAAU;AACpD,UAAM,gBAAgB,gBAAgB,cAAc,cAAc,sBAAsB;AAExF,UAAM,SAAwB,CAAC;AAC/B,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,QAAQ,CAAC;AAAA,MACT,SAAS,gBACL,wBAAwB,eAAe,WAAW,4BAA4B,mBAAmB,MACjG;AAAA,IACN,CAAC;AAGD,UAAM,SAAS,QAAQ,UAAU,CAAC;AAClC,eAAW,SAAS,QAAQ;AAC1B,YAAM,WAAW,MAAM,KAAK,cAAc,QAAQ,IAAI,KAAK;AAC3D,aAAO,KAAK,QAAQ;AAAA,IACtB;AAGA,QAAI;AACJ,UAAM,iBAAiB,QAAQ,kBAAkB,SAAS,aAAa;AAEvE,QAAI,gBAAgB;AAClB,YAAM,aAAa,MAAM,KAAK,eAAe,YAAY,QAAQ,IAAI;AAAA,QACnE,IAAI,OAAOA,QAAO,WAAW,EAAE,MAAM,GAAG,CAAC,CAAC;AAAA,QAC1C,MAAM;AAAA,QACN,QAAQ,EAAE,UAAU,eAAe;AAAA,MACrC,CAAC;AAED,UAAI,WAAW,MAAM,WAAW,QAAQ,SAAS;AAC/C,cAAM,QAAQ,KAAK,gBAAgB;AAAA,UACjC,SAAS;AAAA,UACT,SAAS;AAAA,UACT;AAAA,UACA,WAAW,OAAO;AAAA,QACpB;AACA,YAAI,OAAO;AACT,gCAAsB,MAAM;AAAA,QAC9B;AAAA,MACF;AAAA,IACF;AAGA,QAAI,UAA0B;AAC9B,UAAM,iBAAiB,OAAO,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM;AAEnD,QAAI,kBAAkB,eAAe;AACnC,gBAAU;AAAA,IACZ,OAAO;AAEL,YAAM,sBAAsB,SAAS,YAAY,KAAK,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,SAAS,QAAQ;AACtG,UAAI,uBAAuB,OAAO,WAAW,GAAG;AAC9C,kBAAU;AAAA,MACZ;AAAA,IACF;AAEA,UAAM,qBAAyC;AAAA,MAC7C;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,MACA,aAAa;AAAA,QACX,QAAQ,SAAS,aAAa;AAAA,QAC9B,OAAO;AAAA,MACT;AAAA,MACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,SACE,YAAY,aACR,+DACA,YAAY,WACV,4EACA;AAAA,IACV;AAEA,SAAK,mBAAmB,oBAAoB,QAAQ;AACpD,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,cAAc,WAAmB,OAAgD;AAC7F,QAAI;AACF,cAAQ,MAAM,MAAM;AAAA,QAClB,KAAK,kBAAkB;AACrB,cAAI,CAAC,MAAM,UAAU;AACnB,mBAAO,EAAE,MAAM,MAAM,MAAM,QAAQ,OAAO,SAAS,4BAA4B;AAAA,UACjF;AACA,gBAAM,MAAM,MAAM,KAAK,eAAe,YAAY,WAAW;AAAA,YAC3D,IAAI,OAAOA,QAAO,WAAW,EAAE,MAAM,GAAG,CAAC,CAAC;AAAA,YAC1C,MAAM;AAAA,YACN,QAAQ,EAAE,UAAU,MAAM,SAAS;AAAA,UACrC,CAAC;AACD,gBAAM,SAAS,IAAI,MAAM,CAAC,CAAC,IAAI,QAAQ;AACvC,iBAAO;AAAA,YACL,MAAM,MAAM;AAAA,YACZ,QAAQ;AAAA,YACR,SAAS,SAAS,WAAW,MAAM,QAAQ,mBAAmB,WAAW,MAAM,QAAQ;AAAA,UACzF;AAAA,QACF;AAAA,QAEA,KAAK,mBAAmB;AACtB,cAAI,CAAC,MAAM,UAAU;AACnB,mBAAO,EAAE,MAAM,MAAM,MAAM,QAAQ,OAAO,SAAS,4BAA4B;AAAA,UACjF;AACA,gBAAM,MAAM,MAAM,KAAK,eAAe,YAAY,WAAW;AAAA,YAC3D,IAAI,OAAOA,QAAO,WAAW,EAAE,MAAM,GAAG,CAAC,CAAC;AAAA,YAC1C,MAAM;AAAA,YACN,QAAQ,EAAE,UAAU,MAAM,SAAS;AAAA,UACrC,CAAC;AACD,gBAAM,UAAU,IAAI,MAAM,CAAC,CAAC,IAAI,QAAQ,UAAU,CAAC,CAAC,IAAI,QAAQ;AAChE,iBAAO;AAAA,YACL,MAAM,MAAM;AAAA,YACZ,QAAQ;AAAA,YACR,SAAS,UAAU,WAAW,MAAM,QAAQ,gBAAgB,WAAW,MAAM,QAAQ;AAAA,UACvF;AAAA,QACF;AAAA,QAEA,KAAK,iBAAiB;AACpB,cAAI,CAAC,MAAM,YAAY,CAAC,MAAM,MAAM;AAClC,mBAAO,EAAE,MAAM,MAAM,MAAM,QAAQ,OAAO,SAAS,oCAAoC;AAAA,UACzF;AACA,gBAAM,MAAM,MAAM,KAAK,eAAe,YAAY,WAAW;AAAA,YAC3D,IAAI,OAAOA,QAAO,WAAW,EAAE,MAAM,GAAG,CAAC,CAAC;AAAA,YAC1C,MAAM;AAAA,YACN,QAAQ,EAAE,UAAU,MAAM,SAAS;AAAA,UACrC,CAAC;AACD,gBAAM,YAAY,IAAI,QAAQ,aAAa;AAC3C,gBAAM,WAAW,IAAI,MAAM,UAAU,SAAS,MAAM,IAAI;AACxD,iBAAO;AAAA,YACL,MAAM,MAAM;AAAA,YACZ,QAAQ;AAAA,YACR,SAAS,WACL,WAAW,MAAM,QAAQ,mBAAmB,MAAM,IAAI,MACtD,iBAAiB,SAAS,sBAAsB,MAAM,IAAI;AAAA,UAChE;AAAA,QACF;AAAA,QAEA,KAAK,YAAY;AACf,cAAI,CAAC,MAAM,OAAO;AAChB,mBAAO,EAAE,MAAM,MAAM,MAAM,QAAQ,OAAO,SAAS,yBAAyB;AAAA,UAC9E;AACA,gBAAM,MAAM,MAAM,KAAK,eAAe,YAAY,WAAW;AAAA,YAC3D,IAAI,OAAOA,QAAO,WAAW,EAAE,MAAM,GAAG,CAAC,CAAC;AAAA,YAC1C,MAAM;AAAA,UACR,CAAC;AACD,gBAAM,eAAe,IAAI,QAAQ,SAAS;AAC1C,gBAAM,UAAU,IAAI,OAAO,iBAAiB,MAAM,SAAS,aAAa,WAAW,MAAM,KAAK;AAC9F,iBAAO;AAAA,YACL,MAAM,MAAM;AAAA,YACZ,QAAQ;AAAA,YACR,SAAS,UACL,yBAAyB,MAAM,KAAK,KACpC,qBAAqB,YAAY,gBAAgB,MAAM,KAAK;AAAA,UAClE;AAAA,QACF;AAAA,QAEA;AACE,iBAAO;AAAA,YACL,MAAM,MAAM;AAAA,YACZ,QAAQ;AAAA,YACR,SAAS,SAAS,MAAM,IAAI;AAAA,UAC9B;AAAA,MACJ;AAAA,IACF,SAAS,KAAU;AACjB,aAAO;AAAA,QACL,MAAM,MAAM;AAAA,QACZ,QAAQ;AAAA,QACR,SAAS,KAAK,WAAW;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,mBAAmB,QAA4B,UAA0B;AAC/E,SAAK,GAAG,qBAAqB,SAAS,IAAI,OAAO,MAAM;AACvD,SAAK,GAAG,mBAAmB;AAAA,MACzB,IAAI,OAAOA,QAAO,WAAW,EAAE,MAAM,GAAG,CAAC,CAAC;AAAA,MAC1C,YAAY,SAAS;AAAA,MACrB,QAAQ,OAAO;AAAA,MACf,QAAQ,OAAO;AAAA,MACf,kBAAkB,OAAO,aAAa;AAAA,MACtC,iBAAiB,OAAO,aAAa;AAAA,MACrC,SAAS,OAAO;AAAA,MAChB,WAAW,OAAO;AAAA,IACpB,CAAC;AAAA,EACH;AACF;","names":["path","fs","path","path","fs","crypto","crypto"]}