Files
noteflow/.hygeine/biome.json
2026-01-02 04:22:40 +00:00

2 lines
1.8 MiB
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"summary":{"changed":0,"unchanged":295,"matches":0,"duration":{"secs":0,"nanos":83798325},"scannerDuration":{"secs":0,"nanos":5994933},"errors":178,"warnings":11,"infos":4,"skipped":0,"suggestedFixesSkipped":0,"diagnosticsNotPrinted":0},"diagnostics":[{"category":"lint/style/useNodejsImportProtocol","severity":"information","description":"A Node.js builtin module should be imported with the node: protocol.","message":[{"elements":[],"content":"A Node.js builtin module should be imported with the "},{"elements":["Emphasis"],"content":"node:"},{"elements":[],"content":" protocol."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"Using the "},{"elements":["Emphasis"],"content":"node:"},{"elements":[],"content":" protocol is more explicit and signals that the imported module belongs to Node.js."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Add the "},{"elements":["Emphasis"],"content":"node:"},{"elements":[],"content":" protocol."}]]},{"diff":{"dictionary":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n *\nimport type { Options } from '@wdio/types';\nimport * as path from 'pathnode:path;\nimport * as fs from 'fs';\nimport { fileURLToPath } from 'url'; },\n};\n","ops":[{"diffOp":{"equal":{"range":[0,60]}}},{"equalLines":{"line_count":7}},{"diffOp":{"equal":{"range":[60,127]}}},{"diffOp":{"equal":{"range":[127,128]}}},{"diffOp":{"delete":{"range":[128,132]}}},{"diffOp":{"insert":{"range":[132,141]}}},{"diffOp":{"equal":{"range":[127,128]}}},{"diffOp":{"equal":{"range":[141,205]}}},{"equalLines":{"line_count":253}},{"diffOp":{"equal":{"range":[205,213]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"wdio.conf.ts"},"span":[350,356],"sourceCode":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n *\n * This config runs tests against the actual Tauri desktop app using tauri-driver.\n * Requires: cargo install tauri-driver\n *\n * Usage:\n * 1. Build the app: npm run tauri:build\n * 2. Run tests: npm run test:native\n */\n\nimport type { Options } from '@wdio/types';\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport { fileURLToPath } from 'url';\nimport { spawn, type ChildProcess } from 'child_process';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// Track tauri-driver process\nlet tauriDriverProcess: ChildProcess | null = null;\n\n// Detect the built Tauri binary path based on platform\nfunction getTauriBinaryPath(): string {\n const projectRoot = path.resolve(__dirname, 'src-tauri');\n\n if (process.platform === 'win32') {\n // Windows: look for .exe in release or debug\n // Binary name comes from Cargo.toml package name\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri.exe');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri.exe');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n\n // Fallback to release path (will error if not built)\n return releasePath;\n } else if (process.platform === 'darwin') {\n // macOS: .app bundle\n const releasePath = path.join(\n projectRoot,\n 'target',\n 'release',\n 'bundle',\n 'macos',\n 'NoteFlow.app',\n 'Contents',\n 'MacOS',\n 'noteflow-tauri'\n );\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n } else {\n // Linux: AppImage or direct binary\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n }\n}\n\n// Get tauri-driver path\nfunction getTauriDriverPath(): string {\n if (process.platform === 'win32') {\n // On Windows, tauri-driver is in cargo bin\n const cargoHome = process.env.CARGO_HOME || path.join(process.env.USERPROFILE || '', '.cargo');\n return path.join(cargoHome, 'bin', 'tauri-driver.exe');\n }\n return 'tauri-driver';\n}\n\n// Get msedgedriver path (Windows only)\nasync function getMsEdgeDriverPath(): Promise<string | null> {\n if (process.platform !== 'win32') {\n return null;\n }\n\n // Try edgedriver npm package first\n try {\n const edgedriver = await import('edgedriver');\n // The package provides a function to get/download the binary\n if (typeof edgedriver.default === 'string') {\n return edgedriver.default;\n }\n if (edgedriver.binPath && fs.existsSync(edgedriver.binPath)) {\n return edgedriver.binPath;\n }\n } catch {\n // Package not available or failed\n }\n\n // Check common locations\n const possiblePaths = [\n // Custom env var\n process.env.MSEDGEDRIVER_PATH,\n // Common install locations\n 'C:\\\\Program Files\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n 'C:\\\\Program Files (x86)\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n path.join(process.env.USERPROFILE || '', 'msedgedriver.exe'),\n path.join(process.env.USERPROFILE || '', '.webdrivers', 'msedgedriver.exe'),\n ];\n\n for (const p of possiblePaths) {\n if (p && fs.existsSync(p)) {\n return p;\n }\n }\n\n return null;\n}\n\nexport const config: Options.Testrunner = {\n // Test specs\n specs: ['./e2e-native/**/*.spec.ts'],\n exclude: [],\n\n // Capabilities\n maxInstances: 1, // Tauri apps should run one at a time\n capabilities: [\n {\n // Use tauri-driver as the WebDriver server\n 'tauri:options': {\n application: getTauriBinaryPath(),\n },\n },\n ],\n\n // Test framework\n framework: 'mocha',\n mochaOpts: {\n ui: 'bdd',\n timeout: 60000,\n },\n\n // Reporters\n reporters: ['spec'],\n\n // Log level\n logLevel: 'info',\n\n // Connection settings for tauri-driver\n hostname: '127.0.0.1',\n port: 4444,\n\n // No built-in service - tauri-driver started via onPrepare hook\n services: [],\n\n // Timeouts\n connectionRetryTimeout: 120000,\n connectionRetryCount: 3,\n\n // Hooks\n onPrepare: async function () {\n const driverPath = getTauriDriverPath();\n console.log(`Starting tauri-driver: ${driverPath}`);\n\n // Check if tauri-driver exists\n if (!fs.existsSync(driverPath)) {\n throw new Error(\n `tauri-driver not found at: ${driverPath}\\n` +\n 'Install it with: cargo install tauri-driver'\n );\n }\n\n // On Windows, check for msedgedriver\n const edgeDriverPath = await getMsEdgeDriverPath();\n if (process.platform === 'win32' && !edgeDriverPath) {\n console.warn(\n '\\n⚠ msedgedriver.exe not found in common locations.\\n' +\n ' Download from: https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/\\n' +\n ' Then either:\\n' +\n ' - Add to PATH\\n' +\n ' - Set MSEDGEDRIVER_PATH environment variable\\n' +\n ' - Place in your home directory\\n'\n );\n }\n\n // Build args\n const args = ['--port', '4444'];\n if (edgeDriverPath) {\n args.push('--native-driver', edgeDriverPath);\n console.log(`Using msedgedriver: ${edgeDriverPath}`);\n }\n\n // Start tauri-driver\n tauriDriverProcess = spawn(driverPath, args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n tauriDriverProcess.stdout?.on('data', (data) => {\n console.log(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n tauriDriverProcess.stderr?.on('data', (data) => {\n console.error(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n // Wait for tauri-driver to be ready\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error('tauri-driver failed to start within 10s'));\n }, 10000);\n\n const checkReady = async () => {\n try {\n const response = await fetch('http://127.0.0.1:4444/status');\n if (response.ok) {\n clearTimeout(timeout);\n console.log('tauri-driver is ready');\n resolve();\n }\n } catch {\n // Not ready yet, retry\n setTimeout(checkReady, 200);\n }\n };\n\n // Start checking after a brief delay\n setTimeout(checkReady, 500);\n });\n },\n\n onComplete: async function () {\n // Stop tauri-driver\n if (tauriDriverProcess) {\n console.log('Stopping tauri-driver');\n tauriDriverProcess.kill();\n tauriDriverProcess = null;\n }\n },\n\n beforeSession: async function () {\n const binaryPath = getTauriBinaryPath();\n if (!fs.existsSync(binaryPath)) {\n throw new Error(\n `Tauri binary not found at: ${binaryPath}\\n` +\n 'Please build the app first with: npm run tauri:build'\n );\n }\n console.log(`Using Tauri binary: ${binaryPath}`);\n },\n\n afterTest: async function (test, _context, { error }) {\n if (error) {\n // Take screenshot on failure\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const screenshotPath = `./e2e-native/screenshots/${test.title}-${timestamp}.png`;\n await browser.saveScreenshot(screenshotPath);\n console.log(`Screenshot saved: ${screenshotPath}`);\n }\n },\n};\n"},"tags":["fixable"],"source":null},{"category":"lint/style/useNodejsImportProtocol","severity":"information","description":"A Node.js builtin module should be imported with the node: protocol.","message":[{"elements":[],"content":"A Node.js builtin module should be imported with the "},{"elements":["Emphasis"],"content":"node:"},{"elements":[],"content":" protocol."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"Using the "},{"elements":["Emphasis"],"content":"node:"},{"elements":[],"content":" protocol is more explicit and signals that the imported module belongs to Node.js."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Add the "},{"elements":["Emphasis"],"content":"node:"},{"elements":[],"content":" protocol."}]]},{"diff":{"dictionary":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n *import type { Options } from '@wdio/types';\nimport * as path from 'path';\nimport * as fs from 'fsnode:fs;\nimport { fileURLToPath } from 'url';\nimport { spawn, type ChildProcess } from 'child_process'; },\n};\n","ops":[{"diffOp":{"equal":{"range":[0,60]}}},{"equalLines":{"line_count":8}},{"diffOp":{"equal":{"range":[60,154]}}},{"diffOp":{"equal":{"range":[154,155]}}},{"diffOp":{"delete":{"range":[155,157]}}},{"diffOp":{"insert":{"range":[157,164]}}},{"diffOp":{"equal":{"range":[154,155]}}},{"diffOp":{"equal":{"range":[164,260]}}},{"equalLines":{"line_count":252}},{"diffOp":{"equal":{"range":[260,268]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"wdio.conf.ts"},"span":[378,382],"sourceCode":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n *\n * This config runs tests against the actual Tauri desktop app using tauri-driver.\n * Requires: cargo install tauri-driver\n *\n * Usage:\n * 1. Build the app: npm run tauri:build\n * 2. Run tests: npm run test:native\n */\n\nimport type { Options } from '@wdio/types';\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport { fileURLToPath } from 'url';\nimport { spawn, type ChildProcess } from 'child_process';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// Track tauri-driver process\nlet tauriDriverProcess: ChildProcess | null = null;\n\n// Detect the built Tauri binary path based on platform\nfunction getTauriBinaryPath(): string {\n const projectRoot = path.resolve(__dirname, 'src-tauri');\n\n if (process.platform === 'win32') {\n // Windows: look for .exe in release or debug\n // Binary name comes from Cargo.toml package name\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri.exe');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri.exe');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n\n // Fallback to release path (will error if not built)\n return releasePath;\n } else if (process.platform === 'darwin') {\n // macOS: .app bundle\n const releasePath = path.join(\n projectRoot,\n 'target',\n 'release',\n 'bundle',\n 'macos',\n 'NoteFlow.app',\n 'Contents',\n 'MacOS',\n 'noteflow-tauri'\n );\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n } else {\n // Linux: AppImage or direct binary\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n }\n}\n\n// Get tauri-driver path\nfunction getTauriDriverPath(): string {\n if (process.platform === 'win32') {\n // On Windows, tauri-driver is in cargo bin\n const cargoHome = process.env.CARGO_HOME || path.join(process.env.USERPROFILE || '', '.cargo');\n return path.join(cargoHome, 'bin', 'tauri-driver.exe');\n }\n return 'tauri-driver';\n}\n\n// Get msedgedriver path (Windows only)\nasync function getMsEdgeDriverPath(): Promise<string | null> {\n if (process.platform !== 'win32') {\n return null;\n }\n\n // Try edgedriver npm package first\n try {\n const edgedriver = await import('edgedriver');\n // The package provides a function to get/download the binary\n if (typeof edgedriver.default === 'string') {\n return edgedriver.default;\n }\n if (edgedriver.binPath && fs.existsSync(edgedriver.binPath)) {\n return edgedriver.binPath;\n }\n } catch {\n // Package not available or failed\n }\n\n // Check common locations\n const possiblePaths = [\n // Custom env var\n process.env.MSEDGEDRIVER_PATH,\n // Common install locations\n 'C:\\\\Program Files\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n 'C:\\\\Program Files (x86)\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n path.join(process.env.USERPROFILE || '', 'msedgedriver.exe'),\n path.join(process.env.USERPROFILE || '', '.webdrivers', 'msedgedriver.exe'),\n ];\n\n for (const p of possiblePaths) {\n if (p && fs.existsSync(p)) {\n return p;\n }\n }\n\n return null;\n}\n\nexport const config: Options.Testrunner = {\n // Test specs\n specs: ['./e2e-native/**/*.spec.ts'],\n exclude: [],\n\n // Capabilities\n maxInstances: 1, // Tauri apps should run one at a time\n capabilities: [\n {\n // Use tauri-driver as the WebDriver server\n 'tauri:options': {\n application: getTauriBinaryPath(),\n },\n },\n ],\n\n // Test framework\n framework: 'mocha',\n mochaOpts: {\n ui: 'bdd',\n timeout: 60000,\n },\n\n // Reporters\n reporters: ['spec'],\n\n // Log level\n logLevel: 'info',\n\n // Connection settings for tauri-driver\n hostname: '127.0.0.1',\n port: 4444,\n\n // No built-in service - tauri-driver started via onPrepare hook\n services: [],\n\n // Timeouts\n connectionRetryTimeout: 120000,\n connectionRetryCount: 3,\n\n // Hooks\n onPrepare: async function () {\n const driverPath = getTauriDriverPath();\n console.log(`Starting tauri-driver: ${driverPath}`);\n\n // Check if tauri-driver exists\n if (!fs.existsSync(driverPath)) {\n throw new Error(\n `tauri-driver not found at: ${driverPath}\\n` +\n 'Install it with: cargo install tauri-driver'\n );\n }\n\n // On Windows, check for msedgedriver\n const edgeDriverPath = await getMsEdgeDriverPath();\n if (process.platform === 'win32' && !edgeDriverPath) {\n console.warn(\n '\\n⚠ msedgedriver.exe not found in common locations.\\n' +\n ' Download from: https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/\\n' +\n ' Then either:\\n' +\n ' - Add to PATH\\n' +\n ' - Set MSEDGEDRIVER_PATH environment variable\\n' +\n ' - Place in your home directory\\n'\n );\n }\n\n // Build args\n const args = ['--port', '4444'];\n if (edgeDriverPath) {\n args.push('--native-driver', edgeDriverPath);\n console.log(`Using msedgedriver: ${edgeDriverPath}`);\n }\n\n // Start tauri-driver\n tauriDriverProcess = spawn(driverPath, args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n tauriDriverProcess.stdout?.on('data', (data) => {\n console.log(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n tauriDriverProcess.stderr?.on('data', (data) => {\n console.error(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n // Wait for tauri-driver to be ready\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error('tauri-driver failed to start within 10s'));\n }, 10000);\n\n const checkReady = async () => {\n try {\n const response = await fetch('http://127.0.0.1:4444/status');\n if (response.ok) {\n clearTimeout(timeout);\n console.log('tauri-driver is ready');\n resolve();\n }\n } catch {\n // Not ready yet, retry\n setTimeout(checkReady, 200);\n }\n };\n\n // Start checking after a brief delay\n setTimeout(checkReady, 500);\n });\n },\n\n onComplete: async function () {\n // Stop tauri-driver\n if (tauriDriverProcess) {\n console.log('Stopping tauri-driver');\n tauriDriverProcess.kill();\n tauriDriverProcess = null;\n }\n },\n\n beforeSession: async function () {\n const binaryPath = getTauriBinaryPath();\n if (!fs.existsSync(binaryPath)) {\n throw new Error(\n `Tauri binary not found at: ${binaryPath}\\n` +\n 'Please build the app first with: npm run tauri:build'\n );\n }\n console.log(`Using Tauri binary: ${binaryPath}`);\n },\n\n afterTest: async function (test, _context, { error }) {\n if (error) {\n // Take screenshot on failure\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const screenshotPath = `./e2e-native/screenshots/${test.title}-${timestamp}.png`;\n await browser.saveScreenshot(screenshotPath);\n console.log(`Screenshot saved: ${screenshotPath}`);\n }\n },\n};\n"},"tags":["fixable"],"source":null},{"category":"lint/style/useNodejsImportProtocol","severity":"information","description":"A Node.js builtin module should be imported with the node: protocol.","message":[{"elements":[],"content":"A Node.js builtin module should be imported with the "},{"elements":["Emphasis"],"content":"node:"},{"elements":[],"content":" protocol."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"Using the "},{"elements":["Emphasis"],"content":"node:"},{"elements":[],"content":" protocol is more explicit and signals that the imported module belongs to Node.js."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Add the "},{"elements":["Emphasis"],"content":"node:"},{"elements":[],"content":" protocol."}]]},{"diff":{"dictionary":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n *import * as path from 'path';\nimport * as fs from 'fs';\nimport { fileURLToPath } from 'urlnode:url;\nimport { spawn, type ChildProcess } from 'child_process';\n },\n};\n","ops":[{"diffOp":{"equal":{"range":[0,60]}}},{"equalLines":{"line_count":9}},{"diffOp":{"equal":{"range":[60,146]}}},{"diffOp":{"equal":{"range":[146,147]}}},{"diffOp":{"delete":{"range":[147,150]}}},{"diffOp":{"insert":{"range":[150,158]}}},{"diffOp":{"equal":{"range":[146,147]}}},{"diffOp":{"equal":{"range":[158,218]}}},{"equalLines":{"line_count":251}},{"diffOp":{"equal":{"range":[218,226]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"wdio.conf.ts"},"span":[414,419],"sourceCode":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n *\n * This config runs tests against the actual Tauri desktop app using tauri-driver.\n * Requires: cargo install tauri-driver\n *\n * Usage:\n * 1. Build the app: npm run tauri:build\n * 2. Run tests: npm run test:native\n */\n\nimport type { Options } from '@wdio/types';\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport { fileURLToPath } from 'url';\nimport { spawn, type ChildProcess } from 'child_process';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// Track tauri-driver process\nlet tauriDriverProcess: ChildProcess | null = null;\n\n// Detect the built Tauri binary path based on platform\nfunction getTauriBinaryPath(): string {\n const projectRoot = path.resolve(__dirname, 'src-tauri');\n\n if (process.platform === 'win32') {\n // Windows: look for .exe in release or debug\n // Binary name comes from Cargo.toml package name\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri.exe');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri.exe');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n\n // Fallback to release path (will error if not built)\n return releasePath;\n } else if (process.platform === 'darwin') {\n // macOS: .app bundle\n const releasePath = path.join(\n projectRoot,\n 'target',\n 'release',\n 'bundle',\n 'macos',\n 'NoteFlow.app',\n 'Contents',\n 'MacOS',\n 'noteflow-tauri'\n );\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n } else {\n // Linux: AppImage or direct binary\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n }\n}\n\n// Get tauri-driver path\nfunction getTauriDriverPath(): string {\n if (process.platform === 'win32') {\n // On Windows, tauri-driver is in cargo bin\n const cargoHome = process.env.CARGO_HOME || path.join(process.env.USERPROFILE || '', '.cargo');\n return path.join(cargoHome, 'bin', 'tauri-driver.exe');\n }\n return 'tauri-driver';\n}\n\n// Get msedgedriver path (Windows only)\nasync function getMsEdgeDriverPath(): Promise<string | null> {\n if (process.platform !== 'win32') {\n return null;\n }\n\n // Try edgedriver npm package first\n try {\n const edgedriver = await import('edgedriver');\n // The package provides a function to get/download the binary\n if (typeof edgedriver.default === 'string') {\n return edgedriver.default;\n }\n if (edgedriver.binPath && fs.existsSync(edgedriver.binPath)) {\n return edgedriver.binPath;\n }\n } catch {\n // Package not available or failed\n }\n\n // Check common locations\n const possiblePaths = [\n // Custom env var\n process.env.MSEDGEDRIVER_PATH,\n // Common install locations\n 'C:\\\\Program Files\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n 'C:\\\\Program Files (x86)\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n path.join(process.env.USERPROFILE || '', 'msedgedriver.exe'),\n path.join(process.env.USERPROFILE || '', '.webdrivers', 'msedgedriver.exe'),\n ];\n\n for (const p of possiblePaths) {\n if (p && fs.existsSync(p)) {\n return p;\n }\n }\n\n return null;\n}\n\nexport const config: Options.Testrunner = {\n // Test specs\n specs: ['./e2e-native/**/*.spec.ts'],\n exclude: [],\n\n // Capabilities\n maxInstances: 1, // Tauri apps should run one at a time\n capabilities: [\n {\n // Use tauri-driver as the WebDriver server\n 'tauri:options': {\n application: getTauriBinaryPath(),\n },\n },\n ],\n\n // Test framework\n framework: 'mocha',\n mochaOpts: {\n ui: 'bdd',\n timeout: 60000,\n },\n\n // Reporters\n reporters: ['spec'],\n\n // Log level\n logLevel: 'info',\n\n // Connection settings for tauri-driver\n hostname: '127.0.0.1',\n port: 4444,\n\n // No built-in service - tauri-driver started via onPrepare hook\n services: [],\n\n // Timeouts\n connectionRetryTimeout: 120000,\n connectionRetryCount: 3,\n\n // Hooks\n onPrepare: async function () {\n const driverPath = getTauriDriverPath();\n console.log(`Starting tauri-driver: ${driverPath}`);\n\n // Check if tauri-driver exists\n if (!fs.existsSync(driverPath)) {\n throw new Error(\n `tauri-driver not found at: ${driverPath}\\n` +\n 'Install it with: cargo install tauri-driver'\n );\n }\n\n // On Windows, check for msedgedriver\n const edgeDriverPath = await getMsEdgeDriverPath();\n if (process.platform === 'win32' && !edgeDriverPath) {\n console.warn(\n '\\n⚠ msedgedriver.exe not found in common locations.\\n' +\n ' Download from: https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/\\n' +\n ' Then either:\\n' +\n ' - Add to PATH\\n' +\n ' - Set MSEDGEDRIVER_PATH environment variable\\n' +\n ' - Place in your home directory\\n'\n );\n }\n\n // Build args\n const args = ['--port', '4444'];\n if (edgeDriverPath) {\n args.push('--native-driver', edgeDriverPath);\n console.log(`Using msedgedriver: ${edgeDriverPath}`);\n }\n\n // Start tauri-driver\n tauriDriverProcess = spawn(driverPath, args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n tauriDriverProcess.stdout?.on('data', (data) => {\n console.log(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n tauriDriverProcess.stderr?.on('data', (data) => {\n console.error(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n // Wait for tauri-driver to be ready\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error('tauri-driver failed to start within 10s'));\n }, 10000);\n\n const checkReady = async () => {\n try {\n const response = await fetch('http://127.0.0.1:4444/status');\n if (response.ok) {\n clearTimeout(timeout);\n console.log('tauri-driver is ready');\n resolve();\n }\n } catch {\n // Not ready yet, retry\n setTimeout(checkReady, 200);\n }\n };\n\n // Start checking after a brief delay\n setTimeout(checkReady, 500);\n });\n },\n\n onComplete: async function () {\n // Stop tauri-driver\n if (tauriDriverProcess) {\n console.log('Stopping tauri-driver');\n tauriDriverProcess.kill();\n tauriDriverProcess = null;\n }\n },\n\n beforeSession: async function () {\n const binaryPath = getTauriBinaryPath();\n if (!fs.existsSync(binaryPath)) {\n throw new Error(\n `Tauri binary not found at: ${binaryPath}\\n` +\n 'Please build the app first with: npm run tauri:build'\n );\n }\n console.log(`Using Tauri binary: ${binaryPath}`);\n },\n\n afterTest: async function (test, _context, { error }) {\n if (error) {\n // Take screenshot on failure\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const screenshotPath = `./e2e-native/screenshots/${test.title}-${timestamp}.png`;\n await browser.saveScreenshot(screenshotPath);\n console.log(`Screenshot saved: ${screenshotPath}`);\n }\n },\n};\n"},"tags":["fixable"],"source":null},{"category":"lint/style/useNodejsImportProtocol","severity":"information","description":"A Node.js builtin module should be imported with the node: protocol.","message":[{"elements":[],"content":"A Node.js builtin module should be imported with the "},{"elements":["Emphasis"],"content":"node:"},{"elements":[],"content":" protocol."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"Using the "},{"elements":["Emphasis"],"content":"node:"},{"elements":[],"content":" protocol is more explicit and signals that the imported module belongs to Node.js."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Add the "},{"elements":["Emphasis"],"content":"node:"},{"elements":[],"content":" protocol."}]]},{"diff":{"dictionary":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n *import * as fs from 'fs';\nimport { fileURLToPath } from 'url';\nimport { spawn, type ChildProcess } from 'child_processnode:child_process;\n\nconst __filename = fileURLToPath(import.meta.url); },\n};\n","ops":[{"diffOp":{"equal":{"range":[0,60]}}},{"equalLines":{"line_count":10}},{"diffOp":{"equal":{"range":[60,164]}}},{"diffOp":{"equal":{"range":[164,165]}}},{"diffOp":{"delete":{"range":[165,178]}}},{"diffOp":{"insert":{"range":[178,196]}}},{"diffOp":{"equal":{"range":[164,165]}}},{"diffOp":{"equal":{"range":[196,249]}}},{"equalLines":{"line_count":250}},{"diffOp":{"equal":{"range":[249,257]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"wdio.conf.ts"},"span":[462,477],"sourceCode":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n *\n * This config runs tests against the actual Tauri desktop app using tauri-driver.\n * Requires: cargo install tauri-driver\n *\n * Usage:\n * 1. Build the app: npm run tauri:build\n * 2. Run tests: npm run test:native\n */\n\nimport type { Options } from '@wdio/types';\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport { fileURLToPath } from 'url';\nimport { spawn, type ChildProcess } from 'child_process';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// Track tauri-driver process\nlet tauriDriverProcess: ChildProcess | null = null;\n\n// Detect the built Tauri binary path based on platform\nfunction getTauriBinaryPath(): string {\n const projectRoot = path.resolve(__dirname, 'src-tauri');\n\n if (process.platform === 'win32') {\n // Windows: look for .exe in release or debug\n // Binary name comes from Cargo.toml package name\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri.exe');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri.exe');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n\n // Fallback to release path (will error if not built)\n return releasePath;\n } else if (process.platform === 'darwin') {\n // macOS: .app bundle\n const releasePath = path.join(\n projectRoot,\n 'target',\n 'release',\n 'bundle',\n 'macos',\n 'NoteFlow.app',\n 'Contents',\n 'MacOS',\n 'noteflow-tauri'\n );\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n } else {\n // Linux: AppImage or direct binary\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n }\n}\n\n// Get tauri-driver path\nfunction getTauriDriverPath(): string {\n if (process.platform === 'win32') {\n // On Windows, tauri-driver is in cargo bin\n const cargoHome = process.env.CARGO_HOME || path.join(process.env.USERPROFILE || '', '.cargo');\n return path.join(cargoHome, 'bin', 'tauri-driver.exe');\n }\n return 'tauri-driver';\n}\n\n// Get msedgedriver path (Windows only)\nasync function getMsEdgeDriverPath(): Promise<string | null> {\n if (process.platform !== 'win32') {\n return null;\n }\n\n // Try edgedriver npm package first\n try {\n const edgedriver = await import('edgedriver');\n // The package provides a function to get/download the binary\n if (typeof edgedriver.default === 'string') {\n return edgedriver.default;\n }\n if (edgedriver.binPath && fs.existsSync(edgedriver.binPath)) {\n return edgedriver.binPath;\n }\n } catch {\n // Package not available or failed\n }\n\n // Check common locations\n const possiblePaths = [\n // Custom env var\n process.env.MSEDGEDRIVER_PATH,\n // Common install locations\n 'C:\\\\Program Files\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n 'C:\\\\Program Files (x86)\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n path.join(process.env.USERPROFILE || '', 'msedgedriver.exe'),\n path.join(process.env.USERPROFILE || '', '.webdrivers', 'msedgedriver.exe'),\n ];\n\n for (const p of possiblePaths) {\n if (p && fs.existsSync(p)) {\n return p;\n }\n }\n\n return null;\n}\n\nexport const config: Options.Testrunner = {\n // Test specs\n specs: ['./e2e-native/**/*.spec.ts'],\n exclude: [],\n\n // Capabilities\n maxInstances: 1, // Tauri apps should run one at a time\n capabilities: [\n {\n // Use tauri-driver as the WebDriver server\n 'tauri:options': {\n application: getTauriBinaryPath(),\n },\n },\n ],\n\n // Test framework\n framework: 'mocha',\n mochaOpts: {\n ui: 'bdd',\n timeout: 60000,\n },\n\n // Reporters\n reporters: ['spec'],\n\n // Log level\n logLevel: 'info',\n\n // Connection settings for tauri-driver\n hostname: '127.0.0.1',\n port: 4444,\n\n // No built-in service - tauri-driver started via onPrepare hook\n services: [],\n\n // Timeouts\n connectionRetryTimeout: 120000,\n connectionRetryCount: 3,\n\n // Hooks\n onPrepare: async function () {\n const driverPath = getTauriDriverPath();\n console.log(`Starting tauri-driver: ${driverPath}`);\n\n // Check if tauri-driver exists\n if (!fs.existsSync(driverPath)) {\n throw new Error(\n `tauri-driver not found at: ${driverPath}\\n` +\n 'Install it with: cargo install tauri-driver'\n );\n }\n\n // On Windows, check for msedgedriver\n const edgeDriverPath = await getMsEdgeDriverPath();\n if (process.platform === 'win32' && !edgeDriverPath) {\n console.warn(\n '\\n⚠ msedgedriver.exe not found in common locations.\\n' +\n ' Download from: https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/\\n' +\n ' Then either:\\n' +\n ' - Add to PATH\\n' +\n ' - Set MSEDGEDRIVER_PATH environment variable\\n' +\n ' - Place in your home directory\\n'\n );\n }\n\n // Build args\n const args = ['--port', '4444'];\n if (edgeDriverPath) {\n args.push('--native-driver', edgeDriverPath);\n console.log(`Using msedgedriver: ${edgeDriverPath}`);\n }\n\n // Start tauri-driver\n tauriDriverProcess = spawn(driverPath, args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n tauriDriverProcess.stdout?.on('data', (data) => {\n console.log(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n tauriDriverProcess.stderr?.on('data', (data) => {\n console.error(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n // Wait for tauri-driver to be ready\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error('tauri-driver failed to start within 10s'));\n }, 10000);\n\n const checkReady = async () => {\n try {\n const response = await fetch('http://127.0.0.1:4444/status');\n if (response.ok) {\n clearTimeout(timeout);\n console.log('tauri-driver is ready');\n resolve();\n }\n } catch {\n // Not ready yet, retry\n setTimeout(checkReady, 200);\n }\n };\n\n // Start checking after a brief delay\n setTimeout(checkReady, 500);\n });\n },\n\n onComplete: async function () {\n // Stop tauri-driver\n if (tauriDriverProcess) {\n console.log('Stopping tauri-driver');\n tauriDriverProcess.kill();\n tauriDriverProcess = null;\n }\n },\n\n beforeSession: async function () {\n const binaryPath = getTauriBinaryPath();\n if (!fs.existsSync(binaryPath)) {\n throw new Error(\n `Tauri binary not found at: ${binaryPath}\\n` +\n 'Please build the app first with: npm run tauri:build'\n );\n }\n console.log(`Using Tauri binary: ${binaryPath}`);\n },\n\n afterTest: async function (test, _context, { error }) {\n if (error) {\n // Take screenshot on failure\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const screenshotPath = `./e2e-native/screenshots/${test.title}-${timestamp}.png`;\n await browser.saveScreenshot(screenshotPath);\n console.log(`Screenshot saved: ${screenshotPath}`);\n }\n },\n};\n"},"tags":["fixable"],"source":null},{"category":"lint/a11y/useSemanticElements","severity":"warning","description":"The elements with this role can be changed to the following elements:\n<button>","message":[{"elements":[],"content":"The elements with this role can be changed to the following elements:\n<button>"}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"For examples and more information, see "},{"elements":[{"Hyperlink":{"href":"https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles"}}],"content":"WAI-ARIA Roles"}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"src/components/calendar-events-panel.tsx"},"span":[2626,2639],"sourceCode":"// Calendar events display panel component\n\nimport { Calendar, Clock, Loader2, MapPin, RefreshCw, Users, Video } from 'lucide-react';\nimport { useEffect } from 'react';\nimport type { CalendarEvent } from '@/api/types';\nimport { Badge } from '@/components/ui/badge';\nimport { Button } from '@/components/ui/button';\nimport { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';\nimport { ScrollArea } from '@/components/ui/scroll-area';\nimport { Separator } from '@/components/ui/separator';\nimport { useCalendarSync } from '@/hooks/use-calendar-sync';\n\ninterface CalendarEventsPanelProps {\n hoursAhead?: number;\n limit?: number;\n provider?: string;\n autoRefreshInterval?: number;\n onEventSelect?: (event: CalendarEvent) => void;\n}\n\nfunction formatEventTime(startTime: number, endTime: number): string {\n const start = new Date(startTime * 1000); // Convert Unix timestamp to ms\n const end = new Date(endTime * 1000);\n const now = new Date();\n const isToday = start.toDateString() === now.toDateString();\n const isTomorrow = start.toDateString() === new Date(now.getTime() + 86400000).toDateString();\n\n const timeFormat: Intl.DateTimeFormatOptions = { hour: 'numeric', minute: '2-digit' };\n const startStr = start.toLocaleTimeString(undefined, timeFormat);\n const endStr = end.toLocaleTimeString(undefined, timeFormat);\n\n if (isToday) {\n return `Today, ${startStr} - ${endStr}`;\n }\n if (isTomorrow) {\n return `Tomorrow, ${startStr} - ${endStr}`;\n }\n\n const dateFormat: Intl.DateTimeFormatOptions = {\n weekday: 'short',\n month: 'short',\n day: 'numeric',\n };\n return `${start.toLocaleDateString(undefined, dateFormat)}, ${startStr} - ${endStr}`;\n}\n\nfunction formatRelativeTime(startTime: number): string {\n const start = new Date(startTime * 1000); // Convert Unix timestamp to ms\n const now = new Date();\n const diffMs = start.getTime() - now.getTime();\n const diffMins = Math.round(diffMs / 60000);\n\n if (diffMins < 0) {\n return 'In progress';\n }\n if (diffMins < 60) {\n return `In ${diffMins} min`;\n }\n if (diffMins < 1440) {\n const hours = Math.round(diffMins / 60);\n return `In ${hours} hour${hours > 1 ? 's' : ''}`;\n }\n const days = Math.round(diffMins / 1440);\n return `In ${days} day${days > 1 ? 's' : ''}`;\n}\n\nfunction EventCard({\n event,\n onSelect,\n}: {\n event: CalendarEvent;\n onSelect?: (event: CalendarEvent) => void;\n}) {\n const hasAttendees = event.attendees && event.attendees.length > 0;\n const hasMeetingUrl = Boolean(event.meeting_url);\n const hasLocation = Boolean(event.location);\n\n return (\n <div\n role=\"button\"\n tabIndex={0}\n className=\"rounded-lg border p-3 hover:bg-accent/50 transition-colors cursor-pointer\"\n onClick={() => onSelect?.(event)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n onSelect?.(event);\n }\n }}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex-1 min-w-0\">\n <h4 className=\"font-medium truncate\">{event.title}</h4>\n <p className=\"text-sm text-muted-foreground flex items-center gap-1\">\n <Clock className=\"h-3 w-3\" />\n {formatEventTime(event.start_time, event.end_time)}\n </p>\n </div>\n <Badge variant=\"secondary\" className=\"shrink-0\">\n {formatRelativeTime(event.start_time)}\n </Badge>\n </div>\n\n <div className=\"mt-2 flex flex-wrap items-center gap-2 text-xs text-muted-foreground\">\n {hasAttendees && (\n <span className=\"flex items-center gap-1\">\n <Users className=\"h-3 w-3\" />\n {event.attendees.length} attendee{event.attendees.length > 1 ? 's' : ''}\n </span>\n )}\n {hasMeetingUrl && (\n <span className=\"flex items-center gap-1 text-blue-600\">\n <Video className=\"h-3 w-3\" />\n Video call\n </span>\n )}\n {hasLocation && (\n <span className=\"flex items-center gap-1\">\n <MapPin className=\"h-3 w-3\" />\n {event.location}\n </span>\n )}\n {event.is_recurring && (\n <Badge variant=\"outline\" className=\"text-xs\">\n Recurring\n </Badge>\n )}\n </div>\n </div>\n );\n}\n\nexport function CalendarEventsPanel({\n hoursAhead = 24,\n limit = 10,\n provider,\n autoRefreshInterval = 0,\n onEventSelect,\n}: CalendarEventsPanelProps) {\n const { state, fetchEvents, startAutoRefresh, stopAutoRefresh, isAutoRefreshing } =\n useCalendarSync({\n hoursAhead,\n limit,\n provider,\n autoRefreshInterval,\n });\n\n // Initial fetch\n useEffect(() => {\n fetchEvents();\n }, [fetchEvents]);\n\n // Auto-refresh handling\n useEffect(() => {\n if (autoRefreshInterval > 0) {\n startAutoRefresh();\n return () => stopAutoRefresh();\n }\n return undefined;\n }, [autoRefreshInterval, startAutoRefresh, stopAutoRefresh]);\n\n const handleRefresh = () => {\n fetchEvents();\n };\n\n const isLoading = state.status === 'loading';\n\n return (\n <Card>\n <CardHeader className=\"pb-3\">\n <div className=\"flex items-center justify-between\">\n <div>\n <CardTitle className=\"flex items-center gap-2\">\n <Calendar className=\"h-5 w-5\" />\n Upcoming Events\n </CardTitle>\n <CardDescription>\n Next {hoursAhead} hours\n {state.lastSync && (\n <span className=\"ml-2 text-xs\">\n · Updated {new Date(state.lastSync).toLocaleTimeString()}\n </span>\n )}\n </CardDescription>\n </div>\n <Button variant=\"ghost\" size=\"icon\" onClick={handleRefresh} disabled={isLoading}>\n {isLoading ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <RefreshCw className=\"h-4 w-4\" />\n )}\n </Button>\n </div>\n </CardHeader>\n <Separator />\n <CardContent className=\"pt-4\">\n {state.status === 'error' && <p className=\"text-sm text-destructive\">{state.error}</p>}\n\n {state.events.length === 0 && state.status !== 'loading' && (\n <div className=\"text-center py-8 text-muted-foreground\">\n <Calendar className=\"mx-auto h-12 w-12 opacity-50 mb-2\" />\n <p>No upcoming events</p>\n <p className=\"text-sm\">Connect a calendar to see your schedule</p>\n </div>\n )}\n\n {state.events.length > 0 && (\n <ScrollArea className=\"h-[400px] pr-4\">\n <div className=\"space-y-2\">\n {state.events.map((event) => (\n <EventCard key={event.id} event={event} onSelect={onEventSelect} />\n ))}\n </div>\n </ScrollArea>\n )}\n\n {isAutoRefreshing && (\n <p className=\"text-xs text-muted-foreground mt-4 text-center\">\n Auto-refreshing every {Math.round(autoRefreshInterval / 60000)} minutes\n </p>\n )}\n </CardContent>\n </Card>\n );\n}\n"},"tags":[],"source":null},{"category":"lint/a11y/useSemanticElements","severity":"warning","description":"The elements with this role can be changed to the following elements:\n<button>","message":[{"elements":[],"content":"The elements with this role can be changed to the following elements:\n<button>"}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"For examples and more information, see "},{"elements":[{"Hyperlink":{"href":"https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles"}}],"content":"WAI-ARIA Roles"}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"src/components/entity-highlight.tsx"},"span":[3926,3939],"sourceCode":"import { AnimatePresence, motion } from 'framer-motion';\nimport { Pin, X } from 'lucide-react';\nimport { useEffect, useRef, useState } from 'react';\nimport { Badge } from '@/components/ui/badge';\nimport { type Entity, findMatchingEntities } from '@/lib/entity-store';\nimport { cn } from '@/lib/utils';\n\ninterface EntityTooltipProps {\n entity: Entity;\n isPinned: boolean;\n onPin: () => void;\n onClose: () => void;\n position: { top: number; left: number };\n}\n\nfunction EntityTooltip({ entity, isPinned, onPin, onClose, position }: EntityTooltipProps) {\n const categoryColors: Record<Entity['category'], string> = {\n person: 'bg-blue-500/20 text-blue-400 border-blue-500/30',\n company: 'bg-purple-500/20 text-purple-400 border-purple-500/30',\n product: 'bg-green-500/20 text-green-400 border-green-500/30',\n technical: 'bg-amber-500/20 text-amber-400 border-amber-500/30',\n acronym: 'bg-cyan-500/20 text-cyan-400 border-cyan-500/30',\n location: 'bg-rose-500/20 text-rose-400 border-rose-500/30',\n date: 'bg-indigo-500/20 text-indigo-400 border-indigo-500/30',\n other: 'bg-muted text-muted-foreground border-border',\n };\n\n return (\n <motion.div\n initial={{ opacity: 0, y: 4, scale: 0.95 }}\n animate={{ opacity: 1, y: 0, scale: 1 }}\n exit={{ opacity: 0, y: 4, scale: 0.95 }}\n transition={{ duration: 0.15 }}\n className=\"fixed z-50 w-72 p-3 rounded-lg bg-popover border border-border shadow-xl\"\n style={{\n top: position.top,\n left: position.left,\n transform: 'translateY(8px)',\n }}\n >\n <div className=\"flex items-start justify-between gap-2 mb-2\">\n <div className=\"flex items-center gap-2\">\n <span className=\"font-semibold text-foreground\">{entity.text}</span>\n <Badge\n variant=\"outline\"\n className={cn('text-[10px] px-1.5 py-0', categoryColors[entity.category])}\n >\n {entity.category}\n </Badge>\n </div>\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={onPin}\n className={cn(\n 'p-1 rounded hover:bg-accent transition-colors',\n isPinned && 'text-primary'\n )}\n title={isPinned ? 'Unpin' : 'Pin tooltip'}\n >\n <Pin className=\"h-3.5 w-3.5\" />\n </button>\n {isPinned && (\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1 rounded hover:bg-accent transition-colors\"\n title=\"Close\"\n >\n <X className=\"h-3.5 w-3.5\" />\n </button>\n )}\n </div>\n </div>\n <p className=\"text-xs text-muted-foreground leading-relaxed\">{entity.description}</p>\n {entity.source && (\n <p className=\"text-[10px] text-muted-foreground/70 mt-2 pt-2 border-t border-border\">\n Source: {entity.source}\n </p>\n )}\n </motion.div>\n );\n}\n\ninterface HighlightedTermProps {\n text: string;\n entity: Entity;\n pinnedEntities: Set<string>;\n onTogglePin: (entityId: string) => void;\n}\n\nfunction HighlightedTerm({ text, entity, pinnedEntities, onTogglePin }: HighlightedTermProps) {\n const [isHovered, setIsHovered] = useState(false);\n const [tooltipPosition, setTooltipPosition] = useState({ top: 0, left: 0 });\n const termRef = useRef<HTMLSpanElement>(null);\n const isPinned = pinnedEntities.has(entity.id);\n const showTooltip = isHovered || isPinned;\n\n useEffect(() => {\n if (showTooltip && termRef.current) {\n const rect = termRef.current.getBoundingClientRect();\n setTooltipPosition({\n top: rect.bottom,\n left: Math.max(8, Math.min(rect.left, window.innerWidth - 288)),\n });\n }\n }, [showTooltip]);\n\n const handleClick = () => {\n onTogglePin(entity.id);\n };\n\n return (\n <>\n <span\n ref={termRef}\n role=\"button\"\n tabIndex={0}\n className={cn(\n 'cursor-pointer rounded px-0.5 -mx-0.5 transition-colors',\n 'bg-primary/15 text-primary border-b border-primary/40 border-dashed',\n 'hover:bg-primary/25 hover:border-solid',\n isPinned && 'bg-primary/25 border-solid'\n )}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n onClick={handleClick}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n handleClick();\n }\n }}\n >\n {text}\n </span>\n <AnimatePresence>\n {showTooltip && (\n <EntityTooltip\n entity={entity}\n isPinned={isPinned}\n onPin={handleClick}\n onClose={() => onTogglePin(entity.id)}\n position={tooltipPosition}\n />\n )}\n </AnimatePresence>\n </>\n );\n}\n\ninterface EntityHighlightTextProps {\n text: string;\n pinnedEntities: Set<string>;\n onTogglePin: (entityId: string) => void;\n}\n\nexport function EntityHighlightText({\n text,\n pinnedEntities,\n onTogglePin,\n}: EntityHighlightTextProps) {\n const matches = findMatchingEntities(text);\n\n if (matches.length === 0) {\n return <>{text}</>;\n }\n\n const parts: React.ReactNode[] = [];\n let lastIndex = 0;\n\n for (const match of matches) {\n // Add text before this match\n if (match.startIndex > lastIndex) {\n parts.push(<span key={`text-${lastIndex}`}>{text.slice(lastIndex, match.startIndex)}</span>);\n }\n\n // Add highlighted match\n parts.push(\n <HighlightedTerm\n key={`entity-${match.startIndex}`}\n text={text.slice(match.startIndex, match.endIndex)}\n entity={match.entity}\n pinnedEntities={pinnedEntities}\n onTogglePin={onTogglePin}\n />\n );\n\n lastIndex = match.endIndex;\n }\n\n // Add remaining text\n if (lastIndex < text.length) {\n parts.push(<span key={`text-${lastIndex}`}>{text.slice(lastIndex)}</span>);\n }\n\n return <>{parts}</>;\n}\n"},"tags":[],"source":null},{"category":"lint/a11y/useSemanticElements","severity":"warning","description":"The elements with this role can be changed to the following elements:\n<section>","message":[{"elements":[],"content":"The elements with this role can be changed to the following elements:\n<section>"}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"For examples and more information, see "},{"elements":[{"Hyperlink":{"href":"https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles"}}],"content":"WAI-ARIA Roles"}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"src/components/ui/carousel.tsx"},"span":[3114,3127],"sourceCode":"import useEmblaCarousel, { type UseEmblaCarouselType } from 'embla-carousel-react';\nimport { ArrowLeft, ArrowRight } from 'lucide-react';\nimport * as React from 'react';\nimport { Button } from '@/components/ui/button';\nimport { cn } from '@/lib/utils';\n\ntype CarouselApi = UseEmblaCarouselType[1];\ntype UseCarouselParameters = Parameters<typeof useEmblaCarousel>;\ntype CarouselOptions = UseCarouselParameters[0];\ntype CarouselPlugin = UseCarouselParameters[1];\n\ntype CarouselProps = {\n opts?: CarouselOptions;\n plugins?: CarouselPlugin;\n orientation?: 'horizontal' | 'vertical';\n setApi?: (api: CarouselApi) => void;\n};\n\ntype CarouselContextProps = {\n carouselRef: ReturnType<typeof useEmblaCarousel>[0];\n api: ReturnType<typeof useEmblaCarousel>[1];\n scrollPrev: () => void;\n scrollNext: () => void;\n canScrollPrev: boolean;\n canScrollNext: boolean;\n} & CarouselProps;\n\nconst CarouselContext = React.createContext<CarouselContextProps | null>(null);\n\nfunction useCarousel() {\n const context = React.useContext(CarouselContext);\n\n if (!context) {\n throw new Error('useCarousel must be used within a <Carousel />');\n }\n\n return context;\n}\n\nconst Carousel = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement> & CarouselProps\n>(({ orientation = 'horizontal', opts, setApi, plugins, className, children, ...props }, ref) => {\n const [carouselRef, api] = useEmblaCarousel(\n {\n ...opts,\n axis: orientation === 'horizontal' ? 'x' : 'y',\n },\n plugins\n );\n const [canScrollPrev, setCanScrollPrev] = React.useState(false);\n const [canScrollNext, setCanScrollNext] = React.useState(false);\n\n const onSelect = React.useCallback((api: CarouselApi) => {\n if (!api) {\n return;\n }\n\n setCanScrollPrev(api.canScrollPrev());\n setCanScrollNext(api.canScrollNext());\n }, []);\n\n const scrollPrev = React.useCallback(() => {\n api?.scrollPrev();\n }, [api]);\n\n const scrollNext = React.useCallback(() => {\n api?.scrollNext();\n }, [api]);\n\n const handleKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (event.key === 'ArrowLeft') {\n event.preventDefault();\n scrollPrev();\n } else if (event.key === 'ArrowRight') {\n event.preventDefault();\n scrollNext();\n }\n },\n [scrollPrev, scrollNext]\n );\n\n React.useEffect(() => {\n if (!api || !setApi) {\n return;\n }\n\n setApi(api);\n }, [api, setApi]);\n\n React.useEffect(() => {\n if (!api) {\n return;\n }\n\n onSelect(api);\n api.on('reInit', onSelect);\n api.on('select', onSelect);\n\n return () => {\n api?.off('select', onSelect);\n };\n }, [api, onSelect]);\n\n return (\n <CarouselContext.Provider\n value={{\n carouselRef,\n api: api,\n opts,\n orientation: orientation || (opts?.axis === 'y' ? 'vertical' : 'horizontal'),\n scrollPrev,\n scrollNext,\n canScrollPrev,\n canScrollNext,\n }}\n >\n <div\n ref={ref}\n onKeyDownCapture={handleKeyDown}\n className={cn('relative', className)}\n role=\"region\"\n aria-roledescription=\"carousel\"\n {...props}\n >\n {children}\n </div>\n </CarouselContext.Provider>\n );\n});\nCarousel.displayName = 'Carousel';\n\nconst CarouselContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => {\n const { carouselRef, orientation } = useCarousel();\n\n return (\n <div ref={carouselRef} className=\"overflow-hidden\">\n <div\n ref={ref}\n className={cn(\n 'flex',\n orientation === 'horizontal' ? '-ml-4' : '-mt-4 flex-col',\n className\n )}\n {...props}\n />\n </div>\n );\n }\n);\nCarouselContent.displayName = 'CarouselContent';\n\nconst CarouselItem = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => {\n const { orientation } = useCarousel();\n\n return (\n <div\n ref={ref}\n role=\"group\"\n aria-roledescription=\"slide\"\n className={cn(\n 'min-w-0 shrink-0 grow-0 basis-full',\n orientation === 'horizontal' ? 'pl-4' : 'pt-4',\n className\n )}\n {...props}\n />\n );\n }\n);\nCarouselItem.displayName = 'CarouselItem';\n\nconst CarouselPrevious = React.forwardRef<HTMLButtonElement, React.ComponentProps<typeof Button>>(\n ({ className, variant = 'outline', size = 'icon', ...props }, ref) => {\n const { orientation, scrollPrev, canScrollPrev } = useCarousel();\n\n return (\n <Button\n ref={ref}\n variant={variant}\n size={size}\n className={cn(\n 'absolute h-8 w-8 rounded-full',\n orientation === 'horizontal'\n ? '-left-12 top-1/2 -translate-y-1/2'\n : '-top-12 left-1/2 -translate-x-1/2 rotate-90',\n className\n )}\n disabled={!canScrollPrev}\n onClick={scrollPrev}\n {...props}\n >\n <ArrowLeft className=\"h-4 w-4\" />\n <span className=\"sr-only\">Previous slide</span>\n </Button>\n );\n }\n);\nCarouselPrevious.displayName = 'CarouselPrevious';\n\nconst CarouselNext = React.forwardRef<HTMLButtonElement, React.ComponentProps<typeof Button>>(\n ({ className, variant = 'outline', size = 'icon', ...props }, ref) => {\n const { orientation, scrollNext, canScrollNext } = useCarousel();\n\n return (\n <Button\n ref={ref}\n variant={variant}\n size={size}\n className={cn(\n 'absolute h-8 w-8 rounded-full',\n orientation === 'horizontal'\n ? '-right-12 top-1/2 -translate-y-1/2'\n : '-bottom-12 left-1/2 -translate-x-1/2 rotate-90',\n className\n )}\n disabled={!canScrollNext}\n onClick={scrollNext}\n {...props}\n >\n <ArrowRight className=\"h-4 w-4\" />\n <span className=\"sr-only\">Next slide</span>\n </Button>\n );\n }\n);\nCarouselNext.displayName = 'CarouselNext';\n\nexport {\n type CarouselApi,\n Carousel,\n CarouselContent,\n CarouselItem,\n CarouselPrevious,\n CarouselNext,\n};\n"},"tags":[],"source":null},{"category":"lint/a11y/useSemanticElements","severity":"warning","description":"The elements with this role can be changed to the following elements:\n<fieldset>","message":[{"elements":[],"content":"The elements with this role can be changed to the following elements:\n<fieldset>"}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"For examples and more information, see "},{"elements":[{"Hyperlink":{"href":"https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles"}}],"content":"WAI-ARIA Roles"}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"src/components/ui/carousel.tsx"},"span":[4084,4096],"sourceCode":"import useEmblaCarousel, { type UseEmblaCarouselType } from 'embla-carousel-react';\nimport { ArrowLeft, ArrowRight } from 'lucide-react';\nimport * as React from 'react';\nimport { Button } from '@/components/ui/button';\nimport { cn } from '@/lib/utils';\n\ntype CarouselApi = UseEmblaCarouselType[1];\ntype UseCarouselParameters = Parameters<typeof useEmblaCarousel>;\ntype CarouselOptions = UseCarouselParameters[0];\ntype CarouselPlugin = UseCarouselParameters[1];\n\ntype CarouselProps = {\n opts?: CarouselOptions;\n plugins?: CarouselPlugin;\n orientation?: 'horizontal' | 'vertical';\n setApi?: (api: CarouselApi) => void;\n};\n\ntype CarouselContextProps = {\n carouselRef: ReturnType<typeof useEmblaCarousel>[0];\n api: ReturnType<typeof useEmblaCarousel>[1];\n scrollPrev: () => void;\n scrollNext: () => void;\n canScrollPrev: boolean;\n canScrollNext: boolean;\n} & CarouselProps;\n\nconst CarouselContext = React.createContext<CarouselContextProps | null>(null);\n\nfunction useCarousel() {\n const context = React.useContext(CarouselContext);\n\n if (!context) {\n throw new Error('useCarousel must be used within a <Carousel />');\n }\n\n return context;\n}\n\nconst Carousel = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement> & CarouselProps\n>(({ orientation = 'horizontal', opts, setApi, plugins, className, children, ...props }, ref) => {\n const [carouselRef, api] = useEmblaCarousel(\n {\n ...opts,\n axis: orientation === 'horizontal' ? 'x' : 'y',\n },\n plugins\n );\n const [canScrollPrev, setCanScrollPrev] = React.useState(false);\n const [canScrollNext, setCanScrollNext] = React.useState(false);\n\n const onSelect = React.useCallback((api: CarouselApi) => {\n if (!api) {\n return;\n }\n\n setCanScrollPrev(api.canScrollPrev());\n setCanScrollNext(api.canScrollNext());\n }, []);\n\n const scrollPrev = React.useCallback(() => {\n api?.scrollPrev();\n }, [api]);\n\n const scrollNext = React.useCallback(() => {\n api?.scrollNext();\n }, [api]);\n\n const handleKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (event.key === 'ArrowLeft') {\n event.preventDefault();\n scrollPrev();\n } else if (event.key === 'ArrowRight') {\n event.preventDefault();\n scrollNext();\n }\n },\n [scrollPrev, scrollNext]\n );\n\n React.useEffect(() => {\n if (!api || !setApi) {\n return;\n }\n\n setApi(api);\n }, [api, setApi]);\n\n React.useEffect(() => {\n if (!api) {\n return;\n }\n\n onSelect(api);\n api.on('reInit', onSelect);\n api.on('select', onSelect);\n\n return () => {\n api?.off('select', onSelect);\n };\n }, [api, onSelect]);\n\n return (\n <CarouselContext.Provider\n value={{\n carouselRef,\n api: api,\n opts,\n orientation: orientation || (opts?.axis === 'y' ? 'vertical' : 'horizontal'),\n scrollPrev,\n scrollNext,\n canScrollPrev,\n canScrollNext,\n }}\n >\n <div\n ref={ref}\n onKeyDownCapture={handleKeyDown}\n className={cn('relative', className)}\n role=\"region\"\n aria-roledescription=\"carousel\"\n {...props}\n >\n {children}\n </div>\n </CarouselContext.Provider>\n );\n});\nCarousel.displayName = 'Carousel';\n\nconst CarouselContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => {\n const { carouselRef, orientation } = useCarousel();\n\n return (\n <div ref={carouselRef} className=\"overflow-hidden\">\n <div\n ref={ref}\n className={cn(\n 'flex',\n orientation === 'horizontal' ? '-ml-4' : '-mt-4 flex-col',\n className\n )}\n {...props}\n />\n </div>\n );\n }\n);\nCarouselContent.displayName = 'CarouselContent';\n\nconst CarouselItem = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => {\n const { orientation } = useCarousel();\n\n return (\n <div\n ref={ref}\n role=\"group\"\n aria-roledescription=\"slide\"\n className={cn(\n 'min-w-0 shrink-0 grow-0 basis-full',\n orientation === 'horizontal' ? 'pl-4' : 'pt-4',\n className\n )}\n {...props}\n />\n );\n }\n);\nCarouselItem.displayName = 'CarouselItem';\n\nconst CarouselPrevious = React.forwardRef<HTMLButtonElement, React.ComponentProps<typeof Button>>(\n ({ className, variant = 'outline', size = 'icon', ...props }, ref) => {\n const { orientation, scrollPrev, canScrollPrev } = useCarousel();\n\n return (\n <Button\n ref={ref}\n variant={variant}\n size={size}\n className={cn(\n 'absolute h-8 w-8 rounded-full',\n orientation === 'horizontal'\n ? '-left-12 top-1/2 -translate-y-1/2'\n : '-top-12 left-1/2 -translate-x-1/2 rotate-90',\n className\n )}\n disabled={!canScrollPrev}\n onClick={scrollPrev}\n {...props}\n >\n <ArrowLeft className=\"h-4 w-4\" />\n <span className=\"sr-only\">Previous slide</span>\n </Button>\n );\n }\n);\nCarouselPrevious.displayName = 'CarouselPrevious';\n\nconst CarouselNext = React.forwardRef<HTMLButtonElement, React.ComponentProps<typeof Button>>(\n ({ className, variant = 'outline', size = 'icon', ...props }, ref) => {\n const { orientation, scrollNext, canScrollNext } = useCarousel();\n\n return (\n <Button\n ref={ref}\n variant={variant}\n size={size}\n className={cn(\n 'absolute h-8 w-8 rounded-full',\n orientation === 'horizontal'\n ? '-right-12 top-1/2 -translate-y-1/2'\n : '-bottom-12 left-1/2 -translate-x-1/2 rotate-90',\n className\n )}\n disabled={!canScrollNext}\n onClick={scrollNext}\n {...props}\n >\n <ArrowRight className=\"h-4 w-4\" />\n <span className=\"sr-only\">Next slide</span>\n </Button>\n );\n }\n);\nCarouselNext.displayName = 'CarouselNext';\n\nexport {\n type CarouselApi,\n Carousel,\n CarouselContent,\n CarouselItem,\n CarouselPrevious,\n CarouselNext,\n};\n"},"tags":[],"source":null},{"category":"lint/a11y/useSemanticElements","severity":"warning","description":"The elements with this role can be changed to the following elements:\n<li>","message":[{"elements":[],"content":"The elements with this role can be changed to the following elements:\n<li>"}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"For examples and more information, see "},{"elements":[{"Hyperlink":{"href":"https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles"}}],"content":"WAI-ARIA Roles"}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"src/components/upcoming-meetings.tsx"},"span":[11786,11801],"sourceCode":"import { format } from 'date-fns';\nimport {\n AlertCircle,\n Bell,\n BellOff,\n BellRing,\n CalendarDays,\n CalendarX,\n Clock,\n MapPin,\n RefreshCw,\n Settings,\n Users,\n Video,\n} from 'lucide-react';\nimport { useEffect, useMemo } from 'react';\nimport { Link } from 'react-router-dom';\nimport { Badge } from '@/components/ui/badge';\nimport { Button } from '@/components/ui/button';\nimport { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';\nimport { Checkbox } from '@/components/ui/checkbox';\nimport { Label } from '@/components/ui/label';\nimport { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';\nimport { ScrollArea } from '@/components/ui/scroll-area';\nimport { Skeleton } from '@/components/ui/skeleton';\nimport { Switch } from '@/components/ui/switch';\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';\nimport { useCalendarSync } from '@/hooks/use-calendar-sync';\nimport { useMeetingReminders } from '@/hooks/use-meeting-reminders';\nimport { getDateLabel, groupByDate } from '@/lib/format';\nimport { preferences } from '@/lib/preferences';\nimport { flexLayout, iconSize } from '@/lib/styles';\n\ninterface UpcomingMeetingsProps {\n maxEvents?: number;\n}\n\n/** Loading skeleton for upcoming meetings. */\nfunction UpcomingMeetingsSkeleton() {\n return (\n <Card>\n <CardHeader>\n <div className=\"flex items-center gap-2\">\n <Skeleton className=\"h-5 w-5 rounded\" />\n <Skeleton className=\"h-5 w-36\" />\n </div>\n <Skeleton className=\"h-4 w-48 mt-1\" />\n </CardHeader>\n <CardContent>\n <div className=\"space-y-4\" data-testid=\"upcoming-meetings-skeleton\">\n {[1, 2, 3].map((i) => (\n <div key={i} className=\"space-y-2\">\n <Skeleton className=\"h-4 w-24\" />\n <div className=\"p-3 rounded-lg border\">\n <Skeleton className=\"h-5 w-3/4 mb-2\" />\n <Skeleton className=\"h-4 w-1/2\" />\n </div>\n </div>\n ))}\n </div>\n </CardContent>\n </Card>\n );\n}\n\n/** Error state with retry option. */\nfunction CalendarErrorState({ onRetry, isRetrying }: { onRetry: () => void; isRetrying: boolean }) {\n return (\n <Card>\n <CardHeader>\n <CardTitle className={flexLayout.itemsGap2Lg}>\n <CalendarDays className=\"h-5 w-5 text-primary\" />\n Upcoming Meetings\n </CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"flex flex-col items-center justify-center py-8 text-center\">\n <AlertCircle className=\"h-12 w-12 text-destructive/50 mb-3\" />\n <p className=\"text-muted-foreground mb-4\">Unable to load calendar events</p>\n <Button variant=\"outline\" size=\"sm\" onClick={onRetry} disabled={isRetrying}>\n {isRetrying ? (\n <RefreshCw className=\"h-4 w-4 mr-2 animate-spin\" />\n ) : (\n <RefreshCw className=\"h-4 w-4 mr-2\" />\n )}\n Try again\n </Button>\n </div>\n </CardContent>\n </Card>\n );\n}\n\nexport function UpcomingMeetings({ maxEvents = 10 }: UpcomingMeetingsProps) {\n const integrations = preferences.getIntegrations();\n const calendarIntegrations = integrations.filter((i) => i.type === 'calendar');\n const connectedCalendars = calendarIntegrations.filter((i) => i.status === 'connected');\n\n // Use live calendar API instead of mock data\n const { state, fetchEvents } = useCalendarSync({\n hoursAhead: 24 * 7, // 7 days ahead\n limit: maxEvents,\n });\n\n // Fetch events when connected calendars change\n useEffect(() => {\n if (connectedCalendars.length > 0) {\n void fetchEvents();\n }\n }, [connectedCalendars.length, fetchEvents]);\n\n const events = useMemo(() => state.events.slice(0, maxEvents), [state.events, maxEvents]);\n\n const groupedEvents = useMemo(() => groupByDate(events), [events]);\n\n // Initialize reminder system with events\n const {\n permission,\n settings,\n toggleReminders,\n setReminderMinutes,\n requestPermission,\n isSupported,\n } = useMeetingReminders(events);\n\n const reminderOptions = [\n { value: 30, label: '30 minutes before' },\n { value: 15, label: '15 minutes before' },\n { value: 10, label: '10 minutes before' },\n { value: 5, label: '5 minutes before' },\n ];\n\n const handleReminderToggle = (minutes: number, checked: boolean) => {\n if (checked) {\n setReminderMinutes([...settings.reminderMinutes, minutes].sort((a, b) => b - a));\n } else {\n setReminderMinutes(settings.reminderMinutes.filter((m) => m !== minutes));\n }\n };\n\n // Show skeleton during initial load\n if (state.status === 'loading' && state.events.length === 0 && connectedCalendars.length > 0) {\n return <UpcomingMeetingsSkeleton />;\n }\n\n // Show error state with retry option\n if (state.status === 'error' && connectedCalendars.length > 0) {\n return (\n <CalendarErrorState\n onRetry={() => void fetchEvents()}\n isRetrying={state.status === 'loading'}\n />\n );\n }\n\n if (connectedCalendars.length === 0) {\n return (\n <Card>\n <CardHeader>\n <CardTitle className={flexLayout.itemsGap2Lg}>\n <CalendarDays className=\"h-5 w-5 text-primary\" />\n Upcoming Meetings\n </CardTitle>\n <CardDescription>Connect a calendar to see your upcoming meetings</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"flex flex-col items-center justify-center py-8 text-center\">\n <CalendarX className=\"h-12 w-12 text-muted-foreground/50 mb-3\" />\n <p className=\"text-muted-foreground mb-4\">No calendars connected</p>\n <Button variant=\"outline\" size=\"sm\" asChild>\n <Link to=\"/settings\">\n <Settings className=\"h-4 w-4 mr-2\" />\n Connect Calendar\n </Link>\n </Button>\n </div>\n </CardContent>\n </Card>\n );\n }\n\n if (events.length === 0) {\n return (\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2 text-lg\">\n <CalendarDays className={iconSize.mdPrimary} />\n Upcoming Meetings\n </CardTitle>\n <CardDescription>From {connectedCalendars.map((c) => c.name).join(', ')}</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"flex flex-col items-center justify-center py-8 text-center\">\n <CalendarDays className=\"h-12 w-12 text-muted-foreground/50 mb-3\" />\n <p className=\"text-muted-foreground\">No upcoming meetings scheduled</p>\n </div>\n </CardContent>\n </Card>\n );\n }\n\n const ReminderControls = () => (\n <div className=\"flex items-center gap-2\">\n {isSupported && (\n <Popover>\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger asChild>\n <PopoverTrigger asChild>\n <Button\n variant={settings.enabled && permission === 'granted' ? 'default' : 'outline'}\n size=\"sm\"\n className=\"gap-2\"\n >\n {settings.enabled && permission === 'granted' ? (\n <BellRing className=\"h-4 w-4\" />\n ) : permission === 'denied' ? (\n <BellOff className=\"h-4 w-4\" />\n ) : (\n <Bell className=\"h-4 w-4\" />\n )}\n <span className=\"hidden sm:inline\">Reminders</span>\n </Button>\n </PopoverTrigger>\n </TooltipTrigger>\n <TooltipContent>\n {permission === 'denied'\n ? 'Notifications blocked - enable in browser settings'\n : settings.enabled\n ? 'Reminder settings'\n : 'Enable meeting reminders'}\n </TooltipContent>\n </Tooltip>\n </TooltipProvider>\n <PopoverContent className=\"w-72\" align=\"end\">\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h4 className=\"font-medium\">Meeting Reminders</h4>\n <p className=\"text-xs text-muted-foreground\">Get notified before meetings</p>\n </div>\n <Switch\n checked={settings.enabled && permission === 'granted'}\n onCheckedChange={toggleReminders}\n disabled={permission === 'denied'}\n />\n </div>\n\n {permission === 'denied' && (\n <p className=\"text-xs text-destructive\">\n Notifications are blocked. Please enable them in your browser settings.\n </p>\n )}\n\n {permission === 'default' && !settings.enabled && (\n <Button variant=\"outline\" size=\"sm\" className=\"w-full\" onClick={requestPermission}>\n <Bell className=\"h-4 w-4 mr-2\" />\n Enable Notifications\n </Button>\n )}\n\n {settings.enabled && permission === 'granted' && (\n <div className=\"space-y-3 pt-2 border-t\">\n <p className=\"text-sm font-medium\">Remind me:</p>\n {reminderOptions.map((option) => (\n <div key={option.value} className=\"flex items-center space-x-2\">\n <Checkbox\n id={`reminder-${option.value}`}\n checked={settings.reminderMinutes.includes(option.value)}\n onCheckedChange={(checked) =>\n handleReminderToggle(option.value, checked as boolean)\n }\n />\n <Label\n htmlFor={`reminder-${option.value}`}\n className=\"text-sm font-normal cursor-pointer\"\n >\n {option.label}\n </Label>\n </div>\n ))}\n </div>\n )}\n </div>\n </PopoverContent>\n </Popover>\n )}\n {connectedCalendars.map((cal) => (\n <Badge key={cal.id} variant=\"outline\" className=\"text-xs hidden sm:inline-flex\">\n {cal.name.split(' ')[0]}\n </Badge>\n ))}\n </div>\n );\n\n return (\n <Card>\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <div>\n <CardTitle className=\"flex items-center gap-2 text-lg\">\n <CalendarDays className=\"h-5 w-5 text-primary\" />\n Upcoming Meetings\n </CardTitle>\n <CardDescription>\n {events.length} events from {connectedCalendars.map((c) => c.name).join(', ')}\n </CardDescription>\n </div>\n <ReminderControls />\n </div>\n </CardHeader>\n <CardContent className=\"p-0\">\n <ScrollArea className=\"h-[400px]\">\n <div className=\"p-4 pt-0 space-y-4\">\n {Array.from(groupedEvents.entries()).map(([dateKey, dayEvents]) => (\n <div key={dateKey}>\n <h3 className=\"text-sm font-medium text-muted-foreground mb-2 sticky top-0 bg-card py-1\">\n {getDateLabel(dayEvents[0].start_time)}\n </h3>\n <div className=\"space-y-2\">\n {dayEvents.map((event) => (\n <div\n key={event.id}\n className=\"p-3 rounded-lg border bg-card hover:bg-accent/50 transition-colors group\"\n role=\"listitem\"\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex-1 min-w-0\">\n <h4 className=\"font-medium truncate\">{event.title}</h4>\n <div className=\"flex items-center gap-3 mt-1 text-sm text-muted-foreground\">\n <span className=\"flex items-center gap-1\">\n <Clock className=\"h-3.5 w-3.5\" />\n {format(new Date(event.start_time * 1000), 'h:mm a')} -\n {format(new Date(event.end_time * 1000), 'h:mm a')}\n </span>\n {event.location && (\n <span className=\"flex items-center gap-1\">\n <MapPin className=\"h-3.5 w-3.5\" />\n {event.location}\n </span>\n )}\n </div>\n {event.attendees && event.attendees.length > 0 && (\n <div className=\"flex items-center gap-1 mt-1.5 text-xs text-muted-foreground\">\n <Users className=\"h-3 w-3\" />\n <span>{event.attendees.slice(0, 3).join(', ')}</span>\n {event.attendees.length > 3 && (\n <span>+{event.attendees.length - 3} more</span>\n )}\n </div>\n )}\n </div>\n <div className=\"flex items-center gap-1\">\n {event.meeting_link && (\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n className=\"opacity-0 group-hover:opacity-100 transition-opacity\"\n asChild\n >\n <a\n href={event.meeting_link}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n <Video className=\"h-4 w-4 text-primary\" />\n </a>\n </Button>\n )}\n <Badge variant=\"secondary\" className=\"text-xs shrink-0\">\n {event.calendar_name.split(' ')[0]}\n </Badge>\n </div>\n </div>\n </div>\n ))}\n </div>\n </div>\n ))}\n </div>\n </ScrollArea>\n </CardContent>\n </Card>\n );\n}\n"},"tags":[],"source":null},{"category":"lint/correctness/useExhaustiveDependencies","severity":"warning","description":"This hook specifies a dependency more specific than its captures: state.jobId","message":[{"elements":[],"content":"This hook specifies a dependency more specific than its captures: state.jobId"}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"This capture is more generic than..."}]]},{"frame":{"path":null,"span":[8656,8661],"sourceCode":"/**\n * Speaker Diarization Hook\n *\n * Manages diarization job lifecycle with polling, backoff, and error handling.\n * Provides start, cancel, and reset functions for controlling diarization jobs.\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { initializeAPI } from '@/api';\nimport type { DiarizationJobStatus, JobStatus } from '@/api/types';\nimport { toast } from '@/hooks/use-toast';\nimport { PollingConfig } from '@/lib/config';\n\n/** Diarization job state */\nexport interface DiarizationState {\n /** Current job ID, if any */\n jobId: string | null;\n /** Job status */\n status: JobStatus | null;\n /** Progress percentage (0-100) */\n progress: number;\n /** Error message, if failed */\n error: string | null;\n /** Detected speaker IDs */\n speakerIds: string[];\n /** Number of segments updated */\n segmentsUpdated: number;\n /** Whether a job is currently active */\n isActive: boolean;\n}\n\n/** Hook configuration options */\nexport interface UseDiarizationOptions {\n /** Callback when diarization completes successfully */\n onComplete?: (status: DiarizationJobStatus) => void;\n /** Callback when diarization fails */\n onError?: (error: string) => void;\n /** Base polling interval in ms (default: 2000) */\n pollInterval?: number;\n /** Maximum retry attempts on network failure (default: 3) */\n maxRetries?: number;\n /** Show toast notifications (default: true) */\n showToasts?: boolean;\n}\n\n/** Hook return value */\nexport interface UseDiarizationReturn {\n /** Current diarization state */\n state: DiarizationState;\n /** Start diarization for a meeting */\n start: (meetingId: string, numSpeakers?: number) => Promise<void>;\n /** Cancel the current diarization job */\n cancel: () => Promise<void>;\n /** Reset all state */\n reset: () => void;\n}\n\nconst INITIAL_STATE: DiarizationState = {\n jobId: null,\n status: null,\n progress: 0,\n error: null,\n speakerIds: [],\n segmentsUpdated: 0,\n isActive: false,\n};\n\nconst POLL_BACKOFF_MULTIPLIER = 1.5;\nconst MAX_POLL_INTERVAL_MS = PollingConfig.DIARIZATION_MAX_MS;\nconst RETRY_BACKOFF_MULTIPLIER = 2;\nconst INITIAL_RETRY_DELAY_MS = PollingConfig.DIARIZATION_RETRY_DELAY_MS;\n\n/**\n * Hook for managing speaker diarization jobs\n */\nexport function useDiarization(options: UseDiarizationOptions = {}): UseDiarizationReturn {\n const {\n onComplete,\n onError,\n pollInterval = PollingConfig.DIARIZATION_INITIAL_MS,\n maxRetries = 3,\n showToasts = true,\n } = options;\n\n const [state, setState] = useState<DiarizationState>(INITIAL_STATE);\n\n // Refs for managing async operations\n const pollTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const retryCountRef = useRef(0);\n const currentPollIntervalRef = useRef(pollInterval);\n const meetingIdRef = useRef<string | null>(null);\n const isMountedRef = useRef(true);\n\n /** Stop polling */\n const stopPolling = useCallback(() => {\n if (pollTimeoutRef.current) {\n clearTimeout(pollTimeoutRef.current);\n pollTimeoutRef.current = null;\n }\n currentPollIntervalRef.current = pollInterval;\n retryCountRef.current = 0;\n }, [pollInterval]);\n\n /** Poll for job status with backoff */\n const poll = useCallback(\n async (jobId: string) => {\n if (!isMountedRef.current) {\n return;\n }\n\n try {\n const api = await initializeAPI();\n const status = await api.getDiarizationJobStatus(jobId);\n\n if (!isMountedRef.current) {\n return;\n }\n\n // Reset retry count on successful poll\n retryCountRef.current = 0;\n\n // Update state based on status\n setState((prev) => ({\n ...prev,\n status: status.status,\n progress: status.progress_percent ?? 0,\n speakerIds: status.speaker_ids ?? [],\n segmentsUpdated: status.segments_updated ?? 0,\n error: status.error_message || null,\n }));\n\n // Check terminal states\n if (status.status === 'completed') {\n stopPolling();\n setState((prev) => ({ ...prev, isActive: false }));\n onComplete?.(status);\n if (showToasts) {\n toast({\n title: 'Diarization complete',\n description: `Updated ${status.segments_updated} segments with ${status.speaker_ids?.length ?? 0} speakers`,\n });\n }\n return;\n }\n\n if (status.status === 'failed') {\n stopPolling();\n setState((prev) => ({ ...prev, isActive: false, error: status.error_message }));\n onError?.(status.error_message || 'Diarization failed');\n if (showToasts) {\n toast({\n title: 'Diarization failed',\n description: status.error_message || 'An error occurred during speaker detection',\n variant: 'destructive',\n });\n }\n return;\n }\n\n if (status.status === 'cancelled') {\n stopPolling();\n setState((prev) => ({ ...prev, isActive: false }));\n if (showToasts) {\n toast({\n title: 'Diarization cancelled',\n description: 'Speaker detection was cancelled',\n });\n }\n return;\n }\n\n // Continue polling with backoff for running/queued jobs\n currentPollIntervalRef.current = Math.min(\n currentPollIntervalRef.current * POLL_BACKOFF_MULTIPLIER,\n MAX_POLL_INTERVAL_MS\n );\n pollTimeoutRef.current = setTimeout(() => poll(jobId), currentPollIntervalRef.current);\n } catch (error) {\n if (!isMountedRef.current) {\n return;\n }\n\n const errorMessage = error instanceof Error ? error.message : 'Network error';\n\n // Retry on network failure\n if (retryCountRef.current < maxRetries) {\n retryCountRef.current += 1;\n const retryDelay =\n INITIAL_RETRY_DELAY_MS * RETRY_BACKOFF_MULTIPLIER ** (retryCountRef.current - 1);\n pollTimeoutRef.current = setTimeout(() => poll(jobId), retryDelay);\n return;\n }\n\n // Max retries exceeded\n stopPolling();\n setState((prev) => ({\n ...prev,\n isActive: false,\n error: `Failed to poll status: ${errorMessage}`,\n }));\n onError?.(`Failed to poll status: ${errorMessage}`);\n if (showToasts) {\n toast({\n title: 'Connection lost',\n description: 'Failed to get diarization status after multiple retries',\n variant: 'destructive',\n });\n }\n }\n },\n [maxRetries, onComplete, onError, showToasts, stopPolling]\n );\n\n /** Start diarization for a meeting */\n const start = useCallback(\n async (meetingId: string, numSpeakers?: number) => {\n // Reset state\n stopPolling();\n setState({ ...INITIAL_STATE, isActive: true });\n meetingIdRef.current = meetingId;\n currentPollIntervalRef.current = pollInterval;\n retryCountRef.current = 0;\n\n try {\n const api = await initializeAPI();\n const status = await api.refineSpeakers(meetingId, numSpeakers);\n\n if (!isMountedRef.current) {\n return;\n }\n\n setState((prev) => ({\n ...prev,\n jobId: status.job_id,\n status: status.status,\n progress: status.progress_percent ?? 0,\n }));\n\n // Start polling if job is queued or running\n if (status.status === 'queued' || status.status === 'running') {\n pollTimeoutRef.current = setTimeout(() => poll(status.job_id), pollInterval);\n } else if (status.status === 'completed') {\n setState((prev) => ({ ...prev, isActive: false }));\n onComplete?.(status);\n } else if (status.status === 'failed') {\n setState((prev) => ({ ...prev, isActive: false, error: status.error_message }));\n onError?.(status.error_message || 'Diarization failed');\n }\n } catch (error) {\n if (!isMountedRef.current) {\n return;\n }\n\n const errorMessage = error instanceof Error ? error.message : 'Failed to start diarization';\n setState((prev) => ({ ...prev, isActive: false, error: errorMessage }));\n onError?.(errorMessage);\n\n if (showToasts) {\n toast({\n title: 'Failed to start diarization',\n description: errorMessage,\n variant: 'destructive',\n });\n }\n }\n },\n [onComplete, onError, poll, pollInterval, showToasts, stopPolling]\n );\n\n /** Cancel the current diarization job */\n const cancel = useCallback(async () => {\n const {jobId} = state;\n if (!jobId) {\n return;\n }\n\n stopPolling();\n\n try {\n const api = await initializeAPI();\n const result = await api.cancelDiarization(jobId);\n\n if (!isMountedRef.current) {\n return;\n }\n\n if (result.success) {\n setState((prev) => ({\n ...prev,\n status: 'cancelled',\n isActive: false,\n }));\n } else {\n const errorMsg = result.error_message || 'Failed to cancel';\n setState((prev) => ({ ...prev, error: errorMsg }));\n if (showToasts) {\n toast({\n title: 'Cancel failed',\n description: errorMsg,\n variant: 'destructive',\n });\n }\n }\n } catch (error) {\n if (!isMountedRef.current) {\n return;\n }\n\n const errorMessage = error instanceof Error ? error.message : 'Failed to cancel';\n setState((prev) => ({ ...prev, error: errorMessage }));\n if (showToasts) {\n toast({\n title: 'Cancel failed',\n description: errorMessage,\n variant: 'destructive',\n });\n }\n }\n }, [state.jobId, showToasts, stopPolling]);\n\n /** Reset all state */\n const reset = useCallback(() => {\n stopPolling();\n setState(INITIAL_STATE);\n meetingIdRef.current = null;\n }, [stopPolling]);\n\n // Cleanup on unmount\n useEffect(() => {\n isMountedRef.current = true;\n return () => {\n isMountedRef.current = false;\n stopPolling();\n };\n }, [stopPolling]);\n\n return {\n state,\n start,\n cancel,\n reset,\n };\n}\n"}},{"log":["info",[{"elements":[],"content":"...this dependency."}]]},{"frame":{"path":null,"span":[9775,9786],"sourceCode":"/**\n * Speaker Diarization Hook\n *\n * Manages diarization job lifecycle with polling, backoff, and error handling.\n * Provides start, cancel, and reset functions for controlling diarization jobs.\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { initializeAPI } from '@/api';\nimport type { DiarizationJobStatus, JobStatus } from '@/api/types';\nimport { toast } from '@/hooks/use-toast';\nimport { PollingConfig } from '@/lib/config';\n\n/** Diarization job state */\nexport interface DiarizationState {\n /** Current job ID, if any */\n jobId: string | null;\n /** Job status */\n status: JobStatus | null;\n /** Progress percentage (0-100) */\n progress: number;\n /** Error message, if failed */\n error: string | null;\n /** Detected speaker IDs */\n speakerIds: string[];\n /** Number of segments updated */\n segmentsUpdated: number;\n /** Whether a job is currently active */\n isActive: boolean;\n}\n\n/** Hook configuration options */\nexport interface UseDiarizationOptions {\n /** Callback when diarization completes successfully */\n onComplete?: (status: DiarizationJobStatus) => void;\n /** Callback when diarization fails */\n onError?: (error: string) => void;\n /** Base polling interval in ms (default: 2000) */\n pollInterval?: number;\n /** Maximum retry attempts on network failure (default: 3) */\n maxRetries?: number;\n /** Show toast notifications (default: true) */\n showToasts?: boolean;\n}\n\n/** Hook return value */\nexport interface UseDiarizationReturn {\n /** Current diarization state */\n state: DiarizationState;\n /** Start diarization for a meeting */\n start: (meetingId: string, numSpeakers?: number) => Promise<void>;\n /** Cancel the current diarization job */\n cancel: () => Promise<void>;\n /** Reset all state */\n reset: () => void;\n}\n\nconst INITIAL_STATE: DiarizationState = {\n jobId: null,\n status: null,\n progress: 0,\n error: null,\n speakerIds: [],\n segmentsUpdated: 0,\n isActive: false,\n};\n\nconst POLL_BACKOFF_MULTIPLIER = 1.5;\nconst MAX_POLL_INTERVAL_MS = PollingConfig.DIARIZATION_MAX_MS;\nconst RETRY_BACKOFF_MULTIPLIER = 2;\nconst INITIAL_RETRY_DELAY_MS = PollingConfig.DIARIZATION_RETRY_DELAY_MS;\n\n/**\n * Hook for managing speaker diarization jobs\n */\nexport function useDiarization(options: UseDiarizationOptions = {}): UseDiarizationReturn {\n const {\n onComplete,\n onError,\n pollInterval = PollingConfig.DIARIZATION_INITIAL_MS,\n maxRetries = 3,\n showToasts = true,\n } = options;\n\n const [state, setState] = useState<DiarizationState>(INITIAL_STATE);\n\n // Refs for managing async operations\n const pollTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const retryCountRef = useRef(0);\n const currentPollIntervalRef = useRef(pollInterval);\n const meetingIdRef = useRef<string | null>(null);\n const isMountedRef = useRef(true);\n\n /** Stop polling */\n const stopPolling = useCallback(() => {\n if (pollTimeoutRef.current) {\n clearTimeout(pollTimeoutRef.current);\n pollTimeoutRef.current = null;\n }\n currentPollIntervalRef.current = pollInterval;\n retryCountRef.current = 0;\n }, [pollInterval]);\n\n /** Poll for job status with backoff */\n const poll = useCallback(\n async (jobId: string) => {\n if (!isMountedRef.current) {\n return;\n }\n\n try {\n const api = await initializeAPI();\n const status = await api.getDiarizationJobStatus(jobId);\n\n if (!isMountedRef.current) {\n return;\n }\n\n // Reset retry count on successful poll\n retryCountRef.current = 0;\n\n // Update state based on status\n setState((prev) => ({\n ...prev,\n status: status.status,\n progress: status.progress_percent ?? 0,\n speakerIds: status.speaker_ids ?? [],\n segmentsUpdated: status.segments_updated ?? 0,\n error: status.error_message || null,\n }));\n\n // Check terminal states\n if (status.status === 'completed') {\n stopPolling();\n setState((prev) => ({ ...prev, isActive: false }));\n onComplete?.(status);\n if (showToasts) {\n toast({\n title: 'Diarization complete',\n description: `Updated ${status.segments_updated} segments with ${status.speaker_ids?.length ?? 0} speakers`,\n });\n }\n return;\n }\n\n if (status.status === 'failed') {\n stopPolling();\n setState((prev) => ({ ...prev, isActive: false, error: status.error_message }));\n onError?.(status.error_message || 'Diarization failed');\n if (showToasts) {\n toast({\n title: 'Diarization failed',\n description: status.error_message || 'An error occurred during speaker detection',\n variant: 'destructive',\n });\n }\n return;\n }\n\n if (status.status === 'cancelled') {\n stopPolling();\n setState((prev) => ({ ...prev, isActive: false }));\n if (showToasts) {\n toast({\n title: 'Diarization cancelled',\n description: 'Speaker detection was cancelled',\n });\n }\n return;\n }\n\n // Continue polling with backoff for running/queued jobs\n currentPollIntervalRef.current = Math.min(\n currentPollIntervalRef.current * POLL_BACKOFF_MULTIPLIER,\n MAX_POLL_INTERVAL_MS\n );\n pollTimeoutRef.current = setTimeout(() => poll(jobId), currentPollIntervalRef.current);\n } catch (error) {\n if (!isMountedRef.current) {\n return;\n }\n\n const errorMessage = error instanceof Error ? error.message : 'Network error';\n\n // Retry on network failure\n if (retryCountRef.current < maxRetries) {\n retryCountRef.current += 1;\n const retryDelay =\n INITIAL_RETRY_DELAY_MS * RETRY_BACKOFF_MULTIPLIER ** (retryCountRef.current - 1);\n pollTimeoutRef.current = setTimeout(() => poll(jobId), retryDelay);\n return;\n }\n\n // Max retries exceeded\n stopPolling();\n setState((prev) => ({\n ...prev,\n isActive: false,\n error: `Failed to poll status: ${errorMessage}`,\n }));\n onError?.(`Failed to poll status: ${errorMessage}`);\n if (showToasts) {\n toast({\n title: 'Connection lost',\n description: 'Failed to get diarization status after multiple retries',\n variant: 'destructive',\n });\n }\n }\n },\n [maxRetries, onComplete, onError, showToasts, stopPolling]\n );\n\n /** Start diarization for a meeting */\n const start = useCallback(\n async (meetingId: string, numSpeakers?: number) => {\n // Reset state\n stopPolling();\n setState({ ...INITIAL_STATE, isActive: true });\n meetingIdRef.current = meetingId;\n currentPollIntervalRef.current = pollInterval;\n retryCountRef.current = 0;\n\n try {\n const api = await initializeAPI();\n const status = await api.refineSpeakers(meetingId, numSpeakers);\n\n if (!isMountedRef.current) {\n return;\n }\n\n setState((prev) => ({\n ...prev,\n jobId: status.job_id,\n status: status.status,\n progress: status.progress_percent ?? 0,\n }));\n\n // Start polling if job is queued or running\n if (status.status === 'queued' || status.status === 'running') {\n pollTimeoutRef.current = setTimeout(() => poll(status.job_id), pollInterval);\n } else if (status.status === 'completed') {\n setState((prev) => ({ ...prev, isActive: false }));\n onComplete?.(status);\n } else if (status.status === 'failed') {\n setState((prev) => ({ ...prev, isActive: false, error: status.error_message }));\n onError?.(status.error_message || 'Diarization failed');\n }\n } catch (error) {\n if (!isMountedRef.current) {\n return;\n }\n\n const errorMessage = error instanceof Error ? error.message : 'Failed to start diarization';\n setState((prev) => ({ ...prev, isActive: false, error: errorMessage }));\n onError?.(errorMessage);\n\n if (showToasts) {\n toast({\n title: 'Failed to start diarization',\n description: errorMessage,\n variant: 'destructive',\n });\n }\n }\n },\n [onComplete, onError, poll, pollInterval, showToasts, stopPolling]\n );\n\n /** Cancel the current diarization job */\n const cancel = useCallback(async () => {\n const {jobId} = state;\n if (!jobId) {\n return;\n }\n\n stopPolling();\n\n try {\n const api = await initializeAPI();\n const result = await api.cancelDiarization(jobId);\n\n if (!isMountedRef.current) {\n return;\n }\n\n if (result.success) {\n setState((prev) => ({\n ...prev,\n status: 'cancelled',\n isActive: false,\n }));\n } else {\n const errorMsg = result.error_message || 'Failed to cancel';\n setState((prev) => ({ ...prev, error: errorMsg }));\n if (showToasts) {\n toast({\n title: 'Cancel failed',\n description: errorMsg,\n variant: 'destructive',\n });\n }\n }\n } catch (error) {\n if (!isMountedRef.current) {\n return;\n }\n\n const errorMessage = error instanceof Error ? error.message : 'Failed to cancel';\n setState((prev) => ({ ...prev, error: errorMessage }));\n if (showToasts) {\n toast({\n title: 'Cancel failed',\n description: errorMessage,\n variant: 'destructive',\n });\n }\n }\n }, [state.jobId, showToasts, stopPolling]);\n\n /** Reset all state */\n const reset = useCallback(() => {\n stopPolling();\n setState(INITIAL_STATE);\n meetingIdRef.current = null;\n }, [stopPolling]);\n\n // Cleanup on unmount\n useEffect(() => {\n isMountedRef.current = true;\n return () => {\n isMountedRef.current = false;\n stopPolling();\n };\n }, [stopPolling]);\n\n return {\n state,\n start,\n cancel,\n reset,\n };\n}\n"}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"src/hooks/use-diarization.ts"},"span":[8610,8621],"sourceCode":"/**\n * Speaker Diarization Hook\n *\n * Manages diarization job lifecycle with polling, backoff, and error handling.\n * Provides start, cancel, and reset functions for controlling diarization jobs.\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { initializeAPI } from '@/api';\nimport type { DiarizationJobStatus, JobStatus } from '@/api/types';\nimport { toast } from '@/hooks/use-toast';\nimport { PollingConfig } from '@/lib/config';\n\n/** Diarization job state */\nexport interface DiarizationState {\n /** Current job ID, if any */\n jobId: string | null;\n /** Job status */\n status: JobStatus | null;\n /** Progress percentage (0-100) */\n progress: number;\n /** Error message, if failed */\n error: string | null;\n /** Detected speaker IDs */\n speakerIds: string[];\n /** Number of segments updated */\n segmentsUpdated: number;\n /** Whether a job is currently active */\n isActive: boolean;\n}\n\n/** Hook configuration options */\nexport interface UseDiarizationOptions {\n /** Callback when diarization completes successfully */\n onComplete?: (status: DiarizationJobStatus) => void;\n /** Callback when diarization fails */\n onError?: (error: string) => void;\n /** Base polling interval in ms (default: 2000) */\n pollInterval?: number;\n /** Maximum retry attempts on network failure (default: 3) */\n maxRetries?: number;\n /** Show toast notifications (default: true) */\n showToasts?: boolean;\n}\n\n/** Hook return value */\nexport interface UseDiarizationReturn {\n /** Current diarization state */\n state: DiarizationState;\n /** Start diarization for a meeting */\n start: (meetingId: string, numSpeakers?: number) => Promise<void>;\n /** Cancel the current diarization job */\n cancel: () => Promise<void>;\n /** Reset all state */\n reset: () => void;\n}\n\nconst INITIAL_STATE: DiarizationState = {\n jobId: null,\n status: null,\n progress: 0,\n error: null,\n speakerIds: [],\n segmentsUpdated: 0,\n isActive: false,\n};\n\nconst POLL_BACKOFF_MULTIPLIER = 1.5;\nconst MAX_POLL_INTERVAL_MS = PollingConfig.DIARIZATION_MAX_MS;\nconst RETRY_BACKOFF_MULTIPLIER = 2;\nconst INITIAL_RETRY_DELAY_MS = PollingConfig.DIARIZATION_RETRY_DELAY_MS;\n\n/**\n * Hook for managing speaker diarization jobs\n */\nexport function useDiarization(options: UseDiarizationOptions = {}): UseDiarizationReturn {\n const {\n onComplete,\n onError,\n pollInterval = PollingConfig.DIARIZATION_INITIAL_MS,\n maxRetries = 3,\n showToasts = true,\n } = options;\n\n const [state, setState] = useState<DiarizationState>(INITIAL_STATE);\n\n // Refs for managing async operations\n const pollTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const retryCountRef = useRef(0);\n const currentPollIntervalRef = useRef(pollInterval);\n const meetingIdRef = useRef<string | null>(null);\n const isMountedRef = useRef(true);\n\n /** Stop polling */\n const stopPolling = useCallback(() => {\n if (pollTimeoutRef.current) {\n clearTimeout(pollTimeoutRef.current);\n pollTimeoutRef.current = null;\n }\n currentPollIntervalRef.current = pollInterval;\n retryCountRef.current = 0;\n }, [pollInterval]);\n\n /** Poll for job status with backoff */\n const poll = useCallback(\n async (jobId: string) => {\n if (!isMountedRef.current) {\n return;\n }\n\n try {\n const api = await initializeAPI();\n const status = await api.getDiarizationJobStatus(jobId);\n\n if (!isMountedRef.current) {\n return;\n }\n\n // Reset retry count on successful poll\n retryCountRef.current = 0;\n\n // Update state based on status\n setState((prev) => ({\n ...prev,\n status: status.status,\n progress: status.progress_percent ?? 0,\n speakerIds: status.speaker_ids ?? [],\n segmentsUpdated: status.segments_updated ?? 0,\n error: status.error_message || null,\n }));\n\n // Check terminal states\n if (status.status === 'completed') {\n stopPolling();\n setState((prev) => ({ ...prev, isActive: false }));\n onComplete?.(status);\n if (showToasts) {\n toast({\n title: 'Diarization complete',\n description: `Updated ${status.segments_updated} segments with ${status.speaker_ids?.length ?? 0} speakers`,\n });\n }\n return;\n }\n\n if (status.status === 'failed') {\n stopPolling();\n setState((prev) => ({ ...prev, isActive: false, error: status.error_message }));\n onError?.(status.error_message || 'Diarization failed');\n if (showToasts) {\n toast({\n title: 'Diarization failed',\n description: status.error_message || 'An error occurred during speaker detection',\n variant: 'destructive',\n });\n }\n return;\n }\n\n if (status.status === 'cancelled') {\n stopPolling();\n setState((prev) => ({ ...prev, isActive: false }));\n if (showToasts) {\n toast({\n title: 'Diarization cancelled',\n description: 'Speaker detection was cancelled',\n });\n }\n return;\n }\n\n // Continue polling with backoff for running/queued jobs\n currentPollIntervalRef.current = Math.min(\n currentPollIntervalRef.current * POLL_BACKOFF_MULTIPLIER,\n MAX_POLL_INTERVAL_MS\n );\n pollTimeoutRef.current = setTimeout(() => poll(jobId), currentPollIntervalRef.current);\n } catch (error) {\n if (!isMountedRef.current) {\n return;\n }\n\n const errorMessage = error instanceof Error ? error.message : 'Network error';\n\n // Retry on network failure\n if (retryCountRef.current < maxRetries) {\n retryCountRef.current += 1;\n const retryDelay =\n INITIAL_RETRY_DELAY_MS * RETRY_BACKOFF_MULTIPLIER ** (retryCountRef.current - 1);\n pollTimeoutRef.current = setTimeout(() => poll(jobId), retryDelay);\n return;\n }\n\n // Max retries exceeded\n stopPolling();\n setState((prev) => ({\n ...prev,\n isActive: false,\n error: `Failed to poll status: ${errorMessage}`,\n }));\n onError?.(`Failed to poll status: ${errorMessage}`);\n if (showToasts) {\n toast({\n title: 'Connection lost',\n description: 'Failed to get diarization status after multiple retries',\n variant: 'destructive',\n });\n }\n }\n },\n [maxRetries, onComplete, onError, showToasts, stopPolling]\n );\n\n /** Start diarization for a meeting */\n const start = useCallback(\n async (meetingId: string, numSpeakers?: number) => {\n // Reset state\n stopPolling();\n setState({ ...INITIAL_STATE, isActive: true });\n meetingIdRef.current = meetingId;\n currentPollIntervalRef.current = pollInterval;\n retryCountRef.current = 0;\n\n try {\n const api = await initializeAPI();\n const status = await api.refineSpeakers(meetingId, numSpeakers);\n\n if (!isMountedRef.current) {\n return;\n }\n\n setState((prev) => ({\n ...prev,\n jobId: status.job_id,\n status: status.status,\n progress: status.progress_percent ?? 0,\n }));\n\n // Start polling if job is queued or running\n if (status.status === 'queued' || status.status === 'running') {\n pollTimeoutRef.current = setTimeout(() => poll(status.job_id), pollInterval);\n } else if (status.status === 'completed') {\n setState((prev) => ({ ...prev, isActive: false }));\n onComplete?.(status);\n } else if (status.status === 'failed') {\n setState((prev) => ({ ...prev, isActive: false, error: status.error_message }));\n onError?.(status.error_message || 'Diarization failed');\n }\n } catch (error) {\n if (!isMountedRef.current) {\n return;\n }\n\n const errorMessage = error instanceof Error ? error.message : 'Failed to start diarization';\n setState((prev) => ({ ...prev, isActive: false, error: errorMessage }));\n onError?.(errorMessage);\n\n if (showToasts) {\n toast({\n title: 'Failed to start diarization',\n description: errorMessage,\n variant: 'destructive',\n });\n }\n }\n },\n [onComplete, onError, poll, pollInterval, showToasts, stopPolling]\n );\n\n /** Cancel the current diarization job */\n const cancel = useCallback(async () => {\n const {jobId} = state;\n if (!jobId) {\n return;\n }\n\n stopPolling();\n\n try {\n const api = await initializeAPI();\n const result = await api.cancelDiarization(jobId);\n\n if (!isMountedRef.current) {\n return;\n }\n\n if (result.success) {\n setState((prev) => ({\n ...prev,\n status: 'cancelled',\n isActive: false,\n }));\n } else {\n const errorMsg = result.error_message || 'Failed to cancel';\n setState((prev) => ({ ...prev, error: errorMsg }));\n if (showToasts) {\n toast({\n title: 'Cancel failed',\n description: errorMsg,\n variant: 'destructive',\n });\n }\n }\n } catch (error) {\n if (!isMountedRef.current) {\n return;\n }\n\n const errorMessage = error instanceof Error ? error.message : 'Failed to cancel';\n setState((prev) => ({ ...prev, error: errorMessage }));\n if (showToasts) {\n toast({\n title: 'Cancel failed',\n description: errorMessage,\n variant: 'destructive',\n });\n }\n }\n }, [state.jobId, showToasts, stopPolling]);\n\n /** Reset all state */\n const reset = useCallback(() => {\n stopPolling();\n setState(INITIAL_STATE);\n meetingIdRef.current = null;\n }, [stopPolling]);\n\n // Cleanup on unmount\n useEffect(() => {\n isMountedRef.current = true;\n return () => {\n isMountedRef.current = false;\n stopPolling();\n };\n }, [stopPolling]);\n\n return {\n state,\n start,\n cancel,\n reset,\n };\n}\n"},"tags":[],"source":null},{"category":"lint/correctness/useExhaustiveDependencies","severity":"warning","description":"This hook does not specify its dependency on state.","message":[{"elements":[],"content":"This hook "},{"elements":["Emphasis"],"content":"does not specify"},{"elements":[],"content":" its dependency on "},{"elements":["Emphasis"],"content":"state"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"This dependency is being used here, but is not specified in the hook dependency list."}]]},{"frame":{"path":null,"span":[8656,8661],"sourceCode":"/**\n * Speaker Diarization Hook\n *\n * Manages diarization job lifecycle with polling, backoff, and error handling.\n * Provides start, cancel, and reset functions for controlling diarization jobs.\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { initializeAPI } from '@/api';\nimport type { DiarizationJobStatus, JobStatus } from '@/api/types';\nimport { toast } from '@/hooks/use-toast';\nimport { PollingConfig } from '@/lib/config';\n\n/** Diarization job state */\nexport interface DiarizationState {\n /** Current job ID, if any */\n jobId: string | null;\n /** Job status */\n status: JobStatus | null;\n /** Progress percentage (0-100) */\n progress: number;\n /** Error message, if failed */\n error: string | null;\n /** Detected speaker IDs */\n speakerIds: string[];\n /** Number of segments updated */\n segmentsUpdated: number;\n /** Whether a job is currently active */\n isActive: boolean;\n}\n\n/** Hook configuration options */\nexport interface UseDiarizationOptions {\n /** Callback when diarization completes successfully */\n onComplete?: (status: DiarizationJobStatus) => void;\n /** Callback when diarization fails */\n onError?: (error: string) => void;\n /** Base polling interval in ms (default: 2000) */\n pollInterval?: number;\n /** Maximum retry attempts on network failure (default: 3) */\n maxRetries?: number;\n /** Show toast notifications (default: true) */\n showToasts?: boolean;\n}\n\n/** Hook return value */\nexport interface UseDiarizationReturn {\n /** Current diarization state */\n state: DiarizationState;\n /** Start diarization for a meeting */\n start: (meetingId: string, numSpeakers?: number) => Promise<void>;\n /** Cancel the current diarization job */\n cancel: () => Promise<void>;\n /** Reset all state */\n reset: () => void;\n}\n\nconst INITIAL_STATE: DiarizationState = {\n jobId: null,\n status: null,\n progress: 0,\n error: null,\n speakerIds: [],\n segmentsUpdated: 0,\n isActive: false,\n};\n\nconst POLL_BACKOFF_MULTIPLIER = 1.5;\nconst MAX_POLL_INTERVAL_MS = PollingConfig.DIARIZATION_MAX_MS;\nconst RETRY_BACKOFF_MULTIPLIER = 2;\nconst INITIAL_RETRY_DELAY_MS = PollingConfig.DIARIZATION_RETRY_DELAY_MS;\n\n/**\n * Hook for managing speaker diarization jobs\n */\nexport function useDiarization(options: UseDiarizationOptions = {}): UseDiarizationReturn {\n const {\n onComplete,\n onError,\n pollInterval = PollingConfig.DIARIZATION_INITIAL_MS,\n maxRetries = 3,\n showToasts = true,\n } = options;\n\n const [state, setState] = useState<DiarizationState>(INITIAL_STATE);\n\n // Refs for managing async operations\n const pollTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const retryCountRef = useRef(0);\n const currentPollIntervalRef = useRef(pollInterval);\n const meetingIdRef = useRef<string | null>(null);\n const isMountedRef = useRef(true);\n\n /** Stop polling */\n const stopPolling = useCallback(() => {\n if (pollTimeoutRef.current) {\n clearTimeout(pollTimeoutRef.current);\n pollTimeoutRef.current = null;\n }\n currentPollIntervalRef.current = pollInterval;\n retryCountRef.current = 0;\n }, [pollInterval]);\n\n /** Poll for job status with backoff */\n const poll = useCallback(\n async (jobId: string) => {\n if (!isMountedRef.current) {\n return;\n }\n\n try {\n const api = await initializeAPI();\n const status = await api.getDiarizationJobStatus(jobId);\n\n if (!isMountedRef.current) {\n return;\n }\n\n // Reset retry count on successful poll\n retryCountRef.current = 0;\n\n // Update state based on status\n setState((prev) => ({\n ...prev,\n status: status.status,\n progress: status.progress_percent ?? 0,\n speakerIds: status.speaker_ids ?? [],\n segmentsUpdated: status.segments_updated ?? 0,\n error: status.error_message || null,\n }));\n\n // Check terminal states\n if (status.status === 'completed') {\n stopPolling();\n setState((prev) => ({ ...prev, isActive: false }));\n onComplete?.(status);\n if (showToasts) {\n toast({\n title: 'Diarization complete',\n description: `Updated ${status.segments_updated} segments with ${status.speaker_ids?.length ?? 0} speakers`,\n });\n }\n return;\n }\n\n if (status.status === 'failed') {\n stopPolling();\n setState((prev) => ({ ...prev, isActive: false, error: status.error_message }));\n onError?.(status.error_message || 'Diarization failed');\n if (showToasts) {\n toast({\n title: 'Diarization failed',\n description: status.error_message || 'An error occurred during speaker detection',\n variant: 'destructive',\n });\n }\n return;\n }\n\n if (status.status === 'cancelled') {\n stopPolling();\n setState((prev) => ({ ...prev, isActive: false }));\n if (showToasts) {\n toast({\n title: 'Diarization cancelled',\n description: 'Speaker detection was cancelled',\n });\n }\n return;\n }\n\n // Continue polling with backoff for running/queued jobs\n currentPollIntervalRef.current = Math.min(\n currentPollIntervalRef.current * POLL_BACKOFF_MULTIPLIER,\n MAX_POLL_INTERVAL_MS\n );\n pollTimeoutRef.current = setTimeout(() => poll(jobId), currentPollIntervalRef.current);\n } catch (error) {\n if (!isMountedRef.current) {\n return;\n }\n\n const errorMessage = error instanceof Error ? error.message : 'Network error';\n\n // Retry on network failure\n if (retryCountRef.current < maxRetries) {\n retryCountRef.current += 1;\n const retryDelay =\n INITIAL_RETRY_DELAY_MS * RETRY_BACKOFF_MULTIPLIER ** (retryCountRef.current - 1);\n pollTimeoutRef.current = setTimeout(() => poll(jobId), retryDelay);\n return;\n }\n\n // Max retries exceeded\n stopPolling();\n setState((prev) => ({\n ...prev,\n isActive: false,\n error: `Failed to poll status: ${errorMessage}`,\n }));\n onError?.(`Failed to poll status: ${errorMessage}`);\n if (showToasts) {\n toast({\n title: 'Connection lost',\n description: 'Failed to get diarization status after multiple retries',\n variant: 'destructive',\n });\n }\n }\n },\n [maxRetries, onComplete, onError, showToasts, stopPolling]\n );\n\n /** Start diarization for a meeting */\n const start = useCallback(\n async (meetingId: string, numSpeakers?: number) => {\n // Reset state\n stopPolling();\n setState({ ...INITIAL_STATE, isActive: true });\n meetingIdRef.current = meetingId;\n currentPollIntervalRef.current = pollInterval;\n retryCountRef.current = 0;\n\n try {\n const api = await initializeAPI();\n const status = await api.refineSpeakers(meetingId, numSpeakers);\n\n if (!isMountedRef.current) {\n return;\n }\n\n setState((prev) => ({\n ...prev,\n jobId: status.job_id,\n status: status.status,\n progress: status.progress_percent ?? 0,\n }));\n\n // Start polling if job is queued or running\n if (status.status === 'queued' || status.status === 'running') {\n pollTimeoutRef.current = setTimeout(() => poll(status.job_id), pollInterval);\n } else if (status.status === 'completed') {\n setState((prev) => ({ ...prev, isActive: false }));\n onComplete?.(status);\n } else if (status.status === 'failed') {\n setState((prev) => ({ ...prev, isActive: false, error: status.error_message }));\n onError?.(status.error_message || 'Diarization failed');\n }\n } catch (error) {\n if (!isMountedRef.current) {\n return;\n }\n\n const errorMessage = error instanceof Error ? error.message : 'Failed to start diarization';\n setState((prev) => ({ ...prev, isActive: false, error: errorMessage }));\n onError?.(errorMessage);\n\n if (showToasts) {\n toast({\n title: 'Failed to start diarization',\n description: errorMessage,\n variant: 'destructive',\n });\n }\n }\n },\n [onComplete, onError, poll, pollInterval, showToasts, stopPolling]\n );\n\n /** Cancel the current diarization job */\n const cancel = useCallback(async () => {\n const {jobId} = state;\n if (!jobId) {\n return;\n }\n\n stopPolling();\n\n try {\n const api = await initializeAPI();\n const result = await api.cancelDiarization(jobId);\n\n if (!isMountedRef.current) {\n return;\n }\n\n if (result.success) {\n setState((prev) => ({\n ...prev,\n status: 'cancelled',\n isActive: false,\n }));\n } else {\n const errorMsg = result.error_message || 'Failed to cancel';\n setState((prev) => ({ ...prev, error: errorMsg }));\n if (showToasts) {\n toast({\n title: 'Cancel failed',\n description: errorMsg,\n variant: 'destructive',\n });\n }\n }\n } catch (error) {\n if (!isMountedRef.current) {\n return;\n }\n\n const errorMessage = error instanceof Error ? error.message : 'Failed to cancel';\n setState((prev) => ({ ...prev, error: errorMessage }));\n if (showToasts) {\n toast({\n title: 'Cancel failed',\n description: errorMessage,\n variant: 'destructive',\n });\n }\n }\n }, [state.jobId, showToasts, stopPolling]);\n\n /** Reset all state */\n const reset = useCallback(() => {\n stopPolling();\n setState(INITIAL_STATE);\n meetingIdRef.current = null;\n }, [stopPolling]);\n\n // Cleanup on unmount\n useEffect(() => {\n isMountedRef.current = true;\n return () => {\n isMountedRef.current = false;\n stopPolling();\n };\n }, [stopPolling]);\n\n return {\n state,\n start,\n cancel,\n reset,\n };\n}\n"}},{"log":["info",[{"elements":[],"content":"React relies on hook dependencies to determine when to re-compute Effects.\nFailing to specify dependencies can result in Effects "},{"elements":["Emphasis"],"content":"not updating correctly"},{"elements":[],"content":" when state changes.\nThese \"stale closures\" are a common source of surprising bugs."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Add the missing dependency to the list."}]]},{"diff":{"dictionary":"/**\n * Speaker Diarization Hook\n * }\n }\n }, [state.jobId, showToasts, stopPolling, state]);\n\n /** Reset all state */ };\n}\n","ops":[{"diffOp":{"equal":{"range":[0,34]}}},{"equalLines":{"line_count":313}},{"diffOp":{"equal":{"range":[34,53]}}},{"diffOp":{"equal":{"range":[53,90]}}},{"diffOp":{"insert":{"range":[90,97]}}},{"diffOp":{"equal":{"range":[97,98]}}},{"diffOp":{"equal":{"range":[98,126]}}},{"equalLines":{"line_count":20}},{"diffOp":{"equal":{"range":[126,133]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"src/hooks/use-diarization.ts"},"span":[8610,8621],"sourceCode":"/**\n * Speaker Diarization Hook\n *\n * Manages diarization job lifecycle with polling, backoff, and error handling.\n * Provides start, cancel, and reset functions for controlling diarization jobs.\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { initializeAPI } from '@/api';\nimport type { DiarizationJobStatus, JobStatus } from '@/api/types';\nimport { toast } from '@/hooks/use-toast';\nimport { PollingConfig } from '@/lib/config';\n\n/** Diarization job state */\nexport interface DiarizationState {\n /** Current job ID, if any */\n jobId: string | null;\n /** Job status */\n status: JobStatus | null;\n /** Progress percentage (0-100) */\n progress: number;\n /** Error message, if failed */\n error: string | null;\n /** Detected speaker IDs */\n speakerIds: string[];\n /** Number of segments updated */\n segmentsUpdated: number;\n /** Whether a job is currently active */\n isActive: boolean;\n}\n\n/** Hook configuration options */\nexport interface UseDiarizationOptions {\n /** Callback when diarization completes successfully */\n onComplete?: (status: DiarizationJobStatus) => void;\n /** Callback when diarization fails */\n onError?: (error: string) => void;\n /** Base polling interval in ms (default: 2000) */\n pollInterval?: number;\n /** Maximum retry attempts on network failure (default: 3) */\n maxRetries?: number;\n /** Show toast notifications (default: true) */\n showToasts?: boolean;\n}\n\n/** Hook return value */\nexport interface UseDiarizationReturn {\n /** Current diarization state */\n state: DiarizationState;\n /** Start diarization for a meeting */\n start: (meetingId: string, numSpeakers?: number) => Promise<void>;\n /** Cancel the current diarization job */\n cancel: () => Promise<void>;\n /** Reset all state */\n reset: () => void;\n}\n\nconst INITIAL_STATE: DiarizationState = {\n jobId: null,\n status: null,\n progress: 0,\n error: null,\n speakerIds: [],\n segmentsUpdated: 0,\n isActive: false,\n};\n\nconst POLL_BACKOFF_MULTIPLIER = 1.5;\nconst MAX_POLL_INTERVAL_MS = PollingConfig.DIARIZATION_MAX_MS;\nconst RETRY_BACKOFF_MULTIPLIER = 2;\nconst INITIAL_RETRY_DELAY_MS = PollingConfig.DIARIZATION_RETRY_DELAY_MS;\n\n/**\n * Hook for managing speaker diarization jobs\n */\nexport function useDiarization(options: UseDiarizationOptions = {}): UseDiarizationReturn {\n const {\n onComplete,\n onError,\n pollInterval = PollingConfig.DIARIZATION_INITIAL_MS,\n maxRetries = 3,\n showToasts = true,\n } = options;\n\n const [state, setState] = useState<DiarizationState>(INITIAL_STATE);\n\n // Refs for managing async operations\n const pollTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const retryCountRef = useRef(0);\n const currentPollIntervalRef = useRef(pollInterval);\n const meetingIdRef = useRef<string | null>(null);\n const isMountedRef = useRef(true);\n\n /** Stop polling */\n const stopPolling = useCallback(() => {\n if (pollTimeoutRef.current) {\n clearTimeout(pollTimeoutRef.current);\n pollTimeoutRef.current = null;\n }\n currentPollIntervalRef.current = pollInterval;\n retryCountRef.current = 0;\n }, [pollInterval]);\n\n /** Poll for job status with backoff */\n const poll = useCallback(\n async (jobId: string) => {\n if (!isMountedRef.current) {\n return;\n }\n\n try {\n const api = await initializeAPI();\n const status = await api.getDiarizationJobStatus(jobId);\n\n if (!isMountedRef.current) {\n return;\n }\n\n // Reset retry count on successful poll\n retryCountRef.current = 0;\n\n // Update state based on status\n setState((prev) => ({\n ...prev,\n status: status.status,\n progress: status.progress_percent ?? 0,\n speakerIds: status.speaker_ids ?? [],\n segmentsUpdated: status.segments_updated ?? 0,\n error: status.error_message || null,\n }));\n\n // Check terminal states\n if (status.status === 'completed') {\n stopPolling();\n setState((prev) => ({ ...prev, isActive: false }));\n onComplete?.(status);\n if (showToasts) {\n toast({\n title: 'Diarization complete',\n description: `Updated ${status.segments_updated} segments with ${status.speaker_ids?.length ?? 0} speakers`,\n });\n }\n return;\n }\n\n if (status.status === 'failed') {\n stopPolling();\n setState((prev) => ({ ...prev, isActive: false, error: status.error_message }));\n onError?.(status.error_message || 'Diarization failed');\n if (showToasts) {\n toast({\n title: 'Diarization failed',\n description: status.error_message || 'An error occurred during speaker detection',\n variant: 'destructive',\n });\n }\n return;\n }\n\n if (status.status === 'cancelled') {\n stopPolling();\n setState((prev) => ({ ...prev, isActive: false }));\n if (showToasts) {\n toast({\n title: 'Diarization cancelled',\n description: 'Speaker detection was cancelled',\n });\n }\n return;\n }\n\n // Continue polling with backoff for running/queued jobs\n currentPollIntervalRef.current = Math.min(\n currentPollIntervalRef.current * POLL_BACKOFF_MULTIPLIER,\n MAX_POLL_INTERVAL_MS\n );\n pollTimeoutRef.current = setTimeout(() => poll(jobId), currentPollIntervalRef.current);\n } catch (error) {\n if (!isMountedRef.current) {\n return;\n }\n\n const errorMessage = error instanceof Error ? error.message : 'Network error';\n\n // Retry on network failure\n if (retryCountRef.current < maxRetries) {\n retryCountRef.current += 1;\n const retryDelay =\n INITIAL_RETRY_DELAY_MS * RETRY_BACKOFF_MULTIPLIER ** (retryCountRef.current - 1);\n pollTimeoutRef.current = setTimeout(() => poll(jobId), retryDelay);\n return;\n }\n\n // Max retries exceeded\n stopPolling();\n setState((prev) => ({\n ...prev,\n isActive: false,\n error: `Failed to poll status: ${errorMessage}`,\n }));\n onError?.(`Failed to poll status: ${errorMessage}`);\n if (showToasts) {\n toast({\n title: 'Connection lost',\n description: 'Failed to get diarization status after multiple retries',\n variant: 'destructive',\n });\n }\n }\n },\n [maxRetries, onComplete, onError, showToasts, stopPolling]\n );\n\n /** Start diarization for a meeting */\n const start = useCallback(\n async (meetingId: string, numSpeakers?: number) => {\n // Reset state\n stopPolling();\n setState({ ...INITIAL_STATE, isActive: true });\n meetingIdRef.current = meetingId;\n currentPollIntervalRef.current = pollInterval;\n retryCountRef.current = 0;\n\n try {\n const api = await initializeAPI();\n const status = await api.refineSpeakers(meetingId, numSpeakers);\n\n if (!isMountedRef.current) {\n return;\n }\n\n setState((prev) => ({\n ...prev,\n jobId: status.job_id,\n status: status.status,\n progress: status.progress_percent ?? 0,\n }));\n\n // Start polling if job is queued or running\n if (status.status === 'queued' || status.status === 'running') {\n pollTimeoutRef.current = setTimeout(() => poll(status.job_id), pollInterval);\n } else if (status.status === 'completed') {\n setState((prev) => ({ ...prev, isActive: false }));\n onComplete?.(status);\n } else if (status.status === 'failed') {\n setState((prev) => ({ ...prev, isActive: false, error: status.error_message }));\n onError?.(status.error_message || 'Diarization failed');\n }\n } catch (error) {\n if (!isMountedRef.current) {\n return;\n }\n\n const errorMessage = error instanceof Error ? error.message : 'Failed to start diarization';\n setState((prev) => ({ ...prev, isActive: false, error: errorMessage }));\n onError?.(errorMessage);\n\n if (showToasts) {\n toast({\n title: 'Failed to start diarization',\n description: errorMessage,\n variant: 'destructive',\n });\n }\n }\n },\n [onComplete, onError, poll, pollInterval, showToasts, stopPolling]\n );\n\n /** Cancel the current diarization job */\n const cancel = useCallback(async () => {\n const {jobId} = state;\n if (!jobId) {\n return;\n }\n\n stopPolling();\n\n try {\n const api = await initializeAPI();\n const result = await api.cancelDiarization(jobId);\n\n if (!isMountedRef.current) {\n return;\n }\n\n if (result.success) {\n setState((prev) => ({\n ...prev,\n status: 'cancelled',\n isActive: false,\n }));\n } else {\n const errorMsg = result.error_message || 'Failed to cancel';\n setState((prev) => ({ ...prev, error: errorMsg }));\n if (showToasts) {\n toast({\n title: 'Cancel failed',\n description: errorMsg,\n variant: 'destructive',\n });\n }\n }\n } catch (error) {\n if (!isMountedRef.current) {\n return;\n }\n\n const errorMessage = error instanceof Error ? error.message : 'Failed to cancel';\n setState((prev) => ({ ...prev, error: errorMessage }));\n if (showToasts) {\n toast({\n title: 'Cancel failed',\n description: errorMessage,\n variant: 'destructive',\n });\n }\n }\n }, [state.jobId, showToasts, stopPolling]);\n\n /** Reset all state */\n const reset = useCallback(() => {\n stopPolling();\n setState(INITIAL_STATE);\n meetingIdRef.current = null;\n }, [stopPolling]);\n\n // Cleanup on unmount\n useEffect(() => {\n isMountedRef.current = true;\n return () => {\n isMountedRef.current = false;\n stopPolling();\n };\n }, [stopPolling]);\n\n return {\n state,\n start,\n cancel,\n reset,\n };\n}\n"},"tags":["fixable"],"source":null},{"category":"lint/complexity/useArrowFunction","severity":"warning","description":"This function expression can be turned into an arrow function.","message":[{"elements":[],"content":"This "},{"elements":["Emphasis"],"content":"function expression"},{"elements":[],"content":" can be turned into an "},{"elements":["Emphasis"],"content":"arrow function"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"Function expressions"},{"elements":[],"content":" that don't use "},{"elements":["Emphasis"],"content":"this"},{"elements":[],"content":" can be turned into "},{"elements":["Emphasis"],"content":"arrow functions"},{"elements":[],"content":"."}]]},{"log":["info",[{"elements":[],"content":"Safe fix: Use an "},{"elements":["Emphasis"],"content":"arrow function"},{"elements":[],"content":" instead."}]]},{"diff":{"dictionary":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n *\n // Hooks\n onPrepare: async function () => {\n const driverPath = getTauriDriverPath();\n console.log(`Starting tauri-driver: ${driverPath}`); setTimeout(checkReady, 500);\n });\n },\n\n onComplete: async function () { },\n};\n","ops":[{"diffOp":{"equal":{"range":[0,60]}}},{"equalLines":{"line_count":165}},{"diffOp":{"equal":{"range":[60,85]}}},{"diffOp":{"equal":{"range":[85,91]}}},{"diffOp":{"delete":{"range":[91,100]}}},{"diffOp":{"equal":{"range":[100,103]}}},{"diffOp":{"insert":{"range":[103,106]}}},{"diffOp":{"equal":{"range":[106,209]}}},{"equalLines":{"line_count":63}},{"diffOp":{"equal":{"range":[209,255]}}},{"diffOp":{"equal":{"range":[255,291]}}},{"equalLines":{"line_count":27}},{"diffOp":{"equal":{"range":[291,299]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"wdio.conf.ts"},"span":[4514,6684],"sourceCode":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n *\n * This config runs tests against the actual Tauri desktop app using tauri-driver.\n * Requires: cargo install tauri-driver\n *\n * Usage:\n * 1. Build the app: npm run tauri:build\n * 2. Run tests: npm run test:native\n */\n\nimport type { Options } from '@wdio/types';\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport { fileURLToPath } from 'url';\nimport { spawn, type ChildProcess } from 'child_process';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// Track tauri-driver process\nlet tauriDriverProcess: ChildProcess | null = null;\n\n// Detect the built Tauri binary path based on platform\nfunction getTauriBinaryPath(): string {\n const projectRoot = path.resolve(__dirname, 'src-tauri');\n\n if (process.platform === 'win32') {\n // Windows: look for .exe in release or debug\n // Binary name comes from Cargo.toml package name\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri.exe');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri.exe');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n\n // Fallback to release path (will error if not built)\n return releasePath;\n } else if (process.platform === 'darwin') {\n // macOS: .app bundle\n const releasePath = path.join(\n projectRoot,\n 'target',\n 'release',\n 'bundle',\n 'macos',\n 'NoteFlow.app',\n 'Contents',\n 'MacOS',\n 'noteflow-tauri'\n );\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n } else {\n // Linux: AppImage or direct binary\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n }\n}\n\n// Get tauri-driver path\nfunction getTauriDriverPath(): string {\n if (process.platform === 'win32') {\n // On Windows, tauri-driver is in cargo bin\n const cargoHome = process.env.CARGO_HOME || path.join(process.env.USERPROFILE || '', '.cargo');\n return path.join(cargoHome, 'bin', 'tauri-driver.exe');\n }\n return 'tauri-driver';\n}\n\n// Get msedgedriver path (Windows only)\nasync function getMsEdgeDriverPath(): Promise<string | null> {\n if (process.platform !== 'win32') {\n return null;\n }\n\n // Try edgedriver npm package first\n try {\n const edgedriver = await import('edgedriver');\n // The package provides a function to get/download the binary\n if (typeof edgedriver.default === 'string') {\n return edgedriver.default;\n }\n if (edgedriver.binPath && fs.existsSync(edgedriver.binPath)) {\n return edgedriver.binPath;\n }\n } catch {\n // Package not available or failed\n }\n\n // Check common locations\n const possiblePaths = [\n // Custom env var\n process.env.MSEDGEDRIVER_PATH,\n // Common install locations\n 'C:\\\\Program Files\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n 'C:\\\\Program Files (x86)\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n path.join(process.env.USERPROFILE || '', 'msedgedriver.exe'),\n path.join(process.env.USERPROFILE || '', '.webdrivers', 'msedgedriver.exe'),\n ];\n\n for (const p of possiblePaths) {\n if (p && fs.existsSync(p)) {\n return p;\n }\n }\n\n return null;\n}\n\nexport const config: Options.Testrunner = {\n // Test specs\n specs: ['./e2e-native/**/*.spec.ts'],\n exclude: [],\n\n // Capabilities\n maxInstances: 1, // Tauri apps should run one at a time\n capabilities: [\n {\n // Use tauri-driver as the WebDriver server\n 'tauri:options': {\n application: getTauriBinaryPath(),\n },\n },\n ],\n\n // Test framework\n framework: 'mocha',\n mochaOpts: {\n ui: 'bdd',\n timeout: 60000,\n },\n\n // Reporters\n reporters: ['spec'],\n\n // Log level\n logLevel: 'info',\n\n // Connection settings for tauri-driver\n hostname: '127.0.0.1',\n port: 4444,\n\n // No built-in service - tauri-driver started via onPrepare hook\n services: [],\n\n // Timeouts\n connectionRetryTimeout: 120000,\n connectionRetryCount: 3,\n\n // Hooks\n onPrepare: async function () {\n const driverPath = getTauriDriverPath();\n console.log(`Starting tauri-driver: ${driverPath}`);\n\n // Check if tauri-driver exists\n if (!fs.existsSync(driverPath)) {\n throw new Error(\n `tauri-driver not found at: ${driverPath}\\n` +\n 'Install it with: cargo install tauri-driver'\n );\n }\n\n // On Windows, check for msedgedriver\n const edgeDriverPath = await getMsEdgeDriverPath();\n if (process.platform === 'win32' && !edgeDriverPath) {\n console.warn(\n '\\n⚠ msedgedriver.exe not found in common locations.\\n' +\n ' Download from: https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/\\n' +\n ' Then either:\\n' +\n ' - Add to PATH\\n' +\n ' - Set MSEDGEDRIVER_PATH environment variable\\n' +\n ' - Place in your home directory\\n'\n );\n }\n\n // Build args\n const args = ['--port', '4444'];\n if (edgeDriverPath) {\n args.push('--native-driver', edgeDriverPath);\n console.log(`Using msedgedriver: ${edgeDriverPath}`);\n }\n\n // Start tauri-driver\n tauriDriverProcess = spawn(driverPath, args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n tauriDriverProcess.stdout?.on('data', (data) => {\n console.log(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n tauriDriverProcess.stderr?.on('data', (data) => {\n console.error(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n // Wait for tauri-driver to be ready\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error('tauri-driver failed to start within 10s'));\n }, 10000);\n\n const checkReady = async () => {\n try {\n const response = await fetch('http://127.0.0.1:4444/status');\n if (response.ok) {\n clearTimeout(timeout);\n console.log('tauri-driver is ready');\n resolve();\n }\n } catch {\n // Not ready yet, retry\n setTimeout(checkReady, 200);\n }\n };\n\n // Start checking after a brief delay\n setTimeout(checkReady, 500);\n });\n },\n\n onComplete: async function () {\n // Stop tauri-driver\n if (tauriDriverProcess) {\n console.log('Stopping tauri-driver');\n tauriDriverProcess.kill();\n tauriDriverProcess = null;\n }\n },\n\n beforeSession: async function () {\n const binaryPath = getTauriBinaryPath();\n if (!fs.existsSync(binaryPath)) {\n throw new Error(\n `Tauri binary not found at: ${binaryPath}\\n` +\n 'Please build the app first with: npm run tauri:build'\n );\n }\n console.log(`Using Tauri binary: ${binaryPath}`);\n },\n\n afterTest: async function (test, _context, { error }) {\n if (error) {\n // Take screenshot on failure\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const screenshotPath = `./e2e-native/screenshots/${test.title}-${timestamp}.png`;\n await browser.saveScreenshot(screenshotPath);\n console.log(`Screenshot saved: ${screenshotPath}`);\n }\n },\n};\n"},"tags":["fixable"],"source":null},{"category":"lint/complexity/useArrowFunction","severity":"warning","description":"This function expression can be turned into an arrow function.","message":[{"elements":[],"content":"This "},{"elements":["Emphasis"],"content":"function expression"},{"elements":[],"content":" can be turned into an "},{"elements":["Emphasis"],"content":"arrow function"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"Function expressions"},{"elements":[],"content":" that don't use "},{"elements":["Emphasis"],"content":"this"},{"elements":[],"content":" can be turned into "},{"elements":["Emphasis"],"content":"arrow functions"},{"elements":[],"content":"."}]]},{"log":["info",[{"elements":[],"content":"Safe fix: Use an "},{"elements":["Emphasis"],"content":"arrow function"},{"elements":[],"content":" instead."}]]},{"diff":{"dictionary":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n * },\n\n onComplete: async function () => {\n // Stop tauri-driver\n if (tauriDriverProcess) { tauriDriverProcess = null;\n }\n },\n\n beforeSession: async function () { },\n};\n","ops":[{"diffOp":{"equal":{"range":[0,60]}}},{"equalLines":{"line_count":235}},{"diffOp":{"equal":{"range":[60,80]}}},{"diffOp":{"equal":{"range":[80,86]}}},{"diffOp":{"delete":{"range":[86,95]}}},{"diffOp":{"equal":{"range":[95,98]}}},{"diffOp":{"insert":{"range":[98,101]}}},{"diffOp":{"equal":{"range":[101,157]}}},{"equalLines":{"line_count":2}},{"diffOp":{"equal":{"range":[157,199]}}},{"diffOp":{"equal":{"range":[199,238]}}},{"equalLines":{"line_count":18}},{"diffOp":{"equal":{"range":[238,246]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"wdio.conf.ts"},"span":[6701,6895],"sourceCode":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n *\n * This config runs tests against the actual Tauri desktop app using tauri-driver.\n * Requires: cargo install tauri-driver\n *\n * Usage:\n * 1. Build the app: npm run tauri:build\n * 2. Run tests: npm run test:native\n */\n\nimport type { Options } from '@wdio/types';\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport { fileURLToPath } from 'url';\nimport { spawn, type ChildProcess } from 'child_process';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// Track tauri-driver process\nlet tauriDriverProcess: ChildProcess | null = null;\n\n// Detect the built Tauri binary path based on platform\nfunction getTauriBinaryPath(): string {\n const projectRoot = path.resolve(__dirname, 'src-tauri');\n\n if (process.platform === 'win32') {\n // Windows: look for .exe in release or debug\n // Binary name comes from Cargo.toml package name\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri.exe');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri.exe');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n\n // Fallback to release path (will error if not built)\n return releasePath;\n } else if (process.platform === 'darwin') {\n // macOS: .app bundle\n const releasePath = path.join(\n projectRoot,\n 'target',\n 'release',\n 'bundle',\n 'macos',\n 'NoteFlow.app',\n 'Contents',\n 'MacOS',\n 'noteflow-tauri'\n );\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n } else {\n // Linux: AppImage or direct binary\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n }\n}\n\n// Get tauri-driver path\nfunction getTauriDriverPath(): string {\n if (process.platform === 'win32') {\n // On Windows, tauri-driver is in cargo bin\n const cargoHome = process.env.CARGO_HOME || path.join(process.env.USERPROFILE || '', '.cargo');\n return path.join(cargoHome, 'bin', 'tauri-driver.exe');\n }\n return 'tauri-driver';\n}\n\n// Get msedgedriver path (Windows only)\nasync function getMsEdgeDriverPath(): Promise<string | null> {\n if (process.platform !== 'win32') {\n return null;\n }\n\n // Try edgedriver npm package first\n try {\n const edgedriver = await import('edgedriver');\n // The package provides a function to get/download the binary\n if (typeof edgedriver.default === 'string') {\n return edgedriver.default;\n }\n if (edgedriver.binPath && fs.existsSync(edgedriver.binPath)) {\n return edgedriver.binPath;\n }\n } catch {\n // Package not available or failed\n }\n\n // Check common locations\n const possiblePaths = [\n // Custom env var\n process.env.MSEDGEDRIVER_PATH,\n // Common install locations\n 'C:\\\\Program Files\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n 'C:\\\\Program Files (x86)\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n path.join(process.env.USERPROFILE || '', 'msedgedriver.exe'),\n path.join(process.env.USERPROFILE || '', '.webdrivers', 'msedgedriver.exe'),\n ];\n\n for (const p of possiblePaths) {\n if (p && fs.existsSync(p)) {\n return p;\n }\n }\n\n return null;\n}\n\nexport const config: Options.Testrunner = {\n // Test specs\n specs: ['./e2e-native/**/*.spec.ts'],\n exclude: [],\n\n // Capabilities\n maxInstances: 1, // Tauri apps should run one at a time\n capabilities: [\n {\n // Use tauri-driver as the WebDriver server\n 'tauri:options': {\n application: getTauriBinaryPath(),\n },\n },\n ],\n\n // Test framework\n framework: 'mocha',\n mochaOpts: {\n ui: 'bdd',\n timeout: 60000,\n },\n\n // Reporters\n reporters: ['spec'],\n\n // Log level\n logLevel: 'info',\n\n // Connection settings for tauri-driver\n hostname: '127.0.0.1',\n port: 4444,\n\n // No built-in service - tauri-driver started via onPrepare hook\n services: [],\n\n // Timeouts\n connectionRetryTimeout: 120000,\n connectionRetryCount: 3,\n\n // Hooks\n onPrepare: async function () {\n const driverPath = getTauriDriverPath();\n console.log(`Starting tauri-driver: ${driverPath}`);\n\n // Check if tauri-driver exists\n if (!fs.existsSync(driverPath)) {\n throw new Error(\n `tauri-driver not found at: ${driverPath}\\n` +\n 'Install it with: cargo install tauri-driver'\n );\n }\n\n // On Windows, check for msedgedriver\n const edgeDriverPath = await getMsEdgeDriverPath();\n if (process.platform === 'win32' && !edgeDriverPath) {\n console.warn(\n '\\n⚠ msedgedriver.exe not found in common locations.\\n' +\n ' Download from: https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/\\n' +\n ' Then either:\\n' +\n ' - Add to PATH\\n' +\n ' - Set MSEDGEDRIVER_PATH environment variable\\n' +\n ' - Place in your home directory\\n'\n );\n }\n\n // Build args\n const args = ['--port', '4444'];\n if (edgeDriverPath) {\n args.push('--native-driver', edgeDriverPath);\n console.log(`Using msedgedriver: ${edgeDriverPath}`);\n }\n\n // Start tauri-driver\n tauriDriverProcess = spawn(driverPath, args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n tauriDriverProcess.stdout?.on('data', (data) => {\n console.log(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n tauriDriverProcess.stderr?.on('data', (data) => {\n console.error(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n // Wait for tauri-driver to be ready\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error('tauri-driver failed to start within 10s'));\n }, 10000);\n\n const checkReady = async () => {\n try {\n const response = await fetch('http://127.0.0.1:4444/status');\n if (response.ok) {\n clearTimeout(timeout);\n console.log('tauri-driver is ready');\n resolve();\n }\n } catch {\n // Not ready yet, retry\n setTimeout(checkReady, 200);\n }\n };\n\n // Start checking after a brief delay\n setTimeout(checkReady, 500);\n });\n },\n\n onComplete: async function () {\n // Stop tauri-driver\n if (tauriDriverProcess) {\n console.log('Stopping tauri-driver');\n tauriDriverProcess.kill();\n tauriDriverProcess = null;\n }\n },\n\n beforeSession: async function () {\n const binaryPath = getTauriBinaryPath();\n if (!fs.existsSync(binaryPath)) {\n throw new Error(\n `Tauri binary not found at: ${binaryPath}\\n` +\n 'Please build the app first with: npm run tauri:build'\n );\n }\n console.log(`Using Tauri binary: ${binaryPath}`);\n },\n\n afterTest: async function (test, _context, { error }) {\n if (error) {\n // Take screenshot on failure\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const screenshotPath = `./e2e-native/screenshots/${test.title}-${timestamp}.png`;\n await browser.saveScreenshot(screenshotPath);\n console.log(`Screenshot saved: ${screenshotPath}`);\n }\n },\n};\n"},"tags":["fixable"],"source":null},{"category":"lint/complexity/useArrowFunction","severity":"warning","description":"This function expression can be turned into an arrow function.","message":[{"elements":[],"content":"This "},{"elements":["Emphasis"],"content":"function expression"},{"elements":[],"content":" can be turned into an "},{"elements":["Emphasis"],"content":"arrow function"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"Function expressions"},{"elements":[],"content":" that don't use "},{"elements":["Emphasis"],"content":"this"},{"elements":[],"content":" can be turned into "},{"elements":["Emphasis"],"content":"arrow functions"},{"elements":[],"content":"."}]]},{"log":["info",[{"elements":[],"content":"Safe fix: Use an "},{"elements":["Emphasis"],"content":"arrow function"},{"elements":[],"content":" instead."}]]},{"diff":{"dictionary":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n * },\n\n beforeSession: async function () => {\n const binaryPath = getTauriBinaryPath();\n if (!fs.existsSync(binaryPath)) { }\n console.log(`Using Tauri binary: ${binaryPath}`);\n },\n\n afterTest: async function (test, _context, { error }) { },\n};\n","ops":[{"diffOp":{"equal":{"range":[0,60]}}},{"equalLines":{"line_count":244}},{"diffOp":{"equal":{"range":[60,83]}}},{"diffOp":{"equal":{"range":[83,89]}}},{"diffOp":{"delete":{"range":[89,98]}}},{"diffOp":{"equal":{"range":[98,101]}}},{"diffOp":{"insert":{"range":[101,104]}}},{"diffOp":{"equal":{"range":[104,188]}}},{"equalLines":{"line_count":4}},{"diffOp":{"equal":{"range":[188,251]}}},{"diffOp":{"equal":{"range":[251,311]}}},{"equalLines":{"line_count":7}},{"diffOp":{"equal":{"range":[311,319]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"wdio.conf.ts"},"span":[6915,7233],"sourceCode":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n *\n * This config runs tests against the actual Tauri desktop app using tauri-driver.\n * Requires: cargo install tauri-driver\n *\n * Usage:\n * 1. Build the app: npm run tauri:build\n * 2. Run tests: npm run test:native\n */\n\nimport type { Options } from '@wdio/types';\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport { fileURLToPath } from 'url';\nimport { spawn, type ChildProcess } from 'child_process';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// Track tauri-driver process\nlet tauriDriverProcess: ChildProcess | null = null;\n\n// Detect the built Tauri binary path based on platform\nfunction getTauriBinaryPath(): string {\n const projectRoot = path.resolve(__dirname, 'src-tauri');\n\n if (process.platform === 'win32') {\n // Windows: look for .exe in release or debug\n // Binary name comes from Cargo.toml package name\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri.exe');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri.exe');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n\n // Fallback to release path (will error if not built)\n return releasePath;\n } else if (process.platform === 'darwin') {\n // macOS: .app bundle\n const releasePath = path.join(\n projectRoot,\n 'target',\n 'release',\n 'bundle',\n 'macos',\n 'NoteFlow.app',\n 'Contents',\n 'MacOS',\n 'noteflow-tauri'\n );\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n } else {\n // Linux: AppImage or direct binary\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n }\n}\n\n// Get tauri-driver path\nfunction getTauriDriverPath(): string {\n if (process.platform === 'win32') {\n // On Windows, tauri-driver is in cargo bin\n const cargoHome = process.env.CARGO_HOME || path.join(process.env.USERPROFILE || '', '.cargo');\n return path.join(cargoHome, 'bin', 'tauri-driver.exe');\n }\n return 'tauri-driver';\n}\n\n// Get msedgedriver path (Windows only)\nasync function getMsEdgeDriverPath(): Promise<string | null> {\n if (process.platform !== 'win32') {\n return null;\n }\n\n // Try edgedriver npm package first\n try {\n const edgedriver = await import('edgedriver');\n // The package provides a function to get/download the binary\n if (typeof edgedriver.default === 'string') {\n return edgedriver.default;\n }\n if (edgedriver.binPath && fs.existsSync(edgedriver.binPath)) {\n return edgedriver.binPath;\n }\n } catch {\n // Package not available or failed\n }\n\n // Check common locations\n const possiblePaths = [\n // Custom env var\n process.env.MSEDGEDRIVER_PATH,\n // Common install locations\n 'C:\\\\Program Files\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n 'C:\\\\Program Files (x86)\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n path.join(process.env.USERPROFILE || '', 'msedgedriver.exe'),\n path.join(process.env.USERPROFILE || '', '.webdrivers', 'msedgedriver.exe'),\n ];\n\n for (const p of possiblePaths) {\n if (p && fs.existsSync(p)) {\n return p;\n }\n }\n\n return null;\n}\n\nexport const config: Options.Testrunner = {\n // Test specs\n specs: ['./e2e-native/**/*.spec.ts'],\n exclude: [],\n\n // Capabilities\n maxInstances: 1, // Tauri apps should run one at a time\n capabilities: [\n {\n // Use tauri-driver as the WebDriver server\n 'tauri:options': {\n application: getTauriBinaryPath(),\n },\n },\n ],\n\n // Test framework\n framework: 'mocha',\n mochaOpts: {\n ui: 'bdd',\n timeout: 60000,\n },\n\n // Reporters\n reporters: ['spec'],\n\n // Log level\n logLevel: 'info',\n\n // Connection settings for tauri-driver\n hostname: '127.0.0.1',\n port: 4444,\n\n // No built-in service - tauri-driver started via onPrepare hook\n services: [],\n\n // Timeouts\n connectionRetryTimeout: 120000,\n connectionRetryCount: 3,\n\n // Hooks\n onPrepare: async function () {\n const driverPath = getTauriDriverPath();\n console.log(`Starting tauri-driver: ${driverPath}`);\n\n // Check if tauri-driver exists\n if (!fs.existsSync(driverPath)) {\n throw new Error(\n `tauri-driver not found at: ${driverPath}\\n` +\n 'Install it with: cargo install tauri-driver'\n );\n }\n\n // On Windows, check for msedgedriver\n const edgeDriverPath = await getMsEdgeDriverPath();\n if (process.platform === 'win32' && !edgeDriverPath) {\n console.warn(\n '\\n⚠ msedgedriver.exe not found in common locations.\\n' +\n ' Download from: https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/\\n' +\n ' Then either:\\n' +\n ' - Add to PATH\\n' +\n ' - Set MSEDGEDRIVER_PATH environment variable\\n' +\n ' - Place in your home directory\\n'\n );\n }\n\n // Build args\n const args = ['--port', '4444'];\n if (edgeDriverPath) {\n args.push('--native-driver', edgeDriverPath);\n console.log(`Using msedgedriver: ${edgeDriverPath}`);\n }\n\n // Start tauri-driver\n tauriDriverProcess = spawn(driverPath, args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n tauriDriverProcess.stdout?.on('data', (data) => {\n console.log(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n tauriDriverProcess.stderr?.on('data', (data) => {\n console.error(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n // Wait for tauri-driver to be ready\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error('tauri-driver failed to start within 10s'));\n }, 10000);\n\n const checkReady = async () => {\n try {\n const response = await fetch('http://127.0.0.1:4444/status');\n if (response.ok) {\n clearTimeout(timeout);\n console.log('tauri-driver is ready');\n resolve();\n }\n } catch {\n // Not ready yet, retry\n setTimeout(checkReady, 200);\n }\n };\n\n // Start checking after a brief delay\n setTimeout(checkReady, 500);\n });\n },\n\n onComplete: async function () {\n // Stop tauri-driver\n if (tauriDriverProcess) {\n console.log('Stopping tauri-driver');\n tauriDriverProcess.kill();\n tauriDriverProcess = null;\n }\n },\n\n beforeSession: async function () {\n const binaryPath = getTauriBinaryPath();\n if (!fs.existsSync(binaryPath)) {\n throw new Error(\n `Tauri binary not found at: ${binaryPath}\\n` +\n 'Please build the app first with: npm run tauri:build'\n );\n }\n console.log(`Using Tauri binary: ${binaryPath}`);\n },\n\n afterTest: async function (test, _context, { error }) {\n if (error) {\n // Take screenshot on failure\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const screenshotPath = `./e2e-native/screenshots/${test.title}-${timestamp}.png`;\n await browser.saveScreenshot(screenshotPath);\n console.log(`Screenshot saved: ${screenshotPath}`);\n }\n },\n};\n"},"tags":["fixable"],"source":null},{"category":"lint/complexity/useArrowFunction","severity":"warning","description":"This function expression can be turned into an arrow function.","message":[{"elements":[],"content":"This "},{"elements":["Emphasis"],"content":"function expression"},{"elements":[],"content":" can be turned into an "},{"elements":["Emphasis"],"content":"arrow function"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"Function expressions"},{"elements":[],"content":" that don't use "},{"elements":["Emphasis"],"content":"this"},{"elements":[],"content":" can be turned into "},{"elements":["Emphasis"],"content":"arrow functions"},{"elements":[],"content":"."}]]},{"log":["info",[{"elements":[],"content":"Safe fix: Use an "},{"elements":["Emphasis"],"content":"arrow function"},{"elements":[],"content":" instead."}]]},{"diff":{"dictionary":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n * },\n\n afterTest: async function (test, _context, { error }) => {\n if (error) {\n // Take screenshot on failure console.log(`Screenshot saved: ${screenshotPath}`);\n }\n },\n};\n","ops":[{"diffOp":{"equal":{"range":[0,60]}}},{"equalLines":{"line_count":255}},{"diffOp":{"equal":{"range":[60,79]}}},{"diffOp":{"equal":{"range":[79,85]}}},{"diffOp":{"delete":{"range":[85,94]}}},{"diffOp":{"equal":{"range":[94,122]}}},{"diffOp":{"insert":{"range":[122,125]}}},{"diffOp":{"equal":{"range":[125,179]}}},{"equalLines":{"line_count":3}},{"diffOp":{"equal":{"range":[179,246]}}},{"diffOp":{"equal":{"range":[246,251]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"wdio.conf.ts"},"span":[7249,7626],"sourceCode":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n *\n * This config runs tests against the actual Tauri desktop app using tauri-driver.\n * Requires: cargo install tauri-driver\n *\n * Usage:\n * 1. Build the app: npm run tauri:build\n * 2. Run tests: npm run test:native\n */\n\nimport type { Options } from '@wdio/types';\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport { fileURLToPath } from 'url';\nimport { spawn, type ChildProcess } from 'child_process';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// Track tauri-driver process\nlet tauriDriverProcess: ChildProcess | null = null;\n\n// Detect the built Tauri binary path based on platform\nfunction getTauriBinaryPath(): string {\n const projectRoot = path.resolve(__dirname, 'src-tauri');\n\n if (process.platform === 'win32') {\n // Windows: look for .exe in release or debug\n // Binary name comes from Cargo.toml package name\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri.exe');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri.exe');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n\n // Fallback to release path (will error if not built)\n return releasePath;\n } else if (process.platform === 'darwin') {\n // macOS: .app bundle\n const releasePath = path.join(\n projectRoot,\n 'target',\n 'release',\n 'bundle',\n 'macos',\n 'NoteFlow.app',\n 'Contents',\n 'MacOS',\n 'noteflow-tauri'\n );\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n } else {\n // Linux: AppImage or direct binary\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n }\n}\n\n// Get tauri-driver path\nfunction getTauriDriverPath(): string {\n if (process.platform === 'win32') {\n // On Windows, tauri-driver is in cargo bin\n const cargoHome = process.env.CARGO_HOME || path.join(process.env.USERPROFILE || '', '.cargo');\n return path.join(cargoHome, 'bin', 'tauri-driver.exe');\n }\n return 'tauri-driver';\n}\n\n// Get msedgedriver path (Windows only)\nasync function getMsEdgeDriverPath(): Promise<string | null> {\n if (process.platform !== 'win32') {\n return null;\n }\n\n // Try edgedriver npm package first\n try {\n const edgedriver = await import('edgedriver');\n // The package provides a function to get/download the binary\n if (typeof edgedriver.default === 'string') {\n return edgedriver.default;\n }\n if (edgedriver.binPath && fs.existsSync(edgedriver.binPath)) {\n return edgedriver.binPath;\n }\n } catch {\n // Package not available or failed\n }\n\n // Check common locations\n const possiblePaths = [\n // Custom env var\n process.env.MSEDGEDRIVER_PATH,\n // Common install locations\n 'C:\\\\Program Files\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n 'C:\\\\Program Files (x86)\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n path.join(process.env.USERPROFILE || '', 'msedgedriver.exe'),\n path.join(process.env.USERPROFILE || '', '.webdrivers', 'msedgedriver.exe'),\n ];\n\n for (const p of possiblePaths) {\n if (p && fs.existsSync(p)) {\n return p;\n }\n }\n\n return null;\n}\n\nexport const config: Options.Testrunner = {\n // Test specs\n specs: ['./e2e-native/**/*.spec.ts'],\n exclude: [],\n\n // Capabilities\n maxInstances: 1, // Tauri apps should run one at a time\n capabilities: [\n {\n // Use tauri-driver as the WebDriver server\n 'tauri:options': {\n application: getTauriBinaryPath(),\n },\n },\n ],\n\n // Test framework\n framework: 'mocha',\n mochaOpts: {\n ui: 'bdd',\n timeout: 60000,\n },\n\n // Reporters\n reporters: ['spec'],\n\n // Log level\n logLevel: 'info',\n\n // Connection settings for tauri-driver\n hostname: '127.0.0.1',\n port: 4444,\n\n // No built-in service - tauri-driver started via onPrepare hook\n services: [],\n\n // Timeouts\n connectionRetryTimeout: 120000,\n connectionRetryCount: 3,\n\n // Hooks\n onPrepare: async function () {\n const driverPath = getTauriDriverPath();\n console.log(`Starting tauri-driver: ${driverPath}`);\n\n // Check if tauri-driver exists\n if (!fs.existsSync(driverPath)) {\n throw new Error(\n `tauri-driver not found at: ${driverPath}\\n` +\n 'Install it with: cargo install tauri-driver'\n );\n }\n\n // On Windows, check for msedgedriver\n const edgeDriverPath = await getMsEdgeDriverPath();\n if (process.platform === 'win32' && !edgeDriverPath) {\n console.warn(\n '\\n⚠ msedgedriver.exe not found in common locations.\\n' +\n ' Download from: https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/\\n' +\n ' Then either:\\n' +\n ' - Add to PATH\\n' +\n ' - Set MSEDGEDRIVER_PATH environment variable\\n' +\n ' - Place in your home directory\\n'\n );\n }\n\n // Build args\n const args = ['--port', '4444'];\n if (edgeDriverPath) {\n args.push('--native-driver', edgeDriverPath);\n console.log(`Using msedgedriver: ${edgeDriverPath}`);\n }\n\n // Start tauri-driver\n tauriDriverProcess = spawn(driverPath, args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n tauriDriverProcess.stdout?.on('data', (data) => {\n console.log(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n tauriDriverProcess.stderr?.on('data', (data) => {\n console.error(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n // Wait for tauri-driver to be ready\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error('tauri-driver failed to start within 10s'));\n }, 10000);\n\n const checkReady = async () => {\n try {\n const response = await fetch('http://127.0.0.1:4444/status');\n if (response.ok) {\n clearTimeout(timeout);\n console.log('tauri-driver is ready');\n resolve();\n }\n } catch {\n // Not ready yet, retry\n setTimeout(checkReady, 200);\n }\n };\n\n // Start checking after a brief delay\n setTimeout(checkReady, 500);\n });\n },\n\n onComplete: async function () {\n // Stop tauri-driver\n if (tauriDriverProcess) {\n console.log('Stopping tauri-driver');\n tauriDriverProcess.kill();\n tauriDriverProcess = null;\n }\n },\n\n beforeSession: async function () {\n const binaryPath = getTauriBinaryPath();\n if (!fs.existsSync(binaryPath)) {\n throw new Error(\n `Tauri binary not found at: ${binaryPath}\\n` +\n 'Please build the app first with: npm run tauri:build'\n );\n }\n console.log(`Using Tauri binary: ${binaryPath}`);\n },\n\n afterTest: async function (test, _context, { error }) {\n if (error) {\n // Take screenshot on failure\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const screenshotPath = `./e2e-native/screenshots/${test.title}-${timestamp}.png`;\n await browser.saveScreenshot(screenshotPath);\n console.log(`Screenshot saved: ${screenshotPath}`);\n }\n },\n};\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * Annotations E2E Tests\n * describe('getAnnotation', () => {\n it('should get annotation by ID', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n } });\n});\n","ops":[{"diffOp":{"equal":{"range":[0,31]}}},{"equalLines":{"line_count":198}},{"diffOp":{"equal":{"range":[31,149]}}},{"diffOp":{"delete":{"range":[149,210]}}},{"diffOp":{"equal":{"range":[210,234]}}},{"equalLines":{"line_count":82}},{"diffOp":{"equal":{"range":[234,244]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/annotations.spec.ts"},"span":[6406,6417],"sourceCode":"/**\n * Annotations E2E Tests\n *\n * Tests for annotation CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Annotation Operations', () => {\n let testMeetingId: string | null = null;\n let testAnnotationId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Create a test meeting for annotations\n const title = TestData.createMeetingTitle();\n try {\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n if (meeting && !meeting.error && meeting.id) {\n testMeetingId = meeting.id;\n }\n } catch {\n // Meeting creation may fail if server not connected\n }\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('addAnnotation', () => {\n it('should add an action_item annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'action_item',\n text: 'Follow up on meeting notes',\n start_time: 0,\n end_time: 10,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n expect(result.annotation).toHaveProperty('annotation_type');\n expect(result.annotation.text).toBe('Follow up on meeting notes');\n testAnnotationId = result.annotation.id;\n }\n });\n\n it('should add a decision annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'decision',\n text: 'Approved the new feature design',\n start_time: 15,\n end_time: 30,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n }\n });\n\n it('should add a note annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Important discussion point',\n start_time: 45,\n end_time: 60,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n\n it('should add a risk annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'risk',\n text: 'Potential deadline risk identified',\n start_time: 75,\n end_time: 90,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('listAnnotations', () => {\n it('should list all annotations for a meeting', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId);\n return { success: true, annotations, count: annotations?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.annotations)).toBe(true);\n console.log(`Found ${result.count} annotations`);\n }\n });\n\n it('should filter by time range', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId, 0, 30);\n return { success: true, annotations };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('getAnnotation', () => {\n it('should get annotation by ID', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.getAnnotation(annotationId);\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation.id).toBe(testAnnotationId);\n }\n });\n });\n\n describe('updateAnnotation', () => {\n it('should update annotation text', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateAnnotation({\n annotation_id: annotationId,\n text: 'Updated annotation text',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.text).toBe('Updated annotation text');\n }\n });\n });\n\n describe('deleteAnnotation', () => {\n it('should delete an annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n // Create an annotation to delete\n const createResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Annotation to delete',\n start_time: 100,\n end_time: 110,\n });\n } catch {\n return null;\n }\n }, testMeetingId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.deleteAnnotation(annotationId);\n return { success };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/annotations.spec.ts"},"span":[5921,5924],"sourceCode":"/**\n * Annotations E2E Tests\n *\n * Tests for annotation CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Annotation Operations', () => {\n let testMeetingId: string | null = null;\n let testAnnotationId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Create a test meeting for annotations\n const title = TestData.createMeetingTitle();\n try {\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n if (meeting && !meeting.error && meeting.id) {\n testMeetingId = meeting.id;\n }\n } catch {\n // Meeting creation may fail if server not connected\n }\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('addAnnotation', () => {\n it('should add an action_item annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'action_item',\n text: 'Follow up on meeting notes',\n start_time: 0,\n end_time: 10,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n expect(result.annotation).toHaveProperty('annotation_type');\n expect(result.annotation.text).toBe('Follow up on meeting notes');\n testAnnotationId = result.annotation.id;\n }\n });\n\n it('should add a decision annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'decision',\n text: 'Approved the new feature design',\n start_time: 15,\n end_time: 30,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n }\n });\n\n it('should add a note annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Important discussion point',\n start_time: 45,\n end_time: 60,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n\n it('should add a risk annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'risk',\n text: 'Potential deadline risk identified',\n start_time: 75,\n end_time: 90,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('listAnnotations', () => {\n it('should list all annotations for a meeting', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId);\n return { success: true, annotations, count: annotations?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.annotations)).toBe(true);\n console.log(`Found ${result.count} annotations`);\n }\n });\n\n it('should filter by time range', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId, 0, 30);\n return { success: true, annotations };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('getAnnotation', () => {\n it('should get annotation by ID', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.getAnnotation(annotationId);\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation.id).toBe(testAnnotationId);\n }\n });\n });\n\n describe('updateAnnotation', () => {\n it('should update annotation text', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateAnnotation({\n annotation_id: annotationId,\n text: 'Updated annotation text',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.text).toBe('Updated annotation text');\n }\n });\n });\n\n describe('deleteAnnotation', () => {\n it('should delete an annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n // Create an annotation to delete\n const createResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Annotation to delete',\n start_time: 100,\n end_time: 110,\n });\n } catch {\n return null;\n }\n }, testMeetingId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.deleteAnnotation(annotationId);\n return { success };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/annotations.spec.ts"},"span":[6584,6587],"sourceCode":"/**\n * Annotations E2E Tests\n *\n * Tests for annotation CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Annotation Operations', () => {\n let testMeetingId: string | null = null;\n let testAnnotationId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Create a test meeting for annotations\n const title = TestData.createMeetingTitle();\n try {\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n if (meeting && !meeting.error && meeting.id) {\n testMeetingId = meeting.id;\n }\n } catch {\n // Meeting creation may fail if server not connected\n }\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('addAnnotation', () => {\n it('should add an action_item annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'action_item',\n text: 'Follow up on meeting notes',\n start_time: 0,\n end_time: 10,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n expect(result.annotation).toHaveProperty('annotation_type');\n expect(result.annotation.text).toBe('Follow up on meeting notes');\n testAnnotationId = result.annotation.id;\n }\n });\n\n it('should add a decision annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'decision',\n text: 'Approved the new feature design',\n start_time: 15,\n end_time: 30,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n }\n });\n\n it('should add a note annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Important discussion point',\n start_time: 45,\n end_time: 60,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n\n it('should add a risk annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'risk',\n text: 'Potential deadline risk identified',\n start_time: 75,\n end_time: 90,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('listAnnotations', () => {\n it('should list all annotations for a meeting', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId);\n return { success: true, annotations, count: annotations?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.annotations)).toBe(true);\n console.log(`Found ${result.count} annotations`);\n }\n });\n\n it('should filter by time range', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId, 0, 30);\n return { success: true, annotations };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('getAnnotation', () => {\n it('should get annotation by ID', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.getAnnotation(annotationId);\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation.id).toBe(testAnnotationId);\n }\n });\n });\n\n describe('updateAnnotation', () => {\n it('should update annotation text', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateAnnotation({\n annotation_id: annotationId,\n text: 'Updated annotation text',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.text).toBe('Updated annotation text');\n }\n });\n });\n\n describe('deleteAnnotation', () => {\n it('should delete an annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n // Create an annotation to delete\n const createResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Annotation to delete',\n start_time: 100,\n end_time: 110,\n });\n } catch {\n return null;\n }\n }, testMeetingId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.deleteAnnotation(annotationId);\n return { success };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/annotations.spec.ts"},"span":[7344,7347],"sourceCode":"/**\n * Annotations E2E Tests\n *\n * Tests for annotation CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Annotation Operations', () => {\n let testMeetingId: string | null = null;\n let testAnnotationId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Create a test meeting for annotations\n const title = TestData.createMeetingTitle();\n try {\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n if (meeting && !meeting.error && meeting.id) {\n testMeetingId = meeting.id;\n }\n } catch {\n // Meeting creation may fail if server not connected\n }\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('addAnnotation', () => {\n it('should add an action_item annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'action_item',\n text: 'Follow up on meeting notes',\n start_time: 0,\n end_time: 10,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n expect(result.annotation).toHaveProperty('annotation_type');\n expect(result.annotation.text).toBe('Follow up on meeting notes');\n testAnnotationId = result.annotation.id;\n }\n });\n\n it('should add a decision annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'decision',\n text: 'Approved the new feature design',\n start_time: 15,\n end_time: 30,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n }\n });\n\n it('should add a note annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Important discussion point',\n start_time: 45,\n end_time: 60,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n\n it('should add a risk annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'risk',\n text: 'Potential deadline risk identified',\n start_time: 75,\n end_time: 90,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('listAnnotations', () => {\n it('should list all annotations for a meeting', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId);\n return { success: true, annotations, count: annotations?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.annotations)).toBe(true);\n console.log(`Found ${result.count} annotations`);\n }\n });\n\n it('should filter by time range', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId, 0, 30);\n return { success: true, annotations };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('getAnnotation', () => {\n it('should get annotation by ID', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.getAnnotation(annotationId);\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation.id).toBe(testAnnotationId);\n }\n });\n });\n\n describe('updateAnnotation', () => {\n it('should update annotation text', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateAnnotation({\n annotation_id: annotationId,\n text: 'Updated annotation text',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.text).toBe('Updated annotation text');\n }\n });\n });\n\n describe('deleteAnnotation', () => {\n it('should delete an annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n // Create an annotation to delete\n const createResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Annotation to delete',\n start_time: 100,\n end_time: 110,\n });\n } catch {\n return null;\n }\n }, testMeetingId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.deleteAnnotation(annotationId);\n return { success };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/annotations.spec.ts"},"span":[8233,8236],"sourceCode":"/**\n * Annotations E2E Tests\n *\n * Tests for annotation CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Annotation Operations', () => {\n let testMeetingId: string | null = null;\n let testAnnotationId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Create a test meeting for annotations\n const title = TestData.createMeetingTitle();\n try {\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n if (meeting && !meeting.error && meeting.id) {\n testMeetingId = meeting.id;\n }\n } catch {\n // Meeting creation may fail if server not connected\n }\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('addAnnotation', () => {\n it('should add an action_item annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'action_item',\n text: 'Follow up on meeting notes',\n start_time: 0,\n end_time: 10,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n expect(result.annotation).toHaveProperty('annotation_type');\n expect(result.annotation.text).toBe('Follow up on meeting notes');\n testAnnotationId = result.annotation.id;\n }\n });\n\n it('should add a decision annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'decision',\n text: 'Approved the new feature design',\n start_time: 15,\n end_time: 30,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n }\n });\n\n it('should add a note annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Important discussion point',\n start_time: 45,\n end_time: 60,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n\n it('should add a risk annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'risk',\n text: 'Potential deadline risk identified',\n start_time: 75,\n end_time: 90,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('listAnnotations', () => {\n it('should list all annotations for a meeting', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId);\n return { success: true, annotations, count: annotations?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.annotations)).toBe(true);\n console.log(`Found ${result.count} annotations`);\n }\n });\n\n it('should filter by time range', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId, 0, 30);\n return { success: true, annotations };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('getAnnotation', () => {\n it('should get annotation by ID', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.getAnnotation(annotationId);\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation.id).toBe(testAnnotationId);\n }\n });\n });\n\n describe('updateAnnotation', () => {\n it('should update annotation text', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateAnnotation({\n annotation_id: annotationId,\n text: 'Updated annotation text',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.text).toBe('Updated annotation text');\n }\n });\n });\n\n describe('deleteAnnotation', () => {\n it('should delete an annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n // Create an annotation to delete\n const createResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Annotation to delete',\n start_time: 100,\n end_time: 110,\n });\n } catch {\n return null;\n }\n }, testMeetingId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.deleteAnnotation(annotationId);\n return { success };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/annotations.spec.ts"},"span":[8714,8717],"sourceCode":"/**\n * Annotations E2E Tests\n *\n * Tests for annotation CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Annotation Operations', () => {\n let testMeetingId: string | null = null;\n let testAnnotationId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Create a test meeting for annotations\n const title = TestData.createMeetingTitle();\n try {\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n if (meeting && !meeting.error && meeting.id) {\n testMeetingId = meeting.id;\n }\n } catch {\n // Meeting creation may fail if server not connected\n }\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('addAnnotation', () => {\n it('should add an action_item annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'action_item',\n text: 'Follow up on meeting notes',\n start_time: 0,\n end_time: 10,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n expect(result.annotation).toHaveProperty('annotation_type');\n expect(result.annotation.text).toBe('Follow up on meeting notes');\n testAnnotationId = result.annotation.id;\n }\n });\n\n it('should add a decision annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'decision',\n text: 'Approved the new feature design',\n start_time: 15,\n end_time: 30,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n }\n });\n\n it('should add a note annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Important discussion point',\n start_time: 45,\n end_time: 60,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n\n it('should add a risk annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'risk',\n text: 'Potential deadline risk identified',\n start_time: 75,\n end_time: 90,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('listAnnotations', () => {\n it('should list all annotations for a meeting', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId);\n return { success: true, annotations, count: annotations?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.annotations)).toBe(true);\n console.log(`Found ${result.count} annotations`);\n }\n });\n\n it('should filter by time range', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId, 0, 30);\n return { success: true, annotations };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('getAnnotation', () => {\n it('should get annotation by ID', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.getAnnotation(annotationId);\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation.id).toBe(testAnnotationId);\n }\n });\n });\n\n describe('updateAnnotation', () => {\n it('should update annotation text', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateAnnotation({\n annotation_id: annotationId,\n text: 'Updated annotation text',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.text).toBe('Updated annotation text');\n }\n });\n });\n\n describe('deleteAnnotation', () => {\n it('should delete an annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n // Create an annotation to delete\n const createResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Annotation to delete',\n start_time: 100,\n end_time: 110,\n });\n } catch {\n return null;\n }\n }, testMeetingId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.deleteAnnotation(annotationId);\n return { success };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * Annotations E2E Tests\n * describe('addAnnotation', () => {\n it('should add an action_item annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n } });\n});\n","ops":[{"diffOp":{"equal":{"range":[0,31]}}},{"equalLines":{"line_count":43}},{"diffOp":{"equal":{"range":[31,155]}}},{"diffOp":{"delete":{"range":[155,215]}}},{"diffOp":{"equal":{"range":[215,239]}}},{"equalLines":{"line_count":237}},{"diffOp":{"equal":{"range":[239,249]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/annotations.spec.ts"},"span":[1362,1373],"sourceCode":"/**\n * Annotations E2E Tests\n *\n * Tests for annotation CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Annotation Operations', () => {\n let testMeetingId: string | null = null;\n let testAnnotationId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Create a test meeting for annotations\n const title = TestData.createMeetingTitle();\n try {\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n if (meeting && !meeting.error && meeting.id) {\n testMeetingId = meeting.id;\n }\n } catch {\n // Meeting creation may fail if server not connected\n }\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('addAnnotation', () => {\n it('should add an action_item annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'action_item',\n text: 'Follow up on meeting notes',\n start_time: 0,\n end_time: 10,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n expect(result.annotation).toHaveProperty('annotation_type');\n expect(result.annotation.text).toBe('Follow up on meeting notes');\n testAnnotationId = result.annotation.id;\n }\n });\n\n it('should add a decision annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'decision',\n text: 'Approved the new feature design',\n start_time: 15,\n end_time: 30,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n }\n });\n\n it('should add a note annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Important discussion point',\n start_time: 45,\n end_time: 60,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n\n it('should add a risk annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'risk',\n text: 'Potential deadline risk identified',\n start_time: 75,\n end_time: 90,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('listAnnotations', () => {\n it('should list all annotations for a meeting', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId);\n return { success: true, annotations, count: annotations?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.annotations)).toBe(true);\n console.log(`Found ${result.count} annotations`);\n }\n });\n\n it('should filter by time range', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId, 0, 30);\n return { success: true, annotations };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('getAnnotation', () => {\n it('should get annotation by ID', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.getAnnotation(annotationId);\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation.id).toBe(testAnnotationId);\n }\n });\n });\n\n describe('updateAnnotation', () => {\n it('should update annotation text', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateAnnotation({\n annotation_id: annotationId,\n text: 'Updated annotation text',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.text).toBe('Updated annotation text');\n }\n });\n });\n\n describe('deleteAnnotation', () => {\n it('should delete an annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n // Create an annotation to delete\n const createResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Annotation to delete',\n start_time: 100,\n end_time: 110,\n });\n } catch {\n return null;\n }\n }, testMeetingId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.deleteAnnotation(annotationId);\n return { success };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * Annotations E2E Tests\n *\n it('should add a decision annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n } });\n});\n","ops":[{"diffOp":{"equal":{"range":[0,31]}}},{"equalLines":{"line_count":74}},{"diffOp":{"equal":{"range":[31,116]}}},{"diffOp":{"delete":{"range":[116,176]}}},{"diffOp":{"equal":{"range":[176,200]}}},{"equalLines":{"line_count":206}},{"diffOp":{"equal":{"range":[200,210]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/annotations.spec.ts"},"span":[2439,2450],"sourceCode":"/**\n * Annotations E2E Tests\n *\n * Tests for annotation CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Annotation Operations', () => {\n let testMeetingId: string | null = null;\n let testAnnotationId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Create a test meeting for annotations\n const title = TestData.createMeetingTitle();\n try {\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n if (meeting && !meeting.error && meeting.id) {\n testMeetingId = meeting.id;\n }\n } catch {\n // Meeting creation may fail if server not connected\n }\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('addAnnotation', () => {\n it('should add an action_item annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'action_item',\n text: 'Follow up on meeting notes',\n start_time: 0,\n end_time: 10,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n expect(result.annotation).toHaveProperty('annotation_type');\n expect(result.annotation.text).toBe('Follow up on meeting notes');\n testAnnotationId = result.annotation.id;\n }\n });\n\n it('should add a decision annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'decision',\n text: 'Approved the new feature design',\n start_time: 15,\n end_time: 30,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n }\n });\n\n it('should add a note annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Important discussion point',\n start_time: 45,\n end_time: 60,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n\n it('should add a risk annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'risk',\n text: 'Potential deadline risk identified',\n start_time: 75,\n end_time: 90,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('listAnnotations', () => {\n it('should list all annotations for a meeting', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId);\n return { success: true, annotations, count: annotations?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.annotations)).toBe(true);\n console.log(`Found ${result.count} annotations`);\n }\n });\n\n it('should filter by time range', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId, 0, 30);\n return { success: true, annotations };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('getAnnotation', () => {\n it('should get annotation by ID', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.getAnnotation(annotationId);\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation.id).toBe(testAnnotationId);\n }\n });\n });\n\n describe('updateAnnotation', () => {\n it('should update annotation text', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateAnnotation({\n annotation_id: annotationId,\n text: 'Updated annotation text',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.text).toBe('Updated annotation text');\n }\n });\n });\n\n describe('deleteAnnotation', () => {\n it('should delete an annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n // Create an annotation to delete\n const createResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Annotation to delete',\n start_time: 100,\n end_time: 110,\n });\n } catch {\n return null;\n }\n }, testMeetingId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.deleteAnnotation(annotationId);\n return { success };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * Annotations E2E Tests\n *\n it('should add a note annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n } });\n});\n","ops":[{"diffOp":{"equal":{"range":[0,31]}}},{"equalLines":{"line_count":102}},{"diffOp":{"equal":{"range":[31,112]}}},{"diffOp":{"delete":{"range":[112,172]}}},{"diffOp":{"equal":{"range":[172,196]}}},{"equalLines":{"line_count":178}},{"diffOp":{"equal":{"range":[196,206]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/annotations.spec.ts"},"span":[3322,3333],"sourceCode":"/**\n * Annotations E2E Tests\n *\n * Tests for annotation CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Annotation Operations', () => {\n let testMeetingId: string | null = null;\n let testAnnotationId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Create a test meeting for annotations\n const title = TestData.createMeetingTitle();\n try {\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n if (meeting && !meeting.error && meeting.id) {\n testMeetingId = meeting.id;\n }\n } catch {\n // Meeting creation may fail if server not connected\n }\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('addAnnotation', () => {\n it('should add an action_item annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'action_item',\n text: 'Follow up on meeting notes',\n start_time: 0,\n end_time: 10,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n expect(result.annotation).toHaveProperty('annotation_type');\n expect(result.annotation.text).toBe('Follow up on meeting notes');\n testAnnotationId = result.annotation.id;\n }\n });\n\n it('should add a decision annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'decision',\n text: 'Approved the new feature design',\n start_time: 15,\n end_time: 30,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n }\n });\n\n it('should add a note annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Important discussion point',\n start_time: 45,\n end_time: 60,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n\n it('should add a risk annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'risk',\n text: 'Potential deadline risk identified',\n start_time: 75,\n end_time: 90,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('listAnnotations', () => {\n it('should list all annotations for a meeting', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId);\n return { success: true, annotations, count: annotations?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.annotations)).toBe(true);\n console.log(`Found ${result.count} annotations`);\n }\n });\n\n it('should filter by time range', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId, 0, 30);\n return { success: true, annotations };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('getAnnotation', () => {\n it('should get annotation by ID', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.getAnnotation(annotationId);\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation.id).toBe(testAnnotationId);\n }\n });\n });\n\n describe('updateAnnotation', () => {\n it('should update annotation text', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateAnnotation({\n annotation_id: annotationId,\n text: 'Updated annotation text',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.text).toBe('Updated annotation text');\n }\n });\n });\n\n describe('deleteAnnotation', () => {\n it('should delete an annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n // Create an annotation to delete\n const createResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Annotation to delete',\n start_time: 100,\n end_time: 110,\n });\n } catch {\n return null;\n }\n }, testMeetingId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.deleteAnnotation(annotationId);\n return { success };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * Annotations E2E Tests\n *\n it('should add a risk annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n } });\n});\n","ops":[{"diffOp":{"equal":{"range":[0,31]}}},{"equalLines":{"line_count":127}},{"diffOp":{"equal":{"range":[31,112]}}},{"diffOp":{"delete":{"range":[112,172]}}},{"diffOp":{"equal":{"range":[172,196]}}},{"equalLines":{"line_count":153}},{"diffOp":{"equal":{"range":[196,206]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/annotations.spec.ts"},"span":[4104,4115],"sourceCode":"/**\n * Annotations E2E Tests\n *\n * Tests for annotation CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Annotation Operations', () => {\n let testMeetingId: string | null = null;\n let testAnnotationId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Create a test meeting for annotations\n const title = TestData.createMeetingTitle();\n try {\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n if (meeting && !meeting.error && meeting.id) {\n testMeetingId = meeting.id;\n }\n } catch {\n // Meeting creation may fail if server not connected\n }\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('addAnnotation', () => {\n it('should add an action_item annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'action_item',\n text: 'Follow up on meeting notes',\n start_time: 0,\n end_time: 10,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n expect(result.annotation).toHaveProperty('annotation_type');\n expect(result.annotation.text).toBe('Follow up on meeting notes');\n testAnnotationId = result.annotation.id;\n }\n });\n\n it('should add a decision annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'decision',\n text: 'Approved the new feature design',\n start_time: 15,\n end_time: 30,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n }\n });\n\n it('should add a note annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Important discussion point',\n start_time: 45,\n end_time: 60,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n\n it('should add a risk annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'risk',\n text: 'Potential deadline risk identified',\n start_time: 75,\n end_time: 90,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('listAnnotations', () => {\n it('should list all annotations for a meeting', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId);\n return { success: true, annotations, count: annotations?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.annotations)).toBe(true);\n console.log(`Found ${result.count} annotations`);\n }\n });\n\n it('should filter by time range', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId, 0, 30);\n return { success: true, annotations };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('getAnnotation', () => {\n it('should get annotation by ID', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.getAnnotation(annotationId);\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation.id).toBe(testAnnotationId);\n }\n });\n });\n\n describe('updateAnnotation', () => {\n it('should update annotation text', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateAnnotation({\n annotation_id: annotationId,\n text: 'Updated annotation text',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.text).toBe('Updated annotation text');\n }\n });\n });\n\n describe('deleteAnnotation', () => {\n it('should delete an annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n // Create an annotation to delete\n const createResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Annotation to delete',\n start_time: 100,\n end_time: 110,\n });\n } catch {\n return null;\n }\n }, testMeetingId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.deleteAnnotation(annotationId);\n return { success };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * Annotations E2E Tests\n * describe('listAnnotations', () => {\n it('should list all annotations for a meeting', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n } });\n});\n","ops":[{"diffOp":{"equal":{"range":[0,31]}}},{"equalLines":{"line_count":154}},{"diffOp":{"equal":{"range":[31,162]}}},{"diffOp":{"delete":{"range":[162,222]}}},{"diffOp":{"equal":{"range":[222,246]}}},{"equalLines":{"line_count":126}},{"diffOp":{"equal":{"range":[246,256]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/annotations.spec.ts"},"span":[4951,4962],"sourceCode":"/**\n * Annotations E2E Tests\n *\n * Tests for annotation CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Annotation Operations', () => {\n let testMeetingId: string | null = null;\n let testAnnotationId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Create a test meeting for annotations\n const title = TestData.createMeetingTitle();\n try {\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n if (meeting && !meeting.error && meeting.id) {\n testMeetingId = meeting.id;\n }\n } catch {\n // Meeting creation may fail if server not connected\n }\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('addAnnotation', () => {\n it('should add an action_item annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'action_item',\n text: 'Follow up on meeting notes',\n start_time: 0,\n end_time: 10,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n expect(result.annotation).toHaveProperty('annotation_type');\n expect(result.annotation.text).toBe('Follow up on meeting notes');\n testAnnotationId = result.annotation.id;\n }\n });\n\n it('should add a decision annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'decision',\n text: 'Approved the new feature design',\n start_time: 15,\n end_time: 30,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n }\n });\n\n it('should add a note annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Important discussion point',\n start_time: 45,\n end_time: 60,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n\n it('should add a risk annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'risk',\n text: 'Potential deadline risk identified',\n start_time: 75,\n end_time: 90,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('listAnnotations', () => {\n it('should list all annotations for a meeting', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId);\n return { success: true, annotations, count: annotations?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.annotations)).toBe(true);\n console.log(`Found ${result.count} annotations`);\n }\n });\n\n it('should filter by time range', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId, 0, 30);\n return { success: true, annotations };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('getAnnotation', () => {\n it('should get annotation by ID', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.getAnnotation(annotationId);\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation.id).toBe(testAnnotationId);\n }\n });\n });\n\n describe('updateAnnotation', () => {\n it('should update annotation text', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateAnnotation({\n annotation_id: annotationId,\n text: 'Updated annotation text',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.text).toBe('Updated annotation text');\n }\n });\n });\n\n describe('deleteAnnotation', () => {\n it('should delete an annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n // Create an annotation to delete\n const createResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Annotation to delete',\n start_time: 100,\n end_time: 110,\n });\n } catch {\n return null;\n }\n }, testMeetingId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.deleteAnnotation(annotationId);\n return { success };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * Annotations E2E Tests\n * expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.annotations)).toBe(true);\n console.log(`Found ${result.count} annotations`);\n }\n }); });\n});\n","ops":[{"diffOp":{"equal":{"range":[0,31]}}},{"equalLines":{"line_count":171}},{"diffOp":{"equal":{"range":[31,156]}}},{"diffOp":{"delete":{"range":[156,214]}}},{"diffOp":{"equal":{"range":[214,230]}}},{"equalLines":{"line_count":109}},{"diffOp":{"equal":{"range":[230,240]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/annotations.spec.ts"},"span":[5592,5603],"sourceCode":"/**\n * Annotations E2E Tests\n *\n * Tests for annotation CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Annotation Operations', () => {\n let testMeetingId: string | null = null;\n let testAnnotationId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Create a test meeting for annotations\n const title = TestData.createMeetingTitle();\n try {\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n if (meeting && !meeting.error && meeting.id) {\n testMeetingId = meeting.id;\n }\n } catch {\n // Meeting creation may fail if server not connected\n }\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('addAnnotation', () => {\n it('should add an action_item annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'action_item',\n text: 'Follow up on meeting notes',\n start_time: 0,\n end_time: 10,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n expect(result.annotation).toHaveProperty('annotation_type');\n expect(result.annotation.text).toBe('Follow up on meeting notes');\n testAnnotationId = result.annotation.id;\n }\n });\n\n it('should add a decision annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'decision',\n text: 'Approved the new feature design',\n start_time: 15,\n end_time: 30,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n }\n });\n\n it('should add a note annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Important discussion point',\n start_time: 45,\n end_time: 60,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n\n it('should add a risk annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'risk',\n text: 'Potential deadline risk identified',\n start_time: 75,\n end_time: 90,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('listAnnotations', () => {\n it('should list all annotations for a meeting', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId);\n return { success: true, annotations, count: annotations?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.annotations)).toBe(true);\n console.log(`Found ${result.count} annotations`);\n }\n });\n\n it('should filter by time range', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId, 0, 30);\n return { success: true, annotations };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('getAnnotation', () => {\n it('should get annotation by ID', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.getAnnotation(annotationId);\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation.id).toBe(testAnnotationId);\n }\n });\n });\n\n describe('updateAnnotation', () => {\n it('should update annotation text', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateAnnotation({\n annotation_id: annotationId,\n text: 'Updated annotation text',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.text).toBe('Updated annotation text');\n }\n });\n });\n\n describe('deleteAnnotation', () => {\n it('should delete an annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n // Create an annotation to delete\n const createResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Annotation to delete',\n start_time: 100,\n end_time: 110,\n });\n } catch {\n return null;\n }\n }, testMeetingId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.deleteAnnotation(annotationId);\n return { success };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * Annotations E2E Tests\n *\n it('should filter by time range', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n } });\n});\n","ops":[{"diffOp":{"equal":{"range":[0,31]}}},{"equalLines":{"line_count":177}},{"diffOp":{"equal":{"range":[31,111]}}},{"diffOp":{"delete":{"range":[111,171]}}},{"diffOp":{"equal":{"range":[171,195]}}},{"equalLines":{"line_count":103}},{"diffOp":{"equal":{"range":[195,205]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/annotations.spec.ts"},"span":[5747,5758],"sourceCode":"/**\n * Annotations E2E Tests\n *\n * Tests for annotation CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Annotation Operations', () => {\n let testMeetingId: string | null = null;\n let testAnnotationId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Create a test meeting for annotations\n const title = TestData.createMeetingTitle();\n try {\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n if (meeting && !meeting.error && meeting.id) {\n testMeetingId = meeting.id;\n }\n } catch {\n // Meeting creation may fail if server not connected\n }\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('addAnnotation', () => {\n it('should add an action_item annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'action_item',\n text: 'Follow up on meeting notes',\n start_time: 0,\n end_time: 10,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n expect(result.annotation).toHaveProperty('annotation_type');\n expect(result.annotation.text).toBe('Follow up on meeting notes');\n testAnnotationId = result.annotation.id;\n }\n });\n\n it('should add a decision annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'decision',\n text: 'Approved the new feature design',\n start_time: 15,\n end_time: 30,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n }\n });\n\n it('should add a note annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Important discussion point',\n start_time: 45,\n end_time: 60,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n\n it('should add a risk annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'risk',\n text: 'Potential deadline risk identified',\n start_time: 75,\n end_time: 90,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('listAnnotations', () => {\n it('should list all annotations for a meeting', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId);\n return { success: true, annotations, count: annotations?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.annotations)).toBe(true);\n console.log(`Found ${result.count} annotations`);\n }\n });\n\n it('should filter by time range', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId, 0, 30);\n return { success: true, annotations };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('getAnnotation', () => {\n it('should get annotation by ID', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.getAnnotation(annotationId);\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation.id).toBe(testAnnotationId);\n }\n });\n });\n\n describe('updateAnnotation', () => {\n it('should update annotation text', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateAnnotation({\n annotation_id: annotationId,\n text: 'Updated annotation text',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.text).toBe('Updated annotation text');\n }\n });\n });\n\n describe('deleteAnnotation', () => {\n it('should delete an annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n // Create an annotation to delete\n const createResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Annotation to delete',\n start_time: 100,\n end_time: 110,\n });\n } catch {\n return null;\n }\n }, testMeetingId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.deleteAnnotation(annotationId);\n return { success };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/annotations.spec.ts"},"span":[524,527],"sourceCode":"/**\n * Annotations E2E Tests\n *\n * Tests for annotation CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Annotation Operations', () => {\n let testMeetingId: string | null = null;\n let testAnnotationId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Create a test meeting for annotations\n const title = TestData.createMeetingTitle();\n try {\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n if (meeting && !meeting.error && meeting.id) {\n testMeetingId = meeting.id;\n }\n } catch {\n // Meeting creation may fail if server not connected\n }\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('addAnnotation', () => {\n it('should add an action_item annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'action_item',\n text: 'Follow up on meeting notes',\n start_time: 0,\n end_time: 10,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n expect(result.annotation).toHaveProperty('annotation_type');\n expect(result.annotation.text).toBe('Follow up on meeting notes');\n testAnnotationId = result.annotation.id;\n }\n });\n\n it('should add a decision annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'decision',\n text: 'Approved the new feature design',\n start_time: 15,\n end_time: 30,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n }\n });\n\n it('should add a note annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Important discussion point',\n start_time: 45,\n end_time: 60,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n\n it('should add a risk annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'risk',\n text: 'Potential deadline risk identified',\n start_time: 75,\n end_time: 90,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('listAnnotations', () => {\n it('should list all annotations for a meeting', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId);\n return { success: true, annotations, count: annotations?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.annotations)).toBe(true);\n console.log(`Found ${result.count} annotations`);\n }\n });\n\n it('should filter by time range', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId, 0, 30);\n return { success: true, annotations };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('getAnnotation', () => {\n it('should get annotation by ID', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.getAnnotation(annotationId);\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation.id).toBe(testAnnotationId);\n }\n });\n });\n\n describe('updateAnnotation', () => {\n it('should update annotation text', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateAnnotation({\n annotation_id: annotationId,\n text: 'Updated annotation text',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.text).toBe('Updated annotation text');\n }\n });\n });\n\n describe('deleteAnnotation', () => {\n it('should delete an annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n // Create an annotation to delete\n const createResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Annotation to delete',\n start_time: 100,\n end_time: 110,\n });\n } catch {\n return null;\n }\n }, testMeetingId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.deleteAnnotation(annotationId);\n return { success };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * Annotations E2E Tests\n * describe('updateAnnotation', () => {\n it('should update annotation text', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n } });\n});\n","ops":[{"diffOp":{"equal":{"range":[0,31]}}},{"equalLines":{"line_count":222}},{"diffOp":{"equal":{"range":[31,154]}}},{"diffOp":{"delete":{"range":[154,215]}}},{"diffOp":{"equal":{"range":[215,239]}}},{"equalLines":{"line_count":58}},{"diffOp":{"equal":{"range":[239,249]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/annotations.spec.ts"},"span":[7166,7177],"sourceCode":"/**\n * Annotations E2E Tests\n *\n * Tests for annotation CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Annotation Operations', () => {\n let testMeetingId: string | null = null;\n let testAnnotationId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Create a test meeting for annotations\n const title = TestData.createMeetingTitle();\n try {\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n if (meeting && !meeting.error && meeting.id) {\n testMeetingId = meeting.id;\n }\n } catch {\n // Meeting creation may fail if server not connected\n }\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('addAnnotation', () => {\n it('should add an action_item annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'action_item',\n text: 'Follow up on meeting notes',\n start_time: 0,\n end_time: 10,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n expect(result.annotation).toHaveProperty('annotation_type');\n expect(result.annotation.text).toBe('Follow up on meeting notes');\n testAnnotationId = result.annotation.id;\n }\n });\n\n it('should add a decision annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'decision',\n text: 'Approved the new feature design',\n start_time: 15,\n end_time: 30,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n }\n });\n\n it('should add a note annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Important discussion point',\n start_time: 45,\n end_time: 60,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n\n it('should add a risk annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'risk',\n text: 'Potential deadline risk identified',\n start_time: 75,\n end_time: 90,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('listAnnotations', () => {\n it('should list all annotations for a meeting', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId);\n return { success: true, annotations, count: annotations?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.annotations)).toBe(true);\n console.log(`Found ${result.count} annotations`);\n }\n });\n\n it('should filter by time range', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId, 0, 30);\n return { success: true, annotations };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('getAnnotation', () => {\n it('should get annotation by ID', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.getAnnotation(annotationId);\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation.id).toBe(testAnnotationId);\n }\n });\n });\n\n describe('updateAnnotation', () => {\n it('should update annotation text', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateAnnotation({\n annotation_id: annotationId,\n text: 'Updated annotation text',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.text).toBe('Updated annotation text');\n }\n });\n });\n\n describe('deleteAnnotation', () => {\n it('should delete an annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n // Create an annotation to delete\n const createResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Annotation to delete',\n start_time: 100,\n end_time: 110,\n });\n } catch {\n return null;\n }\n }, testMeetingId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.deleteAnnotation(annotationId);\n return { success };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * Annotations E2E Tests\n * describe('deleteAnnotation', () => {\n it('should delete an annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n } });\n});\n","ops":[{"diffOp":{"equal":{"range":[0,31]}}},{"equalLines":{"line_count":249}},{"diffOp":{"equal":{"range":[31,149]}}},{"diffOp":{"delete":{"range":[149,209]}}},{"diffOp":{"equal":{"range":[209,233]}}},{"equalLines":{"line_count":31}},{"diffOp":{"equal":{"range":[233,243]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/annotations.spec.ts"},"span":[8013,8024],"sourceCode":"/**\n * Annotations E2E Tests\n *\n * Tests for annotation CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Annotation Operations', () => {\n let testMeetingId: string | null = null;\n let testAnnotationId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Create a test meeting for annotations\n const title = TestData.createMeetingTitle();\n try {\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n if (meeting && !meeting.error && meeting.id) {\n testMeetingId = meeting.id;\n }\n } catch {\n // Meeting creation may fail if server not connected\n }\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('addAnnotation', () => {\n it('should add an action_item annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'action_item',\n text: 'Follow up on meeting notes',\n start_time: 0,\n end_time: 10,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n expect(result.annotation).toHaveProperty('annotation_type');\n expect(result.annotation.text).toBe('Follow up on meeting notes');\n testAnnotationId = result.annotation.id;\n }\n });\n\n it('should add a decision annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'decision',\n text: 'Approved the new feature design',\n start_time: 15,\n end_time: 30,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n }\n });\n\n it('should add a note annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Important discussion point',\n start_time: 45,\n end_time: 60,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n\n it('should add a risk annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'risk',\n text: 'Potential deadline risk identified',\n start_time: 75,\n end_time: 90,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('listAnnotations', () => {\n it('should list all annotations for a meeting', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId);\n return { success: true, annotations, count: annotations?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.annotations)).toBe(true);\n console.log(`Found ${result.count} annotations`);\n }\n });\n\n it('should filter by time range', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId, 0, 30);\n return { success: true, annotations };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('getAnnotation', () => {\n it('should get annotation by ID', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.getAnnotation(annotationId);\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation.id).toBe(testAnnotationId);\n }\n });\n });\n\n describe('updateAnnotation', () => {\n it('should update annotation text', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateAnnotation({\n annotation_id: annotationId,\n text: 'Updated annotation text',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.text).toBe('Updated annotation text');\n }\n });\n });\n\n describe('deleteAnnotation', () => {\n it('should delete an annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n // Create an annotation to delete\n const createResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Annotation to delete',\n start_time: 100,\n end_time: 110,\n });\n } catch {\n return null;\n }\n }, testMeetingId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.deleteAnnotation(annotationId);\n return { success };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/annotations.spec.ts"},"span":[1070,1073],"sourceCode":"/**\n * Annotations E2E Tests\n *\n * Tests for annotation CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Annotation Operations', () => {\n let testMeetingId: string | null = null;\n let testAnnotationId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Create a test meeting for annotations\n const title = TestData.createMeetingTitle();\n try {\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n if (meeting && !meeting.error && meeting.id) {\n testMeetingId = meeting.id;\n }\n } catch {\n // Meeting creation may fail if server not connected\n }\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('addAnnotation', () => {\n it('should add an action_item annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'action_item',\n text: 'Follow up on meeting notes',\n start_time: 0,\n end_time: 10,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n expect(result.annotation).toHaveProperty('annotation_type');\n expect(result.annotation.text).toBe('Follow up on meeting notes');\n testAnnotationId = result.annotation.id;\n }\n });\n\n it('should add a decision annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'decision',\n text: 'Approved the new feature design',\n start_time: 15,\n end_time: 30,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n }\n });\n\n it('should add a note annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Important discussion point',\n start_time: 45,\n end_time: 60,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n\n it('should add a risk annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'risk',\n text: 'Potential deadline risk identified',\n start_time: 75,\n end_time: 90,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('listAnnotations', () => {\n it('should list all annotations for a meeting', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId);\n return { success: true, annotations, count: annotations?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.annotations)).toBe(true);\n console.log(`Found ${result.count} annotations`);\n }\n });\n\n it('should filter by time range', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId, 0, 30);\n return { success: true, annotations };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('getAnnotation', () => {\n it('should get annotation by ID', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.getAnnotation(annotationId);\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation.id).toBe(testAnnotationId);\n }\n });\n });\n\n describe('updateAnnotation', () => {\n it('should update annotation text', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateAnnotation({\n annotation_id: annotationId,\n text: 'Updated annotation text',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.text).toBe('Updated annotation text');\n }\n });\n });\n\n describe('deleteAnnotation', () => {\n it('should delete an annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n // Create an annotation to delete\n const createResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Annotation to delete',\n start_time: 100,\n end_time: 110,\n });\n } catch {\n return null;\n }\n }, testMeetingId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.deleteAnnotation(annotationId);\n return { success };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/annotations.spec.ts"},"span":[1536,1539],"sourceCode":"/**\n * Annotations E2E Tests\n *\n * Tests for annotation CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Annotation Operations', () => {\n let testMeetingId: string | null = null;\n let testAnnotationId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Create a test meeting for annotations\n const title = TestData.createMeetingTitle();\n try {\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n if (meeting && !meeting.error && meeting.id) {\n testMeetingId = meeting.id;\n }\n } catch {\n // Meeting creation may fail if server not connected\n }\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('addAnnotation', () => {\n it('should add an action_item annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'action_item',\n text: 'Follow up on meeting notes',\n start_time: 0,\n end_time: 10,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n expect(result.annotation).toHaveProperty('annotation_type');\n expect(result.annotation.text).toBe('Follow up on meeting notes');\n testAnnotationId = result.annotation.id;\n }\n });\n\n it('should add a decision annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'decision',\n text: 'Approved the new feature design',\n start_time: 15,\n end_time: 30,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n }\n });\n\n it('should add a note annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Important discussion point',\n start_time: 45,\n end_time: 60,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n\n it('should add a risk annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'risk',\n text: 'Potential deadline risk identified',\n start_time: 75,\n end_time: 90,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('listAnnotations', () => {\n it('should list all annotations for a meeting', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId);\n return { success: true, annotations, count: annotations?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.annotations)).toBe(true);\n console.log(`Found ${result.count} annotations`);\n }\n });\n\n it('should filter by time range', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId, 0, 30);\n return { success: true, annotations };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('getAnnotation', () => {\n it('should get annotation by ID', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.getAnnotation(annotationId);\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation.id).toBe(testAnnotationId);\n }\n });\n });\n\n describe('updateAnnotation', () => {\n it('should update annotation text', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateAnnotation({\n annotation_id: annotationId,\n text: 'Updated annotation text',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.text).toBe('Updated annotation text');\n }\n });\n });\n\n describe('deleteAnnotation', () => {\n it('should delete an annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n // Create an annotation to delete\n const createResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Annotation to delete',\n start_time: 100,\n end_time: 110,\n });\n } catch {\n return null;\n }\n }, testMeetingId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.deleteAnnotation(annotationId);\n return { success };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/annotations.spec.ts"},"span":[2613,2616],"sourceCode":"/**\n * Annotations E2E Tests\n *\n * Tests for annotation CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Annotation Operations', () => {\n let testMeetingId: string | null = null;\n let testAnnotationId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Create a test meeting for annotations\n const title = TestData.createMeetingTitle();\n try {\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n if (meeting && !meeting.error && meeting.id) {\n testMeetingId = meeting.id;\n }\n } catch {\n // Meeting creation may fail if server not connected\n }\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('addAnnotation', () => {\n it('should add an action_item annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'action_item',\n text: 'Follow up on meeting notes',\n start_time: 0,\n end_time: 10,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n expect(result.annotation).toHaveProperty('annotation_type');\n expect(result.annotation.text).toBe('Follow up on meeting notes');\n testAnnotationId = result.annotation.id;\n }\n });\n\n it('should add a decision annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'decision',\n text: 'Approved the new feature design',\n start_time: 15,\n end_time: 30,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n }\n });\n\n it('should add a note annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Important discussion point',\n start_time: 45,\n end_time: 60,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n\n it('should add a risk annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'risk',\n text: 'Potential deadline risk identified',\n start_time: 75,\n end_time: 90,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('listAnnotations', () => {\n it('should list all annotations for a meeting', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId);\n return { success: true, annotations, count: annotations?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.annotations)).toBe(true);\n console.log(`Found ${result.count} annotations`);\n }\n });\n\n it('should filter by time range', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId, 0, 30);\n return { success: true, annotations };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('getAnnotation', () => {\n it('should get annotation by ID', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.getAnnotation(annotationId);\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation.id).toBe(testAnnotationId);\n }\n });\n });\n\n describe('updateAnnotation', () => {\n it('should update annotation text', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateAnnotation({\n annotation_id: annotationId,\n text: 'Updated annotation text',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.text).toBe('Updated annotation text');\n }\n });\n });\n\n describe('deleteAnnotation', () => {\n it('should delete an annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n // Create an annotation to delete\n const createResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Annotation to delete',\n start_time: 100,\n end_time: 110,\n });\n } catch {\n return null;\n }\n }, testMeetingId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.deleteAnnotation(annotationId);\n return { success };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/annotations.spec.ts"},"span":[3496,3499],"sourceCode":"/**\n * Annotations E2E Tests\n *\n * Tests for annotation CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Annotation Operations', () => {\n let testMeetingId: string | null = null;\n let testAnnotationId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Create a test meeting for annotations\n const title = TestData.createMeetingTitle();\n try {\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n if (meeting && !meeting.error && meeting.id) {\n testMeetingId = meeting.id;\n }\n } catch {\n // Meeting creation may fail if server not connected\n }\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('addAnnotation', () => {\n it('should add an action_item annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'action_item',\n text: 'Follow up on meeting notes',\n start_time: 0,\n end_time: 10,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n expect(result.annotation).toHaveProperty('annotation_type');\n expect(result.annotation.text).toBe('Follow up on meeting notes');\n testAnnotationId = result.annotation.id;\n }\n });\n\n it('should add a decision annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'decision',\n text: 'Approved the new feature design',\n start_time: 15,\n end_time: 30,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n }\n });\n\n it('should add a note annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Important discussion point',\n start_time: 45,\n end_time: 60,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n\n it('should add a risk annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'risk',\n text: 'Potential deadline risk identified',\n start_time: 75,\n end_time: 90,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('listAnnotations', () => {\n it('should list all annotations for a meeting', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId);\n return { success: true, annotations, count: annotations?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.annotations)).toBe(true);\n console.log(`Found ${result.count} annotations`);\n }\n });\n\n it('should filter by time range', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId, 0, 30);\n return { success: true, annotations };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('getAnnotation', () => {\n it('should get annotation by ID', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.getAnnotation(annotationId);\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation.id).toBe(testAnnotationId);\n }\n });\n });\n\n describe('updateAnnotation', () => {\n it('should update annotation text', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateAnnotation({\n annotation_id: annotationId,\n text: 'Updated annotation text',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.text).toBe('Updated annotation text');\n }\n });\n });\n\n describe('deleteAnnotation', () => {\n it('should delete an annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n // Create an annotation to delete\n const createResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Annotation to delete',\n start_time: 100,\n end_time: 110,\n });\n } catch {\n return null;\n }\n }, testMeetingId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.deleteAnnotation(annotationId);\n return { success };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/annotations.spec.ts"},"span":[4278,4281],"sourceCode":"/**\n * Annotations E2E Tests\n *\n * Tests for annotation CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Annotation Operations', () => {\n let testMeetingId: string | null = null;\n let testAnnotationId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Create a test meeting for annotations\n const title = TestData.createMeetingTitle();\n try {\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n if (meeting && !meeting.error && meeting.id) {\n testMeetingId = meeting.id;\n }\n } catch {\n // Meeting creation may fail if server not connected\n }\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('addAnnotation', () => {\n it('should add an action_item annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'action_item',\n text: 'Follow up on meeting notes',\n start_time: 0,\n end_time: 10,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n expect(result.annotation).toHaveProperty('annotation_type');\n expect(result.annotation.text).toBe('Follow up on meeting notes');\n testAnnotationId = result.annotation.id;\n }\n });\n\n it('should add a decision annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'decision',\n text: 'Approved the new feature design',\n start_time: 15,\n end_time: 30,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n }\n });\n\n it('should add a note annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Important discussion point',\n start_time: 45,\n end_time: 60,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n\n it('should add a risk annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'risk',\n text: 'Potential deadline risk identified',\n start_time: 75,\n end_time: 90,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('listAnnotations', () => {\n it('should list all annotations for a meeting', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId);\n return { success: true, annotations, count: annotations?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.annotations)).toBe(true);\n console.log(`Found ${result.count} annotations`);\n }\n });\n\n it('should filter by time range', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId, 0, 30);\n return { success: true, annotations };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('getAnnotation', () => {\n it('should get annotation by ID', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.getAnnotation(annotationId);\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation.id).toBe(testAnnotationId);\n }\n });\n });\n\n describe('updateAnnotation', () => {\n it('should update annotation text', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateAnnotation({\n annotation_id: annotationId,\n text: 'Updated annotation text',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.text).toBe('Updated annotation text');\n }\n });\n });\n\n describe('deleteAnnotation', () => {\n it('should delete an annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n // Create an annotation to delete\n const createResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Annotation to delete',\n start_time: 100,\n end_time: 110,\n });\n } catch {\n return null;\n }\n }, testMeetingId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.deleteAnnotation(annotationId);\n return { success };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/annotations.spec.ts"},"span":[5125,5128],"sourceCode":"/**\n * Annotations E2E Tests\n *\n * Tests for annotation CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Annotation Operations', () => {\n let testMeetingId: string | null = null;\n let testAnnotationId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Create a test meeting for annotations\n const title = TestData.createMeetingTitle();\n try {\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n if (meeting && !meeting.error && meeting.id) {\n testMeetingId = meeting.id;\n }\n } catch {\n // Meeting creation may fail if server not connected\n }\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('addAnnotation', () => {\n it('should add an action_item annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'action_item',\n text: 'Follow up on meeting notes',\n start_time: 0,\n end_time: 10,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n expect(result.annotation).toHaveProperty('annotation_type');\n expect(result.annotation.text).toBe('Follow up on meeting notes');\n testAnnotationId = result.annotation.id;\n }\n });\n\n it('should add a decision annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'decision',\n text: 'Approved the new feature design',\n start_time: 15,\n end_time: 30,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation).toHaveProperty('id');\n }\n });\n\n it('should add a note annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Important discussion point',\n start_time: 45,\n end_time: 60,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n\n it('should add a risk annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'risk',\n text: 'Potential deadline risk identified',\n start_time: 75,\n end_time: 90,\n });\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('listAnnotations', () => {\n it('should list all annotations for a meeting', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId);\n return { success: true, annotations, count: annotations?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.annotations)).toBe(true);\n console.log(`Found ${result.count} annotations`);\n }\n });\n\n it('should filter by time range', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotations = await api.listAnnotations(meetingId, 0, 30);\n return { success: true, annotations };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testMeetingId);\n\n expect(result).toBeDefined();\n });\n });\n\n describe('getAnnotation', () => {\n it('should get annotation by ID', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const annotation = await api.getAnnotation(annotationId);\n return { success: true, annotation };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.annotation.id).toBe(testAnnotationId);\n }\n });\n });\n\n describe('updateAnnotation', () => {\n it('should update annotation text', async () => {\n if (!testAnnotationId) {\n console.log('Skipping: no test annotation created');\n return;\n }\n\n const result = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateAnnotation({\n annotation_id: annotationId,\n text: 'Updated annotation text',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testAnnotationId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.text).toBe('Updated annotation text');\n }\n });\n });\n\n describe('deleteAnnotation', () => {\n it('should delete an annotation', async () => {\n if (!testMeetingId) {\n console.log('Skipping: no test meeting available');\n return;\n }\n\n // Create an annotation to delete\n const createResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.addAnnotation({\n meeting_id: meetingId,\n annotation_type: 'note',\n text: 'Annotation to delete',\n start_time: 100,\n end_time: 110,\n });\n } catch {\n return null;\n }\n }, testMeetingId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (annotationId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.deleteAnnotation(annotationId);\n return { success };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/app.spec.ts"},"span":[5123,5126],"sourceCode":"/**\n * Native App E2E Tests\n *\n * Tests that run against the actual Tauri desktop application.\n * These tests validate real IPC commands and native functionality.\n */\n\nimport {\n waitForAppReady,\n navigateTo,\n isTauriAvailable,\n getWindowTitle,\n waitForLoadingComplete,\n isVisible,\n getText,\n takeScreenshot,\n} from './fixtures';\n\ndescribe('NoteFlow Desktop App', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('app initialization', () => {\n it('should load with correct window title', async () => {\n const title = await getWindowTitle();\n expect(title).toContain('NoteFlow');\n });\n\n it('should have Tauri IPC available', async () => {\n // In Tauri 2.0, __TAURI__ may not be directly on window\n // Instead, verify IPC works by checking if API functions exist\n const result = await browser.execute(() => {\n const api = (window as any).__NOTEFLOW_API__;\n return {\n hasApi: !!api,\n hasFunctions: !!(api?.listMeetings && api?.getPreferences),\n };\n });\n expect(result.hasApi).toBe(true);\n expect(result.hasFunctions).toBe(true);\n });\n\n it('should render the main layout', async () => {\n const hasMain = await isVisible('main');\n expect(hasMain).toBe(true);\n });\n });\n\n describe('navigation', () => {\n it('should navigate to meetings page', async () => {\n await navigateTo('/meetings');\n await waitForLoadingComplete();\n\n const hasContent = await isVisible('main');\n expect(hasContent).toBe(true);\n });\n\n it('should navigate to settings page', async () => {\n await navigateTo('/settings');\n await waitForLoadingComplete();\n\n const hasContent = await isVisible('main');\n expect(hasContent).toBe(true);\n });\n\n it('should navigate to recording page', async () => {\n await navigateTo('/recording/new');\n await waitForLoadingComplete();\n\n const hasContent = await isVisible('main');\n expect(hasContent).toBe(true);\n });\n });\n});\n\ndescribe('gRPC Connection', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n it('should show connection status indicator', async () => {\n // The connection status component should be visible\n const hasStatus = await isVisible('[data-testid=\"connection-status\"]');\n // May or may not be visible depending on UI design\n await takeScreenshot('connection-status');\n });\n\n it('should handle connection to backend', async () => {\n // Check if the app can communicate with the gRPC server\n // This tests real Tauri IPC → Rust → gRPC flow\n const result = await browser.execute(async () => {\n try {\n // Access the API exposed by the app\n const api = (window as any).__NOTEFLOW_API__;\n if (!api) {\n return { available: false, error: 'API not exposed' };\n }\n\n // Try to list meetings (basic connectivity test)\n const meetings = await api.listMeetings({ limit: 1 });\n return { available: true, connected: true, meetings };\n } catch (error) {\n return {\n available: true,\n connected: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n });\n\n expect(result.available).toBe(true);\n // Connection may or may not succeed depending on server state\n console.log('Connection test result:', result);\n });\n});\n\ndescribe('Audio Device Access', () => {\n before(async () => {\n await waitForAppReady();\n await navigateTo('/recording/new');\n await waitForLoadingComplete();\n });\n\n it('should list available audio devices', async () => {\n // Test real audio device enumeration via Tauri IPC\n // Note: getAudioDevices may be invoked differently (direct Tauri invoke vs API wrapper)\n const result = await browser.execute(async () => {\n try {\n // Try API wrapper first\n const api = (window as any).__NOTEFLOW_API__;\n if (api?.getAudioDevices) {\n const devices = await api.getAudioDevices();\n return { available: true, source: 'api', devices };\n }\n\n // Try direct Tauri invoke\n const { invoke } = await import('@tauri-apps/api/core');\n const devices = await invoke('get_audio_devices');\n return { available: true, source: 'invoke', devices };\n } catch (error) {\n return {\n available: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n });\n\n console.log('Audio devices:', result);\n // Test passes if we got any result (available or error with reason)\n expect(result).toBeDefined();\n // If available, should have devices array\n if (result.available) {\n expect(result.devices).toBeDefined();\n }\n });\n});\n\ndescribe('Preferences', () => {\n before(async () => {\n await waitForAppReady();\n await navigateTo('/settings');\n await waitForLoadingComplete();\n });\n\n it('should load user preferences', async () => {\n const result = await browser.execute(async () => {\n try {\n const api = (window as any).__NOTEFLOW_API__;\n if (!api?.getPreferences) {\n return { available: false };\n }\n\n const prefs = await api.getPreferences();\n return { available: true, prefs };\n } catch (error) {\n return {\n available: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n });\n\n console.log('Preferences result:', result);\n expect(result.available).toBe(true);\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/app.spec.ts"},"span":[908,911],"sourceCode":"/**\n * Native App E2E Tests\n *\n * Tests that run against the actual Tauri desktop application.\n * These tests validate real IPC commands and native functionality.\n */\n\nimport {\n waitForAppReady,\n navigateTo,\n isTauriAvailable,\n getWindowTitle,\n waitForLoadingComplete,\n isVisible,\n getText,\n takeScreenshot,\n} from './fixtures';\n\ndescribe('NoteFlow Desktop App', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('app initialization', () => {\n it('should load with correct window title', async () => {\n const title = await getWindowTitle();\n expect(title).toContain('NoteFlow');\n });\n\n it('should have Tauri IPC available', async () => {\n // In Tauri 2.0, __TAURI__ may not be directly on window\n // Instead, verify IPC works by checking if API functions exist\n const result = await browser.execute(() => {\n const api = (window as any).__NOTEFLOW_API__;\n return {\n hasApi: !!api,\n hasFunctions: !!(api?.listMeetings && api?.getPreferences),\n };\n });\n expect(result.hasApi).toBe(true);\n expect(result.hasFunctions).toBe(true);\n });\n\n it('should render the main layout', async () => {\n const hasMain = await isVisible('main');\n expect(hasMain).toBe(true);\n });\n });\n\n describe('navigation', () => {\n it('should navigate to meetings page', async () => {\n await navigateTo('/meetings');\n await waitForLoadingComplete();\n\n const hasContent = await isVisible('main');\n expect(hasContent).toBe(true);\n });\n\n it('should navigate to settings page', async () => {\n await navigateTo('/settings');\n await waitForLoadingComplete();\n\n const hasContent = await isVisible('main');\n expect(hasContent).toBe(true);\n });\n\n it('should navigate to recording page', async () => {\n await navigateTo('/recording/new');\n await waitForLoadingComplete();\n\n const hasContent = await isVisible('main');\n expect(hasContent).toBe(true);\n });\n });\n});\n\ndescribe('gRPC Connection', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n it('should show connection status indicator', async () => {\n // The connection status component should be visible\n const hasStatus = await isVisible('[data-testid=\"connection-status\"]');\n // May or may not be visible depending on UI design\n await takeScreenshot('connection-status');\n });\n\n it('should handle connection to backend', async () => {\n // Check if the app can communicate with the gRPC server\n // This tests real Tauri IPC → Rust → gRPC flow\n const result = await browser.execute(async () => {\n try {\n // Access the API exposed by the app\n const api = (window as any).__NOTEFLOW_API__;\n if (!api) {\n return { available: false, error: 'API not exposed' };\n }\n\n // Try to list meetings (basic connectivity test)\n const meetings = await api.listMeetings({ limit: 1 });\n return { available: true, connected: true, meetings };\n } catch (error) {\n return {\n available: true,\n connected: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n });\n\n expect(result.available).toBe(true);\n // Connection may or may not succeed depending on server state\n console.log('Connection test result:', result);\n });\n});\n\ndescribe('Audio Device Access', () => {\n before(async () => {\n await waitForAppReady();\n await navigateTo('/recording/new');\n await waitForLoadingComplete();\n });\n\n it('should list available audio devices', async () => {\n // Test real audio device enumeration via Tauri IPC\n // Note: getAudioDevices may be invoked differently (direct Tauri invoke vs API wrapper)\n const result = await browser.execute(async () => {\n try {\n // Try API wrapper first\n const api = (window as any).__NOTEFLOW_API__;\n if (api?.getAudioDevices) {\n const devices = await api.getAudioDevices();\n return { available: true, source: 'api', devices };\n }\n\n // Try direct Tauri invoke\n const { invoke } = await import('@tauri-apps/api/core');\n const devices = await invoke('get_audio_devices');\n return { available: true, source: 'invoke', devices };\n } catch (error) {\n return {\n available: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n });\n\n console.log('Audio devices:', result);\n // Test passes if we got any result (available or error with reason)\n expect(result).toBeDefined();\n // If available, should have devices array\n if (result.available) {\n expect(result.devices).toBeDefined();\n }\n });\n});\n\ndescribe('Preferences', () => {\n before(async () => {\n await waitForAppReady();\n await navigateTo('/settings');\n await waitForLoadingComplete();\n });\n\n it('should load user preferences', async () => {\n const result = await browser.execute(async () => {\n try {\n const api = (window as any).__NOTEFLOW_API__;\n if (!api?.getPreferences) {\n return { available: false };\n }\n\n const prefs = await api.getPreferences();\n return { available: true, prefs };\n } catch (error) {\n return {\n available: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n });\n\n console.log('Preferences result:', result);\n expect(result.available).toBe(true);\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/app.spec.ts"},"span":[2763,2766],"sourceCode":"/**\n * Native App E2E Tests\n *\n * Tests that run against the actual Tauri desktop application.\n * These tests validate real IPC commands and native functionality.\n */\n\nimport {\n waitForAppReady,\n navigateTo,\n isTauriAvailable,\n getWindowTitle,\n waitForLoadingComplete,\n isVisible,\n getText,\n takeScreenshot,\n} from './fixtures';\n\ndescribe('NoteFlow Desktop App', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('app initialization', () => {\n it('should load with correct window title', async () => {\n const title = await getWindowTitle();\n expect(title).toContain('NoteFlow');\n });\n\n it('should have Tauri IPC available', async () => {\n // In Tauri 2.0, __TAURI__ may not be directly on window\n // Instead, verify IPC works by checking if API functions exist\n const result = await browser.execute(() => {\n const api = (window as any).__NOTEFLOW_API__;\n return {\n hasApi: !!api,\n hasFunctions: !!(api?.listMeetings && api?.getPreferences),\n };\n });\n expect(result.hasApi).toBe(true);\n expect(result.hasFunctions).toBe(true);\n });\n\n it('should render the main layout', async () => {\n const hasMain = await isVisible('main');\n expect(hasMain).toBe(true);\n });\n });\n\n describe('navigation', () => {\n it('should navigate to meetings page', async () => {\n await navigateTo('/meetings');\n await waitForLoadingComplete();\n\n const hasContent = await isVisible('main');\n expect(hasContent).toBe(true);\n });\n\n it('should navigate to settings page', async () => {\n await navigateTo('/settings');\n await waitForLoadingComplete();\n\n const hasContent = await isVisible('main');\n expect(hasContent).toBe(true);\n });\n\n it('should navigate to recording page', async () => {\n await navigateTo('/recording/new');\n await waitForLoadingComplete();\n\n const hasContent = await isVisible('main');\n expect(hasContent).toBe(true);\n });\n });\n});\n\ndescribe('gRPC Connection', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n it('should show connection status indicator', async () => {\n // The connection status component should be visible\n const hasStatus = await isVisible('[data-testid=\"connection-status\"]');\n // May or may not be visible depending on UI design\n await takeScreenshot('connection-status');\n });\n\n it('should handle connection to backend', async () => {\n // Check if the app can communicate with the gRPC server\n // This tests real Tauri IPC → Rust → gRPC flow\n const result = await browser.execute(async () => {\n try {\n // Access the API exposed by the app\n const api = (window as any).__NOTEFLOW_API__;\n if (!api) {\n return { available: false, error: 'API not exposed' };\n }\n\n // Try to list meetings (basic connectivity test)\n const meetings = await api.listMeetings({ limit: 1 });\n return { available: true, connected: true, meetings };\n } catch (error) {\n return {\n available: true,\n connected: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n });\n\n expect(result.available).toBe(true);\n // Connection may or may not succeed depending on server state\n console.log('Connection test result:', result);\n });\n});\n\ndescribe('Audio Device Access', () => {\n before(async () => {\n await waitForAppReady();\n await navigateTo('/recording/new');\n await waitForLoadingComplete();\n });\n\n it('should list available audio devices', async () => {\n // Test real audio device enumeration via Tauri IPC\n // Note: getAudioDevices may be invoked differently (direct Tauri invoke vs API wrapper)\n const result = await browser.execute(async () => {\n try {\n // Try API wrapper first\n const api = (window as any).__NOTEFLOW_API__;\n if (api?.getAudioDevices) {\n const devices = await api.getAudioDevices();\n return { available: true, source: 'api', devices };\n }\n\n // Try direct Tauri invoke\n const { invoke } = await import('@tauri-apps/api/core');\n const devices = await invoke('get_audio_devices');\n return { available: true, source: 'invoke', devices };\n } catch (error) {\n return {\n available: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n });\n\n console.log('Audio devices:', result);\n // Test passes if we got any result (available or error with reason)\n expect(result).toBeDefined();\n // If available, should have devices array\n if (result.available) {\n expect(result.devices).toBeDefined();\n }\n });\n});\n\ndescribe('Preferences', () => {\n before(async () => {\n await waitForAppReady();\n await navigateTo('/settings');\n await waitForLoadingComplete();\n });\n\n it('should load user preferences', async () => {\n const result = await browser.execute(async () => {\n try {\n const api = (window as any).__NOTEFLOW_API__;\n if (!api?.getPreferences) {\n return { available: false };\n }\n\n const prefs = await api.getPreferences();\n return { available: true, prefs };\n } catch (error) {\n return {\n available: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n });\n\n console.log('Preferences result:', result);\n expect(result.available).toBe(true);\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/app.spec.ts"},"span":[3947,3950],"sourceCode":"/**\n * Native App E2E Tests\n *\n * Tests that run against the actual Tauri desktop application.\n * These tests validate real IPC commands and native functionality.\n */\n\nimport {\n waitForAppReady,\n navigateTo,\n isTauriAvailable,\n getWindowTitle,\n waitForLoadingComplete,\n isVisible,\n getText,\n takeScreenshot,\n} from './fixtures';\n\ndescribe('NoteFlow Desktop App', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('app initialization', () => {\n it('should load with correct window title', async () => {\n const title = await getWindowTitle();\n expect(title).toContain('NoteFlow');\n });\n\n it('should have Tauri IPC available', async () => {\n // In Tauri 2.0, __TAURI__ may not be directly on window\n // Instead, verify IPC works by checking if API functions exist\n const result = await browser.execute(() => {\n const api = (window as any).__NOTEFLOW_API__;\n return {\n hasApi: !!api,\n hasFunctions: !!(api?.listMeetings && api?.getPreferences),\n };\n });\n expect(result.hasApi).toBe(true);\n expect(result.hasFunctions).toBe(true);\n });\n\n it('should render the main layout', async () => {\n const hasMain = await isVisible('main');\n expect(hasMain).toBe(true);\n });\n });\n\n describe('navigation', () => {\n it('should navigate to meetings page', async () => {\n await navigateTo('/meetings');\n await waitForLoadingComplete();\n\n const hasContent = await isVisible('main');\n expect(hasContent).toBe(true);\n });\n\n it('should navigate to settings page', async () => {\n await navigateTo('/settings');\n await waitForLoadingComplete();\n\n const hasContent = await isVisible('main');\n expect(hasContent).toBe(true);\n });\n\n it('should navigate to recording page', async () => {\n await navigateTo('/recording/new');\n await waitForLoadingComplete();\n\n const hasContent = await isVisible('main');\n expect(hasContent).toBe(true);\n });\n });\n});\n\ndescribe('gRPC Connection', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n it('should show connection status indicator', async () => {\n // The connection status component should be visible\n const hasStatus = await isVisible('[data-testid=\"connection-status\"]');\n // May or may not be visible depending on UI design\n await takeScreenshot('connection-status');\n });\n\n it('should handle connection to backend', async () => {\n // Check if the app can communicate with the gRPC server\n // This tests real Tauri IPC → Rust → gRPC flow\n const result = await browser.execute(async () => {\n try {\n // Access the API exposed by the app\n const api = (window as any).__NOTEFLOW_API__;\n if (!api) {\n return { available: false, error: 'API not exposed' };\n }\n\n // Try to list meetings (basic connectivity test)\n const meetings = await api.listMeetings({ limit: 1 });\n return { available: true, connected: true, meetings };\n } catch (error) {\n return {\n available: true,\n connected: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n });\n\n expect(result.available).toBe(true);\n // Connection may or may not succeed depending on server state\n console.log('Connection test result:', result);\n });\n});\n\ndescribe('Audio Device Access', () => {\n before(async () => {\n await waitForAppReady();\n await navigateTo('/recording/new');\n await waitForLoadingComplete();\n });\n\n it('should list available audio devices', async () => {\n // Test real audio device enumeration via Tauri IPC\n // Note: getAudioDevices may be invoked differently (direct Tauri invoke vs API wrapper)\n const result = await browser.execute(async () => {\n try {\n // Try API wrapper first\n const api = (window as any).__NOTEFLOW_API__;\n if (api?.getAudioDevices) {\n const devices = await api.getAudioDevices();\n return { available: true, source: 'api', devices };\n }\n\n // Try direct Tauri invoke\n const { invoke } = await import('@tauri-apps/api/core');\n const devices = await invoke('get_audio_devices');\n return { available: true, source: 'invoke', devices };\n } catch (error) {\n return {\n available: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n });\n\n console.log('Audio devices:', result);\n // Test passes if we got any result (available or error with reason)\n expect(result).toBeDefined();\n // If available, should have devices array\n if (result.available) {\n expect(result.devices).toBeDefined();\n }\n });\n});\n\ndescribe('Preferences', () => {\n before(async () => {\n await waitForAppReady();\n await navigateTo('/settings');\n await waitForLoadingComplete();\n });\n\n it('should load user preferences', async () => {\n const result = await browser.execute(async () => {\n try {\n const api = (window as any).__NOTEFLOW_API__;\n if (!api?.getPreferences) {\n return { available: false };\n }\n\n const prefs = await api.getPreferences();\n return { available: true, prefs };\n } catch (error) {\n return {\n available: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n });\n\n console.log('Preferences result:', result);\n expect(result.available).toBe(true);\n });\n});\n"},"tags":[],"source":null},{"category":"lint/correctness/noUnusedImports","severity":"error","description":"Several of these imports are unused.","message":[{"elements":[],"content":"Several of these "},{"elements":["Emphasis"],"content":"imports"},{"elements":[],"content":" are unused."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"Unused imports might be the result of an incomplete refactoring."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove the unused imports."}]]},{"diff":{"dictionary":"/**\n * Native App E2E Tests\n *import {\n waitForAppReady,\n navigateTo,\n isTauriAvailable,\n getWindowTitle,\n waitForLoadingComplete,\n isVisible,\n getText\n takeScreenshot,\n} from './fixtures'; });\n});\n","ops":[{"diffOp":{"equal":{"range":[0,30]}}},{"equalLines":{"line_count":4}},{"diffOp":{"equal":{"range":[30,71]}}},{"diffOp":{"delete":{"range":[71,90]}}},{"diffOp":{"delete":{"range":[90,91]}}},{"diffOp":{"equal":{"range":[91,148]}}},{"diffOp":{"delete":{"range":[148,158]}}},{"diffOp":{"delete":{"range":[90,91]}}},{"diffOp":{"equal":{"range":[158,197]}}},{"equalLines":{"line_count":168}},{"diffOp":{"equal":{"range":[197,207]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/app.spec.ts"},"span":[212,296],"sourceCode":"/**\n * Native App E2E Tests\n *\n * Tests that run against the actual Tauri desktop application.\n * These tests validate real IPC commands and native functionality.\n */\n\nimport {\n waitForAppReady,\n navigateTo,\n isTauriAvailable,\n getWindowTitle,\n waitForLoadingComplete,\n isVisible,\n getText,\n takeScreenshot,\n} from './fixtures';\n\ndescribe('NoteFlow Desktop App', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('app initialization', () => {\n it('should load with correct window title', async () => {\n const title = await getWindowTitle();\n expect(title).toContain('NoteFlow');\n });\n\n it('should have Tauri IPC available', async () => {\n // In Tauri 2.0, __TAURI__ may not be directly on window\n // Instead, verify IPC works by checking if API functions exist\n const result = await browser.execute(() => {\n const api = (window as any).__NOTEFLOW_API__;\n return {\n hasApi: !!api,\n hasFunctions: !!(api?.listMeetings && api?.getPreferences),\n };\n });\n expect(result.hasApi).toBe(true);\n expect(result.hasFunctions).toBe(true);\n });\n\n it('should render the main layout', async () => {\n const hasMain = await isVisible('main');\n expect(hasMain).toBe(true);\n });\n });\n\n describe('navigation', () => {\n it('should navigate to meetings page', async () => {\n await navigateTo('/meetings');\n await waitForLoadingComplete();\n\n const hasContent = await isVisible('main');\n expect(hasContent).toBe(true);\n });\n\n it('should navigate to settings page', async () => {\n await navigateTo('/settings');\n await waitForLoadingComplete();\n\n const hasContent = await isVisible('main');\n expect(hasContent).toBe(true);\n });\n\n it('should navigate to recording page', async () => {\n await navigateTo('/recording/new');\n await waitForLoadingComplete();\n\n const hasContent = await isVisible('main');\n expect(hasContent).toBe(true);\n });\n });\n});\n\ndescribe('gRPC Connection', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n it('should show connection status indicator', async () => {\n // The connection status component should be visible\n const hasStatus = await isVisible('[data-testid=\"connection-status\"]');\n // May or may not be visible depending on UI design\n await takeScreenshot('connection-status');\n });\n\n it('should handle connection to backend', async () => {\n // Check if the app can communicate with the gRPC server\n // This tests real Tauri IPC → Rust → gRPC flow\n const result = await browser.execute(async () => {\n try {\n // Access the API exposed by the app\n const api = (window as any).__NOTEFLOW_API__;\n if (!api) {\n return { available: false, error: 'API not exposed' };\n }\n\n // Try to list meetings (basic connectivity test)\n const meetings = await api.listMeetings({ limit: 1 });\n return { available: true, connected: true, meetings };\n } catch (error) {\n return {\n available: true,\n connected: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n });\n\n expect(result.available).toBe(true);\n // Connection may or may not succeed depending on server state\n console.log('Connection test result:', result);\n });\n});\n\ndescribe('Audio Device Access', () => {\n before(async () => {\n await waitForAppReady();\n await navigateTo('/recording/new');\n await waitForLoadingComplete();\n });\n\n it('should list available audio devices', async () => {\n // Test real audio device enumeration via Tauri IPC\n // Note: getAudioDevices may be invoked differently (direct Tauri invoke vs API wrapper)\n const result = await browser.execute(async () => {\n try {\n // Try API wrapper first\n const api = (window as any).__NOTEFLOW_API__;\n if (api?.getAudioDevices) {\n const devices = await api.getAudioDevices();\n return { available: true, source: 'api', devices };\n }\n\n // Try direct Tauri invoke\n const { invoke } = await import('@tauri-apps/api/core');\n const devices = await invoke('get_audio_devices');\n return { available: true, source: 'invoke', devices };\n } catch (error) {\n return {\n available: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n });\n\n console.log('Audio devices:', result);\n // Test passes if we got any result (available or error with reason)\n expect(result).toBeDefined();\n // If available, should have devices array\n if (result.available) {\n expect(result.devices).toBeDefined();\n }\n });\n});\n\ndescribe('Preferences', () => {\n before(async () => {\n await waitForAppReady();\n await navigateTo('/settings');\n await waitForLoadingComplete();\n });\n\n it('should load user preferences', async () => {\n const result = await browser.execute(async () => {\n try {\n const api = (window as any).__NOTEFLOW_API__;\n if (!api?.getPreferences) {\n return { available: false };\n }\n\n const prefs = await api.getPreferences();\n return { available: true, prefs };\n } catch (error) {\n return {\n available: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n });\n\n console.log('Preferences result:', result);\n expect(result.available).toBe(true);\n });\n});\n"},"tags":["fixable"],"source":null},{"category":"lint/correctness/noUnusedVariables","severity":"error","description":"This variable hasStatus is unused.","message":[{"elements":[],"content":"This variable "},{"elements":["Emphasis"],"content":"hasStatus"},{"elements":[],"content":" is unused."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"Unused variables are often the result of typos, incomplete refactors, or other sources of bugs."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: If this is intentional, prepend "},{"elements":["Emphasis"],"content":"hasStatus"},{"elements":[],"content":" with an underscore."}]]},{"diff":{"dictionary":"/**\n * Native App E2E Tests\n * it('should show connection status indicator', async () => {\n // The connection status component should be visible\n const hasStatus_hasStatus= await isVisible('[data-testid=\"connection-status\"]');\n // May or may not be visible depending on UI design\n await takeScreenshot('connection-status'); });\n});\n","ops":[{"diffOp":{"equal":{"range":[0,30]}}},{"equalLines":{"line_count":78}},{"diffOp":{"equal":{"range":[30,159]}}},{"diffOp":{"delete":{"range":[159,168]}}},{"diffOp":{"insert":{"range":[168,178]}}},{"diffOp":{"equal":{"range":[30,31]}}},{"diffOp":{"equal":{"range":[178,336]}}},{"equalLines":{"line_count":99}},{"diffOp":{"equal":{"range":[336,346]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/app.spec.ts"},"span":[2269,2278],"sourceCode":"/**\n * Native App E2E Tests\n *\n * Tests that run against the actual Tauri desktop application.\n * These tests validate real IPC commands and native functionality.\n */\n\nimport {\n waitForAppReady,\n navigateTo,\n isTauriAvailable,\n getWindowTitle,\n waitForLoadingComplete,\n isVisible,\n getText,\n takeScreenshot,\n} from './fixtures';\n\ndescribe('NoteFlow Desktop App', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('app initialization', () => {\n it('should load with correct window title', async () => {\n const title = await getWindowTitle();\n expect(title).toContain('NoteFlow');\n });\n\n it('should have Tauri IPC available', async () => {\n // In Tauri 2.0, __TAURI__ may not be directly on window\n // Instead, verify IPC works by checking if API functions exist\n const result = await browser.execute(() => {\n const api = (window as any).__NOTEFLOW_API__;\n return {\n hasApi: !!api,\n hasFunctions: !!(api?.listMeetings && api?.getPreferences),\n };\n });\n expect(result.hasApi).toBe(true);\n expect(result.hasFunctions).toBe(true);\n });\n\n it('should render the main layout', async () => {\n const hasMain = await isVisible('main');\n expect(hasMain).toBe(true);\n });\n });\n\n describe('navigation', () => {\n it('should navigate to meetings page', async () => {\n await navigateTo('/meetings');\n await waitForLoadingComplete();\n\n const hasContent = await isVisible('main');\n expect(hasContent).toBe(true);\n });\n\n it('should navigate to settings page', async () => {\n await navigateTo('/settings');\n await waitForLoadingComplete();\n\n const hasContent = await isVisible('main');\n expect(hasContent).toBe(true);\n });\n\n it('should navigate to recording page', async () => {\n await navigateTo('/recording/new');\n await waitForLoadingComplete();\n\n const hasContent = await isVisible('main');\n expect(hasContent).toBe(true);\n });\n });\n});\n\ndescribe('gRPC Connection', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n it('should show connection status indicator', async () => {\n // The connection status component should be visible\n const hasStatus = await isVisible('[data-testid=\"connection-status\"]');\n // May or may not be visible depending on UI design\n await takeScreenshot('connection-status');\n });\n\n it('should handle connection to backend', async () => {\n // Check if the app can communicate with the gRPC server\n // This tests real Tauri IPC → Rust → gRPC flow\n const result = await browser.execute(async () => {\n try {\n // Access the API exposed by the app\n const api = (window as any).__NOTEFLOW_API__;\n if (!api) {\n return { available: false, error: 'API not exposed' };\n }\n\n // Try to list meetings (basic connectivity test)\n const meetings = await api.listMeetings({ limit: 1 });\n return { available: true, connected: true, meetings };\n } catch (error) {\n return {\n available: true,\n connected: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n });\n\n expect(result.available).toBe(true);\n // Connection may or may not succeed depending on server state\n console.log('Connection test result:', result);\n });\n});\n\ndescribe('Audio Device Access', () => {\n before(async () => {\n await waitForAppReady();\n await navigateTo('/recording/new');\n await waitForLoadingComplete();\n });\n\n it('should list available audio devices', async () => {\n // Test real audio device enumeration via Tauri IPC\n // Note: getAudioDevices may be invoked differently (direct Tauri invoke vs API wrapper)\n const result = await browser.execute(async () => {\n try {\n // Try API wrapper first\n const api = (window as any).__NOTEFLOW_API__;\n if (api?.getAudioDevices) {\n const devices = await api.getAudioDevices();\n return { available: true, source: 'api', devices };\n }\n\n // Try direct Tauri invoke\n const { invoke } = await import('@tauri-apps/api/core');\n const devices = await invoke('get_audio_devices');\n return { available: true, source: 'invoke', devices };\n } catch (error) {\n return {\n available: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n });\n\n console.log('Audio devices:', result);\n // Test passes if we got any result (available or error with reason)\n expect(result).toBeDefined();\n // If available, should have devices array\n if (result.available) {\n expect(result.devices).toBeDefined();\n }\n });\n});\n\ndescribe('Preferences', () => {\n before(async () => {\n await waitForAppReady();\n await navigateTo('/settings');\n await waitForLoadingComplete();\n });\n\n it('should load user preferences', async () => {\n const result = await browser.execute(async () => {\n try {\n const api = (window as any).__NOTEFLOW_API__;\n if (!api?.getPreferences) {\n return { available: false };\n }\n\n const prefs = await api.getPreferences();\n return { available: true, prefs };\n } catch (error) {\n return {\n available: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n });\n\n console.log('Preferences result:', result);\n expect(result.available).toBe(true);\n });\n});\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * Native App E2E Tests\n * });\n\n expect(result.available).toBe(true);\n // Connection may or may not succeed depending on server state\n console.log('Connection test result:', result);\n });\n}); });\n});\n","ops":[{"diffOp":{"equal":{"range":[0,30]}}},{"equalLines":{"line_count":106}},{"diffOp":{"equal":{"range":[30,79]}}},{"diffOp":{"delete":{"range":[79,198]}}},{"diffOp":{"equal":{"range":[198,208]}}},{"equalLines":{"line_count":69}},{"diffOp":{"equal":{"range":[208,218]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/app.spec.ts"},"span":[3375,3386],"sourceCode":"/**\n * Native App E2E Tests\n *\n * Tests that run against the actual Tauri desktop application.\n * These tests validate real IPC commands and native functionality.\n */\n\nimport {\n waitForAppReady,\n navigateTo,\n isTauriAvailable,\n getWindowTitle,\n waitForLoadingComplete,\n isVisible,\n getText,\n takeScreenshot,\n} from './fixtures';\n\ndescribe('NoteFlow Desktop App', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('app initialization', () => {\n it('should load with correct window title', async () => {\n const title = await getWindowTitle();\n expect(title).toContain('NoteFlow');\n });\n\n it('should have Tauri IPC available', async () => {\n // In Tauri 2.0, __TAURI__ may not be directly on window\n // Instead, verify IPC works by checking if API functions exist\n const result = await browser.execute(() => {\n const api = (window as any).__NOTEFLOW_API__;\n return {\n hasApi: !!api,\n hasFunctions: !!(api?.listMeetings && api?.getPreferences),\n };\n });\n expect(result.hasApi).toBe(true);\n expect(result.hasFunctions).toBe(true);\n });\n\n it('should render the main layout', async () => {\n const hasMain = await isVisible('main');\n expect(hasMain).toBe(true);\n });\n });\n\n describe('navigation', () => {\n it('should navigate to meetings page', async () => {\n await navigateTo('/meetings');\n await waitForLoadingComplete();\n\n const hasContent = await isVisible('main');\n expect(hasContent).toBe(true);\n });\n\n it('should navigate to settings page', async () => {\n await navigateTo('/settings');\n await waitForLoadingComplete();\n\n const hasContent = await isVisible('main');\n expect(hasContent).toBe(true);\n });\n\n it('should navigate to recording page', async () => {\n await navigateTo('/recording/new');\n await waitForLoadingComplete();\n\n const hasContent = await isVisible('main');\n expect(hasContent).toBe(true);\n });\n });\n});\n\ndescribe('gRPC Connection', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n it('should show connection status indicator', async () => {\n // The connection status component should be visible\n const hasStatus = await isVisible('[data-testid=\"connection-status\"]');\n // May or may not be visible depending on UI design\n await takeScreenshot('connection-status');\n });\n\n it('should handle connection to backend', async () => {\n // Check if the app can communicate with the gRPC server\n // This tests real Tauri IPC → Rust → gRPC flow\n const result = await browser.execute(async () => {\n try {\n // Access the API exposed by the app\n const api = (window as any).__NOTEFLOW_API__;\n if (!api) {\n return { available: false, error: 'API not exposed' };\n }\n\n // Try to list meetings (basic connectivity test)\n const meetings = await api.listMeetings({ limit: 1 });\n return { available: true, connected: true, meetings };\n } catch (error) {\n return {\n available: true,\n connected: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n });\n\n expect(result.available).toBe(true);\n // Connection may or may not succeed depending on server state\n console.log('Connection test result:', result);\n });\n});\n\ndescribe('Audio Device Access', () => {\n before(async () => {\n await waitForAppReady();\n await navigateTo('/recording/new');\n await waitForLoadingComplete();\n });\n\n it('should list available audio devices', async () => {\n // Test real audio device enumeration via Tauri IPC\n // Note: getAudioDevices may be invoked differently (direct Tauri invoke vs API wrapper)\n const result = await browser.execute(async () => {\n try {\n // Try API wrapper first\n const api = (window as any).__NOTEFLOW_API__;\n if (api?.getAudioDevices) {\n const devices = await api.getAudioDevices();\n return { available: true, source: 'api', devices };\n }\n\n // Try direct Tauri invoke\n const { invoke } = await import('@tauri-apps/api/core');\n const devices = await invoke('get_audio_devices');\n return { available: true, source: 'invoke', devices };\n } catch (error) {\n return {\n available: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n });\n\n console.log('Audio devices:', result);\n // Test passes if we got any result (available or error with reason)\n expect(result).toBeDefined();\n // If available, should have devices array\n if (result.available) {\n expect(result.devices).toBeDefined();\n }\n });\n});\n\ndescribe('Preferences', () => {\n before(async () => {\n await waitForAppReady();\n await navigateTo('/settings');\n await waitForLoadingComplete();\n });\n\n it('should load user preferences', async () => {\n const result = await browser.execute(async () => {\n try {\n const api = (window as any).__NOTEFLOW_API__;\n if (!api?.getPreferences) {\n return { available: false };\n }\n\n const prefs = await api.getPreferences();\n return { available: true, prefs };\n } catch (error) {\n return {\n available: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n });\n\n console.log('Preferences result:', result);\n expect(result.available).toBe(true);\n });\n});\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * Native App E2E Tests\n * };\n }\n });\n\n console.log('Audio devices:', result);\n // Test passes if we got any result (available or error with reason)\n expect(result).toBeDefined(); });\n});\n","ops":[{"diffOp":{"equal":{"range":[0,30]}}},{"equalLines":{"line_count":141}},{"diffOp":{"equal":{"range":[30,56]}}},{"diffOp":{"delete":{"range":[56,100]}}},{"diffOp":{"equal":{"range":[100,207]}}},{"equalLines":{"line_count":34}},{"diffOp":{"equal":{"range":[207,217]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/app.spec.ts"},"span":[4530,4541],"sourceCode":"/**\n * Native App E2E Tests\n *\n * Tests that run against the actual Tauri desktop application.\n * These tests validate real IPC commands and native functionality.\n */\n\nimport {\n waitForAppReady,\n navigateTo,\n isTauriAvailable,\n getWindowTitle,\n waitForLoadingComplete,\n isVisible,\n getText,\n takeScreenshot,\n} from './fixtures';\n\ndescribe('NoteFlow Desktop App', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('app initialization', () => {\n it('should load with correct window title', async () => {\n const title = await getWindowTitle();\n expect(title).toContain('NoteFlow');\n });\n\n it('should have Tauri IPC available', async () => {\n // In Tauri 2.0, __TAURI__ may not be directly on window\n // Instead, verify IPC works by checking if API functions exist\n const result = await browser.execute(() => {\n const api = (window as any).__NOTEFLOW_API__;\n return {\n hasApi: !!api,\n hasFunctions: !!(api?.listMeetings && api?.getPreferences),\n };\n });\n expect(result.hasApi).toBe(true);\n expect(result.hasFunctions).toBe(true);\n });\n\n it('should render the main layout', async () => {\n const hasMain = await isVisible('main');\n expect(hasMain).toBe(true);\n });\n });\n\n describe('navigation', () => {\n it('should navigate to meetings page', async () => {\n await navigateTo('/meetings');\n await waitForLoadingComplete();\n\n const hasContent = await isVisible('main');\n expect(hasContent).toBe(true);\n });\n\n it('should navigate to settings page', async () => {\n await navigateTo('/settings');\n await waitForLoadingComplete();\n\n const hasContent = await isVisible('main');\n expect(hasContent).toBe(true);\n });\n\n it('should navigate to recording page', async () => {\n await navigateTo('/recording/new');\n await waitForLoadingComplete();\n\n const hasContent = await isVisible('main');\n expect(hasContent).toBe(true);\n });\n });\n});\n\ndescribe('gRPC Connection', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n it('should show connection status indicator', async () => {\n // The connection status component should be visible\n const hasStatus = await isVisible('[data-testid=\"connection-status\"]');\n // May or may not be visible depending on UI design\n await takeScreenshot('connection-status');\n });\n\n it('should handle connection to backend', async () => {\n // Check if the app can communicate with the gRPC server\n // This tests real Tauri IPC → Rust → gRPC flow\n const result = await browser.execute(async () => {\n try {\n // Access the API exposed by the app\n const api = (window as any).__NOTEFLOW_API__;\n if (!api) {\n return { available: false, error: 'API not exposed' };\n }\n\n // Try to list meetings (basic connectivity test)\n const meetings = await api.listMeetings({ limit: 1 });\n return { available: true, connected: true, meetings };\n } catch (error) {\n return {\n available: true,\n connected: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n });\n\n expect(result.available).toBe(true);\n // Connection may or may not succeed depending on server state\n console.log('Connection test result:', result);\n });\n});\n\ndescribe('Audio Device Access', () => {\n before(async () => {\n await waitForAppReady();\n await navigateTo('/recording/new');\n await waitForLoadingComplete();\n });\n\n it('should list available audio devices', async () => {\n // Test real audio device enumeration via Tauri IPC\n // Note: getAudioDevices may be invoked differently (direct Tauri invoke vs API wrapper)\n const result = await browser.execute(async () => {\n try {\n // Try API wrapper first\n const api = (window as any).__NOTEFLOW_API__;\n if (api?.getAudioDevices) {\n const devices = await api.getAudioDevices();\n return { available: true, source: 'api', devices };\n }\n\n // Try direct Tauri invoke\n const { invoke } = await import('@tauri-apps/api/core');\n const devices = await invoke('get_audio_devices');\n return { available: true, source: 'invoke', devices };\n } catch (error) {\n return {\n available: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n });\n\n console.log('Audio devices:', result);\n // Test passes if we got any result (available or error with reason)\n expect(result).toBeDefined();\n // If available, should have devices array\n if (result.available) {\n expect(result.devices).toBeDefined();\n }\n });\n});\n\ndescribe('Preferences', () => {\n before(async () => {\n await waitForAppReady();\n await navigateTo('/settings');\n await waitForLoadingComplete();\n });\n\n it('should load user preferences', async () => {\n const result = await browser.execute(async () => {\n try {\n const api = (window as any).__NOTEFLOW_API__;\n if (!api?.getPreferences) {\n return { available: false };\n }\n\n const prefs = await api.getPreferences();\n return { available: true, prefs };\n } catch (error) {\n return {\n available: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n });\n\n console.log('Preferences result:', result);\n expect(result.available).toBe(true);\n });\n});\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * Native App E2E Tests\n * };\n }\n });\n\n console.log('Preferences result:', result);\n expect(result.available).toBe(true);\n });\n});\n","ops":[{"diffOp":{"equal":{"range":[0,30]}}},{"equalLines":{"line_count":176}},{"diffOp":{"equal":{"range":[30,56]}}},{"diffOp":{"delete":{"range":[56,105]}}},{"diffOp":{"equal":{"range":[105,157]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/app.spec.ts"},"span":[5499,5510],"sourceCode":"/**\n * Native App E2E Tests\n *\n * Tests that run against the actual Tauri desktop application.\n * These tests validate real IPC commands and native functionality.\n */\n\nimport {\n waitForAppReady,\n navigateTo,\n isTauriAvailable,\n getWindowTitle,\n waitForLoadingComplete,\n isVisible,\n getText,\n takeScreenshot,\n} from './fixtures';\n\ndescribe('NoteFlow Desktop App', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('app initialization', () => {\n it('should load with correct window title', async () => {\n const title = await getWindowTitle();\n expect(title).toContain('NoteFlow');\n });\n\n it('should have Tauri IPC available', async () => {\n // In Tauri 2.0, __TAURI__ may not be directly on window\n // Instead, verify IPC works by checking if API functions exist\n const result = await browser.execute(() => {\n const api = (window as any).__NOTEFLOW_API__;\n return {\n hasApi: !!api,\n hasFunctions: !!(api?.listMeetings && api?.getPreferences),\n };\n });\n expect(result.hasApi).toBe(true);\n expect(result.hasFunctions).toBe(true);\n });\n\n it('should render the main layout', async () => {\n const hasMain = await isVisible('main');\n expect(hasMain).toBe(true);\n });\n });\n\n describe('navigation', () => {\n it('should navigate to meetings page', async () => {\n await navigateTo('/meetings');\n await waitForLoadingComplete();\n\n const hasContent = await isVisible('main');\n expect(hasContent).toBe(true);\n });\n\n it('should navigate to settings page', async () => {\n await navigateTo('/settings');\n await waitForLoadingComplete();\n\n const hasContent = await isVisible('main');\n expect(hasContent).toBe(true);\n });\n\n it('should navigate to recording page', async () => {\n await navigateTo('/recording/new');\n await waitForLoadingComplete();\n\n const hasContent = await isVisible('main');\n expect(hasContent).toBe(true);\n });\n });\n});\n\ndescribe('gRPC Connection', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n it('should show connection status indicator', async () => {\n // The connection status component should be visible\n const hasStatus = await isVisible('[data-testid=\"connection-status\"]');\n // May or may not be visible depending on UI design\n await takeScreenshot('connection-status');\n });\n\n it('should handle connection to backend', async () => {\n // Check if the app can communicate with the gRPC server\n // This tests real Tauri IPC → Rust → gRPC flow\n const result = await browser.execute(async () => {\n try {\n // Access the API exposed by the app\n const api = (window as any).__NOTEFLOW_API__;\n if (!api) {\n return { available: false, error: 'API not exposed' };\n }\n\n // Try to list meetings (basic connectivity test)\n const meetings = await api.listMeetings({ limit: 1 });\n return { available: true, connected: true, meetings };\n } catch (error) {\n return {\n available: true,\n connected: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n });\n\n expect(result.available).toBe(true);\n // Connection may or may not succeed depending on server state\n console.log('Connection test result:', result);\n });\n});\n\ndescribe('Audio Device Access', () => {\n before(async () => {\n await waitForAppReady();\n await navigateTo('/recording/new');\n await waitForLoadingComplete();\n });\n\n it('should list available audio devices', async () => {\n // Test real audio device enumeration via Tauri IPC\n // Note: getAudioDevices may be invoked differently (direct Tauri invoke vs API wrapper)\n const result = await browser.execute(async () => {\n try {\n // Try API wrapper first\n const api = (window as any).__NOTEFLOW_API__;\n if (api?.getAudioDevices) {\n const devices = await api.getAudioDevices();\n return { available: true, source: 'api', devices };\n }\n\n // Try direct Tauri invoke\n const { invoke } = await import('@tauri-apps/api/core');\n const devices = await invoke('get_audio_devices');\n return { available: true, source: 'invoke', devices };\n } catch (error) {\n return {\n available: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n });\n\n console.log('Audio devices:', result);\n // Test passes if we got any result (available or error with reason)\n expect(result).toBeDefined();\n // If available, should have devices array\n if (result.available) {\n expect(result.devices).toBeDefined();\n }\n });\n});\n\ndescribe('Preferences', () => {\n before(async () => {\n await waitForAppReady();\n await navigateTo('/settings');\n await waitForLoadingComplete();\n });\n\n it('should load user preferences', async () => {\n const result = await browser.execute(async () => {\n try {\n const api = (window as any).__NOTEFLOW_API__;\n if (!api?.getPreferences) {\n return { available: false };\n }\n\n const prefs = await api.getPreferences();\n return { available: true, prefs };\n } catch (error) {\n return {\n available: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n });\n\n console.log('Preferences result:', result);\n expect(result.available).toBe(true);\n });\n});\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/calendar.spec.ts"},"span":[444,447],"sourceCode":"/**\n * Calendar Integration E2E Tests\n *\n * Tests for calendar providers and OAuth integration.\n */\n\nimport { waitForAppReady } from './fixtures';\n\ndescribe('Calendar Integration', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCalendarProviders', () => {\n it('should list available calendar providers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getCalendarProviders();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('providers');\n expect(Array.isArray(result.providers)).toBe(true);\n }\n });\n });\n\n describe('listCalendarEvents', () => {\n it('should list upcoming calendar events', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listCalendarEvents(24, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('events');\n expect(Array.isArray(result.events)).toBe(true);\n }\n });\n\n it('should filter by provider', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listCalendarEvents(24, 10, 'google');\n return { success: true, ...response };\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n // Provider not connected is a valid state\n if (errorMsg.includes('not connected')) {\n return { success: true, events: [], notConnected: true };\n }\n return { success: false, error: errorMsg };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n if (result.notConnected) {\n console.log('Google Calendar not connected - skipping event verification');\n } else {\n expect(result).toHaveProperty('events');\n expect(Array.isArray(result.events)).toBe(true);\n }\n }\n });\n });\n\n describe('getOAuthConnectionStatus', () => {\n it('should check Google OAuth status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getOAuthConnectionStatus('google');\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.status).toHaveProperty('connected');\n }\n });\n\n it('should check Outlook OAuth status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getOAuthConnectionStatus('outlook');\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n\n describe('initiateCalendarAuth', () => {\n it('should initiate OAuth flow (returns auth URL)', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.initiateCalendarAuth('google');\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n // May fail if OAuth not configured\n if (result.success && result.auth_url) {\n expect(result.auth_url).toContain('http');\n }\n });\n });\n\n describe('disconnectCalendar', () => {\n it('should handle disconnect when not connected', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.disconnectCalendar('google');\n return { success: true, ...response };\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n // Not connected is a valid state for disconnect\n if (errorMsg.includes('not connected') || errorMsg.includes('not found')) {\n return { success: true, alreadyDisconnected: true };\n }\n return { success: false, error: errorMsg };\n }\n });\n\n expect(result).toBeDefined();\n // Either successful disconnect or was already disconnected\n expect(result.success).toBe(true);\n });\n });\n});\n\ndescribe('Integration Sync', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('listSyncHistory', () => {\n it('should list sync history for integration', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n // Use a placeholder integration ID - may return empty\n const response = await api.listSyncHistory('test-integration-id', 10, 0);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/calendar.spec.ts"},"span":[1111,1114],"sourceCode":"/**\n * Calendar Integration E2E Tests\n *\n * Tests for calendar providers and OAuth integration.\n */\n\nimport { waitForAppReady } from './fixtures';\n\ndescribe('Calendar Integration', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCalendarProviders', () => {\n it('should list available calendar providers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getCalendarProviders();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('providers');\n expect(Array.isArray(result.providers)).toBe(true);\n }\n });\n });\n\n describe('listCalendarEvents', () => {\n it('should list upcoming calendar events', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listCalendarEvents(24, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('events');\n expect(Array.isArray(result.events)).toBe(true);\n }\n });\n\n it('should filter by provider', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listCalendarEvents(24, 10, 'google');\n return { success: true, ...response };\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n // Provider not connected is a valid state\n if (errorMsg.includes('not connected')) {\n return { success: true, events: [], notConnected: true };\n }\n return { success: false, error: errorMsg };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n if (result.notConnected) {\n console.log('Google Calendar not connected - skipping event verification');\n } else {\n expect(result).toHaveProperty('events');\n expect(Array.isArray(result.events)).toBe(true);\n }\n }\n });\n });\n\n describe('getOAuthConnectionStatus', () => {\n it('should check Google OAuth status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getOAuthConnectionStatus('google');\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.status).toHaveProperty('connected');\n }\n });\n\n it('should check Outlook OAuth status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getOAuthConnectionStatus('outlook');\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n\n describe('initiateCalendarAuth', () => {\n it('should initiate OAuth flow (returns auth URL)', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.initiateCalendarAuth('google');\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n // May fail if OAuth not configured\n if (result.success && result.auth_url) {\n expect(result.auth_url).toContain('http');\n }\n });\n });\n\n describe('disconnectCalendar', () => {\n it('should handle disconnect when not connected', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.disconnectCalendar('google');\n return { success: true, ...response };\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n // Not connected is a valid state for disconnect\n if (errorMsg.includes('not connected') || errorMsg.includes('not found')) {\n return { success: true, alreadyDisconnected: true };\n }\n return { success: false, error: errorMsg };\n }\n });\n\n expect(result).toBeDefined();\n // Either successful disconnect or was already disconnected\n expect(result.success).toBe(true);\n });\n });\n});\n\ndescribe('Integration Sync', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('listSyncHistory', () => {\n it('should list sync history for integration', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n // Use a placeholder integration ID - may return empty\n const response = await api.listSyncHistory('test-integration-id', 10, 0);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/calendar.spec.ts"},"span":[1718,1721],"sourceCode":"/**\n * Calendar Integration E2E Tests\n *\n * Tests for calendar providers and OAuth integration.\n */\n\nimport { waitForAppReady } from './fixtures';\n\ndescribe('Calendar Integration', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCalendarProviders', () => {\n it('should list available calendar providers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getCalendarProviders();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('providers');\n expect(Array.isArray(result.providers)).toBe(true);\n }\n });\n });\n\n describe('listCalendarEvents', () => {\n it('should list upcoming calendar events', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listCalendarEvents(24, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('events');\n expect(Array.isArray(result.events)).toBe(true);\n }\n });\n\n it('should filter by provider', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listCalendarEvents(24, 10, 'google');\n return { success: true, ...response };\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n // Provider not connected is a valid state\n if (errorMsg.includes('not connected')) {\n return { success: true, events: [], notConnected: true };\n }\n return { success: false, error: errorMsg };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n if (result.notConnected) {\n console.log('Google Calendar not connected - skipping event verification');\n } else {\n expect(result).toHaveProperty('events');\n expect(Array.isArray(result.events)).toBe(true);\n }\n }\n });\n });\n\n describe('getOAuthConnectionStatus', () => {\n it('should check Google OAuth status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getOAuthConnectionStatus('google');\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.status).toHaveProperty('connected');\n }\n });\n\n it('should check Outlook OAuth status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getOAuthConnectionStatus('outlook');\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n\n describe('initiateCalendarAuth', () => {\n it('should initiate OAuth flow (returns auth URL)', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.initiateCalendarAuth('google');\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n // May fail if OAuth not configured\n if (result.success && result.auth_url) {\n expect(result.auth_url).toContain('http');\n }\n });\n });\n\n describe('disconnectCalendar', () => {\n it('should handle disconnect when not connected', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.disconnectCalendar('google');\n return { success: true, ...response };\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n // Not connected is a valid state for disconnect\n if (errorMsg.includes('not connected') || errorMsg.includes('not found')) {\n return { success: true, alreadyDisconnected: true };\n }\n return { success: false, error: errorMsg };\n }\n });\n\n expect(result).toBeDefined();\n // Either successful disconnect or was already disconnected\n expect(result.success).toBe(true);\n });\n });\n});\n\ndescribe('Integration Sync', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('listSyncHistory', () => {\n it('should list sync history for integration', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n // Use a placeholder integration ID - may return empty\n const response = await api.listSyncHistory('test-integration-id', 10, 0);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/calendar.spec.ts"},"span":[2771,2774],"sourceCode":"/**\n * Calendar Integration E2E Tests\n *\n * Tests for calendar providers and OAuth integration.\n */\n\nimport { waitForAppReady } from './fixtures';\n\ndescribe('Calendar Integration', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCalendarProviders', () => {\n it('should list available calendar providers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getCalendarProviders();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('providers');\n expect(Array.isArray(result.providers)).toBe(true);\n }\n });\n });\n\n describe('listCalendarEvents', () => {\n it('should list upcoming calendar events', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listCalendarEvents(24, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('events');\n expect(Array.isArray(result.events)).toBe(true);\n }\n });\n\n it('should filter by provider', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listCalendarEvents(24, 10, 'google');\n return { success: true, ...response };\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n // Provider not connected is a valid state\n if (errorMsg.includes('not connected')) {\n return { success: true, events: [], notConnected: true };\n }\n return { success: false, error: errorMsg };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n if (result.notConnected) {\n console.log('Google Calendar not connected - skipping event verification');\n } else {\n expect(result).toHaveProperty('events');\n expect(Array.isArray(result.events)).toBe(true);\n }\n }\n });\n });\n\n describe('getOAuthConnectionStatus', () => {\n it('should check Google OAuth status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getOAuthConnectionStatus('google');\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.status).toHaveProperty('connected');\n }\n });\n\n it('should check Outlook OAuth status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getOAuthConnectionStatus('outlook');\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n\n describe('initiateCalendarAuth', () => {\n it('should initiate OAuth flow (returns auth URL)', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.initiateCalendarAuth('google');\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n // May fail if OAuth not configured\n if (result.success && result.auth_url) {\n expect(result.auth_url).toContain('http');\n }\n });\n });\n\n describe('disconnectCalendar', () => {\n it('should handle disconnect when not connected', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.disconnectCalendar('google');\n return { success: true, ...response };\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n // Not connected is a valid state for disconnect\n if (errorMsg.includes('not connected') || errorMsg.includes('not found')) {\n return { success: true, alreadyDisconnected: true };\n }\n return { success: false, error: errorMsg };\n }\n });\n\n expect(result).toBeDefined();\n // Either successful disconnect or was already disconnected\n expect(result.success).toBe(true);\n });\n });\n});\n\ndescribe('Integration Sync', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('listSyncHistory', () => {\n it('should list sync history for integration', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n // Use a placeholder integration ID - may return empty\n const response = await api.listSyncHistory('test-integration-id', 10, 0);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/calendar.spec.ts"},"span":[3340,3343],"sourceCode":"/**\n * Calendar Integration E2E Tests\n *\n * Tests for calendar providers and OAuth integration.\n */\n\nimport { waitForAppReady } from './fixtures';\n\ndescribe('Calendar Integration', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCalendarProviders', () => {\n it('should list available calendar providers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getCalendarProviders();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('providers');\n expect(Array.isArray(result.providers)).toBe(true);\n }\n });\n });\n\n describe('listCalendarEvents', () => {\n it('should list upcoming calendar events', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listCalendarEvents(24, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('events');\n expect(Array.isArray(result.events)).toBe(true);\n }\n });\n\n it('should filter by provider', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listCalendarEvents(24, 10, 'google');\n return { success: true, ...response };\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n // Provider not connected is a valid state\n if (errorMsg.includes('not connected')) {\n return { success: true, events: [], notConnected: true };\n }\n return { success: false, error: errorMsg };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n if (result.notConnected) {\n console.log('Google Calendar not connected - skipping event verification');\n } else {\n expect(result).toHaveProperty('events');\n expect(Array.isArray(result.events)).toBe(true);\n }\n }\n });\n });\n\n describe('getOAuthConnectionStatus', () => {\n it('should check Google OAuth status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getOAuthConnectionStatus('google');\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.status).toHaveProperty('connected');\n }\n });\n\n it('should check Outlook OAuth status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getOAuthConnectionStatus('outlook');\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n\n describe('initiateCalendarAuth', () => {\n it('should initiate OAuth flow (returns auth URL)', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.initiateCalendarAuth('google');\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n // May fail if OAuth not configured\n if (result.success && result.auth_url) {\n expect(result.auth_url).toContain('http');\n }\n });\n });\n\n describe('disconnectCalendar', () => {\n it('should handle disconnect when not connected', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.disconnectCalendar('google');\n return { success: true, ...response };\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n // Not connected is a valid state for disconnect\n if (errorMsg.includes('not connected') || errorMsg.includes('not found')) {\n return { success: true, alreadyDisconnected: true };\n }\n return { success: false, error: errorMsg };\n }\n });\n\n expect(result).toBeDefined();\n // Either successful disconnect or was already disconnected\n expect(result.success).toBe(true);\n });\n });\n});\n\ndescribe('Integration Sync', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('listSyncHistory', () => {\n it('should list sync history for integration', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n // Use a placeholder integration ID - may return empty\n const response = await api.listSyncHistory('test-integration-id', 10, 0);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/calendar.spec.ts"},"span":[3876,3879],"sourceCode":"/**\n * Calendar Integration E2E Tests\n *\n * Tests for calendar providers and OAuth integration.\n */\n\nimport { waitForAppReady } from './fixtures';\n\ndescribe('Calendar Integration', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCalendarProviders', () => {\n it('should list available calendar providers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getCalendarProviders();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('providers');\n expect(Array.isArray(result.providers)).toBe(true);\n }\n });\n });\n\n describe('listCalendarEvents', () => {\n it('should list upcoming calendar events', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listCalendarEvents(24, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('events');\n expect(Array.isArray(result.events)).toBe(true);\n }\n });\n\n it('should filter by provider', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listCalendarEvents(24, 10, 'google');\n return { success: true, ...response };\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n // Provider not connected is a valid state\n if (errorMsg.includes('not connected')) {\n return { success: true, events: [], notConnected: true };\n }\n return { success: false, error: errorMsg };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n if (result.notConnected) {\n console.log('Google Calendar not connected - skipping event verification');\n } else {\n expect(result).toHaveProperty('events');\n expect(Array.isArray(result.events)).toBe(true);\n }\n }\n });\n });\n\n describe('getOAuthConnectionStatus', () => {\n it('should check Google OAuth status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getOAuthConnectionStatus('google');\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.status).toHaveProperty('connected');\n }\n });\n\n it('should check Outlook OAuth status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getOAuthConnectionStatus('outlook');\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n\n describe('initiateCalendarAuth', () => {\n it('should initiate OAuth flow (returns auth URL)', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.initiateCalendarAuth('google');\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n // May fail if OAuth not configured\n if (result.success && result.auth_url) {\n expect(result.auth_url).toContain('http');\n }\n });\n });\n\n describe('disconnectCalendar', () => {\n it('should handle disconnect when not connected', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.disconnectCalendar('google');\n return { success: true, ...response };\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n // Not connected is a valid state for disconnect\n if (errorMsg.includes('not connected') || errorMsg.includes('not found')) {\n return { success: true, alreadyDisconnected: true };\n }\n return { success: false, error: errorMsg };\n }\n });\n\n expect(result).toBeDefined();\n // Either successful disconnect or was already disconnected\n expect(result.success).toBe(true);\n });\n });\n});\n\ndescribe('Integration Sync', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('listSyncHistory', () => {\n it('should list sync history for integration', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n // Use a placeholder integration ID - may return empty\n const response = await api.listSyncHistory('test-integration-id', 10, 0);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/calendar.spec.ts"},"span":[4558,4561],"sourceCode":"/**\n * Calendar Integration E2E Tests\n *\n * Tests for calendar providers and OAuth integration.\n */\n\nimport { waitForAppReady } from './fixtures';\n\ndescribe('Calendar Integration', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCalendarProviders', () => {\n it('should list available calendar providers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getCalendarProviders();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('providers');\n expect(Array.isArray(result.providers)).toBe(true);\n }\n });\n });\n\n describe('listCalendarEvents', () => {\n it('should list upcoming calendar events', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listCalendarEvents(24, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('events');\n expect(Array.isArray(result.events)).toBe(true);\n }\n });\n\n it('should filter by provider', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listCalendarEvents(24, 10, 'google');\n return { success: true, ...response };\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n // Provider not connected is a valid state\n if (errorMsg.includes('not connected')) {\n return { success: true, events: [], notConnected: true };\n }\n return { success: false, error: errorMsg };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n if (result.notConnected) {\n console.log('Google Calendar not connected - skipping event verification');\n } else {\n expect(result).toHaveProperty('events');\n expect(Array.isArray(result.events)).toBe(true);\n }\n }\n });\n });\n\n describe('getOAuthConnectionStatus', () => {\n it('should check Google OAuth status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getOAuthConnectionStatus('google');\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.status).toHaveProperty('connected');\n }\n });\n\n it('should check Outlook OAuth status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getOAuthConnectionStatus('outlook');\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n\n describe('initiateCalendarAuth', () => {\n it('should initiate OAuth flow (returns auth URL)', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.initiateCalendarAuth('google');\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n // May fail if OAuth not configured\n if (result.success && result.auth_url) {\n expect(result.auth_url).toContain('http');\n }\n });\n });\n\n describe('disconnectCalendar', () => {\n it('should handle disconnect when not connected', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.disconnectCalendar('google');\n return { success: true, ...response };\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n // Not connected is a valid state for disconnect\n if (errorMsg.includes('not connected') || errorMsg.includes('not found')) {\n return { success: true, alreadyDisconnected: true };\n }\n return { success: false, error: errorMsg };\n }\n });\n\n expect(result).toBeDefined();\n // Either successful disconnect or was already disconnected\n expect(result.success).toBe(true);\n });\n });\n});\n\ndescribe('Integration Sync', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('listSyncHistory', () => {\n it('should list sync history for integration', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n // Use a placeholder integration ID - may return empty\n const response = await api.listSyncHistory('test-integration-id', 10, 0);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/calendar.spec.ts"},"span":[5550,5553],"sourceCode":"/**\n * Calendar Integration E2E Tests\n *\n * Tests for calendar providers and OAuth integration.\n */\n\nimport { waitForAppReady } from './fixtures';\n\ndescribe('Calendar Integration', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCalendarProviders', () => {\n it('should list available calendar providers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getCalendarProviders();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('providers');\n expect(Array.isArray(result.providers)).toBe(true);\n }\n });\n });\n\n describe('listCalendarEvents', () => {\n it('should list upcoming calendar events', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listCalendarEvents(24, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('events');\n expect(Array.isArray(result.events)).toBe(true);\n }\n });\n\n it('should filter by provider', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listCalendarEvents(24, 10, 'google');\n return { success: true, ...response };\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n // Provider not connected is a valid state\n if (errorMsg.includes('not connected')) {\n return { success: true, events: [], notConnected: true };\n }\n return { success: false, error: errorMsg };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n if (result.notConnected) {\n console.log('Google Calendar not connected - skipping event verification');\n } else {\n expect(result).toHaveProperty('events');\n expect(Array.isArray(result.events)).toBe(true);\n }\n }\n });\n });\n\n describe('getOAuthConnectionStatus', () => {\n it('should check Google OAuth status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getOAuthConnectionStatus('google');\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.status).toHaveProperty('connected');\n }\n });\n\n it('should check Outlook OAuth status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getOAuthConnectionStatus('outlook');\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n\n describe('initiateCalendarAuth', () => {\n it('should initiate OAuth flow (returns auth URL)', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.initiateCalendarAuth('google');\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n // May fail if OAuth not configured\n if (result.success && result.auth_url) {\n expect(result.auth_url).toContain('http');\n }\n });\n });\n\n describe('disconnectCalendar', () => {\n it('should handle disconnect when not connected', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.disconnectCalendar('google');\n return { success: true, ...response };\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n // Not connected is a valid state for disconnect\n if (errorMsg.includes('not connected') || errorMsg.includes('not found')) {\n return { success: true, alreadyDisconnected: true };\n }\n return { success: false, error: errorMsg };\n }\n });\n\n expect(result).toBeDefined();\n // Either successful disconnect or was already disconnected\n expect(result.success).toBe(true);\n });\n });\n});\n\ndescribe('Integration Sync', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('listSyncHistory', () => {\n it('should list sync history for integration', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n // Use a placeholder integration ID - may return empty\n const response = await api.listSyncHistory('test-integration-id', 10, 0);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * Calendar Integration E2E Tests\n * expect(result).toBeDefined();\n if (result.success) {\n if (result.notConnected) {\n console.log('Google Calendar not connected - skipping event verification');\n } else {\n expect(result).toHaveProperty('events'); });\n});\n","ops":[{"diffOp":{"equal":{"range":[0,40]}}},{"equalLines":{"line_count":65}},{"diffOp":{"equal":{"range":[40,138]}}},{"diffOp":{"delete":{"range":[138,224]}}},{"diffOp":{"equal":{"range":[224,292]}}},{"equalLines":{"line_count":103}},{"diffOp":{"equal":{"range":[292,302]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/calendar.spec.ts"},"span":[2343,2354],"sourceCode":"/**\n * Calendar Integration E2E Tests\n *\n * Tests for calendar providers and OAuth integration.\n */\n\nimport { waitForAppReady } from './fixtures';\n\ndescribe('Calendar Integration', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCalendarProviders', () => {\n it('should list available calendar providers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getCalendarProviders();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('providers');\n expect(Array.isArray(result.providers)).toBe(true);\n }\n });\n });\n\n describe('listCalendarEvents', () => {\n it('should list upcoming calendar events', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listCalendarEvents(24, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('events');\n expect(Array.isArray(result.events)).toBe(true);\n }\n });\n\n it('should filter by provider', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listCalendarEvents(24, 10, 'google');\n return { success: true, ...response };\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n // Provider not connected is a valid state\n if (errorMsg.includes('not connected')) {\n return { success: true, events: [], notConnected: true };\n }\n return { success: false, error: errorMsg };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n if (result.notConnected) {\n console.log('Google Calendar not connected - skipping event verification');\n } else {\n expect(result).toHaveProperty('events');\n expect(Array.isArray(result.events)).toBe(true);\n }\n }\n });\n });\n\n describe('getOAuthConnectionStatus', () => {\n it('should check Google OAuth status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getOAuthConnectionStatus('google');\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.status).toHaveProperty('connected');\n }\n });\n\n it('should check Outlook OAuth status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getOAuthConnectionStatus('outlook');\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n\n describe('initiateCalendarAuth', () => {\n it('should initiate OAuth flow (returns auth URL)', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.initiateCalendarAuth('google');\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n // May fail if OAuth not configured\n if (result.success && result.auth_url) {\n expect(result.auth_url).toContain('http');\n }\n });\n });\n\n describe('disconnectCalendar', () => {\n it('should handle disconnect when not connected', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.disconnectCalendar('google');\n return { success: true, ...response };\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n // Not connected is a valid state for disconnect\n if (errorMsg.includes('not connected') || errorMsg.includes('not found')) {\n return { success: true, alreadyDisconnected: true };\n }\n return { success: false, error: errorMsg };\n }\n });\n\n expect(result).toBeDefined();\n // Either successful disconnect or was already disconnected\n expect(result.success).toBe(true);\n });\n });\n});\n\ndescribe('Integration Sync', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('listSyncHistory', () => {\n it('should list sync history for integration', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n // Use a placeholder integration ID - may return empty\n const response = await api.listSyncHistory('test-integration-id', 10, 0);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/connection.spec.ts"},"span":[413,416],"sourceCode":"/**\n * Server Connection E2E Tests\n *\n * Tests for gRPC server connection management.\n */\n\nimport { waitForAppReady } from './fixtures';\n\ndescribe('Server Connection', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('isConnected', () => {\n it('should return connection status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const connected = await api.isConnected();\n return { success: true, connected };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(typeof result.connected).toBe('boolean');\n });\n });\n\n describe('getServerInfo', () => {\n it('should return server information when connected', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const info = await api.getServerInfo();\n return { success: true, info };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.info).toHaveProperty('version');\n }\n });\n });\n\n describe('connect', () => {\n it('should connect to server with default URL', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const info = await api.connect();\n return { success: true, info };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n // May fail if server not running, but should not crash\n });\n\n it('should handle invalid server URL gracefully', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.connect('http://invalid-server:12345');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should fail for invalid server\n expect(result).toBeDefined();\n });\n });\n});\n\ndescribe('Identity', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCurrentUser', () => {\n it('should return current user info', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getCurrentUser();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('user');\n }\n });\n });\n\n describe('listWorkspaces', () => {\n it('should list available workspaces', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWorkspaces();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('workspaces');\n expect(Array.isArray(result.workspaces)).toBe(true);\n }\n });\n });\n});\n\ndescribe('Projects', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('listProjects', () => {\n it('should list projects', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n // Need workspace_id - try to get one first\n const workspaces = await api.listWorkspaces();\n if (workspaces?.workspaces?.length > 0) {\n const response = await api.listProjects({\n workspace_id: workspaces.workspaces[0].id,\n include_archived: false,\n limit: 10,\n });\n return { success: true, ...response };\n }\n return { success: true, skipped: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/connection.spec.ts"},"span":[988,991],"sourceCode":"/**\n * Server Connection E2E Tests\n *\n * Tests for gRPC server connection management.\n */\n\nimport { waitForAppReady } from './fixtures';\n\ndescribe('Server Connection', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('isConnected', () => {\n it('should return connection status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const connected = await api.isConnected();\n return { success: true, connected };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(typeof result.connected).toBe('boolean');\n });\n });\n\n describe('getServerInfo', () => {\n it('should return server information when connected', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const info = await api.getServerInfo();\n return { success: true, info };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.info).toHaveProperty('version');\n }\n });\n });\n\n describe('connect', () => {\n it('should connect to server with default URL', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const info = await api.connect();\n return { success: true, info };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n // May fail if server not running, but should not crash\n });\n\n it('should handle invalid server URL gracefully', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.connect('http://invalid-server:12345');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should fail for invalid server\n expect(result).toBeDefined();\n });\n });\n});\n\ndescribe('Identity', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCurrentUser', () => {\n it('should return current user info', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getCurrentUser();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('user');\n }\n });\n });\n\n describe('listWorkspaces', () => {\n it('should list available workspaces', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWorkspaces();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('workspaces');\n expect(Array.isArray(result.workspaces)).toBe(true);\n }\n });\n });\n});\n\ndescribe('Projects', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('listProjects', () => {\n it('should list projects', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n // Need workspace_id - try to get one first\n const workspaces = await api.listWorkspaces();\n if (workspaces?.workspaces?.length > 0) {\n const response = await api.listProjects({\n workspace_id: workspaces.workspaces[0].id,\n include_archived: false,\n limit: 10,\n });\n return { success: true, ...response };\n }\n return { success: true, skipped: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/connection.spec.ts"},"span":[1574,1577],"sourceCode":"/**\n * Server Connection E2E Tests\n *\n * Tests for gRPC server connection management.\n */\n\nimport { waitForAppReady } from './fixtures';\n\ndescribe('Server Connection', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('isConnected', () => {\n it('should return connection status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const connected = await api.isConnected();\n return { success: true, connected };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(typeof result.connected).toBe('boolean');\n });\n });\n\n describe('getServerInfo', () => {\n it('should return server information when connected', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const info = await api.getServerInfo();\n return { success: true, info };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.info).toHaveProperty('version');\n }\n });\n });\n\n describe('connect', () => {\n it('should connect to server with default URL', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const info = await api.connect();\n return { success: true, info };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n // May fail if server not running, but should not crash\n });\n\n it('should handle invalid server URL gracefully', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.connect('http://invalid-server:12345');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should fail for invalid server\n expect(result).toBeDefined();\n });\n });\n});\n\ndescribe('Identity', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCurrentUser', () => {\n it('should return current user info', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getCurrentUser();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('user');\n }\n });\n });\n\n describe('listWorkspaces', () => {\n it('should list available workspaces', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWorkspaces();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('workspaces');\n expect(Array.isArray(result.workspaces)).toBe(true);\n }\n });\n });\n});\n\ndescribe('Projects', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('listProjects', () => {\n it('should list projects', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n // Need workspace_id - try to get one first\n const workspaces = await api.listWorkspaces();\n if (workspaces?.workspaces?.length > 0) {\n const response = await api.listProjects({\n workspace_id: workspaces.workspaces[0].id,\n include_archived: false,\n limit: 10,\n });\n return { success: true, ...response };\n }\n return { success: true, skipped: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/connection.spec.ts"},"span":[2091,2094],"sourceCode":"/**\n * Server Connection E2E Tests\n *\n * Tests for gRPC server connection management.\n */\n\nimport { waitForAppReady } from './fixtures';\n\ndescribe('Server Connection', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('isConnected', () => {\n it('should return connection status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const connected = await api.isConnected();\n return { success: true, connected };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(typeof result.connected).toBe('boolean');\n });\n });\n\n describe('getServerInfo', () => {\n it('should return server information when connected', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const info = await api.getServerInfo();\n return { success: true, info };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.info).toHaveProperty('version');\n }\n });\n });\n\n describe('connect', () => {\n it('should connect to server with default URL', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const info = await api.connect();\n return { success: true, info };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n // May fail if server not running, but should not crash\n });\n\n it('should handle invalid server URL gracefully', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.connect('http://invalid-server:12345');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should fail for invalid server\n expect(result).toBeDefined();\n });\n });\n});\n\ndescribe('Identity', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCurrentUser', () => {\n it('should return current user info', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getCurrentUser();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('user');\n }\n });\n });\n\n describe('listWorkspaces', () => {\n it('should list available workspaces', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWorkspaces();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('workspaces');\n expect(Array.isArray(result.workspaces)).toBe(true);\n }\n });\n });\n});\n\ndescribe('Projects', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('listProjects', () => {\n it('should list projects', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n // Need workspace_id - try to get one first\n const workspaces = await api.listWorkspaces();\n if (workspaces?.workspaces?.length > 0) {\n const response = await api.listProjects({\n workspace_id: workspaces.workspaces[0].id,\n include_archived: false,\n limit: 10,\n });\n return { success: true, ...response };\n }\n return { success: true, skipped: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/connection.spec.ts"},"span":[2719,2722],"sourceCode":"/**\n * Server Connection E2E Tests\n *\n * Tests for gRPC server connection management.\n */\n\nimport { waitForAppReady } from './fixtures';\n\ndescribe('Server Connection', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('isConnected', () => {\n it('should return connection status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const connected = await api.isConnected();\n return { success: true, connected };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(typeof result.connected).toBe('boolean');\n });\n });\n\n describe('getServerInfo', () => {\n it('should return server information when connected', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const info = await api.getServerInfo();\n return { success: true, info };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.info).toHaveProperty('version');\n }\n });\n });\n\n describe('connect', () => {\n it('should connect to server with default URL', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const info = await api.connect();\n return { success: true, info };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n // May fail if server not running, but should not crash\n });\n\n it('should handle invalid server URL gracefully', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.connect('http://invalid-server:12345');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should fail for invalid server\n expect(result).toBeDefined();\n });\n });\n});\n\ndescribe('Identity', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCurrentUser', () => {\n it('should return current user info', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getCurrentUser();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('user');\n }\n });\n });\n\n describe('listWorkspaces', () => {\n it('should list available workspaces', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWorkspaces();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('workspaces');\n expect(Array.isArray(result.workspaces)).toBe(true);\n }\n });\n });\n});\n\ndescribe('Projects', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('listProjects', () => {\n it('should list projects', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n // Need workspace_id - try to get one first\n const workspaces = await api.listWorkspaces();\n if (workspaces?.workspaces?.length > 0) {\n const response = await api.listProjects({\n workspace_id: workspaces.workspaces[0].id,\n include_archived: false,\n limit: 10,\n });\n return { success: true, ...response };\n }\n return { success: true, skipped: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/connection.spec.ts"},"span":[3307,3310],"sourceCode":"/**\n * Server Connection E2E Tests\n *\n * Tests for gRPC server connection management.\n */\n\nimport { waitForAppReady } from './fixtures';\n\ndescribe('Server Connection', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('isConnected', () => {\n it('should return connection status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const connected = await api.isConnected();\n return { success: true, connected };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(typeof result.connected).toBe('boolean');\n });\n });\n\n describe('getServerInfo', () => {\n it('should return server information when connected', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const info = await api.getServerInfo();\n return { success: true, info };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.info).toHaveProperty('version');\n }\n });\n });\n\n describe('connect', () => {\n it('should connect to server with default URL', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const info = await api.connect();\n return { success: true, info };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n // May fail if server not running, but should not crash\n });\n\n it('should handle invalid server URL gracefully', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.connect('http://invalid-server:12345');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should fail for invalid server\n expect(result).toBeDefined();\n });\n });\n});\n\ndescribe('Identity', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCurrentUser', () => {\n it('should return current user info', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getCurrentUser();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('user');\n }\n });\n });\n\n describe('listWorkspaces', () => {\n it('should list available workspaces', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWorkspaces();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('workspaces');\n expect(Array.isArray(result.workspaces)).toBe(true);\n }\n });\n });\n});\n\ndescribe('Projects', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('listProjects', () => {\n it('should list projects', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n // Need workspace_id - try to get one first\n const workspaces = await api.listWorkspaces();\n if (workspaces?.workspaces?.length > 0) {\n const response = await api.listProjects({\n workspace_id: workspaces.workspaces[0].id,\n include_archived: false,\n limit: 10,\n });\n return { success: true, ...response };\n }\n return { success: true, skipped: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/connection.spec.ts"},"span":[4040,4043],"sourceCode":"/**\n * Server Connection E2E Tests\n *\n * Tests for gRPC server connection management.\n */\n\nimport { waitForAppReady } from './fixtures';\n\ndescribe('Server Connection', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('isConnected', () => {\n it('should return connection status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const connected = await api.isConnected();\n return { success: true, connected };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(typeof result.connected).toBe('boolean');\n });\n });\n\n describe('getServerInfo', () => {\n it('should return server information when connected', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const info = await api.getServerInfo();\n return { success: true, info };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.info).toHaveProperty('version');\n }\n });\n });\n\n describe('connect', () => {\n it('should connect to server with default URL', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const info = await api.connect();\n return { success: true, info };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n // May fail if server not running, but should not crash\n });\n\n it('should handle invalid server URL gracefully', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.connect('http://invalid-server:12345');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should fail for invalid server\n expect(result).toBeDefined();\n });\n });\n});\n\ndescribe('Identity', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCurrentUser', () => {\n it('should return current user info', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getCurrentUser();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('user');\n }\n });\n });\n\n describe('listWorkspaces', () => {\n it('should list available workspaces', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWorkspaces();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('workspaces');\n expect(Array.isArray(result.workspaces)).toBe(true);\n }\n });\n });\n});\n\ndescribe('Projects', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('listProjects', () => {\n it('should list projects', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n // Need workspace_id - try to get one first\n const workspaces = await api.listWorkspaces();\n if (workspaces?.workspaces?.length > 0) {\n const response = await api.listProjects({\n workspace_id: workspaces.workspaces[0].id,\n include_archived: false,\n limit: 10,\n });\n return { success: true, ...response };\n }\n return { success: true, skipped: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/diarization.spec.ts"},"span":[882,885],"sourceCode":"/**\n * Speaker Diarization E2E Tests\n *\n * Tests for speaker diarization and refinement operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Speaker Diarization', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('refineSpeakers', () => {\n it('should start speaker refinement job', async () => {\n // Create a test meeting\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n // Try to start refinement\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.job).toHaveProperty('job_id');\n expect(result.job).toHaveProperty('status');\n expect(['queued', 'running', 'completed', 'failed']).toContain(result.job.status);\n }\n });\n\n it('should accept optional speaker count', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId, 2);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('getDiarizationJobStatus', () => {\n it('should get job status by ID', async () => {\n // Create meeting and start job\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const status = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getDiarizationJobStatus(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(status).toBeDefined();\n if (!status.error) {\n expect(status).toHaveProperty('status');\n }\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should handle non-existent job ID', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getDiarizationJobStatus('non-existent-job-id');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should fail for non-existent job\n expect(result.success).toBe(false);\n });\n });\n\n describe('cancelDiarization', () => {\n it('should cancel a running job', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const cancelResult = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.cancelDiarization(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(cancelResult).toBeDefined();\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('renameSpeaker', () => {\n it('should rename a speaker', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.renameSpeaker(meetingId, 'SPEAKER_0', 'John Doe');\n return { success };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/diarization.spec.ts"},"span":[5855,5858],"sourceCode":"/**\n * Speaker Diarization E2E Tests\n *\n * Tests for speaker diarization and refinement operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Speaker Diarization', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('refineSpeakers', () => {\n it('should start speaker refinement job', async () => {\n // Create a test meeting\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n // Try to start refinement\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.job).toHaveProperty('job_id');\n expect(result.job).toHaveProperty('status');\n expect(['queued', 'running', 'completed', 'failed']).toContain(result.job.status);\n }\n });\n\n it('should accept optional speaker count', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId, 2);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('getDiarizationJobStatus', () => {\n it('should get job status by ID', async () => {\n // Create meeting and start job\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const status = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getDiarizationJobStatus(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(status).toBeDefined();\n if (!status.error) {\n expect(status).toHaveProperty('status');\n }\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should handle non-existent job ID', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getDiarizationJobStatus('non-existent-job-id');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should fail for non-existent job\n expect(result.success).toBe(false);\n });\n });\n\n describe('cancelDiarization', () => {\n it('should cancel a running job', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const cancelResult = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.cancelDiarization(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(cancelResult).toBeDefined();\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('renameSpeaker', () => {\n it('should rename a speaker', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.renameSpeaker(meetingId, 'SPEAKER_0', 'John Doe');\n return { success };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/diarization.spec.ts"},"span":[7296,7299],"sourceCode":"/**\n * Speaker Diarization E2E Tests\n *\n * Tests for speaker diarization and refinement operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Speaker Diarization', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('refineSpeakers', () => {\n it('should start speaker refinement job', async () => {\n // Create a test meeting\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n // Try to start refinement\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.job).toHaveProperty('job_id');\n expect(result.job).toHaveProperty('status');\n expect(['queued', 'running', 'completed', 'failed']).toContain(result.job.status);\n }\n });\n\n it('should accept optional speaker count', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId, 2);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('getDiarizationJobStatus', () => {\n it('should get job status by ID', async () => {\n // Create meeting and start job\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const status = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getDiarizationJobStatus(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(status).toBeDefined();\n if (!status.error) {\n expect(status).toHaveProperty('status');\n }\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should handle non-existent job ID', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getDiarizationJobStatus('non-existent-job-id');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should fail for non-existent job\n expect(result.success).toBe(false);\n });\n });\n\n describe('cancelDiarization', () => {\n it('should cancel a running job', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const cancelResult = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.cancelDiarization(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(cancelResult).toBeDefined();\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('renameSpeaker', () => {\n it('should rename a speaker', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.renameSpeaker(meetingId, 'SPEAKER_0', 'John Doe');\n return { success };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/diarization.spec.ts"},"span":[7706,7709],"sourceCode":"/**\n * Speaker Diarization E2E Tests\n *\n * Tests for speaker diarization and refinement operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Speaker Diarization', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('refineSpeakers', () => {\n it('should start speaker refinement job', async () => {\n // Create a test meeting\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n // Try to start refinement\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.job).toHaveProperty('job_id');\n expect(result.job).toHaveProperty('status');\n expect(['queued', 'running', 'completed', 'failed']).toContain(result.job.status);\n }\n });\n\n it('should accept optional speaker count', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId, 2);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('getDiarizationJobStatus', () => {\n it('should get job status by ID', async () => {\n // Create meeting and start job\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const status = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getDiarizationJobStatus(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(status).toBeDefined();\n if (!status.error) {\n expect(status).toHaveProperty('status');\n }\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should handle non-existent job ID', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getDiarizationJobStatus('non-existent-job-id');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should fail for non-existent job\n expect(result.success).toBe(false);\n });\n });\n\n describe('cancelDiarization', () => {\n it('should cancel a running job', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const cancelResult = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.cancelDiarization(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(cancelResult).toBeDefined();\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('renameSpeaker', () => {\n it('should rename a speaker', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.renameSpeaker(meetingId, 'SPEAKER_0', 'John Doe');\n return { success };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/diarization.spec.ts"},"span":[6154,6157],"sourceCode":"/**\n * Speaker Diarization E2E Tests\n *\n * Tests for speaker diarization and refinement operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Speaker Diarization', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('refineSpeakers', () => {\n it('should start speaker refinement job', async () => {\n // Create a test meeting\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n // Try to start refinement\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.job).toHaveProperty('job_id');\n expect(result.job).toHaveProperty('status');\n expect(['queued', 'running', 'completed', 'failed']).toContain(result.job.status);\n }\n });\n\n it('should accept optional speaker count', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId, 2);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('getDiarizationJobStatus', () => {\n it('should get job status by ID', async () => {\n // Create meeting and start job\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const status = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getDiarizationJobStatus(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(status).toBeDefined();\n if (!status.error) {\n expect(status).toHaveProperty('status');\n }\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should handle non-existent job ID', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getDiarizationJobStatus('non-existent-job-id');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should fail for non-existent job\n expect(result.success).toBe(false);\n });\n });\n\n describe('cancelDiarization', () => {\n it('should cancel a running job', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const cancelResult = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.cancelDiarization(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(cancelResult).toBeDefined();\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('renameSpeaker', () => {\n it('should rename a speaker', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.renameSpeaker(meetingId, 'SPEAKER_0', 'John Doe');\n return { success };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/diarization.spec.ts"},"span":[6534,6537],"sourceCode":"/**\n * Speaker Diarization E2E Tests\n *\n * Tests for speaker diarization and refinement operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Speaker Diarization', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('refineSpeakers', () => {\n it('should start speaker refinement job', async () => {\n // Create a test meeting\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n // Try to start refinement\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.job).toHaveProperty('job_id');\n expect(result.job).toHaveProperty('status');\n expect(['queued', 'running', 'completed', 'failed']).toContain(result.job.status);\n }\n });\n\n it('should accept optional speaker count', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId, 2);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('getDiarizationJobStatus', () => {\n it('should get job status by ID', async () => {\n // Create meeting and start job\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const status = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getDiarizationJobStatus(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(status).toBeDefined();\n if (!status.error) {\n expect(status).toHaveProperty('status');\n }\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should handle non-existent job ID', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getDiarizationJobStatus('non-existent-job-id');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should fail for non-existent job\n expect(result.success).toBe(false);\n });\n });\n\n describe('cancelDiarization', () => {\n it('should cancel a running job', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const cancelResult = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.cancelDiarization(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(cancelResult).toBeDefined();\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('renameSpeaker', () => {\n it('should rename a speaker', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.renameSpeaker(meetingId, 'SPEAKER_0', 'John Doe');\n return { success };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/diarization.spec.ts"},"span":[6867,6870],"sourceCode":"/**\n * Speaker Diarization E2E Tests\n *\n * Tests for speaker diarization and refinement operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Speaker Diarization', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('refineSpeakers', () => {\n it('should start speaker refinement job', async () => {\n // Create a test meeting\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n // Try to start refinement\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.job).toHaveProperty('job_id');\n expect(result.job).toHaveProperty('status');\n expect(['queued', 'running', 'completed', 'failed']).toContain(result.job.status);\n }\n });\n\n it('should accept optional speaker count', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId, 2);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('getDiarizationJobStatus', () => {\n it('should get job status by ID', async () => {\n // Create meeting and start job\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const status = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getDiarizationJobStatus(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(status).toBeDefined();\n if (!status.error) {\n expect(status).toHaveProperty('status');\n }\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should handle non-existent job ID', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getDiarizationJobStatus('non-existent-job-id');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should fail for non-existent job\n expect(result.success).toBe(false);\n });\n });\n\n describe('cancelDiarization', () => {\n it('should cancel a running job', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const cancelResult = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.cancelDiarization(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(cancelResult).toBeDefined();\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('renameSpeaker', () => {\n it('should rename a speaker', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.renameSpeaker(meetingId, 'SPEAKER_0', 'John Doe');\n return { success };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/diarization.spec.ts"},"span":[443,446],"sourceCode":"/**\n * Speaker Diarization E2E Tests\n *\n * Tests for speaker diarization and refinement operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Speaker Diarization', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('refineSpeakers', () => {\n it('should start speaker refinement job', async () => {\n // Create a test meeting\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n // Try to start refinement\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.job).toHaveProperty('job_id');\n expect(result.job).toHaveProperty('status');\n expect(['queued', 'running', 'completed', 'failed']).toContain(result.job.status);\n }\n });\n\n it('should accept optional speaker count', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId, 2);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('getDiarizationJobStatus', () => {\n it('should get job status by ID', async () => {\n // Create meeting and start job\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const status = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getDiarizationJobStatus(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(status).toBeDefined();\n if (!status.error) {\n expect(status).toHaveProperty('status');\n }\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should handle non-existent job ID', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getDiarizationJobStatus('non-existent-job-id');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should fail for non-existent job\n expect(result.success).toBe(false);\n });\n });\n\n describe('cancelDiarization', () => {\n it('should cancel a running job', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const cancelResult = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.cancelDiarization(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(cancelResult).toBeDefined();\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('renameSpeaker', () => {\n it('should rename a speaker', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.renameSpeaker(meetingId, 'SPEAKER_0', 'John Doe');\n return { success };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/diarization.spec.ts"},"span":[1379,1382],"sourceCode":"/**\n * Speaker Diarization E2E Tests\n *\n * Tests for speaker diarization and refinement operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Speaker Diarization', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('refineSpeakers', () => {\n it('should start speaker refinement job', async () => {\n // Create a test meeting\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n // Try to start refinement\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.job).toHaveProperty('job_id');\n expect(result.job).toHaveProperty('status');\n expect(['queued', 'running', 'completed', 'failed']).toContain(result.job.status);\n }\n });\n\n it('should accept optional speaker count', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId, 2);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('getDiarizationJobStatus', () => {\n it('should get job status by ID', async () => {\n // Create meeting and start job\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const status = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getDiarizationJobStatus(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(status).toBeDefined();\n if (!status.error) {\n expect(status).toHaveProperty('status');\n }\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should handle non-existent job ID', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getDiarizationJobStatus('non-existent-job-id');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should fail for non-existent job\n expect(result.success).toBe(false);\n });\n });\n\n describe('cancelDiarization', () => {\n it('should cancel a running job', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const cancelResult = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.cancelDiarization(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(cancelResult).toBeDefined();\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('renameSpeaker', () => {\n it('should rename a speaker', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.renameSpeaker(meetingId, 'SPEAKER_0', 'John Doe');\n return { success };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/diarization.spec.ts"},"span":[2150,2153],"sourceCode":"/**\n * Speaker Diarization E2E Tests\n *\n * Tests for speaker diarization and refinement operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Speaker Diarization', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('refineSpeakers', () => {\n it('should start speaker refinement job', async () => {\n // Create a test meeting\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n // Try to start refinement\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.job).toHaveProperty('job_id');\n expect(result.job).toHaveProperty('status');\n expect(['queued', 'running', 'completed', 'failed']).toContain(result.job.status);\n }\n });\n\n it('should accept optional speaker count', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId, 2);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('getDiarizationJobStatus', () => {\n it('should get job status by ID', async () => {\n // Create meeting and start job\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const status = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getDiarizationJobStatus(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(status).toBeDefined();\n if (!status.error) {\n expect(status).toHaveProperty('status');\n }\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should handle non-existent job ID', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getDiarizationJobStatus('non-existent-job-id');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should fail for non-existent job\n expect(result.success).toBe(false);\n });\n });\n\n describe('cancelDiarization', () => {\n it('should cancel a running job', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const cancelResult = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.cancelDiarization(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(cancelResult).toBeDefined();\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('renameSpeaker', () => {\n it('should rename a speaker', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.renameSpeaker(meetingId, 'SPEAKER_0', 'John Doe');\n return { success };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/diarization.spec.ts"},"span":[2579,2582],"sourceCode":"/**\n * Speaker Diarization E2E Tests\n *\n * Tests for speaker diarization and refinement operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Speaker Diarization', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('refineSpeakers', () => {\n it('should start speaker refinement job', async () => {\n // Create a test meeting\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n // Try to start refinement\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.job).toHaveProperty('job_id');\n expect(result.job).toHaveProperty('status');\n expect(['queued', 'running', 'completed', 'failed']).toContain(result.job.status);\n }\n });\n\n it('should accept optional speaker count', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId, 2);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('getDiarizationJobStatus', () => {\n it('should get job status by ID', async () => {\n // Create meeting and start job\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const status = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getDiarizationJobStatus(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(status).toBeDefined();\n if (!status.error) {\n expect(status).toHaveProperty('status');\n }\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should handle non-existent job ID', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getDiarizationJobStatus('non-existent-job-id');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should fail for non-existent job\n expect(result.success).toBe(false);\n });\n });\n\n describe('cancelDiarization', () => {\n it('should cancel a running job', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const cancelResult = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.cancelDiarization(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(cancelResult).toBeDefined();\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('renameSpeaker', () => {\n it('should rename a speaker', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.renameSpeaker(meetingId, 'SPEAKER_0', 'John Doe');\n return { success };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/diarization.spec.ts"},"span":[2991,2994],"sourceCode":"/**\n * Speaker Diarization E2E Tests\n *\n * Tests for speaker diarization and refinement operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Speaker Diarization', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('refineSpeakers', () => {\n it('should start speaker refinement job', async () => {\n // Create a test meeting\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n // Try to start refinement\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.job).toHaveProperty('job_id');\n expect(result.job).toHaveProperty('status');\n expect(['queued', 'running', 'completed', 'failed']).toContain(result.job.status);\n }\n });\n\n it('should accept optional speaker count', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId, 2);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('getDiarizationJobStatus', () => {\n it('should get job status by ID', async () => {\n // Create meeting and start job\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const status = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getDiarizationJobStatus(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(status).toBeDefined();\n if (!status.error) {\n expect(status).toHaveProperty('status');\n }\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should handle non-existent job ID', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getDiarizationJobStatus('non-existent-job-id');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should fail for non-existent job\n expect(result.success).toBe(false);\n });\n });\n\n describe('cancelDiarization', () => {\n it('should cancel a running job', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const cancelResult = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.cancelDiarization(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(cancelResult).toBeDefined();\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('renameSpeaker', () => {\n it('should rename a speaker', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.renameSpeaker(meetingId, 'SPEAKER_0', 'John Doe');\n return { success };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/diarization.spec.ts"},"span":[3376,3379],"sourceCode":"/**\n * Speaker Diarization E2E Tests\n *\n * Tests for speaker diarization and refinement operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Speaker Diarization', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('refineSpeakers', () => {\n it('should start speaker refinement job', async () => {\n // Create a test meeting\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n // Try to start refinement\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.job).toHaveProperty('job_id');\n expect(result.job).toHaveProperty('status');\n expect(['queued', 'running', 'completed', 'failed']).toContain(result.job.status);\n }\n });\n\n it('should accept optional speaker count', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId, 2);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('getDiarizationJobStatus', () => {\n it('should get job status by ID', async () => {\n // Create meeting and start job\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const status = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getDiarizationJobStatus(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(status).toBeDefined();\n if (!status.error) {\n expect(status).toHaveProperty('status');\n }\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should handle non-existent job ID', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getDiarizationJobStatus('non-existent-job-id');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should fail for non-existent job\n expect(result.success).toBe(false);\n });\n });\n\n describe('cancelDiarization', () => {\n it('should cancel a running job', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const cancelResult = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.cancelDiarization(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(cancelResult).toBeDefined();\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('renameSpeaker', () => {\n it('should rename a speaker', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.renameSpeaker(meetingId, 'SPEAKER_0', 'John Doe');\n return { success };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/diarization.spec.ts"},"span":[3808,3811],"sourceCode":"/**\n * Speaker Diarization E2E Tests\n *\n * Tests for speaker diarization and refinement operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Speaker Diarization', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('refineSpeakers', () => {\n it('should start speaker refinement job', async () => {\n // Create a test meeting\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n // Try to start refinement\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.job).toHaveProperty('job_id');\n expect(result.job).toHaveProperty('status');\n expect(['queued', 'running', 'completed', 'failed']).toContain(result.job.status);\n }\n });\n\n it('should accept optional speaker count', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId, 2);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('getDiarizationJobStatus', () => {\n it('should get job status by ID', async () => {\n // Create meeting and start job\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const status = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getDiarizationJobStatus(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(status).toBeDefined();\n if (!status.error) {\n expect(status).toHaveProperty('status');\n }\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should handle non-existent job ID', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getDiarizationJobStatus('non-existent-job-id');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should fail for non-existent job\n expect(result.success).toBe(false);\n });\n });\n\n describe('cancelDiarization', () => {\n it('should cancel a running job', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const cancelResult = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.cancelDiarization(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(cancelResult).toBeDefined();\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('renameSpeaker', () => {\n it('should rename a speaker', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.renameSpeaker(meetingId, 'SPEAKER_0', 'John Doe');\n return { success };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/diarization.spec.ts"},"span":[4101,4104],"sourceCode":"/**\n * Speaker Diarization E2E Tests\n *\n * Tests for speaker diarization and refinement operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Speaker Diarization', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('refineSpeakers', () => {\n it('should start speaker refinement job', async () => {\n // Create a test meeting\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n // Try to start refinement\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.job).toHaveProperty('job_id');\n expect(result.job).toHaveProperty('status');\n expect(['queued', 'running', 'completed', 'failed']).toContain(result.job.status);\n }\n });\n\n it('should accept optional speaker count', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId, 2);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('getDiarizationJobStatus', () => {\n it('should get job status by ID', async () => {\n // Create meeting and start job\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const status = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getDiarizationJobStatus(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(status).toBeDefined();\n if (!status.error) {\n expect(status).toHaveProperty('status');\n }\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should handle non-existent job ID', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getDiarizationJobStatus('non-existent-job-id');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should fail for non-existent job\n expect(result.success).toBe(false);\n });\n });\n\n describe('cancelDiarization', () => {\n it('should cancel a running job', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const cancelResult = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.cancelDiarization(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(cancelResult).toBeDefined();\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('renameSpeaker', () => {\n it('should rename a speaker', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.renameSpeaker(meetingId, 'SPEAKER_0', 'John Doe');\n return { success };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/diarization.spec.ts"},"span":[4571,4574],"sourceCode":"/**\n * Speaker Diarization E2E Tests\n *\n * Tests for speaker diarization and refinement operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Speaker Diarization', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('refineSpeakers', () => {\n it('should start speaker refinement job', async () => {\n // Create a test meeting\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n // Try to start refinement\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.job).toHaveProperty('job_id');\n expect(result.job).toHaveProperty('status');\n expect(['queued', 'running', 'completed', 'failed']).toContain(result.job.status);\n }\n });\n\n it('should accept optional speaker count', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId, 2);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('getDiarizationJobStatus', () => {\n it('should get job status by ID', async () => {\n // Create meeting and start job\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const status = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getDiarizationJobStatus(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(status).toBeDefined();\n if (!status.error) {\n expect(status).toHaveProperty('status');\n }\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should handle non-existent job ID', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getDiarizationJobStatus('non-existent-job-id');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should fail for non-existent job\n expect(result.success).toBe(false);\n });\n });\n\n describe('cancelDiarization', () => {\n it('should cancel a running job', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const cancelResult = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.cancelDiarization(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(cancelResult).toBeDefined();\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('renameSpeaker', () => {\n it('should rename a speaker', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.renameSpeaker(meetingId, 'SPEAKER_0', 'John Doe');\n return { success };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/diarization.spec.ts"},"span":[4808,4811],"sourceCode":"/**\n * Speaker Diarization E2E Tests\n *\n * Tests for speaker diarization and refinement operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Speaker Diarization', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('refineSpeakers', () => {\n it('should start speaker refinement job', async () => {\n // Create a test meeting\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n // Try to start refinement\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.job).toHaveProperty('job_id');\n expect(result.job).toHaveProperty('status');\n expect(['queued', 'running', 'completed', 'failed']).toContain(result.job.status);\n }\n });\n\n it('should accept optional speaker count', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId, 2);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('getDiarizationJobStatus', () => {\n it('should get job status by ID', async () => {\n // Create meeting and start job\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const status = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getDiarizationJobStatus(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(status).toBeDefined();\n if (!status.error) {\n expect(status).toHaveProperty('status');\n }\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should handle non-existent job ID', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getDiarizationJobStatus('non-existent-job-id');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should fail for non-existent job\n expect(result.success).toBe(false);\n });\n });\n\n describe('cancelDiarization', () => {\n it('should cancel a running job', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const cancelResult = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.cancelDiarization(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(cancelResult).toBeDefined();\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('renameSpeaker', () => {\n it('should rename a speaker', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.renameSpeaker(meetingId, 'SPEAKER_0', 'John Doe');\n return { success };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/diarization.spec.ts"},"span":[5423,5426],"sourceCode":"/**\n * Speaker Diarization E2E Tests\n *\n * Tests for speaker diarization and refinement operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Speaker Diarization', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('refineSpeakers', () => {\n it('should start speaker refinement job', async () => {\n // Create a test meeting\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n // Try to start refinement\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.job).toHaveProperty('job_id');\n expect(result.job).toHaveProperty('status');\n expect(['queued', 'running', 'completed', 'failed']).toContain(result.job.status);\n }\n });\n\n it('should accept optional speaker count', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const job = await api.refineSpeakers(meetingId, 2);\n return { success: true, job };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('getDiarizationJobStatus', () => {\n it('should get job status by ID', async () => {\n // Create meeting and start job\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const status = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getDiarizationJobStatus(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(status).toBeDefined();\n if (!status.error) {\n expect(status).toHaveProperty('status');\n }\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should handle non-existent job ID', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getDiarizationJobStatus('non-existent-job-id');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should fail for non-existent job\n expect(result.success).toBe(false);\n });\n });\n\n describe('cancelDiarization', () => {\n it('should cancel a running job', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const jobResult = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.refineSpeakers(meetingId);\n } catch {\n return null;\n }\n }, meeting.id);\n\n if (jobResult?.job_id) {\n const cancelResult = await browser.execute(async (jobId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.cancelDiarization(jobId);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, jobResult.job_id);\n\n expect(cancelResult).toBeDefined();\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n\n describe('renameSpeaker', () => {\n it('should rename a speaker', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const success = await api.renameSpeaker(meetingId, 'SPEAKER_0', 'John Doe');\n return { success };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/export.spec.ts"},"span":[2407,2410],"sourceCode":"/**\n * Export E2E Tests\n *\n * Tests for transcript export functionality.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Export Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('exportTranscript', () => {\n it('should export as markdown', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const exported = await api.exportTranscript(meetingId, 'markdown');\n return { success: true, exported };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.exported).toHaveProperty('content');\n expect(result.exported).toHaveProperty('format');\n }\n });\n\n it('should export as HTML', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const exported = await api.exportTranscript(meetingId, 'html');\n return { success: true, exported };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success && result.exported?.content) {\n expect(result.exported.content).toContain('<');\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should export as PDF', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const exported = await api.exportTranscript(meetingId, 'pdf');\n return { success: true, exported };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n // PDF may fail if WeasyPrint not installed\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should handle non-existent meeting', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.exportTranscript('non-existent-meeting-id', 'markdown');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(false);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/export.spec.ts"},"span":[2956,2959],"sourceCode":"/**\n * Export E2E Tests\n *\n * Tests for transcript export functionality.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Export Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('exportTranscript', () => {\n it('should export as markdown', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const exported = await api.exportTranscript(meetingId, 'markdown');\n return { success: true, exported };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.exported).toHaveProperty('content');\n expect(result.exported).toHaveProperty('format');\n }\n });\n\n it('should export as HTML', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const exported = await api.exportTranscript(meetingId, 'html');\n return { success: true, exported };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success && result.exported?.content) {\n expect(result.exported.content).toContain('<');\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should export as PDF', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const exported = await api.exportTranscript(meetingId, 'pdf');\n return { success: true, exported };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n // PDF may fail if WeasyPrint not installed\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should handle non-existent meeting', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.exportTranscript('non-existent-meeting-id', 'markdown');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(false);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/export.spec.ts"},"span":[3244,3247],"sourceCode":"/**\n * Export E2E Tests\n *\n * Tests for transcript export functionality.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Export Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('exportTranscript', () => {\n it('should export as markdown', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const exported = await api.exportTranscript(meetingId, 'markdown');\n return { success: true, exported };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.exported).toHaveProperty('content');\n expect(result.exported).toHaveProperty('format');\n }\n });\n\n it('should export as HTML', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const exported = await api.exportTranscript(meetingId, 'html');\n return { success: true, exported };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success && result.exported?.content) {\n expect(result.exported.content).toContain('<');\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should export as PDF', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const exported = await api.exportTranscript(meetingId, 'pdf');\n return { success: true, exported };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n // PDF may fail if WeasyPrint not installed\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should handle non-existent meeting', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.exportTranscript('non-existent-meeting-id', 'markdown');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(false);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/export.spec.ts"},"span":[3673,3676],"sourceCode":"/**\n * Export E2E Tests\n *\n * Tests for transcript export functionality.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Export Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('exportTranscript', () => {\n it('should export as markdown', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const exported = await api.exportTranscript(meetingId, 'markdown');\n return { success: true, exported };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.exported).toHaveProperty('content');\n expect(result.exported).toHaveProperty('format');\n }\n });\n\n it('should export as HTML', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const exported = await api.exportTranscript(meetingId, 'html');\n return { success: true, exported };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success && result.exported?.content) {\n expect(result.exported.content).toContain('<');\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should export as PDF', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const exported = await api.exportTranscript(meetingId, 'pdf');\n return { success: true, exported };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n // PDF may fail if WeasyPrint not installed\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should handle non-existent meeting', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.exportTranscript('non-existent-meeting-id', 'markdown');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(false);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/export.spec.ts"},"span":[4151,4154],"sourceCode":"/**\n * Export E2E Tests\n *\n * Tests for transcript export functionality.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Export Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('exportTranscript', () => {\n it('should export as markdown', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const exported = await api.exportTranscript(meetingId, 'markdown');\n return { success: true, exported };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.exported).toHaveProperty('content');\n expect(result.exported).toHaveProperty('format');\n }\n });\n\n it('should export as HTML', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const exported = await api.exportTranscript(meetingId, 'html');\n return { success: true, exported };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success && result.exported?.content) {\n expect(result.exported.content).toContain('<');\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should export as PDF', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const exported = await api.exportTranscript(meetingId, 'pdf');\n return { success: true, exported };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n // PDF may fail if WeasyPrint not installed\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should handle non-existent meeting', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.exportTranscript('non-existent-meeting-id', 'markdown');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(false);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/export.spec.ts"},"span":[4389,4392],"sourceCode":"/**\n * Export E2E Tests\n *\n * Tests for transcript export functionality.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Export Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('exportTranscript', () => {\n it('should export as markdown', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const exported = await api.exportTranscript(meetingId, 'markdown');\n return { success: true, exported };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.exported).toHaveProperty('content');\n expect(result.exported).toHaveProperty('format');\n }\n });\n\n it('should export as HTML', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const exported = await api.exportTranscript(meetingId, 'html');\n return { success: true, exported };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success && result.exported?.content) {\n expect(result.exported.content).toContain('<');\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should export as PDF', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const exported = await api.exportTranscript(meetingId, 'pdf');\n return { success: true, exported };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n // PDF may fail if WeasyPrint not installed\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should handle non-existent meeting', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.exportTranscript('non-existent-meeting-id', 'markdown');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(false);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/export.spec.ts"},"span":[414,417],"sourceCode":"/**\n * Export E2E Tests\n *\n * Tests for transcript export functionality.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Export Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('exportTranscript', () => {\n it('should export as markdown', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const exported = await api.exportTranscript(meetingId, 'markdown');\n return { success: true, exported };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.exported).toHaveProperty('content');\n expect(result.exported).toHaveProperty('format');\n }\n });\n\n it('should export as HTML', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const exported = await api.exportTranscript(meetingId, 'html');\n return { success: true, exported };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success && result.exported?.content) {\n expect(result.exported.content).toContain('<');\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should export as PDF', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const exported = await api.exportTranscript(meetingId, 'pdf');\n return { success: true, exported };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n // PDF may fail if WeasyPrint not installed\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should handle non-existent meeting', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.exportTranscript('non-existent-meeting-id', 'markdown');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(false);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/export.spec.ts"},"span":[814,817],"sourceCode":"/**\n * Export E2E Tests\n *\n * Tests for transcript export functionality.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Export Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('exportTranscript', () => {\n it('should export as markdown', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const exported = await api.exportTranscript(meetingId, 'markdown');\n return { success: true, exported };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.exported).toHaveProperty('content');\n expect(result.exported).toHaveProperty('format');\n }\n });\n\n it('should export as HTML', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const exported = await api.exportTranscript(meetingId, 'html');\n return { success: true, exported };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success && result.exported?.content) {\n expect(result.exported.content).toContain('<');\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should export as PDF', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const exported = await api.exportTranscript(meetingId, 'pdf');\n return { success: true, exported };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n // PDF may fail if WeasyPrint not installed\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should handle non-existent meeting', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.exportTranscript('non-existent-meeting-id', 'markdown');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(false);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/export.spec.ts"},"span":[1278,1281],"sourceCode":"/**\n * Export E2E Tests\n *\n * Tests for transcript export functionality.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Export Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('exportTranscript', () => {\n it('should export as markdown', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const exported = await api.exportTranscript(meetingId, 'markdown');\n return { success: true, exported };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.exported).toHaveProperty('content');\n expect(result.exported).toHaveProperty('format');\n }\n });\n\n it('should export as HTML', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const exported = await api.exportTranscript(meetingId, 'html');\n return { success: true, exported };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success && result.exported?.content) {\n expect(result.exported.content).toContain('<');\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should export as PDF', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const exported = await api.exportTranscript(meetingId, 'pdf');\n return { success: true, exported };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n // PDF may fail if WeasyPrint not installed\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should handle non-existent meeting', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.exportTranscript('non-existent-meeting-id', 'markdown');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(false);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/export.spec.ts"},"span":[1978,1981],"sourceCode":"/**\n * Export E2E Tests\n *\n * Tests for transcript export functionality.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Export Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('exportTranscript', () => {\n it('should export as markdown', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const exported = await api.exportTranscript(meetingId, 'markdown');\n return { success: true, exported };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.exported).toHaveProperty('content');\n expect(result.exported).toHaveProperty('format');\n }\n });\n\n it('should export as HTML', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const exported = await api.exportTranscript(meetingId, 'html');\n return { success: true, exported };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n if (result.success && result.exported?.content) {\n expect(result.exported.content).toContain('<');\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should export as PDF', async () => {\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const exported = await api.exportTranscript(meetingId, 'pdf');\n return { success: true, exported };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n expect(result).toBeDefined();\n // PDF may fail if WeasyPrint not installed\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, meeting.id);\n });\n\n it('should handle non-existent meeting', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.exportTranscript('non-existent-meeting-id', 'markdown');\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(false);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/fixtures.ts"},"span":[1523,1526],"sourceCode":"/**\n * Native E2E Test Fixtures\n *\n * Helpers for testing the actual Tauri desktop application\n * with real IPC commands and native features.\n */\n\n/**\n * Wait for the app window to be fully loaded\n */\nexport async function waitForAppReady(): Promise<void> {\n // Wait for the root React element\n await browser.waitUntil(\n async () => {\n const root = await $('#root');\n return root.isDisplayed();\n },\n {\n timeout: 30000,\n timeoutMsg: 'App root element not found within 30s',\n }\n );\n\n // Wait for main content to render\n await browser.waitUntil(\n async () => {\n const main = await $('main');\n return main.isDisplayed();\n },\n {\n timeout: 10000,\n timeoutMsg: 'Main content not rendered within 10s',\n }\n );\n}\n\n/**\n * Navigate to a route using React Router\n */\nexport async function navigateTo(path: string): Promise<void> {\n // Use window.location for navigation in WebView\n await browser.execute((path) => {\n window.history.pushState({}, '', path);\n window.dispatchEvent(new PopStateEvent('popstate'));\n }, path);\n\n await browser.pause(500); // Allow React to render\n}\n\n/**\n * Check if Tauri IPC is available\n * In Tauri 2.0, checks for the API wrapper instead of __TAURI__ directly\n */\nexport async function isTauriAvailable(): Promise<boolean> {\n return browser.execute(() => {\n // Check for Tauri 2.0 API or the NoteFlow API wrapper\n const hasTauri = typeof (window as any).__TAURI__ !== 'undefined';\n const hasApi = typeof (window as any).__NOTEFLOW_API__ !== 'undefined';\n return hasTauri || hasApi;\n });\n}\n\n/**\n * Invoke a Tauri command directly\n */\nexport async function invokeCommand<T>(command: string, args?: Record<string, unknown>): Promise<T> {\n return browser.execute(\n async (cmd, cmdArgs) => {\n const { invoke } = await import('@tauri-apps/api/core');\n return invoke(cmd, cmdArgs);\n },\n command,\n args || {}\n );\n}\n\n/**\n * Get the window title\n */\nexport async function getWindowTitle(): Promise<string> {\n return browser.getTitle();\n}\n\n/**\n * Wait for a loading spinner to disappear\n */\nexport async function waitForLoadingComplete(timeout = 10000): Promise<void> {\n const spinner = await $('[data-testid=\"spinner\"], .animate-spin');\n if (await spinner.isExisting()) {\n await spinner.waitForDisplayed({ reverse: true, timeout });\n }\n}\n\n/**\n * Click a button by its text content\n */\nexport async function clickButton(text: string): Promise<void> {\n const button = await $(`button=${text}`);\n await button.waitForClickable({ timeout: 5000 });\n await button.click();\n}\n\n/**\n * Fill an input field by label or placeholder\n */\nexport async function fillInput(selector: string, value: string): Promise<void> {\n const input = await $(selector);\n await input.waitForDisplayed({ timeout: 5000 });\n await input.clearValue();\n await input.setValue(value);\n}\n\n/**\n * Wait for a toast notification\n */\nexport async function waitForToast(textPattern?: string, timeout = 5000): Promise<void> {\n const toastSelector = textPattern\n ? `[data-sonner-toast]:has-text(\"${textPattern}\")`\n : '[data-sonner-toast]';\n\n const toast = await $(toastSelector);\n await toast.waitForDisplayed({ timeout });\n}\n\n/**\n * Check if an element exists and is visible\n */\nexport async function isVisible(selector: string): Promise<boolean> {\n const element = await $(selector);\n return element.isDisplayed();\n}\n\n/**\n * Get text content of an element\n */\nexport async function getText(selector: string): Promise<string> {\n const element = await $(selector);\n await element.waitForDisplayed({ timeout: 5000 });\n return element.getText();\n}\n\n/**\n * Take a screenshot with a descriptive name\n */\nexport async function takeScreenshot(name: string): Promise<void> {\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n await browser.saveScreenshot(`./e2e-native/screenshots/${name}-${timestamp}.png`);\n}\n\n/**\n * Test data generators\n */\nexport const TestData = {\n generateTestId(): string {\n return `test-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n },\n\n createMeetingTitle(): string {\n return `Native Test Meeting ${this.generateTestId()}`;\n },\n};\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/fixtures.ts"},"span":[1454,1457],"sourceCode":"/**\n * Native E2E Test Fixtures\n *\n * Helpers for testing the actual Tauri desktop application\n * with real IPC commands and native features.\n */\n\n/**\n * Wait for the app window to be fully loaded\n */\nexport async function waitForAppReady(): Promise<void> {\n // Wait for the root React element\n await browser.waitUntil(\n async () => {\n const root = await $('#root');\n return root.isDisplayed();\n },\n {\n timeout: 30000,\n timeoutMsg: 'App root element not found within 30s',\n }\n );\n\n // Wait for main content to render\n await browser.waitUntil(\n async () => {\n const main = await $('main');\n return main.isDisplayed();\n },\n {\n timeout: 10000,\n timeoutMsg: 'Main content not rendered within 10s',\n }\n );\n}\n\n/**\n * Navigate to a route using React Router\n */\nexport async function navigateTo(path: string): Promise<void> {\n // Use window.location for navigation in WebView\n await browser.execute((path) => {\n window.history.pushState({}, '', path);\n window.dispatchEvent(new PopStateEvent('popstate'));\n }, path);\n\n await browser.pause(500); // Allow React to render\n}\n\n/**\n * Check if Tauri IPC is available\n * In Tauri 2.0, checks for the API wrapper instead of __TAURI__ directly\n */\nexport async function isTauriAvailable(): Promise<boolean> {\n return browser.execute(() => {\n // Check for Tauri 2.0 API or the NoteFlow API wrapper\n const hasTauri = typeof (window as any).__TAURI__ !== 'undefined';\n const hasApi = typeof (window as any).__NOTEFLOW_API__ !== 'undefined';\n return hasTauri || hasApi;\n });\n}\n\n/**\n * Invoke a Tauri command directly\n */\nexport async function invokeCommand<T>(command: string, args?: Record<string, unknown>): Promise<T> {\n return browser.execute(\n async (cmd, cmdArgs) => {\n const { invoke } = await import('@tauri-apps/api/core');\n return invoke(cmd, cmdArgs);\n },\n command,\n args || {}\n );\n}\n\n/**\n * Get the window title\n */\nexport async function getWindowTitle(): Promise<string> {\n return browser.getTitle();\n}\n\n/**\n * Wait for a loading spinner to disappear\n */\nexport async function waitForLoadingComplete(timeout = 10000): Promise<void> {\n const spinner = await $('[data-testid=\"spinner\"], .animate-spin');\n if (await spinner.isExisting()) {\n await spinner.waitForDisplayed({ reverse: true, timeout });\n }\n}\n\n/**\n * Click a button by its text content\n */\nexport async function clickButton(text: string): Promise<void> {\n const button = await $(`button=${text}`);\n await button.waitForClickable({ timeout: 5000 });\n await button.click();\n}\n\n/**\n * Fill an input field by label or placeholder\n */\nexport async function fillInput(selector: string, value: string): Promise<void> {\n const input = await $(selector);\n await input.waitForDisplayed({ timeout: 5000 });\n await input.clearValue();\n await input.setValue(value);\n}\n\n/**\n * Wait for a toast notification\n */\nexport async function waitForToast(textPattern?: string, timeout = 5000): Promise<void> {\n const toastSelector = textPattern\n ? `[data-sonner-toast]:has-text(\"${textPattern}\")`\n : '[data-sonner-toast]';\n\n const toast = await $(toastSelector);\n await toast.waitForDisplayed({ timeout });\n}\n\n/**\n * Check if an element exists and is visible\n */\nexport async function isVisible(selector: string): Promise<boolean> {\n const element = await $(selector);\n return element.isDisplayed();\n}\n\n/**\n * Get text content of an element\n */\nexport async function getText(selector: string): Promise<string> {\n const element = await $(selector);\n await element.waitForDisplayed({ timeout: 5000 });\n return element.getText();\n}\n\n/**\n * Take a screenshot with a descriptive name\n */\nexport async function takeScreenshot(name: string): Promise<void> {\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n await browser.saveScreenshot(`./e2e-native/screenshots/${name}-${timestamp}.png`);\n}\n\n/**\n * Test data generators\n */\nexport const TestData = {\n generateTestId(): string {\n return `test-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n },\n\n createMeetingTitle(): string {\n return `Native Test Meeting ${this.generateTestId()}`;\n },\n};\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/meetings.spec.ts"},"span":[4727,4730],"sourceCode":"/**\n * Meeting Operations E2E Tests\n *\n * Tests for meeting CRUD operations via Tauri IPC.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Meeting Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup: delete test meeting if created\n if (testMeetingId) {\n try {\n await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api?.deleteMeeting(meetingId);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('listMeetings', () => {\n it('should list meetings with default parameters', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result).toHaveProperty('total_count');\n expect(Array.isArray(result.meetings)).toBe(true);\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should support pagination', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 5, offset: 0 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result.meetings.length).toBeLessThanOrEqual(5);\n } else {\n expect(result).toBeDefined();\n }\n });\n\n it('should filter by state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ states: ['completed'], limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error && result.meetings) {\n // All returned meetings should be completed\n for (const meeting of result.meetings) {\n expect(meeting.state).toBe('completed');\n }\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('createMeeting', () => {\n it('should create a new meeting', async () => {\n const title = TestData.createMeetingTitle();\n\n const result = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result).toHaveProperty('title', title);\n expect(result).toHaveProperty('state');\n expect(result).toHaveProperty('created_at');\n testMeetingId = result.id;\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should create meeting with metadata', async () => {\n const title = TestData.createMeetingTitle();\n const metadata = { test_key: 'test_value', source: 'e2e-native' };\n\n const result = await browser.execute(\n async (meetingTitle, meta) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle, metadata: meta });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n },\n title,\n metadata\n );\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result.metadata).toEqual(metadata);\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, result.id);\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('getMeeting', () => {\n it('should retrieve a meeting by ID', async () => {\n // First create a meeting\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n // Then retrieve it\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting.id).toBe(created.id);\n expect(meeting.title).toBe(title);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should include segments when requested', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id, include_segments: true });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting).toHaveProperty('segments');\n expect(Array.isArray(meeting.segments)).toBe(true);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should handle non-existent meeting gracefully', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: 'non-existent-id-12345' });\n return { error: null };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.error).not.toBeNull();\n });\n });\n\n describe('stopMeeting', () => {\n it('should stop an active meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const stopped = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.stopMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!stopped.error) {\n expect(stopped.id).toBe(created.id);\n expect(['stopped', 'completed']).toContain(stopped.state);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n });\n\n describe('deleteMeeting', () => {\n it('should delete a meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const deleted = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.deleteMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (typeof deleted === 'boolean') {\n expect(deleted).toBe(true);\n }\n\n // Verify it's deleted\n const result = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: id });\n return { found: true };\n } catch {\n return { found: false };\n }\n }, created.id);\n\n expect(result.found).toBe(false);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/meetings.spec.ts"},"span":[9683,9686],"sourceCode":"/**\n * Meeting Operations E2E Tests\n *\n * Tests for meeting CRUD operations via Tauri IPC.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Meeting Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup: delete test meeting if created\n if (testMeetingId) {\n try {\n await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api?.deleteMeeting(meetingId);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('listMeetings', () => {\n it('should list meetings with default parameters', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result).toHaveProperty('total_count');\n expect(Array.isArray(result.meetings)).toBe(true);\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should support pagination', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 5, offset: 0 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result.meetings.length).toBeLessThanOrEqual(5);\n } else {\n expect(result).toBeDefined();\n }\n });\n\n it('should filter by state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ states: ['completed'], limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error && result.meetings) {\n // All returned meetings should be completed\n for (const meeting of result.meetings) {\n expect(meeting.state).toBe('completed');\n }\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('createMeeting', () => {\n it('should create a new meeting', async () => {\n const title = TestData.createMeetingTitle();\n\n const result = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result).toHaveProperty('title', title);\n expect(result).toHaveProperty('state');\n expect(result).toHaveProperty('created_at');\n testMeetingId = result.id;\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should create meeting with metadata', async () => {\n const title = TestData.createMeetingTitle();\n const metadata = { test_key: 'test_value', source: 'e2e-native' };\n\n const result = await browser.execute(\n async (meetingTitle, meta) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle, metadata: meta });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n },\n title,\n metadata\n );\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result.metadata).toEqual(metadata);\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, result.id);\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('getMeeting', () => {\n it('should retrieve a meeting by ID', async () => {\n // First create a meeting\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n // Then retrieve it\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting.id).toBe(created.id);\n expect(meeting.title).toBe(title);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should include segments when requested', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id, include_segments: true });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting).toHaveProperty('segments');\n expect(Array.isArray(meeting.segments)).toBe(true);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should handle non-existent meeting gracefully', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: 'non-existent-id-12345' });\n return { error: null };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.error).not.toBeNull();\n });\n });\n\n describe('stopMeeting', () => {\n it('should stop an active meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const stopped = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.stopMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!stopped.error) {\n expect(stopped.id).toBe(created.id);\n expect(['stopped', 'completed']).toContain(stopped.state);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n });\n\n describe('deleteMeeting', () => {\n it('should delete a meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const deleted = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.deleteMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (typeof deleted === 'boolean') {\n expect(deleted).toBe(true);\n }\n\n // Verify it's deleted\n const result = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: id });\n return { found: true };\n } catch {\n return { found: false };\n }\n }, created.id);\n\n expect(result.found).toBe(false);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/meetings.spec.ts"},"span":[487,490],"sourceCode":"/**\n * Meeting Operations E2E Tests\n *\n * Tests for meeting CRUD operations via Tauri IPC.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Meeting Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup: delete test meeting if created\n if (testMeetingId) {\n try {\n await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api?.deleteMeeting(meetingId);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('listMeetings', () => {\n it('should list meetings with default parameters', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result).toHaveProperty('total_count');\n expect(Array.isArray(result.meetings)).toBe(true);\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should support pagination', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 5, offset: 0 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result.meetings.length).toBeLessThanOrEqual(5);\n } else {\n expect(result).toBeDefined();\n }\n });\n\n it('should filter by state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ states: ['completed'], limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error && result.meetings) {\n // All returned meetings should be completed\n for (const meeting of result.meetings) {\n expect(meeting.state).toBe('completed');\n }\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('createMeeting', () => {\n it('should create a new meeting', async () => {\n const title = TestData.createMeetingTitle();\n\n const result = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result).toHaveProperty('title', title);\n expect(result).toHaveProperty('state');\n expect(result).toHaveProperty('created_at');\n testMeetingId = result.id;\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should create meeting with metadata', async () => {\n const title = TestData.createMeetingTitle();\n const metadata = { test_key: 'test_value', source: 'e2e-native' };\n\n const result = await browser.execute(\n async (meetingTitle, meta) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle, metadata: meta });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n },\n title,\n metadata\n );\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result.metadata).toEqual(metadata);\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, result.id);\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('getMeeting', () => {\n it('should retrieve a meeting by ID', async () => {\n // First create a meeting\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n // Then retrieve it\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting.id).toBe(created.id);\n expect(meeting.title).toBe(title);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should include segments when requested', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id, include_segments: true });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting).toHaveProperty('segments');\n expect(Array.isArray(meeting.segments)).toBe(true);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should handle non-existent meeting gracefully', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: 'non-existent-id-12345' });\n return { error: null };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.error).not.toBeNull();\n });\n });\n\n describe('stopMeeting', () => {\n it('should stop an active meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const stopped = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.stopMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!stopped.error) {\n expect(stopped.id).toBe(created.id);\n expect(['stopped', 'completed']).toContain(stopped.state);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n });\n\n describe('deleteMeeting', () => {\n it('should delete a meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const deleted = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.deleteMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (typeof deleted === 'boolean') {\n expect(deleted).toBe(true);\n }\n\n // Verify it's deleted\n const result = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: id });\n return { found: true };\n } catch {\n return { found: false };\n }\n }, created.id);\n\n expect(result.found).toBe(false);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/meetings.spec.ts"},"span":[846,849],"sourceCode":"/**\n * Meeting Operations E2E Tests\n *\n * Tests for meeting CRUD operations via Tauri IPC.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Meeting Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup: delete test meeting if created\n if (testMeetingId) {\n try {\n await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api?.deleteMeeting(meetingId);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('listMeetings', () => {\n it('should list meetings with default parameters', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result).toHaveProperty('total_count');\n expect(Array.isArray(result.meetings)).toBe(true);\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should support pagination', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 5, offset: 0 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result.meetings.length).toBeLessThanOrEqual(5);\n } else {\n expect(result).toBeDefined();\n }\n });\n\n it('should filter by state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ states: ['completed'], limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error && result.meetings) {\n // All returned meetings should be completed\n for (const meeting of result.meetings) {\n expect(meeting.state).toBe('completed');\n }\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('createMeeting', () => {\n it('should create a new meeting', async () => {\n const title = TestData.createMeetingTitle();\n\n const result = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result).toHaveProperty('title', title);\n expect(result).toHaveProperty('state');\n expect(result).toHaveProperty('created_at');\n testMeetingId = result.id;\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should create meeting with metadata', async () => {\n const title = TestData.createMeetingTitle();\n const metadata = { test_key: 'test_value', source: 'e2e-native' };\n\n const result = await browser.execute(\n async (meetingTitle, meta) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle, metadata: meta });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n },\n title,\n metadata\n );\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result.metadata).toEqual(metadata);\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, result.id);\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('getMeeting', () => {\n it('should retrieve a meeting by ID', async () => {\n // First create a meeting\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n // Then retrieve it\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting.id).toBe(created.id);\n expect(meeting.title).toBe(title);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should include segments when requested', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id, include_segments: true });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting).toHaveProperty('segments');\n expect(Array.isArray(meeting.segments)).toBe(true);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should handle non-existent meeting gracefully', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: 'non-existent-id-12345' });\n return { error: null };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.error).not.toBeNull();\n });\n });\n\n describe('stopMeeting', () => {\n it('should stop an active meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const stopped = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.stopMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!stopped.error) {\n expect(stopped.id).toBe(created.id);\n expect(['stopped', 'completed']).toContain(stopped.state);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n });\n\n describe('deleteMeeting', () => {\n it('should delete a meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const deleted = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.deleteMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (typeof deleted === 'boolean') {\n expect(deleted).toBe(true);\n }\n\n // Verify it's deleted\n const result = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: id });\n return { found: true };\n } catch {\n return { found: false };\n }\n }, created.id);\n\n expect(result.found).toBe(false);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/meetings.spec.ts"},"span":[1515,1518],"sourceCode":"/**\n * Meeting Operations E2E Tests\n *\n * Tests for meeting CRUD operations via Tauri IPC.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Meeting Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup: delete test meeting if created\n if (testMeetingId) {\n try {\n await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api?.deleteMeeting(meetingId);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('listMeetings', () => {\n it('should list meetings with default parameters', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result).toHaveProperty('total_count');\n expect(Array.isArray(result.meetings)).toBe(true);\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should support pagination', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 5, offset: 0 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result.meetings.length).toBeLessThanOrEqual(5);\n } else {\n expect(result).toBeDefined();\n }\n });\n\n it('should filter by state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ states: ['completed'], limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error && result.meetings) {\n // All returned meetings should be completed\n for (const meeting of result.meetings) {\n expect(meeting.state).toBe('completed');\n }\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('createMeeting', () => {\n it('should create a new meeting', async () => {\n const title = TestData.createMeetingTitle();\n\n const result = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result).toHaveProperty('title', title);\n expect(result).toHaveProperty('state');\n expect(result).toHaveProperty('created_at');\n testMeetingId = result.id;\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should create meeting with metadata', async () => {\n const title = TestData.createMeetingTitle();\n const metadata = { test_key: 'test_value', source: 'e2e-native' };\n\n const result = await browser.execute(\n async (meetingTitle, meta) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle, metadata: meta });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n },\n title,\n metadata\n );\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result.metadata).toEqual(metadata);\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, result.id);\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('getMeeting', () => {\n it('should retrieve a meeting by ID', async () => {\n // First create a meeting\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n // Then retrieve it\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting.id).toBe(created.id);\n expect(meeting.title).toBe(title);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should include segments when requested', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id, include_segments: true });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting).toHaveProperty('segments');\n expect(Array.isArray(meeting.segments)).toBe(true);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should handle non-existent meeting gracefully', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: 'non-existent-id-12345' });\n return { error: null };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.error).not.toBeNull();\n });\n });\n\n describe('stopMeeting', () => {\n it('should stop an active meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const stopped = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.stopMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!stopped.error) {\n expect(stopped.id).toBe(created.id);\n expect(['stopped', 'completed']).toContain(stopped.state);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n });\n\n describe('deleteMeeting', () => {\n it('should delete a meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const deleted = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.deleteMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (typeof deleted === 'boolean') {\n expect(deleted).toBe(true);\n }\n\n // Verify it's deleted\n const result = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: id });\n return { found: true };\n } catch {\n return { found: false };\n }\n }, created.id);\n\n expect(result.found).toBe(false);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/meetings.spec.ts"},"span":[2079,2082],"sourceCode":"/**\n * Meeting Operations E2E Tests\n *\n * Tests for meeting CRUD operations via Tauri IPC.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Meeting Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup: delete test meeting if created\n if (testMeetingId) {\n try {\n await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api?.deleteMeeting(meetingId);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('listMeetings', () => {\n it('should list meetings with default parameters', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result).toHaveProperty('total_count');\n expect(Array.isArray(result.meetings)).toBe(true);\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should support pagination', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 5, offset: 0 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result.meetings.length).toBeLessThanOrEqual(5);\n } else {\n expect(result).toBeDefined();\n }\n });\n\n it('should filter by state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ states: ['completed'], limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error && result.meetings) {\n // All returned meetings should be completed\n for (const meeting of result.meetings) {\n expect(meeting.state).toBe('completed');\n }\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('createMeeting', () => {\n it('should create a new meeting', async () => {\n const title = TestData.createMeetingTitle();\n\n const result = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result).toHaveProperty('title', title);\n expect(result).toHaveProperty('state');\n expect(result).toHaveProperty('created_at');\n testMeetingId = result.id;\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should create meeting with metadata', async () => {\n const title = TestData.createMeetingTitle();\n const metadata = { test_key: 'test_value', source: 'e2e-native' };\n\n const result = await browser.execute(\n async (meetingTitle, meta) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle, metadata: meta });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n },\n title,\n metadata\n );\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result.metadata).toEqual(metadata);\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, result.id);\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('getMeeting', () => {\n it('should retrieve a meeting by ID', async () => {\n // First create a meeting\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n // Then retrieve it\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting.id).toBe(created.id);\n expect(meeting.title).toBe(title);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should include segments when requested', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id, include_segments: true });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting).toHaveProperty('segments');\n expect(Array.isArray(meeting.segments)).toBe(true);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should handle non-existent meeting gracefully', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: 'non-existent-id-12345' });\n return { error: null };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.error).not.toBeNull();\n });\n });\n\n describe('stopMeeting', () => {\n it('should stop an active meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const stopped = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.stopMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!stopped.error) {\n expect(stopped.id).toBe(created.id);\n expect(['stopped', 'completed']).toContain(stopped.state);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n });\n\n describe('deleteMeeting', () => {\n it('should delete a meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const deleted = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.deleteMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (typeof deleted === 'boolean') {\n expect(deleted).toBe(true);\n }\n\n // Verify it's deleted\n const result = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: id });\n return { found: true };\n } catch {\n return { found: false };\n }\n }, created.id);\n\n expect(result.found).toBe(false);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/meetings.spec.ts"},"span":[2835,2838],"sourceCode":"/**\n * Meeting Operations E2E Tests\n *\n * Tests for meeting CRUD operations via Tauri IPC.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Meeting Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup: delete test meeting if created\n if (testMeetingId) {\n try {\n await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api?.deleteMeeting(meetingId);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('listMeetings', () => {\n it('should list meetings with default parameters', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result).toHaveProperty('total_count');\n expect(Array.isArray(result.meetings)).toBe(true);\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should support pagination', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 5, offset: 0 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result.meetings.length).toBeLessThanOrEqual(5);\n } else {\n expect(result).toBeDefined();\n }\n });\n\n it('should filter by state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ states: ['completed'], limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error && result.meetings) {\n // All returned meetings should be completed\n for (const meeting of result.meetings) {\n expect(meeting.state).toBe('completed');\n }\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('createMeeting', () => {\n it('should create a new meeting', async () => {\n const title = TestData.createMeetingTitle();\n\n const result = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result).toHaveProperty('title', title);\n expect(result).toHaveProperty('state');\n expect(result).toHaveProperty('created_at');\n testMeetingId = result.id;\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should create meeting with metadata', async () => {\n const title = TestData.createMeetingTitle();\n const metadata = { test_key: 'test_value', source: 'e2e-native' };\n\n const result = await browser.execute(\n async (meetingTitle, meta) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle, metadata: meta });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n },\n title,\n metadata\n );\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result.metadata).toEqual(metadata);\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, result.id);\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('getMeeting', () => {\n it('should retrieve a meeting by ID', async () => {\n // First create a meeting\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n // Then retrieve it\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting.id).toBe(created.id);\n expect(meeting.title).toBe(title);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should include segments when requested', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id, include_segments: true });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting).toHaveProperty('segments');\n expect(Array.isArray(meeting.segments)).toBe(true);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should handle non-existent meeting gracefully', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: 'non-existent-id-12345' });\n return { error: null };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.error).not.toBeNull();\n });\n });\n\n describe('stopMeeting', () => {\n it('should stop an active meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const stopped = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.stopMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!stopped.error) {\n expect(stopped.id).toBe(created.id);\n expect(['stopped', 'completed']).toContain(stopped.state);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n });\n\n describe('deleteMeeting', () => {\n it('should delete a meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const deleted = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.deleteMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (typeof deleted === 'boolean') {\n expect(deleted).toBe(true);\n }\n\n // Verify it's deleted\n const result = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: id });\n return { found: true };\n } catch {\n return { found: false };\n }\n }, created.id);\n\n expect(result.found).toBe(false);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/meetings.spec.ts"},"span":[3771,3774],"sourceCode":"/**\n * Meeting Operations E2E Tests\n *\n * Tests for meeting CRUD operations via Tauri IPC.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Meeting Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup: delete test meeting if created\n if (testMeetingId) {\n try {\n await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api?.deleteMeeting(meetingId);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('listMeetings', () => {\n it('should list meetings with default parameters', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result).toHaveProperty('total_count');\n expect(Array.isArray(result.meetings)).toBe(true);\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should support pagination', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 5, offset: 0 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result.meetings.length).toBeLessThanOrEqual(5);\n } else {\n expect(result).toBeDefined();\n }\n });\n\n it('should filter by state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ states: ['completed'], limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error && result.meetings) {\n // All returned meetings should be completed\n for (const meeting of result.meetings) {\n expect(meeting.state).toBe('completed');\n }\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('createMeeting', () => {\n it('should create a new meeting', async () => {\n const title = TestData.createMeetingTitle();\n\n const result = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result).toHaveProperty('title', title);\n expect(result).toHaveProperty('state');\n expect(result).toHaveProperty('created_at');\n testMeetingId = result.id;\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should create meeting with metadata', async () => {\n const title = TestData.createMeetingTitle();\n const metadata = { test_key: 'test_value', source: 'e2e-native' };\n\n const result = await browser.execute(\n async (meetingTitle, meta) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle, metadata: meta });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n },\n title,\n metadata\n );\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result.metadata).toEqual(metadata);\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, result.id);\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('getMeeting', () => {\n it('should retrieve a meeting by ID', async () => {\n // First create a meeting\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n // Then retrieve it\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting.id).toBe(created.id);\n expect(meeting.title).toBe(title);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should include segments when requested', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id, include_segments: true });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting).toHaveProperty('segments');\n expect(Array.isArray(meeting.segments)).toBe(true);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should handle non-existent meeting gracefully', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: 'non-existent-id-12345' });\n return { error: null };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.error).not.toBeNull();\n });\n });\n\n describe('stopMeeting', () => {\n it('should stop an active meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const stopped = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.stopMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!stopped.error) {\n expect(stopped.id).toBe(created.id);\n expect(['stopped', 'completed']).toContain(stopped.state);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n });\n\n describe('deleteMeeting', () => {\n it('should delete a meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const deleted = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.deleteMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (typeof deleted === 'boolean') {\n expect(deleted).toBe(true);\n }\n\n // Verify it's deleted\n const result = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: id });\n return { found: true };\n } catch {\n return { found: false };\n }\n }, created.id);\n\n expect(result.found).toBe(false);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/meetings.spec.ts"},"span":[4293,4296],"sourceCode":"/**\n * Meeting Operations E2E Tests\n *\n * Tests for meeting CRUD operations via Tauri IPC.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Meeting Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup: delete test meeting if created\n if (testMeetingId) {\n try {\n await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api?.deleteMeeting(meetingId);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('listMeetings', () => {\n it('should list meetings with default parameters', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result).toHaveProperty('total_count');\n expect(Array.isArray(result.meetings)).toBe(true);\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should support pagination', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 5, offset: 0 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result.meetings.length).toBeLessThanOrEqual(5);\n } else {\n expect(result).toBeDefined();\n }\n });\n\n it('should filter by state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ states: ['completed'], limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error && result.meetings) {\n // All returned meetings should be completed\n for (const meeting of result.meetings) {\n expect(meeting.state).toBe('completed');\n }\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('createMeeting', () => {\n it('should create a new meeting', async () => {\n const title = TestData.createMeetingTitle();\n\n const result = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result).toHaveProperty('title', title);\n expect(result).toHaveProperty('state');\n expect(result).toHaveProperty('created_at');\n testMeetingId = result.id;\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should create meeting with metadata', async () => {\n const title = TestData.createMeetingTitle();\n const metadata = { test_key: 'test_value', source: 'e2e-native' };\n\n const result = await browser.execute(\n async (meetingTitle, meta) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle, metadata: meta });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n },\n title,\n metadata\n );\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result.metadata).toEqual(metadata);\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, result.id);\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('getMeeting', () => {\n it('should retrieve a meeting by ID', async () => {\n // First create a meeting\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n // Then retrieve it\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting.id).toBe(created.id);\n expect(meeting.title).toBe(title);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should include segments when requested', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id, include_segments: true });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting).toHaveProperty('segments');\n expect(Array.isArray(meeting.segments)).toBe(true);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should handle non-existent meeting gracefully', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: 'non-existent-id-12345' });\n return { error: null };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.error).not.toBeNull();\n });\n });\n\n describe('stopMeeting', () => {\n it('should stop an active meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const stopped = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.stopMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!stopped.error) {\n expect(stopped.id).toBe(created.id);\n expect(['stopped', 'completed']).toContain(stopped.state);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n });\n\n describe('deleteMeeting', () => {\n it('should delete a meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const deleted = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.deleteMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (typeof deleted === 'boolean') {\n expect(deleted).toBe(true);\n }\n\n // Verify it's deleted\n const result = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: id });\n return { found: true };\n } catch {\n return { found: false };\n }\n }, created.id);\n\n expect(result.found).toBe(false);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/meetings.spec.ts"},"span":[5176,5179],"sourceCode":"/**\n * Meeting Operations E2E Tests\n *\n * Tests for meeting CRUD operations via Tauri IPC.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Meeting Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup: delete test meeting if created\n if (testMeetingId) {\n try {\n await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api?.deleteMeeting(meetingId);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('listMeetings', () => {\n it('should list meetings with default parameters', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result).toHaveProperty('total_count');\n expect(Array.isArray(result.meetings)).toBe(true);\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should support pagination', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 5, offset: 0 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result.meetings.length).toBeLessThanOrEqual(5);\n } else {\n expect(result).toBeDefined();\n }\n });\n\n it('should filter by state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ states: ['completed'], limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error && result.meetings) {\n // All returned meetings should be completed\n for (const meeting of result.meetings) {\n expect(meeting.state).toBe('completed');\n }\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('createMeeting', () => {\n it('should create a new meeting', async () => {\n const title = TestData.createMeetingTitle();\n\n const result = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result).toHaveProperty('title', title);\n expect(result).toHaveProperty('state');\n expect(result).toHaveProperty('created_at');\n testMeetingId = result.id;\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should create meeting with metadata', async () => {\n const title = TestData.createMeetingTitle();\n const metadata = { test_key: 'test_value', source: 'e2e-native' };\n\n const result = await browser.execute(\n async (meetingTitle, meta) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle, metadata: meta });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n },\n title,\n metadata\n );\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result.metadata).toEqual(metadata);\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, result.id);\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('getMeeting', () => {\n it('should retrieve a meeting by ID', async () => {\n // First create a meeting\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n // Then retrieve it\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting.id).toBe(created.id);\n expect(meeting.title).toBe(title);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should include segments when requested', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id, include_segments: true });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting).toHaveProperty('segments');\n expect(Array.isArray(meeting.segments)).toBe(true);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should handle non-existent meeting gracefully', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: 'non-existent-id-12345' });\n return { error: null };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.error).not.toBeNull();\n });\n });\n\n describe('stopMeeting', () => {\n it('should stop an active meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const stopped = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.stopMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!stopped.error) {\n expect(stopped.id).toBe(created.id);\n expect(['stopped', 'completed']).toContain(stopped.state);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n });\n\n describe('deleteMeeting', () => {\n it('should delete a meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const deleted = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.deleteMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (typeof deleted === 'boolean') {\n expect(deleted).toBe(true);\n }\n\n // Verify it's deleted\n const result = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: id });\n return { found: true };\n } catch {\n return { found: false };\n }\n }, created.id);\n\n expect(result.found).toBe(false);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/meetings.spec.ts"},"span":[5616,5619],"sourceCode":"/**\n * Meeting Operations E2E Tests\n *\n * Tests for meeting CRUD operations via Tauri IPC.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Meeting Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup: delete test meeting if created\n if (testMeetingId) {\n try {\n await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api?.deleteMeeting(meetingId);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('listMeetings', () => {\n it('should list meetings with default parameters', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result).toHaveProperty('total_count');\n expect(Array.isArray(result.meetings)).toBe(true);\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should support pagination', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 5, offset: 0 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result.meetings.length).toBeLessThanOrEqual(5);\n } else {\n expect(result).toBeDefined();\n }\n });\n\n it('should filter by state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ states: ['completed'], limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error && result.meetings) {\n // All returned meetings should be completed\n for (const meeting of result.meetings) {\n expect(meeting.state).toBe('completed');\n }\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('createMeeting', () => {\n it('should create a new meeting', async () => {\n const title = TestData.createMeetingTitle();\n\n const result = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result).toHaveProperty('title', title);\n expect(result).toHaveProperty('state');\n expect(result).toHaveProperty('created_at');\n testMeetingId = result.id;\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should create meeting with metadata', async () => {\n const title = TestData.createMeetingTitle();\n const metadata = { test_key: 'test_value', source: 'e2e-native' };\n\n const result = await browser.execute(\n async (meetingTitle, meta) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle, metadata: meta });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n },\n title,\n metadata\n );\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result.metadata).toEqual(metadata);\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, result.id);\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('getMeeting', () => {\n it('should retrieve a meeting by ID', async () => {\n // First create a meeting\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n // Then retrieve it\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting.id).toBe(created.id);\n expect(meeting.title).toBe(title);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should include segments when requested', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id, include_segments: true });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting).toHaveProperty('segments');\n expect(Array.isArray(meeting.segments)).toBe(true);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should handle non-existent meeting gracefully', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: 'non-existent-id-12345' });\n return { error: null };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.error).not.toBeNull();\n });\n });\n\n describe('stopMeeting', () => {\n it('should stop an active meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const stopped = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.stopMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!stopped.error) {\n expect(stopped.id).toBe(created.id);\n expect(['stopped', 'completed']).toContain(stopped.state);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n });\n\n describe('deleteMeeting', () => {\n it('should delete a meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const deleted = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.deleteMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (typeof deleted === 'boolean') {\n expect(deleted).toBe(true);\n }\n\n // Verify it's deleted\n const result = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: id });\n return { found: true };\n } catch {\n return { found: false };\n }\n }, created.id);\n\n expect(result.found).toBe(false);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/meetings.spec.ts"},"span":[5922,5925],"sourceCode":"/**\n * Meeting Operations E2E Tests\n *\n * Tests for meeting CRUD operations via Tauri IPC.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Meeting Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup: delete test meeting if created\n if (testMeetingId) {\n try {\n await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api?.deleteMeeting(meetingId);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('listMeetings', () => {\n it('should list meetings with default parameters', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result).toHaveProperty('total_count');\n expect(Array.isArray(result.meetings)).toBe(true);\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should support pagination', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 5, offset: 0 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result.meetings.length).toBeLessThanOrEqual(5);\n } else {\n expect(result).toBeDefined();\n }\n });\n\n it('should filter by state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ states: ['completed'], limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error && result.meetings) {\n // All returned meetings should be completed\n for (const meeting of result.meetings) {\n expect(meeting.state).toBe('completed');\n }\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('createMeeting', () => {\n it('should create a new meeting', async () => {\n const title = TestData.createMeetingTitle();\n\n const result = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result).toHaveProperty('title', title);\n expect(result).toHaveProperty('state');\n expect(result).toHaveProperty('created_at');\n testMeetingId = result.id;\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should create meeting with metadata', async () => {\n const title = TestData.createMeetingTitle();\n const metadata = { test_key: 'test_value', source: 'e2e-native' };\n\n const result = await browser.execute(\n async (meetingTitle, meta) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle, metadata: meta });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n },\n title,\n metadata\n );\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result.metadata).toEqual(metadata);\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, result.id);\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('getMeeting', () => {\n it('should retrieve a meeting by ID', async () => {\n // First create a meeting\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n // Then retrieve it\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting.id).toBe(created.id);\n expect(meeting.title).toBe(title);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should include segments when requested', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id, include_segments: true });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting).toHaveProperty('segments');\n expect(Array.isArray(meeting.segments)).toBe(true);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should handle non-existent meeting gracefully', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: 'non-existent-id-12345' });\n return { error: null };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.error).not.toBeNull();\n });\n });\n\n describe('stopMeeting', () => {\n it('should stop an active meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const stopped = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.stopMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!stopped.error) {\n expect(stopped.id).toBe(created.id);\n expect(['stopped', 'completed']).toContain(stopped.state);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n });\n\n describe('deleteMeeting', () => {\n it('should delete a meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const deleted = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.deleteMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (typeof deleted === 'boolean') {\n expect(deleted).toBe(true);\n }\n\n // Verify it's deleted\n const result = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: id });\n return { found: true };\n } catch {\n return { found: false };\n }\n }, created.id);\n\n expect(result.found).toBe(false);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/meetings.spec.ts"},"span":[6345,6348],"sourceCode":"/**\n * Meeting Operations E2E Tests\n *\n * Tests for meeting CRUD operations via Tauri IPC.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Meeting Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup: delete test meeting if created\n if (testMeetingId) {\n try {\n await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api?.deleteMeeting(meetingId);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('listMeetings', () => {\n it('should list meetings with default parameters', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result).toHaveProperty('total_count');\n expect(Array.isArray(result.meetings)).toBe(true);\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should support pagination', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 5, offset: 0 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result.meetings.length).toBeLessThanOrEqual(5);\n } else {\n expect(result).toBeDefined();\n }\n });\n\n it('should filter by state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ states: ['completed'], limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error && result.meetings) {\n // All returned meetings should be completed\n for (const meeting of result.meetings) {\n expect(meeting.state).toBe('completed');\n }\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('createMeeting', () => {\n it('should create a new meeting', async () => {\n const title = TestData.createMeetingTitle();\n\n const result = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result).toHaveProperty('title', title);\n expect(result).toHaveProperty('state');\n expect(result).toHaveProperty('created_at');\n testMeetingId = result.id;\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should create meeting with metadata', async () => {\n const title = TestData.createMeetingTitle();\n const metadata = { test_key: 'test_value', source: 'e2e-native' };\n\n const result = await browser.execute(\n async (meetingTitle, meta) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle, metadata: meta });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n },\n title,\n metadata\n );\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result.metadata).toEqual(metadata);\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, result.id);\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('getMeeting', () => {\n it('should retrieve a meeting by ID', async () => {\n // First create a meeting\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n // Then retrieve it\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting.id).toBe(created.id);\n expect(meeting.title).toBe(title);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should include segments when requested', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id, include_segments: true });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting).toHaveProperty('segments');\n expect(Array.isArray(meeting.segments)).toBe(true);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should handle non-existent meeting gracefully', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: 'non-existent-id-12345' });\n return { error: null };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.error).not.toBeNull();\n });\n });\n\n describe('stopMeeting', () => {\n it('should stop an active meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const stopped = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.stopMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!stopped.error) {\n expect(stopped.id).toBe(created.id);\n expect(['stopped', 'completed']).toContain(stopped.state);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n });\n\n describe('deleteMeeting', () => {\n it('should delete a meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const deleted = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.deleteMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (typeof deleted === 'boolean') {\n expect(deleted).toBe(true);\n }\n\n // Verify it's deleted\n const result = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: id });\n return { found: true };\n } catch {\n return { found: false };\n }\n }, created.id);\n\n expect(result.found).toBe(false);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/meetings.spec.ts"},"span":[6833,6836],"sourceCode":"/**\n * Meeting Operations E2E Tests\n *\n * Tests for meeting CRUD operations via Tauri IPC.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Meeting Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup: delete test meeting if created\n if (testMeetingId) {\n try {\n await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api?.deleteMeeting(meetingId);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('listMeetings', () => {\n it('should list meetings with default parameters', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result).toHaveProperty('total_count');\n expect(Array.isArray(result.meetings)).toBe(true);\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should support pagination', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 5, offset: 0 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result.meetings.length).toBeLessThanOrEqual(5);\n } else {\n expect(result).toBeDefined();\n }\n });\n\n it('should filter by state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ states: ['completed'], limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error && result.meetings) {\n // All returned meetings should be completed\n for (const meeting of result.meetings) {\n expect(meeting.state).toBe('completed');\n }\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('createMeeting', () => {\n it('should create a new meeting', async () => {\n const title = TestData.createMeetingTitle();\n\n const result = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result).toHaveProperty('title', title);\n expect(result).toHaveProperty('state');\n expect(result).toHaveProperty('created_at');\n testMeetingId = result.id;\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should create meeting with metadata', async () => {\n const title = TestData.createMeetingTitle();\n const metadata = { test_key: 'test_value', source: 'e2e-native' };\n\n const result = await browser.execute(\n async (meetingTitle, meta) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle, metadata: meta });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n },\n title,\n metadata\n );\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result.metadata).toEqual(metadata);\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, result.id);\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('getMeeting', () => {\n it('should retrieve a meeting by ID', async () => {\n // First create a meeting\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n // Then retrieve it\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting.id).toBe(created.id);\n expect(meeting.title).toBe(title);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should include segments when requested', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id, include_segments: true });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting).toHaveProperty('segments');\n expect(Array.isArray(meeting.segments)).toBe(true);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should handle non-existent meeting gracefully', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: 'non-existent-id-12345' });\n return { error: null };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.error).not.toBeNull();\n });\n });\n\n describe('stopMeeting', () => {\n it('should stop an active meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const stopped = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.stopMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!stopped.error) {\n expect(stopped.id).toBe(created.id);\n expect(['stopped', 'completed']).toContain(stopped.state);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n });\n\n describe('deleteMeeting', () => {\n it('should delete a meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const deleted = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.deleteMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (typeof deleted === 'boolean') {\n expect(deleted).toBe(true);\n }\n\n // Verify it's deleted\n const result = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: id });\n return { found: true };\n } catch {\n return { found: false };\n }\n }, created.id);\n\n expect(result.found).toBe(false);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/meetings.spec.ts"},"span":[7082,7085],"sourceCode":"/**\n * Meeting Operations E2E Tests\n *\n * Tests for meeting CRUD operations via Tauri IPC.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Meeting Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup: delete test meeting if created\n if (testMeetingId) {\n try {\n await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api?.deleteMeeting(meetingId);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('listMeetings', () => {\n it('should list meetings with default parameters', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result).toHaveProperty('total_count');\n expect(Array.isArray(result.meetings)).toBe(true);\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should support pagination', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 5, offset: 0 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result.meetings.length).toBeLessThanOrEqual(5);\n } else {\n expect(result).toBeDefined();\n }\n });\n\n it('should filter by state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ states: ['completed'], limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error && result.meetings) {\n // All returned meetings should be completed\n for (const meeting of result.meetings) {\n expect(meeting.state).toBe('completed');\n }\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('createMeeting', () => {\n it('should create a new meeting', async () => {\n const title = TestData.createMeetingTitle();\n\n const result = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result).toHaveProperty('title', title);\n expect(result).toHaveProperty('state');\n expect(result).toHaveProperty('created_at');\n testMeetingId = result.id;\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should create meeting with metadata', async () => {\n const title = TestData.createMeetingTitle();\n const metadata = { test_key: 'test_value', source: 'e2e-native' };\n\n const result = await browser.execute(\n async (meetingTitle, meta) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle, metadata: meta });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n },\n title,\n metadata\n );\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result.metadata).toEqual(metadata);\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, result.id);\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('getMeeting', () => {\n it('should retrieve a meeting by ID', async () => {\n // First create a meeting\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n // Then retrieve it\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting.id).toBe(created.id);\n expect(meeting.title).toBe(title);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should include segments when requested', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id, include_segments: true });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting).toHaveProperty('segments');\n expect(Array.isArray(meeting.segments)).toBe(true);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should handle non-existent meeting gracefully', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: 'non-existent-id-12345' });\n return { error: null };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.error).not.toBeNull();\n });\n });\n\n describe('stopMeeting', () => {\n it('should stop an active meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const stopped = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.stopMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!stopped.error) {\n expect(stopped.id).toBe(created.id);\n expect(['stopped', 'completed']).toContain(stopped.state);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n });\n\n describe('deleteMeeting', () => {\n it('should delete a meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const deleted = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.deleteMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (typeof deleted === 'boolean') {\n expect(deleted).toBe(true);\n }\n\n // Verify it's deleted\n const result = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: id });\n return { found: true };\n } catch {\n return { found: false };\n }\n }, created.id);\n\n expect(result.found).toBe(false);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/meetings.spec.ts"},"span":[7639,7642],"sourceCode":"/**\n * Meeting Operations E2E Tests\n *\n * Tests for meeting CRUD operations via Tauri IPC.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Meeting Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup: delete test meeting if created\n if (testMeetingId) {\n try {\n await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api?.deleteMeeting(meetingId);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('listMeetings', () => {\n it('should list meetings with default parameters', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result).toHaveProperty('total_count');\n expect(Array.isArray(result.meetings)).toBe(true);\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should support pagination', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 5, offset: 0 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result.meetings.length).toBeLessThanOrEqual(5);\n } else {\n expect(result).toBeDefined();\n }\n });\n\n it('should filter by state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ states: ['completed'], limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error && result.meetings) {\n // All returned meetings should be completed\n for (const meeting of result.meetings) {\n expect(meeting.state).toBe('completed');\n }\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('createMeeting', () => {\n it('should create a new meeting', async () => {\n const title = TestData.createMeetingTitle();\n\n const result = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result).toHaveProperty('title', title);\n expect(result).toHaveProperty('state');\n expect(result).toHaveProperty('created_at');\n testMeetingId = result.id;\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should create meeting with metadata', async () => {\n const title = TestData.createMeetingTitle();\n const metadata = { test_key: 'test_value', source: 'e2e-native' };\n\n const result = await browser.execute(\n async (meetingTitle, meta) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle, metadata: meta });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n },\n title,\n metadata\n );\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result.metadata).toEqual(metadata);\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, result.id);\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('getMeeting', () => {\n it('should retrieve a meeting by ID', async () => {\n // First create a meeting\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n // Then retrieve it\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting.id).toBe(created.id);\n expect(meeting.title).toBe(title);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should include segments when requested', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id, include_segments: true });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting).toHaveProperty('segments');\n expect(Array.isArray(meeting.segments)).toBe(true);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should handle non-existent meeting gracefully', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: 'non-existent-id-12345' });\n return { error: null };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.error).not.toBeNull();\n });\n });\n\n describe('stopMeeting', () => {\n it('should stop an active meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const stopped = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.stopMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!stopped.error) {\n expect(stopped.id).toBe(created.id);\n expect(['stopped', 'completed']).toContain(stopped.state);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n });\n\n describe('deleteMeeting', () => {\n it('should delete a meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const deleted = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.deleteMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (typeof deleted === 'boolean') {\n expect(deleted).toBe(true);\n }\n\n // Verify it's deleted\n const result = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: id });\n return { found: true };\n } catch {\n return { found: false };\n }\n }, created.id);\n\n expect(result.found).toBe(false);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/meetings.spec.ts"},"span":[8062,8065],"sourceCode":"/**\n * Meeting Operations E2E Tests\n *\n * Tests for meeting CRUD operations via Tauri IPC.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Meeting Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup: delete test meeting if created\n if (testMeetingId) {\n try {\n await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api?.deleteMeeting(meetingId);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('listMeetings', () => {\n it('should list meetings with default parameters', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result).toHaveProperty('total_count');\n expect(Array.isArray(result.meetings)).toBe(true);\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should support pagination', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 5, offset: 0 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result.meetings.length).toBeLessThanOrEqual(5);\n } else {\n expect(result).toBeDefined();\n }\n });\n\n it('should filter by state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ states: ['completed'], limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error && result.meetings) {\n // All returned meetings should be completed\n for (const meeting of result.meetings) {\n expect(meeting.state).toBe('completed');\n }\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('createMeeting', () => {\n it('should create a new meeting', async () => {\n const title = TestData.createMeetingTitle();\n\n const result = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result).toHaveProperty('title', title);\n expect(result).toHaveProperty('state');\n expect(result).toHaveProperty('created_at');\n testMeetingId = result.id;\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should create meeting with metadata', async () => {\n const title = TestData.createMeetingTitle();\n const metadata = { test_key: 'test_value', source: 'e2e-native' };\n\n const result = await browser.execute(\n async (meetingTitle, meta) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle, metadata: meta });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n },\n title,\n metadata\n );\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result.metadata).toEqual(metadata);\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, result.id);\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('getMeeting', () => {\n it('should retrieve a meeting by ID', async () => {\n // First create a meeting\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n // Then retrieve it\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting.id).toBe(created.id);\n expect(meeting.title).toBe(title);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should include segments when requested', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id, include_segments: true });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting).toHaveProperty('segments');\n expect(Array.isArray(meeting.segments)).toBe(true);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should handle non-existent meeting gracefully', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: 'non-existent-id-12345' });\n return { error: null };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.error).not.toBeNull();\n });\n });\n\n describe('stopMeeting', () => {\n it('should stop an active meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const stopped = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.stopMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!stopped.error) {\n expect(stopped.id).toBe(created.id);\n expect(['stopped', 'completed']).toContain(stopped.state);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n });\n\n describe('deleteMeeting', () => {\n it('should delete a meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const deleted = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.deleteMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (typeof deleted === 'boolean') {\n expect(deleted).toBe(true);\n }\n\n // Verify it's deleted\n const result = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: id });\n return { found: true };\n } catch {\n return { found: false };\n }\n }, created.id);\n\n expect(result.found).toBe(false);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/meetings.spec.ts"},"span":[8511,8514],"sourceCode":"/**\n * Meeting Operations E2E Tests\n *\n * Tests for meeting CRUD operations via Tauri IPC.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Meeting Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup: delete test meeting if created\n if (testMeetingId) {\n try {\n await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api?.deleteMeeting(meetingId);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('listMeetings', () => {\n it('should list meetings with default parameters', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result).toHaveProperty('total_count');\n expect(Array.isArray(result.meetings)).toBe(true);\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should support pagination', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 5, offset: 0 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result.meetings.length).toBeLessThanOrEqual(5);\n } else {\n expect(result).toBeDefined();\n }\n });\n\n it('should filter by state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ states: ['completed'], limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error && result.meetings) {\n // All returned meetings should be completed\n for (const meeting of result.meetings) {\n expect(meeting.state).toBe('completed');\n }\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('createMeeting', () => {\n it('should create a new meeting', async () => {\n const title = TestData.createMeetingTitle();\n\n const result = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result).toHaveProperty('title', title);\n expect(result).toHaveProperty('state');\n expect(result).toHaveProperty('created_at');\n testMeetingId = result.id;\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should create meeting with metadata', async () => {\n const title = TestData.createMeetingTitle();\n const metadata = { test_key: 'test_value', source: 'e2e-native' };\n\n const result = await browser.execute(\n async (meetingTitle, meta) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle, metadata: meta });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n },\n title,\n metadata\n );\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result.metadata).toEqual(metadata);\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, result.id);\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('getMeeting', () => {\n it('should retrieve a meeting by ID', async () => {\n // First create a meeting\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n // Then retrieve it\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting.id).toBe(created.id);\n expect(meeting.title).toBe(title);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should include segments when requested', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id, include_segments: true });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting).toHaveProperty('segments');\n expect(Array.isArray(meeting.segments)).toBe(true);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should handle non-existent meeting gracefully', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: 'non-existent-id-12345' });\n return { error: null };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.error).not.toBeNull();\n });\n });\n\n describe('stopMeeting', () => {\n it('should stop an active meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const stopped = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.stopMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!stopped.error) {\n expect(stopped.id).toBe(created.id);\n expect(['stopped', 'completed']).toContain(stopped.state);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n });\n\n describe('deleteMeeting', () => {\n it('should delete a meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const deleted = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.deleteMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (typeof deleted === 'boolean') {\n expect(deleted).toBe(true);\n }\n\n // Verify it's deleted\n const result = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: id });\n return { found: true };\n } catch {\n return { found: false };\n }\n }, created.id);\n\n expect(result.found).toBe(false);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/meetings.spec.ts"},"span":[8844,8847],"sourceCode":"/**\n * Meeting Operations E2E Tests\n *\n * Tests for meeting CRUD operations via Tauri IPC.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Meeting Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup: delete test meeting if created\n if (testMeetingId) {\n try {\n await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api?.deleteMeeting(meetingId);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('listMeetings', () => {\n it('should list meetings with default parameters', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result).toHaveProperty('total_count');\n expect(Array.isArray(result.meetings)).toBe(true);\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should support pagination', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 5, offset: 0 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result.meetings.length).toBeLessThanOrEqual(5);\n } else {\n expect(result).toBeDefined();\n }\n });\n\n it('should filter by state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ states: ['completed'], limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error && result.meetings) {\n // All returned meetings should be completed\n for (const meeting of result.meetings) {\n expect(meeting.state).toBe('completed');\n }\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('createMeeting', () => {\n it('should create a new meeting', async () => {\n const title = TestData.createMeetingTitle();\n\n const result = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result).toHaveProperty('title', title);\n expect(result).toHaveProperty('state');\n expect(result).toHaveProperty('created_at');\n testMeetingId = result.id;\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should create meeting with metadata', async () => {\n const title = TestData.createMeetingTitle();\n const metadata = { test_key: 'test_value', source: 'e2e-native' };\n\n const result = await browser.execute(\n async (meetingTitle, meta) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle, metadata: meta });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n },\n title,\n metadata\n );\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result.metadata).toEqual(metadata);\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, result.id);\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('getMeeting', () => {\n it('should retrieve a meeting by ID', async () => {\n // First create a meeting\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n // Then retrieve it\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting.id).toBe(created.id);\n expect(meeting.title).toBe(title);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should include segments when requested', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id, include_segments: true });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting).toHaveProperty('segments');\n expect(Array.isArray(meeting.segments)).toBe(true);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should handle non-existent meeting gracefully', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: 'non-existent-id-12345' });\n return { error: null };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.error).not.toBeNull();\n });\n });\n\n describe('stopMeeting', () => {\n it('should stop an active meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const stopped = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.stopMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!stopped.error) {\n expect(stopped.id).toBe(created.id);\n expect(['stopped', 'completed']).toContain(stopped.state);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n });\n\n describe('deleteMeeting', () => {\n it('should delete a meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const deleted = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.deleteMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (typeof deleted === 'boolean') {\n expect(deleted).toBe(true);\n }\n\n // Verify it's deleted\n const result = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: id });\n return { found: true };\n } catch {\n return { found: false };\n }\n }, created.id);\n\n expect(result.found).toBe(false);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/meetings.spec.ts"},"span":[9267,9270],"sourceCode":"/**\n * Meeting Operations E2E Tests\n *\n * Tests for meeting CRUD operations via Tauri IPC.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Meeting Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup: delete test meeting if created\n if (testMeetingId) {\n try {\n await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api?.deleteMeeting(meetingId);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('listMeetings', () => {\n it('should list meetings with default parameters', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result).toHaveProperty('total_count');\n expect(Array.isArray(result.meetings)).toBe(true);\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should support pagination', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ limit: 5, offset: 0 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error) {\n expect(result).toHaveProperty('meetings');\n expect(result.meetings.length).toBeLessThanOrEqual(5);\n } else {\n expect(result).toBeDefined();\n }\n });\n\n it('should filter by state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.listMeetings({ states: ['completed'], limit: 10 });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (!result.error && result.meetings) {\n // All returned meetings should be completed\n for (const meeting of result.meetings) {\n expect(meeting.state).toBe('completed');\n }\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('createMeeting', () => {\n it('should create a new meeting', async () => {\n const title = TestData.createMeetingTitle();\n\n const result = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result).toHaveProperty('title', title);\n expect(result).toHaveProperty('state');\n expect(result).toHaveProperty('created_at');\n testMeetingId = result.id;\n } else {\n // Server not connected - test should pass gracefully\n expect(result).toBeDefined();\n }\n });\n\n it('should create meeting with metadata', async () => {\n const title = TestData.createMeetingTitle();\n const metadata = { test_key: 'test_value', source: 'e2e-native' };\n\n const result = await browser.execute(\n async (meetingTitle, meta) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle, metadata: meta });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n },\n title,\n metadata\n );\n\n if (!result.error && result.id) {\n expect(result).toHaveProperty('id');\n expect(result.metadata).toEqual(metadata);\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, result.id);\n } else {\n expect(result).toBeDefined();\n }\n });\n });\n\n describe('getMeeting', () => {\n it('should retrieve a meeting by ID', async () => {\n // First create a meeting\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n // Then retrieve it\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting.id).toBe(created.id);\n expect(meeting.title).toBe(title);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should include segments when requested', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const meeting = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.getMeeting({ meeting_id: id, include_segments: true });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!meeting.error) {\n expect(meeting).toHaveProperty('segments');\n expect(Array.isArray(meeting.segments)).toBe(true);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n\n it('should handle non-existent meeting gracefully', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: 'non-existent-id-12345' });\n return { error: null };\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.error).not.toBeNull();\n });\n });\n\n describe('stopMeeting', () => {\n it('should stop an active meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const stopped = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.stopMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (!stopped.error) {\n expect(stopped.id).toBe(created.id);\n expect(['stopped', 'completed']).toContain(stopped.state);\n }\n\n // Cleanup\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteMeeting(id);\n }, created.id);\n });\n });\n\n describe('deleteMeeting', () => {\n it('should delete a meeting', async () => {\n const title = TestData.createMeetingTitle();\n const created = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (created.error || !created.id) {\n expect(created).toBeDefined();\n return;\n }\n\n const deleted = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.deleteMeeting(id);\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, created.id);\n\n if (typeof deleted === 'boolean') {\n expect(deleted).toBe(true);\n }\n\n // Verify it's deleted\n const result = await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.getMeeting({ meeting_id: id });\n return { found: true };\n } catch {\n return { found: false };\n }\n }, created.id);\n\n expect(result.found).toBe(false);\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/observability.spec.ts"},"span":[398,401],"sourceCode":"/**\n * Observability E2E Tests\n *\n * Tests for logs and performance metrics.\n */\n\nimport { waitForAppReady } from './fixtures';\n\ndescribe('Observability', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getRecentLogs', () => {\n it('should retrieve recent logs', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getRecentLogs({ limit: 50 });\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('logs');\n expect(Array.isArray(result.logs)).toBe(true);\n }\n });\n\n it('should filter logs by level', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getRecentLogs({ limit: 20, level: 'error' });\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n\n it('should filter logs by source', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getRecentLogs({ limit: 20, source: 'grpc' });\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n\n it('should respect limit parameter', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getRecentLogs({ limit: 5 });\n return { success: true, logs: response.logs, count: response.logs?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.count).toBeLessThanOrEqual(5);\n }\n });\n });\n\n describe('getPerformanceMetrics', () => {\n it('should retrieve performance metrics', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getPerformanceMetrics({ history_limit: 10 });\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('current');\n expect(result).toHaveProperty('history');\n }\n });\n\n it('should include current CPU and memory metrics', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getPerformanceMetrics({});\n return { success: true, current: response.current };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success && result.current) {\n expect(result.current).toHaveProperty('cpu_percent');\n expect(result.current).toHaveProperty('memory_percent');\n expect(typeof result.current.cpu_percent).toBe('number');\n expect(typeof result.current.memory_percent).toBe('number');\n }\n });\n\n it('should include historical metrics', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getPerformanceMetrics({ history_limit: 5 });\n return { success: true, history: response.history, count: response.history?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.history)).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/observability.spec.ts"},"span":[1005,1008],"sourceCode":"/**\n * Observability E2E Tests\n *\n * Tests for logs and performance metrics.\n */\n\nimport { waitForAppReady } from './fixtures';\n\ndescribe('Observability', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getRecentLogs', () => {\n it('should retrieve recent logs', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getRecentLogs({ limit: 50 });\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('logs');\n expect(Array.isArray(result.logs)).toBe(true);\n }\n });\n\n it('should filter logs by level', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getRecentLogs({ limit: 20, level: 'error' });\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n\n it('should filter logs by source', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getRecentLogs({ limit: 20, source: 'grpc' });\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n\n it('should respect limit parameter', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getRecentLogs({ limit: 5 });\n return { success: true, logs: response.logs, count: response.logs?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.count).toBeLessThanOrEqual(5);\n }\n });\n });\n\n describe('getPerformanceMetrics', () => {\n it('should retrieve performance metrics', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getPerformanceMetrics({ history_limit: 10 });\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('current');\n expect(result).toHaveProperty('history');\n }\n });\n\n it('should include current CPU and memory metrics', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getPerformanceMetrics({});\n return { success: true, current: response.current };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success && result.current) {\n expect(result.current).toHaveProperty('cpu_percent');\n expect(result.current).toHaveProperty('memory_percent');\n expect(typeof result.current.cpu_percent).toBe('number');\n expect(typeof result.current.memory_percent).toBe('number');\n }\n });\n\n it('should include historical metrics', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getPerformanceMetrics({ history_limit: 5 });\n return { success: true, history: response.history, count: response.history?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.history)).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/observability.spec.ts"},"span":[4078,4081],"sourceCode":"/**\n * Observability E2E Tests\n *\n * Tests for logs and performance metrics.\n */\n\nimport { waitForAppReady } from './fixtures';\n\ndescribe('Observability', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getRecentLogs', () => {\n it('should retrieve recent logs', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getRecentLogs({ limit: 50 });\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('logs');\n expect(Array.isArray(result.logs)).toBe(true);\n }\n });\n\n it('should filter logs by level', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getRecentLogs({ limit: 20, level: 'error' });\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n\n it('should filter logs by source', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getRecentLogs({ limit: 20, source: 'grpc' });\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n\n it('should respect limit parameter', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getRecentLogs({ limit: 5 });\n return { success: true, logs: response.logs, count: response.logs?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.count).toBeLessThanOrEqual(5);\n }\n });\n });\n\n describe('getPerformanceMetrics', () => {\n it('should retrieve performance metrics', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getPerformanceMetrics({ history_limit: 10 });\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('current');\n expect(result).toHaveProperty('history');\n }\n });\n\n it('should include current CPU and memory metrics', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getPerformanceMetrics({});\n return { success: true, current: response.current };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success && result.current) {\n expect(result.current).toHaveProperty('cpu_percent');\n expect(result.current).toHaveProperty('memory_percent');\n expect(typeof result.current.cpu_percent).toBe('number');\n expect(typeof result.current.memory_percent).toBe('number');\n }\n });\n\n it('should include historical metrics', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getPerformanceMetrics({ history_limit: 5 });\n return { success: true, history: response.history, count: response.history?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.history)).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/observability.spec.ts"},"span":[1491,1494],"sourceCode":"/**\n * Observability E2E Tests\n *\n * Tests for logs and performance metrics.\n */\n\nimport { waitForAppReady } from './fixtures';\n\ndescribe('Observability', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getRecentLogs', () => {\n it('should retrieve recent logs', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getRecentLogs({ limit: 50 });\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('logs');\n expect(Array.isArray(result.logs)).toBe(true);\n }\n });\n\n it('should filter logs by level', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getRecentLogs({ limit: 20, level: 'error' });\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n\n it('should filter logs by source', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getRecentLogs({ limit: 20, source: 'grpc' });\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n\n it('should respect limit parameter', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getRecentLogs({ limit: 5 });\n return { success: true, logs: response.logs, count: response.logs?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.count).toBeLessThanOrEqual(5);\n }\n });\n });\n\n describe('getPerformanceMetrics', () => {\n it('should retrieve performance metrics', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getPerformanceMetrics({ history_limit: 10 });\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('current');\n expect(result).toHaveProperty('history');\n }\n });\n\n it('should include current CPU and memory metrics', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getPerformanceMetrics({});\n return { success: true, current: response.current };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success && result.current) {\n expect(result.current).toHaveProperty('cpu_percent');\n expect(result.current).toHaveProperty('memory_percent');\n expect(typeof result.current.cpu_percent).toBe('number');\n expect(typeof result.current.memory_percent).toBe('number');\n }\n });\n\n it('should include historical metrics', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getPerformanceMetrics({ history_limit: 5 });\n return { success: true, history: response.history, count: response.history?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.history)).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/observability.spec.ts"},"span":[1979,1982],"sourceCode":"/**\n * Observability E2E Tests\n *\n * Tests for logs and performance metrics.\n */\n\nimport { waitForAppReady } from './fixtures';\n\ndescribe('Observability', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getRecentLogs', () => {\n it('should retrieve recent logs', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getRecentLogs({ limit: 50 });\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('logs');\n expect(Array.isArray(result.logs)).toBe(true);\n }\n });\n\n it('should filter logs by level', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getRecentLogs({ limit: 20, level: 'error' });\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n\n it('should filter logs by source', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getRecentLogs({ limit: 20, source: 'grpc' });\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n\n it('should respect limit parameter', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getRecentLogs({ limit: 5 });\n return { success: true, logs: response.logs, count: response.logs?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.count).toBeLessThanOrEqual(5);\n }\n });\n });\n\n describe('getPerformanceMetrics', () => {\n it('should retrieve performance metrics', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getPerformanceMetrics({ history_limit: 10 });\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('current');\n expect(result).toHaveProperty('history');\n }\n });\n\n it('should include current CPU and memory metrics', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getPerformanceMetrics({});\n return { success: true, current: response.current };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success && result.current) {\n expect(result.current).toHaveProperty('cpu_percent');\n expect(result.current).toHaveProperty('memory_percent');\n expect(typeof result.current.cpu_percent).toBe('number');\n expect(typeof result.current.memory_percent).toBe('number');\n }\n });\n\n it('should include historical metrics', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getPerformanceMetrics({ history_limit: 5 });\n return { success: true, history: response.history, count: response.history?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.history)).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/observability.spec.ts"},"span":[2637,2640],"sourceCode":"/**\n * Observability E2E Tests\n *\n * Tests for logs and performance metrics.\n */\n\nimport { waitForAppReady } from './fixtures';\n\ndescribe('Observability', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getRecentLogs', () => {\n it('should retrieve recent logs', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getRecentLogs({ limit: 50 });\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('logs');\n expect(Array.isArray(result.logs)).toBe(true);\n }\n });\n\n it('should filter logs by level', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getRecentLogs({ limit: 20, level: 'error' });\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n\n it('should filter logs by source', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getRecentLogs({ limit: 20, source: 'grpc' });\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n\n it('should respect limit parameter', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getRecentLogs({ limit: 5 });\n return { success: true, logs: response.logs, count: response.logs?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.count).toBeLessThanOrEqual(5);\n }\n });\n });\n\n describe('getPerformanceMetrics', () => {\n it('should retrieve performance metrics', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getPerformanceMetrics({ history_limit: 10 });\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('current');\n expect(result).toHaveProperty('history');\n }\n });\n\n it('should include current CPU and memory metrics', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getPerformanceMetrics({});\n return { success: true, current: response.current };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success && result.current) {\n expect(result.current).toHaveProperty('cpu_percent');\n expect(result.current).toHaveProperty('memory_percent');\n expect(typeof result.current.cpu_percent).toBe('number');\n expect(typeof result.current.memory_percent).toBe('number');\n }\n });\n\n it('should include historical metrics', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getPerformanceMetrics({ history_limit: 5 });\n return { success: true, history: response.history, count: response.history?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.history)).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/observability.spec.ts"},"span":[3276,3279],"sourceCode":"/**\n * Observability E2E Tests\n *\n * Tests for logs and performance metrics.\n */\n\nimport { waitForAppReady } from './fixtures';\n\ndescribe('Observability', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getRecentLogs', () => {\n it('should retrieve recent logs', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getRecentLogs({ limit: 50 });\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('logs');\n expect(Array.isArray(result.logs)).toBe(true);\n }\n });\n\n it('should filter logs by level', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getRecentLogs({ limit: 20, level: 'error' });\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n\n it('should filter logs by source', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getRecentLogs({ limit: 20, source: 'grpc' });\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n\n it('should respect limit parameter', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getRecentLogs({ limit: 5 });\n return { success: true, logs: response.logs, count: response.logs?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.count).toBeLessThanOrEqual(5);\n }\n });\n });\n\n describe('getPerformanceMetrics', () => {\n it('should retrieve performance metrics', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getPerformanceMetrics({ history_limit: 10 });\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('current');\n expect(result).toHaveProperty('history');\n }\n });\n\n it('should include current CPU and memory metrics', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getPerformanceMetrics({});\n return { success: true, current: response.current };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success && result.current) {\n expect(result.current).toHaveProperty('cpu_percent');\n expect(result.current).toHaveProperty('memory_percent');\n expect(typeof result.current.cpu_percent).toBe('number');\n expect(typeof result.current.memory_percent).toBe('number');\n }\n });\n\n it('should include historical metrics', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getPerformanceMetrics({ history_limit: 5 });\n return { success: true, history: response.history, count: response.history?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.history)).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/recording.spec.ts"},"span":[446,449],"sourceCode":"/**\n * Recording & Audio E2E Tests\n *\n * Tests for audio device management and recording functionality.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Audio Devices', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('listAudioDevices', () => {\n it('should list available audio devices', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const devices = await api.listAudioDevices();\n return { success: true, devices, count: devices?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Devices may or may not be available depending on system\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.devices)).toBe(true);\n console.log(`Found ${result.count} audio devices`);\n }\n });\n\n it('should return device info with required properties', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const devices = await api.listAudioDevices();\n if (devices && devices.length > 0) {\n const device = devices[0];\n return {\n success: true,\n hasId: 'id' in device || 'device_id' in device,\n hasName: 'name' in device || 'device_name' in device,\n sample: device,\n };\n }\n return { success: true, noDevices: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (result.success && !result.noDevices) {\n expect(result.hasId || result.hasName).toBe(true);\n }\n });\n });\n\n describe('getDefaultAudioDevice', () => {\n it('should get default input device', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const device = await api.getDefaultAudioDevice(true);\n return { success: true, device };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n // May be null if no default device\n if (result.success && result.device) {\n console.log('Default input device:', result.device);\n }\n });\n\n it('should get default output device', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const device = await api.getDefaultAudioDevice(false);\n return { success: true, device };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n\ndescribe('Recording Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.stopMeeting(id);\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('startTranscription', () => {\n it('should start transcription for a meeting', async () => {\n // Create a meeting first\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n // Start transcription\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const stream = await api.startTranscription(meetingId);\n return { success: true, hasStream: !!stream };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n // May fail if no audio device available\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.hasStream).toBe(true);\n }\n\n // Stop the recording\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.stopMeeting(id);\n } catch {\n // May fail\n }\n }, meeting.id);\n });\n });\n});\n\ndescribe('Playback Operations', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getPlaybackState', () => {\n it('should return playback state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const state = await api.getPlaybackState();\n return { success: true, state };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.state).toHaveProperty('is_playing');\n }\n });\n });\n\n describe('playback controls', () => {\n it('should handle pausePlayback when nothing playing', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.pausePlayback();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should not throw even if nothing is playing\n expect(result).toBeDefined();\n });\n\n it('should handle stopPlayback when nothing playing', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.stopPlayback();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/recording.spec.ts"},"span":[1171,1174],"sourceCode":"/**\n * Recording & Audio E2E Tests\n *\n * Tests for audio device management and recording functionality.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Audio Devices', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('listAudioDevices', () => {\n it('should list available audio devices', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const devices = await api.listAudioDevices();\n return { success: true, devices, count: devices?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Devices may or may not be available depending on system\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.devices)).toBe(true);\n console.log(`Found ${result.count} audio devices`);\n }\n });\n\n it('should return device info with required properties', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const devices = await api.listAudioDevices();\n if (devices && devices.length > 0) {\n const device = devices[0];\n return {\n success: true,\n hasId: 'id' in device || 'device_id' in device,\n hasName: 'name' in device || 'device_name' in device,\n sample: device,\n };\n }\n return { success: true, noDevices: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (result.success && !result.noDevices) {\n expect(result.hasId || result.hasName).toBe(true);\n }\n });\n });\n\n describe('getDefaultAudioDevice', () => {\n it('should get default input device', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const device = await api.getDefaultAudioDevice(true);\n return { success: true, device };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n // May be null if no default device\n if (result.success && result.device) {\n console.log('Default input device:', result.device);\n }\n });\n\n it('should get default output device', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const device = await api.getDefaultAudioDevice(false);\n return { success: true, device };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n\ndescribe('Recording Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.stopMeeting(id);\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('startTranscription', () => {\n it('should start transcription for a meeting', async () => {\n // Create a meeting first\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n // Start transcription\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const stream = await api.startTranscription(meetingId);\n return { success: true, hasStream: !!stream };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n // May fail if no audio device available\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.hasStream).toBe(true);\n }\n\n // Stop the recording\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.stopMeeting(id);\n } catch {\n // May fail\n }\n }, meeting.id);\n });\n });\n});\n\ndescribe('Playback Operations', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getPlaybackState', () => {\n it('should return playback state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const state = await api.getPlaybackState();\n return { success: true, state };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.state).toHaveProperty('is_playing');\n }\n });\n });\n\n describe('playback controls', () => {\n it('should handle pausePlayback when nothing playing', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.pausePlayback();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should not throw even if nothing is playing\n expect(result).toBeDefined();\n });\n\n it('should handle stopPlayback when nothing playing', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.stopPlayback();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/recording.spec.ts"},"span":[2090,2093],"sourceCode":"/**\n * Recording & Audio E2E Tests\n *\n * Tests for audio device management and recording functionality.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Audio Devices', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('listAudioDevices', () => {\n it('should list available audio devices', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const devices = await api.listAudioDevices();\n return { success: true, devices, count: devices?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Devices may or may not be available depending on system\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.devices)).toBe(true);\n console.log(`Found ${result.count} audio devices`);\n }\n });\n\n it('should return device info with required properties', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const devices = await api.listAudioDevices();\n if (devices && devices.length > 0) {\n const device = devices[0];\n return {\n success: true,\n hasId: 'id' in device || 'device_id' in device,\n hasName: 'name' in device || 'device_name' in device,\n sample: device,\n };\n }\n return { success: true, noDevices: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (result.success && !result.noDevices) {\n expect(result.hasId || result.hasName).toBe(true);\n }\n });\n });\n\n describe('getDefaultAudioDevice', () => {\n it('should get default input device', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const device = await api.getDefaultAudioDevice(true);\n return { success: true, device };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n // May be null if no default device\n if (result.success && result.device) {\n console.log('Default input device:', result.device);\n }\n });\n\n it('should get default output device', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const device = await api.getDefaultAudioDevice(false);\n return { success: true, device };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n\ndescribe('Recording Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.stopMeeting(id);\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('startTranscription', () => {\n it('should start transcription for a meeting', async () => {\n // Create a meeting first\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n // Start transcription\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const stream = await api.startTranscription(meetingId);\n return { success: true, hasStream: !!stream };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n // May fail if no audio device available\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.hasStream).toBe(true);\n }\n\n // Stop the recording\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.stopMeeting(id);\n } catch {\n // May fail\n }\n }, meeting.id);\n });\n });\n});\n\ndescribe('Playback Operations', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getPlaybackState', () => {\n it('should return playback state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const state = await api.getPlaybackState();\n return { success: true, state };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.state).toHaveProperty('is_playing');\n }\n });\n });\n\n describe('playback controls', () => {\n it('should handle pausePlayback when nothing playing', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.pausePlayback();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should not throw even if nothing is playing\n expect(result).toBeDefined();\n });\n\n it('should handle stopPlayback when nothing playing', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.stopPlayback();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/recording.spec.ts"},"span":[2712,2715],"sourceCode":"/**\n * Recording & Audio E2E Tests\n *\n * Tests for audio device management and recording functionality.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Audio Devices', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('listAudioDevices', () => {\n it('should list available audio devices', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const devices = await api.listAudioDevices();\n return { success: true, devices, count: devices?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Devices may or may not be available depending on system\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.devices)).toBe(true);\n console.log(`Found ${result.count} audio devices`);\n }\n });\n\n it('should return device info with required properties', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const devices = await api.listAudioDevices();\n if (devices && devices.length > 0) {\n const device = devices[0];\n return {\n success: true,\n hasId: 'id' in device || 'device_id' in device,\n hasName: 'name' in device || 'device_name' in device,\n sample: device,\n };\n }\n return { success: true, noDevices: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (result.success && !result.noDevices) {\n expect(result.hasId || result.hasName).toBe(true);\n }\n });\n });\n\n describe('getDefaultAudioDevice', () => {\n it('should get default input device', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const device = await api.getDefaultAudioDevice(true);\n return { success: true, device };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n // May be null if no default device\n if (result.success && result.device) {\n console.log('Default input device:', result.device);\n }\n });\n\n it('should get default output device', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const device = await api.getDefaultAudioDevice(false);\n return { success: true, device };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n\ndescribe('Recording Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.stopMeeting(id);\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('startTranscription', () => {\n it('should start transcription for a meeting', async () => {\n // Create a meeting first\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n // Start transcription\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const stream = await api.startTranscription(meetingId);\n return { success: true, hasStream: !!stream };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n // May fail if no audio device available\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.hasStream).toBe(true);\n }\n\n // Stop the recording\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.stopMeeting(id);\n } catch {\n // May fail\n }\n }, meeting.id);\n });\n });\n});\n\ndescribe('Playback Operations', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getPlaybackState', () => {\n it('should return playback state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const state = await api.getPlaybackState();\n return { success: true, state };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.state).toHaveProperty('is_playing');\n }\n });\n });\n\n describe('playback controls', () => {\n it('should handle pausePlayback when nothing playing', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.pausePlayback();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should not throw even if nothing is playing\n expect(result).toBeDefined();\n });\n\n it('should handle stopPlayback when nothing playing', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.stopPlayback();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/recording.spec.ts"},"span":[3341,3344],"sourceCode":"/**\n * Recording & Audio E2E Tests\n *\n * Tests for audio device management and recording functionality.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Audio Devices', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('listAudioDevices', () => {\n it('should list available audio devices', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const devices = await api.listAudioDevices();\n return { success: true, devices, count: devices?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Devices may or may not be available depending on system\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.devices)).toBe(true);\n console.log(`Found ${result.count} audio devices`);\n }\n });\n\n it('should return device info with required properties', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const devices = await api.listAudioDevices();\n if (devices && devices.length > 0) {\n const device = devices[0];\n return {\n success: true,\n hasId: 'id' in device || 'device_id' in device,\n hasName: 'name' in device || 'device_name' in device,\n sample: device,\n };\n }\n return { success: true, noDevices: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (result.success && !result.noDevices) {\n expect(result.hasId || result.hasName).toBe(true);\n }\n });\n });\n\n describe('getDefaultAudioDevice', () => {\n it('should get default input device', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const device = await api.getDefaultAudioDevice(true);\n return { success: true, device };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n // May be null if no default device\n if (result.success && result.device) {\n console.log('Default input device:', result.device);\n }\n });\n\n it('should get default output device', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const device = await api.getDefaultAudioDevice(false);\n return { success: true, device };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n\ndescribe('Recording Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.stopMeeting(id);\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('startTranscription', () => {\n it('should start transcription for a meeting', async () => {\n // Create a meeting first\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n // Start transcription\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const stream = await api.startTranscription(meetingId);\n return { success: true, hasStream: !!stream };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n // May fail if no audio device available\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.hasStream).toBe(true);\n }\n\n // Stop the recording\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.stopMeeting(id);\n } catch {\n // May fail\n }\n }, meeting.id);\n });\n });\n});\n\ndescribe('Playback Operations', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getPlaybackState', () => {\n it('should return playback state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const state = await api.getPlaybackState();\n return { success: true, state };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.state).toHaveProperty('is_playing');\n }\n });\n });\n\n describe('playback controls', () => {\n it('should handle pausePlayback when nothing playing', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.pausePlayback();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should not throw even if nothing is playing\n expect(result).toBeDefined();\n });\n\n it('should handle stopPlayback when nothing playing', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.stopPlayback();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/recording.spec.ts"},"span":[3827,3830],"sourceCode":"/**\n * Recording & Audio E2E Tests\n *\n * Tests for audio device management and recording functionality.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Audio Devices', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('listAudioDevices', () => {\n it('should list available audio devices', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const devices = await api.listAudioDevices();\n return { success: true, devices, count: devices?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Devices may or may not be available depending on system\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.devices)).toBe(true);\n console.log(`Found ${result.count} audio devices`);\n }\n });\n\n it('should return device info with required properties', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const devices = await api.listAudioDevices();\n if (devices && devices.length > 0) {\n const device = devices[0];\n return {\n success: true,\n hasId: 'id' in device || 'device_id' in device,\n hasName: 'name' in device || 'device_name' in device,\n sample: device,\n };\n }\n return { success: true, noDevices: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (result.success && !result.noDevices) {\n expect(result.hasId || result.hasName).toBe(true);\n }\n });\n });\n\n describe('getDefaultAudioDevice', () => {\n it('should get default input device', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const device = await api.getDefaultAudioDevice(true);\n return { success: true, device };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n // May be null if no default device\n if (result.success && result.device) {\n console.log('Default input device:', result.device);\n }\n });\n\n it('should get default output device', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const device = await api.getDefaultAudioDevice(false);\n return { success: true, device };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n\ndescribe('Recording Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.stopMeeting(id);\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('startTranscription', () => {\n it('should start transcription for a meeting', async () => {\n // Create a meeting first\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n // Start transcription\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const stream = await api.startTranscription(meetingId);\n return { success: true, hasStream: !!stream };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n // May fail if no audio device available\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.hasStream).toBe(true);\n }\n\n // Stop the recording\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.stopMeeting(id);\n } catch {\n // May fail\n }\n }, meeting.id);\n });\n });\n});\n\ndescribe('Playback Operations', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getPlaybackState', () => {\n it('should return playback state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const state = await api.getPlaybackState();\n return { success: true, state };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.state).toHaveProperty('is_playing');\n }\n });\n });\n\n describe('playback controls', () => {\n it('should handle pausePlayback when nothing playing', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.pausePlayback();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should not throw even if nothing is playing\n expect(result).toBeDefined();\n });\n\n it('should handle stopPlayback when nothing playing', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.stopPlayback();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/recording.spec.ts"},"span":[4320,4323],"sourceCode":"/**\n * Recording & Audio E2E Tests\n *\n * Tests for audio device management and recording functionality.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Audio Devices', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('listAudioDevices', () => {\n it('should list available audio devices', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const devices = await api.listAudioDevices();\n return { success: true, devices, count: devices?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Devices may or may not be available depending on system\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.devices)).toBe(true);\n console.log(`Found ${result.count} audio devices`);\n }\n });\n\n it('should return device info with required properties', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const devices = await api.listAudioDevices();\n if (devices && devices.length > 0) {\n const device = devices[0];\n return {\n success: true,\n hasId: 'id' in device || 'device_id' in device,\n hasName: 'name' in device || 'device_name' in device,\n sample: device,\n };\n }\n return { success: true, noDevices: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (result.success && !result.noDevices) {\n expect(result.hasId || result.hasName).toBe(true);\n }\n });\n });\n\n describe('getDefaultAudioDevice', () => {\n it('should get default input device', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const device = await api.getDefaultAudioDevice(true);\n return { success: true, device };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n // May be null if no default device\n if (result.success && result.device) {\n console.log('Default input device:', result.device);\n }\n });\n\n it('should get default output device', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const device = await api.getDefaultAudioDevice(false);\n return { success: true, device };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n\ndescribe('Recording Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.stopMeeting(id);\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('startTranscription', () => {\n it('should start transcription for a meeting', async () => {\n // Create a meeting first\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n // Start transcription\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const stream = await api.startTranscription(meetingId);\n return { success: true, hasStream: !!stream };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n // May fail if no audio device available\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.hasStream).toBe(true);\n }\n\n // Stop the recording\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.stopMeeting(id);\n } catch {\n // May fail\n }\n }, meeting.id);\n });\n });\n});\n\ndescribe('Playback Operations', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getPlaybackState', () => {\n it('should return playback state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const state = await api.getPlaybackState();\n return { success: true, state };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.state).toHaveProperty('is_playing');\n }\n });\n });\n\n describe('playback controls', () => {\n it('should handle pausePlayback when nothing playing', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.pausePlayback();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should not throw even if nothing is playing\n expect(result).toBeDefined();\n });\n\n it('should handle stopPlayback when nothing playing', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.stopPlayback();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/recording.spec.ts"},"span":[4891,4894],"sourceCode":"/**\n * Recording & Audio E2E Tests\n *\n * Tests for audio device management and recording functionality.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Audio Devices', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('listAudioDevices', () => {\n it('should list available audio devices', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const devices = await api.listAudioDevices();\n return { success: true, devices, count: devices?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Devices may or may not be available depending on system\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.devices)).toBe(true);\n console.log(`Found ${result.count} audio devices`);\n }\n });\n\n it('should return device info with required properties', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const devices = await api.listAudioDevices();\n if (devices && devices.length > 0) {\n const device = devices[0];\n return {\n success: true,\n hasId: 'id' in device || 'device_id' in device,\n hasName: 'name' in device || 'device_name' in device,\n sample: device,\n };\n }\n return { success: true, noDevices: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (result.success && !result.noDevices) {\n expect(result.hasId || result.hasName).toBe(true);\n }\n });\n });\n\n describe('getDefaultAudioDevice', () => {\n it('should get default input device', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const device = await api.getDefaultAudioDevice(true);\n return { success: true, device };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n // May be null if no default device\n if (result.success && result.device) {\n console.log('Default input device:', result.device);\n }\n });\n\n it('should get default output device', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const device = await api.getDefaultAudioDevice(false);\n return { success: true, device };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n\ndescribe('Recording Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.stopMeeting(id);\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('startTranscription', () => {\n it('should start transcription for a meeting', async () => {\n // Create a meeting first\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n // Start transcription\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const stream = await api.startTranscription(meetingId);\n return { success: true, hasStream: !!stream };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n // May fail if no audio device available\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.hasStream).toBe(true);\n }\n\n // Stop the recording\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.stopMeeting(id);\n } catch {\n // May fail\n }\n }, meeting.id);\n });\n });\n});\n\ndescribe('Playback Operations', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getPlaybackState', () => {\n it('should return playback state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const state = await api.getPlaybackState();\n return { success: true, state };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.state).toHaveProperty('is_playing');\n }\n });\n });\n\n describe('playback controls', () => {\n it('should handle pausePlayback when nothing playing', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.pausePlayback();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should not throw even if nothing is playing\n expect(result).toBeDefined();\n });\n\n it('should handle stopPlayback when nothing playing', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.stopPlayback();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/recording.spec.ts"},"span":[5335,5338],"sourceCode":"/**\n * Recording & Audio E2E Tests\n *\n * Tests for audio device management and recording functionality.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Audio Devices', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('listAudioDevices', () => {\n it('should list available audio devices', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const devices = await api.listAudioDevices();\n return { success: true, devices, count: devices?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Devices may or may not be available depending on system\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.devices)).toBe(true);\n console.log(`Found ${result.count} audio devices`);\n }\n });\n\n it('should return device info with required properties', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const devices = await api.listAudioDevices();\n if (devices && devices.length > 0) {\n const device = devices[0];\n return {\n success: true,\n hasId: 'id' in device || 'device_id' in device,\n hasName: 'name' in device || 'device_name' in device,\n sample: device,\n };\n }\n return { success: true, noDevices: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (result.success && !result.noDevices) {\n expect(result.hasId || result.hasName).toBe(true);\n }\n });\n });\n\n describe('getDefaultAudioDevice', () => {\n it('should get default input device', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const device = await api.getDefaultAudioDevice(true);\n return { success: true, device };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n // May be null if no default device\n if (result.success && result.device) {\n console.log('Default input device:', result.device);\n }\n });\n\n it('should get default output device', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const device = await api.getDefaultAudioDevice(false);\n return { success: true, device };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n\ndescribe('Recording Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.stopMeeting(id);\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('startTranscription', () => {\n it('should start transcription for a meeting', async () => {\n // Create a meeting first\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n // Start transcription\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const stream = await api.startTranscription(meetingId);\n return { success: true, hasStream: !!stream };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n // May fail if no audio device available\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.hasStream).toBe(true);\n }\n\n // Stop the recording\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.stopMeeting(id);\n } catch {\n // May fail\n }\n }, meeting.id);\n });\n });\n});\n\ndescribe('Playback Operations', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getPlaybackState', () => {\n it('should return playback state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const state = await api.getPlaybackState();\n return { success: true, state };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.state).toHaveProperty('is_playing');\n }\n });\n });\n\n describe('playback controls', () => {\n it('should handle pausePlayback when nothing playing', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.pausePlayback();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should not throw even if nothing is playing\n expect(result).toBeDefined();\n });\n\n it('should handle stopPlayback when nothing playing', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.stopPlayback();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/recording.spec.ts"},"span":[5947,5950],"sourceCode":"/**\n * Recording & Audio E2E Tests\n *\n * Tests for audio device management and recording functionality.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Audio Devices', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('listAudioDevices', () => {\n it('should list available audio devices', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const devices = await api.listAudioDevices();\n return { success: true, devices, count: devices?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Devices may or may not be available depending on system\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.devices)).toBe(true);\n console.log(`Found ${result.count} audio devices`);\n }\n });\n\n it('should return device info with required properties', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const devices = await api.listAudioDevices();\n if (devices && devices.length > 0) {\n const device = devices[0];\n return {\n success: true,\n hasId: 'id' in device || 'device_id' in device,\n hasName: 'name' in device || 'device_name' in device,\n sample: device,\n };\n }\n return { success: true, noDevices: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (result.success && !result.noDevices) {\n expect(result.hasId || result.hasName).toBe(true);\n }\n });\n });\n\n describe('getDefaultAudioDevice', () => {\n it('should get default input device', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const device = await api.getDefaultAudioDevice(true);\n return { success: true, device };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n // May be null if no default device\n if (result.success && result.device) {\n console.log('Default input device:', result.device);\n }\n });\n\n it('should get default output device', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const device = await api.getDefaultAudioDevice(false);\n return { success: true, device };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n\ndescribe('Recording Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.stopMeeting(id);\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('startTranscription', () => {\n it('should start transcription for a meeting', async () => {\n // Create a meeting first\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n // Start transcription\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const stream = await api.startTranscription(meetingId);\n return { success: true, hasStream: !!stream };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n // May fail if no audio device available\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.hasStream).toBe(true);\n }\n\n // Stop the recording\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.stopMeeting(id);\n } catch {\n // May fail\n }\n }, meeting.id);\n });\n });\n});\n\ndescribe('Playback Operations', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getPlaybackState', () => {\n it('should return playback state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const state = await api.getPlaybackState();\n return { success: true, state };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.state).toHaveProperty('is_playing');\n }\n });\n });\n\n describe('playback controls', () => {\n it('should handle pausePlayback when nothing playing', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.pausePlayback();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should not throw even if nothing is playing\n expect(result).toBeDefined();\n });\n\n it('should handle stopPlayback when nothing playing', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.stopPlayback();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/recording.spec.ts"},"span":[6446,6449],"sourceCode":"/**\n * Recording & Audio E2E Tests\n *\n * Tests for audio device management and recording functionality.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Audio Devices', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('listAudioDevices', () => {\n it('should list available audio devices', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const devices = await api.listAudioDevices();\n return { success: true, devices, count: devices?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Devices may or may not be available depending on system\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.devices)).toBe(true);\n console.log(`Found ${result.count} audio devices`);\n }\n });\n\n it('should return device info with required properties', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const devices = await api.listAudioDevices();\n if (devices && devices.length > 0) {\n const device = devices[0];\n return {\n success: true,\n hasId: 'id' in device || 'device_id' in device,\n hasName: 'name' in device || 'device_name' in device,\n sample: device,\n };\n }\n return { success: true, noDevices: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (result.success && !result.noDevices) {\n expect(result.hasId || result.hasName).toBe(true);\n }\n });\n });\n\n describe('getDefaultAudioDevice', () => {\n it('should get default input device', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const device = await api.getDefaultAudioDevice(true);\n return { success: true, device };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n // May be null if no default device\n if (result.success && result.device) {\n console.log('Default input device:', result.device);\n }\n });\n\n it('should get default output device', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const device = await api.getDefaultAudioDevice(false);\n return { success: true, device };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n\ndescribe('Recording Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.stopMeeting(id);\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('startTranscription', () => {\n it('should start transcription for a meeting', async () => {\n // Create a meeting first\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n // Start transcription\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const stream = await api.startTranscription(meetingId);\n return { success: true, hasStream: !!stream };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n // May fail if no audio device available\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.hasStream).toBe(true);\n }\n\n // Stop the recording\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.stopMeeting(id);\n } catch {\n // May fail\n }\n }, meeting.id);\n });\n });\n});\n\ndescribe('Playback Operations', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getPlaybackState', () => {\n it('should return playback state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const state = await api.getPlaybackState();\n return { success: true, state };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.state).toHaveProperty('is_playing');\n }\n });\n });\n\n describe('playback controls', () => {\n it('should handle pausePlayback when nothing playing', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.pausePlayback();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should not throw even if nothing is playing\n expect(result).toBeDefined();\n });\n\n it('should handle stopPlayback when nothing playing', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.stopPlayback();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * Recording & Audio E2E Tests\n * expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.devices)).toBe(true);\n console.log(`Found ${result.count} audio devices`);\n }\n }); });\n});\n","ops":[{"diffOp":{"equal":{"range":[0,37]}}},{"equalLines":{"line_count":23}},{"diffOp":{"equal":{"range":[37,158]}}},{"diffOp":{"delete":{"range":[158,218]}}},{"diffOp":{"equal":{"range":[218,234]}}},{"equalLines":{"line_count":187}},{"diffOp":{"equal":{"range":[234,244]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/recording.spec.ts"},"span":[939,950],"sourceCode":"/**\n * Recording & Audio E2E Tests\n *\n * Tests for audio device management and recording functionality.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Audio Devices', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('listAudioDevices', () => {\n it('should list available audio devices', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const devices = await api.listAudioDevices();\n return { success: true, devices, count: devices?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Devices may or may not be available depending on system\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.devices)).toBe(true);\n console.log(`Found ${result.count} audio devices`);\n }\n });\n\n it('should return device info with required properties', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const devices = await api.listAudioDevices();\n if (devices && devices.length > 0) {\n const device = devices[0];\n return {\n success: true,\n hasId: 'id' in device || 'device_id' in device,\n hasName: 'name' in device || 'device_name' in device,\n sample: device,\n };\n }\n return { success: true, noDevices: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (result.success && !result.noDevices) {\n expect(result.hasId || result.hasName).toBe(true);\n }\n });\n });\n\n describe('getDefaultAudioDevice', () => {\n it('should get default input device', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const device = await api.getDefaultAudioDevice(true);\n return { success: true, device };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n // May be null if no default device\n if (result.success && result.device) {\n console.log('Default input device:', result.device);\n }\n });\n\n it('should get default output device', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const device = await api.getDefaultAudioDevice(false);\n return { success: true, device };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n\ndescribe('Recording Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.stopMeeting(id);\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('startTranscription', () => {\n it('should start transcription for a meeting', async () => {\n // Create a meeting first\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n // Start transcription\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const stream = await api.startTranscription(meetingId);\n return { success: true, hasStream: !!stream };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n // May fail if no audio device available\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.hasStream).toBe(true);\n }\n\n // Stop the recording\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.stopMeeting(id);\n } catch {\n // May fail\n }\n }, meeting.id);\n });\n });\n});\n\ndescribe('Playback Operations', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getPlaybackState', () => {\n it('should return playback state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const state = await api.getPlaybackState();\n return { success: true, state };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.state).toHaveProperty('is_playing');\n }\n });\n });\n\n describe('playback controls', () => {\n it('should handle pausePlayback when nothing playing', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.pausePlayback();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should not throw even if nothing is playing\n expect(result).toBeDefined();\n });\n\n it('should handle stopPlayback when nothing playing', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.stopPlayback();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * Recording & Audio E2E Tests\n * expect(result).toBeDefined();\n // May be null if no default device\n if (result.success && result.device) {\n console.log('Default input device:', result.device);\n }\n }); });\n});\n","ops":[{"diffOp":{"equal":{"range":[0,37]}}},{"equalLines":{"line_count":68}},{"diffOp":{"equal":{"range":[37,159]}}},{"diffOp":{"delete":{"range":[159,220]}}},{"diffOp":{"equal":{"range":[220,236]}}},{"equalLines":{"line_count":142}},{"diffOp":{"equal":{"range":[236,246]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/recording.spec.ts"},"span":[2497,2508],"sourceCode":"/**\n * Recording & Audio E2E Tests\n *\n * Tests for audio device management and recording functionality.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Audio Devices', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('listAudioDevices', () => {\n it('should list available audio devices', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const devices = await api.listAudioDevices();\n return { success: true, devices, count: devices?.length ?? 0 };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Devices may or may not be available depending on system\n expect(result).toBeDefined();\n if (result.success) {\n expect(Array.isArray(result.devices)).toBe(true);\n console.log(`Found ${result.count} audio devices`);\n }\n });\n\n it('should return device info with required properties', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const devices = await api.listAudioDevices();\n if (devices && devices.length > 0) {\n const device = devices[0];\n return {\n success: true,\n hasId: 'id' in device || 'device_id' in device,\n hasName: 'name' in device || 'device_name' in device,\n sample: device,\n };\n }\n return { success: true, noDevices: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n if (result.success && !result.noDevices) {\n expect(result.hasId || result.hasName).toBe(true);\n }\n });\n });\n\n describe('getDefaultAudioDevice', () => {\n it('should get default input device', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const device = await api.getDefaultAudioDevice(true);\n return { success: true, device };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n // May be null if no default device\n if (result.success && result.device) {\n console.log('Default input device:', result.device);\n }\n });\n\n it('should get default output device', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const device = await api.getDefaultAudioDevice(false);\n return { success: true, device };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n\ndescribe('Recording Operations', () => {\n let testMeetingId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n });\n\n after(async () => {\n // Cleanup\n if (testMeetingId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.stopMeeting(id);\n await api.deleteMeeting(id);\n }, testMeetingId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('startTranscription', () => {\n it('should start transcription for a meeting', async () => {\n // Create a meeting first\n const title = TestData.createMeetingTitle();\n const meeting = await browser.execute(async (meetingTitle) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.createMeeting({ title: meetingTitle });\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n }, title);\n\n if (meeting.error || !meeting.id) {\n expect(meeting).toBeDefined();\n return;\n }\n\n testMeetingId = meeting.id;\n\n // Start transcription\n const result = await browser.execute(async (meetingId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const stream = await api.startTranscription(meetingId);\n return { success: true, hasStream: !!stream };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, meeting.id);\n\n // May fail if no audio device available\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.hasStream).toBe(true);\n }\n\n // Stop the recording\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.stopMeeting(id);\n } catch {\n // May fail\n }\n }, meeting.id);\n });\n });\n});\n\ndescribe('Playback Operations', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getPlaybackState', () => {\n it('should return playback state', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const state = await api.getPlaybackState();\n return { success: true, state };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.state).toHaveProperty('is_playing');\n }\n });\n });\n\n describe('playback controls', () => {\n it('should handle pausePlayback when nothing playing', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.pausePlayback();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should not throw even if nothing is playing\n expect(result).toBeDefined();\n });\n\n it('should handle stopPlayback when nothing playing', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.stopPlayback();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/settings.spec.ts"},"span":[5135,5138],"sourceCode":"/**\n * Settings & Preferences E2E Tests\n *\n * Tests for preferences, triggers, and cloud consent.\n */\n\nimport { waitForAppReady, navigateTo, waitForLoadingComplete } from './fixtures';\n\ndescribe('User Preferences', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getPreferences', () => {\n it('should retrieve user preferences', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const prefs = await api.getPreferences();\n return { success: true, prefs };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.prefs).toBeDefined();\n expect(result.prefs).toHaveProperty('default_export_format');\n });\n\n it('should have expected preference structure', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n const prefs = await api.getPreferences();\n return {\n hasAiConfig: 'ai_config' in prefs,\n hasAiTemplate: 'ai_template' in prefs,\n hasAudioDevices: 'audio_devices' in prefs,\n hasExportFormat: 'default_export_format' in prefs,\n hasIntegrations: 'integrations' in prefs,\n };\n });\n\n expect(result.hasAiConfig).toBe(true);\n expect(result.hasAiTemplate).toBe(true);\n expect(result.hasAudioDevices).toBe(true);\n expect(result.hasExportFormat).toBe(true);\n expect(result.hasIntegrations).toBe(true);\n });\n });\n\n describe('savePreferences', () => {\n it('should save and persist preferences', async () => {\n // Get current prefs\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n // Modify and save\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n default_export_format: prefs.default_export_format === 'markdown' ? 'html' : 'markdown',\n };\n await api.savePreferences(modified);\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n\n // Verify change\n const updated = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n expect(updated.default_export_format).not.toBe(original.default_export_format);\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n\n it('should save AI template settings', async () => {\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n ai_template: {\n tone: 'professional',\n format: 'bullet_points',\n verbosity: 'balanced',\n },\n };\n await api.savePreferences(modified);\n const saved = await api.getPreferences();\n return { success: true, saved };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n if (result.saved?.ai_template) {\n expect(result.saved.ai_template.tone).toBe('professional');\n }\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n });\n});\n\ndescribe('Cloud Consent', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCloudConsentStatus', () => {\n it('should return consent status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getCloudConsentStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('consentGranted');\n expect(typeof result.status.consentGranted).toBe('boolean');\n });\n });\n\n describe('grantCloudConsent', () => {\n it('should grant cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.grantCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(true);\n });\n });\n\n describe('revokeCloudConsent', () => {\n it('should revoke cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.revokeCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(false);\n });\n });\n});\n\ndescribe('Trigger Settings', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getTriggerStatus', () => {\n it('should return trigger status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getTriggerStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('enabled');\n expect(result.status).toHaveProperty('is_snoozed');\n });\n });\n\n describe('setTriggerEnabled', () => {\n it('should enable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(true);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(true);\n });\n\n it('should disable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(false);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(false);\n });\n });\n\n describe('snoozeTriggers', () => {\n it('should snooze triggers for specified minutes', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.snoozeTriggers(15);\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(true);\n });\n });\n\n describe('resetSnooze', () => {\n it('should reset snooze', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.resetSnooze();\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(false);\n });\n });\n\n describe('dismissTrigger', () => {\n it('should dismiss active trigger', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.dismissTrigger();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should not throw even if no active trigger\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/settings.spec.ts"},"span":[464,467],"sourceCode":"/**\n * Settings & Preferences E2E Tests\n *\n * Tests for preferences, triggers, and cloud consent.\n */\n\nimport { waitForAppReady, navigateTo, waitForLoadingComplete } from './fixtures';\n\ndescribe('User Preferences', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getPreferences', () => {\n it('should retrieve user preferences', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const prefs = await api.getPreferences();\n return { success: true, prefs };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.prefs).toBeDefined();\n expect(result.prefs).toHaveProperty('default_export_format');\n });\n\n it('should have expected preference structure', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n const prefs = await api.getPreferences();\n return {\n hasAiConfig: 'ai_config' in prefs,\n hasAiTemplate: 'ai_template' in prefs,\n hasAudioDevices: 'audio_devices' in prefs,\n hasExportFormat: 'default_export_format' in prefs,\n hasIntegrations: 'integrations' in prefs,\n };\n });\n\n expect(result.hasAiConfig).toBe(true);\n expect(result.hasAiTemplate).toBe(true);\n expect(result.hasAudioDevices).toBe(true);\n expect(result.hasExportFormat).toBe(true);\n expect(result.hasIntegrations).toBe(true);\n });\n });\n\n describe('savePreferences', () => {\n it('should save and persist preferences', async () => {\n // Get current prefs\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n // Modify and save\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n default_export_format: prefs.default_export_format === 'markdown' ? 'html' : 'markdown',\n };\n await api.savePreferences(modified);\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n\n // Verify change\n const updated = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n expect(updated.default_export_format).not.toBe(original.default_export_format);\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n\n it('should save AI template settings', async () => {\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n ai_template: {\n tone: 'professional',\n format: 'bullet_points',\n verbosity: 'balanced',\n },\n };\n await api.savePreferences(modified);\n const saved = await api.getPreferences();\n return { success: true, saved };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n if (result.saved?.ai_template) {\n expect(result.saved.ai_template.tone).toBe('professional');\n }\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n });\n});\n\ndescribe('Cloud Consent', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCloudConsentStatus', () => {\n it('should return consent status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getCloudConsentStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('consentGranted');\n expect(typeof result.status.consentGranted).toBe('boolean');\n });\n });\n\n describe('grantCloudConsent', () => {\n it('should grant cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.grantCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(true);\n });\n });\n\n describe('revokeCloudConsent', () => {\n it('should revoke cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.revokeCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(false);\n });\n });\n});\n\ndescribe('Trigger Settings', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getTriggerStatus', () => {\n it('should return trigger status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getTriggerStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('enabled');\n expect(result.status).toHaveProperty('is_snoozed');\n });\n });\n\n describe('setTriggerEnabled', () => {\n it('should enable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(true);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(true);\n });\n\n it('should disable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(false);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(false);\n });\n });\n\n describe('snoozeTriggers', () => {\n it('should snooze triggers for specified minutes', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.snoozeTriggers(15);\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(true);\n });\n });\n\n describe('resetSnooze', () => {\n it('should reset snooze', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.resetSnooze();\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(false);\n });\n });\n\n describe('dismissTrigger', () => {\n it('should dismiss active trigger', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.dismissTrigger();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should not throw even if no active trigger\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/settings.spec.ts"},"span":[1041,1044],"sourceCode":"/**\n * Settings & Preferences E2E Tests\n *\n * Tests for preferences, triggers, and cloud consent.\n */\n\nimport { waitForAppReady, navigateTo, waitForLoadingComplete } from './fixtures';\n\ndescribe('User Preferences', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getPreferences', () => {\n it('should retrieve user preferences', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const prefs = await api.getPreferences();\n return { success: true, prefs };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.prefs).toBeDefined();\n expect(result.prefs).toHaveProperty('default_export_format');\n });\n\n it('should have expected preference structure', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n const prefs = await api.getPreferences();\n return {\n hasAiConfig: 'ai_config' in prefs,\n hasAiTemplate: 'ai_template' in prefs,\n hasAudioDevices: 'audio_devices' in prefs,\n hasExportFormat: 'default_export_format' in prefs,\n hasIntegrations: 'integrations' in prefs,\n };\n });\n\n expect(result.hasAiConfig).toBe(true);\n expect(result.hasAiTemplate).toBe(true);\n expect(result.hasAudioDevices).toBe(true);\n expect(result.hasExportFormat).toBe(true);\n expect(result.hasIntegrations).toBe(true);\n });\n });\n\n describe('savePreferences', () => {\n it('should save and persist preferences', async () => {\n // Get current prefs\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n // Modify and save\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n default_export_format: prefs.default_export_format === 'markdown' ? 'html' : 'markdown',\n };\n await api.savePreferences(modified);\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n\n // Verify change\n const updated = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n expect(updated.default_export_format).not.toBe(original.default_export_format);\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n\n it('should save AI template settings', async () => {\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n ai_template: {\n tone: 'professional',\n format: 'bullet_points',\n verbosity: 'balanced',\n },\n };\n await api.savePreferences(modified);\n const saved = await api.getPreferences();\n return { success: true, saved };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n if (result.saved?.ai_template) {\n expect(result.saved.ai_template.tone).toBe('professional');\n }\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n });\n});\n\ndescribe('Cloud Consent', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCloudConsentStatus', () => {\n it('should return consent status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getCloudConsentStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('consentGranted');\n expect(typeof result.status.consentGranted).toBe('boolean');\n });\n });\n\n describe('grantCloudConsent', () => {\n it('should grant cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.grantCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(true);\n });\n });\n\n describe('revokeCloudConsent', () => {\n it('should revoke cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.revokeCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(false);\n });\n });\n});\n\ndescribe('Trigger Settings', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getTriggerStatus', () => {\n it('should return trigger status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getTriggerStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('enabled');\n expect(result.status).toHaveProperty('is_snoozed');\n });\n });\n\n describe('setTriggerEnabled', () => {\n it('should enable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(true);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(true);\n });\n\n it('should disable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(false);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(false);\n });\n });\n\n describe('snoozeTriggers', () => {\n it('should snooze triggers for specified minutes', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.snoozeTriggers(15);\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(true);\n });\n });\n\n describe('resetSnooze', () => {\n it('should reset snooze', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.resetSnooze();\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(false);\n });\n });\n\n describe('dismissTrigger', () => {\n it('should dismiss active trigger', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.dismissTrigger();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should not throw even if no active trigger\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/settings.spec.ts"},"span":[1882,1885],"sourceCode":"/**\n * Settings & Preferences E2E Tests\n *\n * Tests for preferences, triggers, and cloud consent.\n */\n\nimport { waitForAppReady, navigateTo, waitForLoadingComplete } from './fixtures';\n\ndescribe('User Preferences', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getPreferences', () => {\n it('should retrieve user preferences', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const prefs = await api.getPreferences();\n return { success: true, prefs };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.prefs).toBeDefined();\n expect(result.prefs).toHaveProperty('default_export_format');\n });\n\n it('should have expected preference structure', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n const prefs = await api.getPreferences();\n return {\n hasAiConfig: 'ai_config' in prefs,\n hasAiTemplate: 'ai_template' in prefs,\n hasAudioDevices: 'audio_devices' in prefs,\n hasExportFormat: 'default_export_format' in prefs,\n hasIntegrations: 'integrations' in prefs,\n };\n });\n\n expect(result.hasAiConfig).toBe(true);\n expect(result.hasAiTemplate).toBe(true);\n expect(result.hasAudioDevices).toBe(true);\n expect(result.hasExportFormat).toBe(true);\n expect(result.hasIntegrations).toBe(true);\n });\n });\n\n describe('savePreferences', () => {\n it('should save and persist preferences', async () => {\n // Get current prefs\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n // Modify and save\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n default_export_format: prefs.default_export_format === 'markdown' ? 'html' : 'markdown',\n };\n await api.savePreferences(modified);\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n\n // Verify change\n const updated = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n expect(updated.default_export_format).not.toBe(original.default_export_format);\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n\n it('should save AI template settings', async () => {\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n ai_template: {\n tone: 'professional',\n format: 'bullet_points',\n verbosity: 'balanced',\n },\n };\n await api.savePreferences(modified);\n const saved = await api.getPreferences();\n return { success: true, saved };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n if (result.saved?.ai_template) {\n expect(result.saved.ai_template.tone).toBe('professional');\n }\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n });\n});\n\ndescribe('Cloud Consent', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCloudConsentStatus', () => {\n it('should return consent status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getCloudConsentStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('consentGranted');\n expect(typeof result.status.consentGranted).toBe('boolean');\n });\n });\n\n describe('grantCloudConsent', () => {\n it('should grant cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.grantCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(true);\n });\n });\n\n describe('revokeCloudConsent', () => {\n it('should revoke cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.revokeCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(false);\n });\n });\n});\n\ndescribe('Trigger Settings', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getTriggerStatus', () => {\n it('should return trigger status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getTriggerStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('enabled');\n expect(result.status).toHaveProperty('is_snoozed');\n });\n });\n\n describe('setTriggerEnabled', () => {\n it('should enable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(true);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(true);\n });\n\n it('should disable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(false);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(false);\n });\n });\n\n describe('snoozeTriggers', () => {\n it('should snooze triggers for specified minutes', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.snoozeTriggers(15);\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(true);\n });\n });\n\n describe('resetSnooze', () => {\n it('should reset snooze', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.resetSnooze();\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(false);\n });\n });\n\n describe('dismissTrigger', () => {\n it('should dismiss active trigger', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.dismissTrigger();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should not throw even if no active trigger\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/settings.spec.ts"},"span":[2071,2074],"sourceCode":"/**\n * Settings & Preferences E2E Tests\n *\n * Tests for preferences, triggers, and cloud consent.\n */\n\nimport { waitForAppReady, navigateTo, waitForLoadingComplete } from './fixtures';\n\ndescribe('User Preferences', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getPreferences', () => {\n it('should retrieve user preferences', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const prefs = await api.getPreferences();\n return { success: true, prefs };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.prefs).toBeDefined();\n expect(result.prefs).toHaveProperty('default_export_format');\n });\n\n it('should have expected preference structure', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n const prefs = await api.getPreferences();\n return {\n hasAiConfig: 'ai_config' in prefs,\n hasAiTemplate: 'ai_template' in prefs,\n hasAudioDevices: 'audio_devices' in prefs,\n hasExportFormat: 'default_export_format' in prefs,\n hasIntegrations: 'integrations' in prefs,\n };\n });\n\n expect(result.hasAiConfig).toBe(true);\n expect(result.hasAiTemplate).toBe(true);\n expect(result.hasAudioDevices).toBe(true);\n expect(result.hasExportFormat).toBe(true);\n expect(result.hasIntegrations).toBe(true);\n });\n });\n\n describe('savePreferences', () => {\n it('should save and persist preferences', async () => {\n // Get current prefs\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n // Modify and save\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n default_export_format: prefs.default_export_format === 'markdown' ? 'html' : 'markdown',\n };\n await api.savePreferences(modified);\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n\n // Verify change\n const updated = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n expect(updated.default_export_format).not.toBe(original.default_export_format);\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n\n it('should save AI template settings', async () => {\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n ai_template: {\n tone: 'professional',\n format: 'bullet_points',\n verbosity: 'balanced',\n },\n };\n await api.savePreferences(modified);\n const saved = await api.getPreferences();\n return { success: true, saved };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n if (result.saved?.ai_template) {\n expect(result.saved.ai_template.tone).toBe('professional');\n }\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n });\n});\n\ndescribe('Cloud Consent', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCloudConsentStatus', () => {\n it('should return consent status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getCloudConsentStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('consentGranted');\n expect(typeof result.status.consentGranted).toBe('boolean');\n });\n });\n\n describe('grantCloudConsent', () => {\n it('should grant cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.grantCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(true);\n });\n });\n\n describe('revokeCloudConsent', () => {\n it('should revoke cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.revokeCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(false);\n });\n });\n});\n\ndescribe('Trigger Settings', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getTriggerStatus', () => {\n it('should return trigger status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getTriggerStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('enabled');\n expect(result.status).toHaveProperty('is_snoozed');\n });\n });\n\n describe('setTriggerEnabled', () => {\n it('should enable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(true);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(true);\n });\n\n it('should disable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(false);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(false);\n });\n });\n\n describe('snoozeTriggers', () => {\n it('should snooze triggers for specified minutes', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.snoozeTriggers(15);\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(true);\n });\n });\n\n describe('resetSnooze', () => {\n it('should reset snooze', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.resetSnooze();\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(false);\n });\n });\n\n describe('dismissTrigger', () => {\n it('should dismiss active trigger', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.dismissTrigger();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should not throw even if no active trigger\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/settings.spec.ts"},"span":[2651,2654],"sourceCode":"/**\n * Settings & Preferences E2E Tests\n *\n * Tests for preferences, triggers, and cloud consent.\n */\n\nimport { waitForAppReady, navigateTo, waitForLoadingComplete } from './fixtures';\n\ndescribe('User Preferences', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getPreferences', () => {\n it('should retrieve user preferences', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const prefs = await api.getPreferences();\n return { success: true, prefs };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.prefs).toBeDefined();\n expect(result.prefs).toHaveProperty('default_export_format');\n });\n\n it('should have expected preference structure', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n const prefs = await api.getPreferences();\n return {\n hasAiConfig: 'ai_config' in prefs,\n hasAiTemplate: 'ai_template' in prefs,\n hasAudioDevices: 'audio_devices' in prefs,\n hasExportFormat: 'default_export_format' in prefs,\n hasIntegrations: 'integrations' in prefs,\n };\n });\n\n expect(result.hasAiConfig).toBe(true);\n expect(result.hasAiTemplate).toBe(true);\n expect(result.hasAudioDevices).toBe(true);\n expect(result.hasExportFormat).toBe(true);\n expect(result.hasIntegrations).toBe(true);\n });\n });\n\n describe('savePreferences', () => {\n it('should save and persist preferences', async () => {\n // Get current prefs\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n // Modify and save\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n default_export_format: prefs.default_export_format === 'markdown' ? 'html' : 'markdown',\n };\n await api.savePreferences(modified);\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n\n // Verify change\n const updated = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n expect(updated.default_export_format).not.toBe(original.default_export_format);\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n\n it('should save AI template settings', async () => {\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n ai_template: {\n tone: 'professional',\n format: 'bullet_points',\n verbosity: 'balanced',\n },\n };\n await api.savePreferences(modified);\n const saved = await api.getPreferences();\n return { success: true, saved };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n if (result.saved?.ai_template) {\n expect(result.saved.ai_template.tone).toBe('professional');\n }\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n });\n});\n\ndescribe('Cloud Consent', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCloudConsentStatus', () => {\n it('should return consent status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getCloudConsentStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('consentGranted');\n expect(typeof result.status.consentGranted).toBe('boolean');\n });\n });\n\n describe('grantCloudConsent', () => {\n it('should grant cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.grantCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(true);\n });\n });\n\n describe('revokeCloudConsent', () => {\n it('should revoke cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.revokeCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(false);\n });\n });\n});\n\ndescribe('Trigger Settings', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getTriggerStatus', () => {\n it('should return trigger status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getTriggerStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('enabled');\n expect(result.status).toHaveProperty('is_snoozed');\n });\n });\n\n describe('setTriggerEnabled', () => {\n it('should enable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(true);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(true);\n });\n\n it('should disable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(false);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(false);\n });\n });\n\n describe('snoozeTriggers', () => {\n it('should snooze triggers for specified minutes', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.snoozeTriggers(15);\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(true);\n });\n });\n\n describe('resetSnooze', () => {\n it('should reset snooze', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.resetSnooze();\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(false);\n });\n });\n\n describe('dismissTrigger', () => {\n it('should dismiss active trigger', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.dismissTrigger();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should not throw even if no active trigger\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/settings.spec.ts"},"span":[2913,2916],"sourceCode":"/**\n * Settings & Preferences E2E Tests\n *\n * Tests for preferences, triggers, and cloud consent.\n */\n\nimport { waitForAppReady, navigateTo, waitForLoadingComplete } from './fixtures';\n\ndescribe('User Preferences', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getPreferences', () => {\n it('should retrieve user preferences', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const prefs = await api.getPreferences();\n return { success: true, prefs };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.prefs).toBeDefined();\n expect(result.prefs).toHaveProperty('default_export_format');\n });\n\n it('should have expected preference structure', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n const prefs = await api.getPreferences();\n return {\n hasAiConfig: 'ai_config' in prefs,\n hasAiTemplate: 'ai_template' in prefs,\n hasAudioDevices: 'audio_devices' in prefs,\n hasExportFormat: 'default_export_format' in prefs,\n hasIntegrations: 'integrations' in prefs,\n };\n });\n\n expect(result.hasAiConfig).toBe(true);\n expect(result.hasAiTemplate).toBe(true);\n expect(result.hasAudioDevices).toBe(true);\n expect(result.hasExportFormat).toBe(true);\n expect(result.hasIntegrations).toBe(true);\n });\n });\n\n describe('savePreferences', () => {\n it('should save and persist preferences', async () => {\n // Get current prefs\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n // Modify and save\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n default_export_format: prefs.default_export_format === 'markdown' ? 'html' : 'markdown',\n };\n await api.savePreferences(modified);\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n\n // Verify change\n const updated = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n expect(updated.default_export_format).not.toBe(original.default_export_format);\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n\n it('should save AI template settings', async () => {\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n ai_template: {\n tone: 'professional',\n format: 'bullet_points',\n verbosity: 'balanced',\n },\n };\n await api.savePreferences(modified);\n const saved = await api.getPreferences();\n return { success: true, saved };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n if (result.saved?.ai_template) {\n expect(result.saved.ai_template.tone).toBe('professional');\n }\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n });\n});\n\ndescribe('Cloud Consent', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCloudConsentStatus', () => {\n it('should return consent status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getCloudConsentStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('consentGranted');\n expect(typeof result.status.consentGranted).toBe('boolean');\n });\n });\n\n describe('grantCloudConsent', () => {\n it('should grant cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.grantCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(true);\n });\n });\n\n describe('revokeCloudConsent', () => {\n it('should revoke cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.revokeCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(false);\n });\n });\n});\n\ndescribe('Trigger Settings', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getTriggerStatus', () => {\n it('should return trigger status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getTriggerStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('enabled');\n expect(result.status).toHaveProperty('is_snoozed');\n });\n });\n\n describe('setTriggerEnabled', () => {\n it('should enable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(true);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(true);\n });\n\n it('should disable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(false);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(false);\n });\n });\n\n describe('snoozeTriggers', () => {\n it('should snooze triggers for specified minutes', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.snoozeTriggers(15);\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(true);\n });\n });\n\n describe('resetSnooze', () => {\n it('should reset snooze', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.resetSnooze();\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(false);\n });\n });\n\n describe('dismissTrigger', () => {\n it('should dismiss active trigger', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.dismissTrigger();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should not throw even if no active trigger\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/settings.spec.ts"},"span":[3154,3157],"sourceCode":"/**\n * Settings & Preferences E2E Tests\n *\n * Tests for preferences, triggers, and cloud consent.\n */\n\nimport { waitForAppReady, navigateTo, waitForLoadingComplete } from './fixtures';\n\ndescribe('User Preferences', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getPreferences', () => {\n it('should retrieve user preferences', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const prefs = await api.getPreferences();\n return { success: true, prefs };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.prefs).toBeDefined();\n expect(result.prefs).toHaveProperty('default_export_format');\n });\n\n it('should have expected preference structure', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n const prefs = await api.getPreferences();\n return {\n hasAiConfig: 'ai_config' in prefs,\n hasAiTemplate: 'ai_template' in prefs,\n hasAudioDevices: 'audio_devices' in prefs,\n hasExportFormat: 'default_export_format' in prefs,\n hasIntegrations: 'integrations' in prefs,\n };\n });\n\n expect(result.hasAiConfig).toBe(true);\n expect(result.hasAiTemplate).toBe(true);\n expect(result.hasAudioDevices).toBe(true);\n expect(result.hasExportFormat).toBe(true);\n expect(result.hasIntegrations).toBe(true);\n });\n });\n\n describe('savePreferences', () => {\n it('should save and persist preferences', async () => {\n // Get current prefs\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n // Modify and save\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n default_export_format: prefs.default_export_format === 'markdown' ? 'html' : 'markdown',\n };\n await api.savePreferences(modified);\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n\n // Verify change\n const updated = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n expect(updated.default_export_format).not.toBe(original.default_export_format);\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n\n it('should save AI template settings', async () => {\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n ai_template: {\n tone: 'professional',\n format: 'bullet_points',\n verbosity: 'balanced',\n },\n };\n await api.savePreferences(modified);\n const saved = await api.getPreferences();\n return { success: true, saved };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n if (result.saved?.ai_template) {\n expect(result.saved.ai_template.tone).toBe('professional');\n }\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n });\n});\n\ndescribe('Cloud Consent', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCloudConsentStatus', () => {\n it('should return consent status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getCloudConsentStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('consentGranted');\n expect(typeof result.status.consentGranted).toBe('boolean');\n });\n });\n\n describe('grantCloudConsent', () => {\n it('should grant cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.grantCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(true);\n });\n });\n\n describe('revokeCloudConsent', () => {\n it('should revoke cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.revokeCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(false);\n });\n });\n});\n\ndescribe('Trigger Settings', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getTriggerStatus', () => {\n it('should return trigger status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getTriggerStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('enabled');\n expect(result.status).toHaveProperty('is_snoozed');\n });\n });\n\n describe('setTriggerEnabled', () => {\n it('should enable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(true);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(true);\n });\n\n it('should disable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(false);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(false);\n });\n });\n\n describe('snoozeTriggers', () => {\n it('should snooze triggers for specified minutes', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.snoozeTriggers(15);\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(true);\n });\n });\n\n describe('resetSnooze', () => {\n it('should reset snooze', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.resetSnooze();\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(false);\n });\n });\n\n describe('dismissTrigger', () => {\n it('should dismiss active trigger', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.dismissTrigger();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should not throw even if no active trigger\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/settings.spec.ts"},"span":[3318,3321],"sourceCode":"/**\n * Settings & Preferences E2E Tests\n *\n * Tests for preferences, triggers, and cloud consent.\n */\n\nimport { waitForAppReady, navigateTo, waitForLoadingComplete } from './fixtures';\n\ndescribe('User Preferences', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getPreferences', () => {\n it('should retrieve user preferences', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const prefs = await api.getPreferences();\n return { success: true, prefs };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.prefs).toBeDefined();\n expect(result.prefs).toHaveProperty('default_export_format');\n });\n\n it('should have expected preference structure', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n const prefs = await api.getPreferences();\n return {\n hasAiConfig: 'ai_config' in prefs,\n hasAiTemplate: 'ai_template' in prefs,\n hasAudioDevices: 'audio_devices' in prefs,\n hasExportFormat: 'default_export_format' in prefs,\n hasIntegrations: 'integrations' in prefs,\n };\n });\n\n expect(result.hasAiConfig).toBe(true);\n expect(result.hasAiTemplate).toBe(true);\n expect(result.hasAudioDevices).toBe(true);\n expect(result.hasExportFormat).toBe(true);\n expect(result.hasIntegrations).toBe(true);\n });\n });\n\n describe('savePreferences', () => {\n it('should save and persist preferences', async () => {\n // Get current prefs\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n // Modify and save\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n default_export_format: prefs.default_export_format === 'markdown' ? 'html' : 'markdown',\n };\n await api.savePreferences(modified);\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n\n // Verify change\n const updated = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n expect(updated.default_export_format).not.toBe(original.default_export_format);\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n\n it('should save AI template settings', async () => {\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n ai_template: {\n tone: 'professional',\n format: 'bullet_points',\n verbosity: 'balanced',\n },\n };\n await api.savePreferences(modified);\n const saved = await api.getPreferences();\n return { success: true, saved };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n if (result.saved?.ai_template) {\n expect(result.saved.ai_template.tone).toBe('professional');\n }\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n });\n});\n\ndescribe('Cloud Consent', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCloudConsentStatus', () => {\n it('should return consent status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getCloudConsentStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('consentGranted');\n expect(typeof result.status.consentGranted).toBe('boolean');\n });\n });\n\n describe('grantCloudConsent', () => {\n it('should grant cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.grantCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(true);\n });\n });\n\n describe('revokeCloudConsent', () => {\n it('should revoke cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.revokeCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(false);\n });\n });\n});\n\ndescribe('Trigger Settings', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getTriggerStatus', () => {\n it('should return trigger status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getTriggerStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('enabled');\n expect(result.status).toHaveProperty('is_snoozed');\n });\n });\n\n describe('setTriggerEnabled', () => {\n it('should enable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(true);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(true);\n });\n\n it('should disable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(false);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(false);\n });\n });\n\n describe('snoozeTriggers', () => {\n it('should snooze triggers for specified minutes', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.snoozeTriggers(15);\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(true);\n });\n });\n\n describe('resetSnooze', () => {\n it('should reset snooze', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.resetSnooze();\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(false);\n });\n });\n\n describe('dismissTrigger', () => {\n it('should dismiss active trigger', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.dismissTrigger();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should not throw even if no active trigger\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/settings.spec.ts"},"span":[4117,4120],"sourceCode":"/**\n * Settings & Preferences E2E Tests\n *\n * Tests for preferences, triggers, and cloud consent.\n */\n\nimport { waitForAppReady, navigateTo, waitForLoadingComplete } from './fixtures';\n\ndescribe('User Preferences', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getPreferences', () => {\n it('should retrieve user preferences', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const prefs = await api.getPreferences();\n return { success: true, prefs };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.prefs).toBeDefined();\n expect(result.prefs).toHaveProperty('default_export_format');\n });\n\n it('should have expected preference structure', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n const prefs = await api.getPreferences();\n return {\n hasAiConfig: 'ai_config' in prefs,\n hasAiTemplate: 'ai_template' in prefs,\n hasAudioDevices: 'audio_devices' in prefs,\n hasExportFormat: 'default_export_format' in prefs,\n hasIntegrations: 'integrations' in prefs,\n };\n });\n\n expect(result.hasAiConfig).toBe(true);\n expect(result.hasAiTemplate).toBe(true);\n expect(result.hasAudioDevices).toBe(true);\n expect(result.hasExportFormat).toBe(true);\n expect(result.hasIntegrations).toBe(true);\n });\n });\n\n describe('savePreferences', () => {\n it('should save and persist preferences', async () => {\n // Get current prefs\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n // Modify and save\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n default_export_format: prefs.default_export_format === 'markdown' ? 'html' : 'markdown',\n };\n await api.savePreferences(modified);\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n\n // Verify change\n const updated = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n expect(updated.default_export_format).not.toBe(original.default_export_format);\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n\n it('should save AI template settings', async () => {\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n ai_template: {\n tone: 'professional',\n format: 'bullet_points',\n verbosity: 'balanced',\n },\n };\n await api.savePreferences(modified);\n const saved = await api.getPreferences();\n return { success: true, saved };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n if (result.saved?.ai_template) {\n expect(result.saved.ai_template.tone).toBe('professional');\n }\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n });\n});\n\ndescribe('Cloud Consent', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCloudConsentStatus', () => {\n it('should return consent status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getCloudConsentStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('consentGranted');\n expect(typeof result.status.consentGranted).toBe('boolean');\n });\n });\n\n describe('grantCloudConsent', () => {\n it('should grant cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.grantCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(true);\n });\n });\n\n describe('revokeCloudConsent', () => {\n it('should revoke cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.revokeCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(false);\n });\n });\n});\n\ndescribe('Trigger Settings', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getTriggerStatus', () => {\n it('should return trigger status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getTriggerStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('enabled');\n expect(result.status).toHaveProperty('is_snoozed');\n });\n });\n\n describe('setTriggerEnabled', () => {\n it('should enable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(true);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(true);\n });\n\n it('should disable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(false);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(false);\n });\n });\n\n describe('snoozeTriggers', () => {\n it('should snooze triggers for specified minutes', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.snoozeTriggers(15);\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(true);\n });\n });\n\n describe('resetSnooze', () => {\n it('should reset snooze', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.resetSnooze();\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(false);\n });\n });\n\n describe('dismissTrigger', () => {\n it('should dismiss active trigger', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.dismissTrigger();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should not throw even if no active trigger\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/settings.spec.ts"},"span":[4499,4502],"sourceCode":"/**\n * Settings & Preferences E2E Tests\n *\n * Tests for preferences, triggers, and cloud consent.\n */\n\nimport { waitForAppReady, navigateTo, waitForLoadingComplete } from './fixtures';\n\ndescribe('User Preferences', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getPreferences', () => {\n it('should retrieve user preferences', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const prefs = await api.getPreferences();\n return { success: true, prefs };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.prefs).toBeDefined();\n expect(result.prefs).toHaveProperty('default_export_format');\n });\n\n it('should have expected preference structure', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n const prefs = await api.getPreferences();\n return {\n hasAiConfig: 'ai_config' in prefs,\n hasAiTemplate: 'ai_template' in prefs,\n hasAudioDevices: 'audio_devices' in prefs,\n hasExportFormat: 'default_export_format' in prefs,\n hasIntegrations: 'integrations' in prefs,\n };\n });\n\n expect(result.hasAiConfig).toBe(true);\n expect(result.hasAiTemplate).toBe(true);\n expect(result.hasAudioDevices).toBe(true);\n expect(result.hasExportFormat).toBe(true);\n expect(result.hasIntegrations).toBe(true);\n });\n });\n\n describe('savePreferences', () => {\n it('should save and persist preferences', async () => {\n // Get current prefs\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n // Modify and save\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n default_export_format: prefs.default_export_format === 'markdown' ? 'html' : 'markdown',\n };\n await api.savePreferences(modified);\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n\n // Verify change\n const updated = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n expect(updated.default_export_format).not.toBe(original.default_export_format);\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n\n it('should save AI template settings', async () => {\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n ai_template: {\n tone: 'professional',\n format: 'bullet_points',\n verbosity: 'balanced',\n },\n };\n await api.savePreferences(modified);\n const saved = await api.getPreferences();\n return { success: true, saved };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n if (result.saved?.ai_template) {\n expect(result.saved.ai_template.tone).toBe('professional');\n }\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n });\n});\n\ndescribe('Cloud Consent', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCloudConsentStatus', () => {\n it('should return consent status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getCloudConsentStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('consentGranted');\n expect(typeof result.status.consentGranted).toBe('boolean');\n });\n });\n\n describe('grantCloudConsent', () => {\n it('should grant cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.grantCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(true);\n });\n });\n\n describe('revokeCloudConsent', () => {\n it('should revoke cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.revokeCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(false);\n });\n });\n});\n\ndescribe('Trigger Settings', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getTriggerStatus', () => {\n it('should return trigger status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getTriggerStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('enabled');\n expect(result.status).toHaveProperty('is_snoozed');\n });\n });\n\n describe('setTriggerEnabled', () => {\n it('should enable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(true);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(true);\n });\n\n it('should disable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(false);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(false);\n });\n });\n\n describe('snoozeTriggers', () => {\n it('should snooze triggers for specified minutes', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.snoozeTriggers(15);\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(true);\n });\n });\n\n describe('resetSnooze', () => {\n it('should reset snooze', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.resetSnooze();\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(false);\n });\n });\n\n describe('dismissTrigger', () => {\n it('should dismiss active trigger', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.dismissTrigger();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should not throw even if no active trigger\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/settings.spec.ts"},"span":[5750,5753],"sourceCode":"/**\n * Settings & Preferences E2E Tests\n *\n * Tests for preferences, triggers, and cloud consent.\n */\n\nimport { waitForAppReady, navigateTo, waitForLoadingComplete } from './fixtures';\n\ndescribe('User Preferences', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getPreferences', () => {\n it('should retrieve user preferences', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const prefs = await api.getPreferences();\n return { success: true, prefs };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.prefs).toBeDefined();\n expect(result.prefs).toHaveProperty('default_export_format');\n });\n\n it('should have expected preference structure', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n const prefs = await api.getPreferences();\n return {\n hasAiConfig: 'ai_config' in prefs,\n hasAiTemplate: 'ai_template' in prefs,\n hasAudioDevices: 'audio_devices' in prefs,\n hasExportFormat: 'default_export_format' in prefs,\n hasIntegrations: 'integrations' in prefs,\n };\n });\n\n expect(result.hasAiConfig).toBe(true);\n expect(result.hasAiTemplate).toBe(true);\n expect(result.hasAudioDevices).toBe(true);\n expect(result.hasExportFormat).toBe(true);\n expect(result.hasIntegrations).toBe(true);\n });\n });\n\n describe('savePreferences', () => {\n it('should save and persist preferences', async () => {\n // Get current prefs\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n // Modify and save\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n default_export_format: prefs.default_export_format === 'markdown' ? 'html' : 'markdown',\n };\n await api.savePreferences(modified);\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n\n // Verify change\n const updated = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n expect(updated.default_export_format).not.toBe(original.default_export_format);\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n\n it('should save AI template settings', async () => {\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n ai_template: {\n tone: 'professional',\n format: 'bullet_points',\n verbosity: 'balanced',\n },\n };\n await api.savePreferences(modified);\n const saved = await api.getPreferences();\n return { success: true, saved };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n if (result.saved?.ai_template) {\n expect(result.saved.ai_template.tone).toBe('professional');\n }\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n });\n});\n\ndescribe('Cloud Consent', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCloudConsentStatus', () => {\n it('should return consent status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getCloudConsentStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('consentGranted');\n expect(typeof result.status.consentGranted).toBe('boolean');\n });\n });\n\n describe('grantCloudConsent', () => {\n it('should grant cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.grantCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(true);\n });\n });\n\n describe('revokeCloudConsent', () => {\n it('should revoke cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.revokeCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(false);\n });\n });\n});\n\ndescribe('Trigger Settings', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getTriggerStatus', () => {\n it('should return trigger status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getTriggerStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('enabled');\n expect(result.status).toHaveProperty('is_snoozed');\n });\n });\n\n describe('setTriggerEnabled', () => {\n it('should enable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(true);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(true);\n });\n\n it('should disable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(false);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(false);\n });\n });\n\n describe('snoozeTriggers', () => {\n it('should snooze triggers for specified minutes', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.snoozeTriggers(15);\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(true);\n });\n });\n\n describe('resetSnooze', () => {\n it('should reset snooze', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.resetSnooze();\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(false);\n });\n });\n\n describe('dismissTrigger', () => {\n it('should dismiss active trigger', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.dismissTrigger();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should not throw even if no active trigger\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/settings.spec.ts"},"span":[6466,6469],"sourceCode":"/**\n * Settings & Preferences E2E Tests\n *\n * Tests for preferences, triggers, and cloud consent.\n */\n\nimport { waitForAppReady, navigateTo, waitForLoadingComplete } from './fixtures';\n\ndescribe('User Preferences', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getPreferences', () => {\n it('should retrieve user preferences', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const prefs = await api.getPreferences();\n return { success: true, prefs };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.prefs).toBeDefined();\n expect(result.prefs).toHaveProperty('default_export_format');\n });\n\n it('should have expected preference structure', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n const prefs = await api.getPreferences();\n return {\n hasAiConfig: 'ai_config' in prefs,\n hasAiTemplate: 'ai_template' in prefs,\n hasAudioDevices: 'audio_devices' in prefs,\n hasExportFormat: 'default_export_format' in prefs,\n hasIntegrations: 'integrations' in prefs,\n };\n });\n\n expect(result.hasAiConfig).toBe(true);\n expect(result.hasAiTemplate).toBe(true);\n expect(result.hasAudioDevices).toBe(true);\n expect(result.hasExportFormat).toBe(true);\n expect(result.hasIntegrations).toBe(true);\n });\n });\n\n describe('savePreferences', () => {\n it('should save and persist preferences', async () => {\n // Get current prefs\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n // Modify and save\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n default_export_format: prefs.default_export_format === 'markdown' ? 'html' : 'markdown',\n };\n await api.savePreferences(modified);\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n\n // Verify change\n const updated = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n expect(updated.default_export_format).not.toBe(original.default_export_format);\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n\n it('should save AI template settings', async () => {\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n ai_template: {\n tone: 'professional',\n format: 'bullet_points',\n verbosity: 'balanced',\n },\n };\n await api.savePreferences(modified);\n const saved = await api.getPreferences();\n return { success: true, saved };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n if (result.saved?.ai_template) {\n expect(result.saved.ai_template.tone).toBe('professional');\n }\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n });\n});\n\ndescribe('Cloud Consent', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCloudConsentStatus', () => {\n it('should return consent status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getCloudConsentStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('consentGranted');\n expect(typeof result.status.consentGranted).toBe('boolean');\n });\n });\n\n describe('grantCloudConsent', () => {\n it('should grant cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.grantCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(true);\n });\n });\n\n describe('revokeCloudConsent', () => {\n it('should revoke cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.revokeCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(false);\n });\n });\n});\n\ndescribe('Trigger Settings', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getTriggerStatus', () => {\n it('should return trigger status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getTriggerStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('enabled');\n expect(result.status).toHaveProperty('is_snoozed');\n });\n });\n\n describe('setTriggerEnabled', () => {\n it('should enable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(true);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(true);\n });\n\n it('should disable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(false);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(false);\n });\n });\n\n describe('snoozeTriggers', () => {\n it('should snooze triggers for specified minutes', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.snoozeTriggers(15);\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(true);\n });\n });\n\n describe('resetSnooze', () => {\n it('should reset snooze', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.resetSnooze();\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(false);\n });\n });\n\n describe('dismissTrigger', () => {\n it('should dismiss active trigger', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.dismissTrigger();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should not throw even if no active trigger\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/settings.spec.ts"},"span":[7077,7080],"sourceCode":"/**\n * Settings & Preferences E2E Tests\n *\n * Tests for preferences, triggers, and cloud consent.\n */\n\nimport { waitForAppReady, navigateTo, waitForLoadingComplete } from './fixtures';\n\ndescribe('User Preferences', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getPreferences', () => {\n it('should retrieve user preferences', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const prefs = await api.getPreferences();\n return { success: true, prefs };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.prefs).toBeDefined();\n expect(result.prefs).toHaveProperty('default_export_format');\n });\n\n it('should have expected preference structure', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n const prefs = await api.getPreferences();\n return {\n hasAiConfig: 'ai_config' in prefs,\n hasAiTemplate: 'ai_template' in prefs,\n hasAudioDevices: 'audio_devices' in prefs,\n hasExportFormat: 'default_export_format' in prefs,\n hasIntegrations: 'integrations' in prefs,\n };\n });\n\n expect(result.hasAiConfig).toBe(true);\n expect(result.hasAiTemplate).toBe(true);\n expect(result.hasAudioDevices).toBe(true);\n expect(result.hasExportFormat).toBe(true);\n expect(result.hasIntegrations).toBe(true);\n });\n });\n\n describe('savePreferences', () => {\n it('should save and persist preferences', async () => {\n // Get current prefs\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n // Modify and save\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n default_export_format: prefs.default_export_format === 'markdown' ? 'html' : 'markdown',\n };\n await api.savePreferences(modified);\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n\n // Verify change\n const updated = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n expect(updated.default_export_format).not.toBe(original.default_export_format);\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n\n it('should save AI template settings', async () => {\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n ai_template: {\n tone: 'professional',\n format: 'bullet_points',\n verbosity: 'balanced',\n },\n };\n await api.savePreferences(modified);\n const saved = await api.getPreferences();\n return { success: true, saved };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n if (result.saved?.ai_template) {\n expect(result.saved.ai_template.tone).toBe('professional');\n }\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n });\n});\n\ndescribe('Cloud Consent', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCloudConsentStatus', () => {\n it('should return consent status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getCloudConsentStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('consentGranted');\n expect(typeof result.status.consentGranted).toBe('boolean');\n });\n });\n\n describe('grantCloudConsent', () => {\n it('should grant cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.grantCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(true);\n });\n });\n\n describe('revokeCloudConsent', () => {\n it('should revoke cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.revokeCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(false);\n });\n });\n});\n\ndescribe('Trigger Settings', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getTriggerStatus', () => {\n it('should return trigger status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getTriggerStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('enabled');\n expect(result.status).toHaveProperty('is_snoozed');\n });\n });\n\n describe('setTriggerEnabled', () => {\n it('should enable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(true);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(true);\n });\n\n it('should disable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(false);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(false);\n });\n });\n\n describe('snoozeTriggers', () => {\n it('should snooze triggers for specified minutes', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.snoozeTriggers(15);\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(true);\n });\n });\n\n describe('resetSnooze', () => {\n it('should reset snooze', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.resetSnooze();\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(false);\n });\n });\n\n describe('dismissTrigger', () => {\n it('should dismiss active trigger', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.dismissTrigger();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should not throw even if no active trigger\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/settings.spec.ts"},"span":[7633,7636],"sourceCode":"/**\n * Settings & Preferences E2E Tests\n *\n * Tests for preferences, triggers, and cloud consent.\n */\n\nimport { waitForAppReady, navigateTo, waitForLoadingComplete } from './fixtures';\n\ndescribe('User Preferences', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getPreferences', () => {\n it('should retrieve user preferences', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const prefs = await api.getPreferences();\n return { success: true, prefs };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.prefs).toBeDefined();\n expect(result.prefs).toHaveProperty('default_export_format');\n });\n\n it('should have expected preference structure', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n const prefs = await api.getPreferences();\n return {\n hasAiConfig: 'ai_config' in prefs,\n hasAiTemplate: 'ai_template' in prefs,\n hasAudioDevices: 'audio_devices' in prefs,\n hasExportFormat: 'default_export_format' in prefs,\n hasIntegrations: 'integrations' in prefs,\n };\n });\n\n expect(result.hasAiConfig).toBe(true);\n expect(result.hasAiTemplate).toBe(true);\n expect(result.hasAudioDevices).toBe(true);\n expect(result.hasExportFormat).toBe(true);\n expect(result.hasIntegrations).toBe(true);\n });\n });\n\n describe('savePreferences', () => {\n it('should save and persist preferences', async () => {\n // Get current prefs\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n // Modify and save\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n default_export_format: prefs.default_export_format === 'markdown' ? 'html' : 'markdown',\n };\n await api.savePreferences(modified);\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n\n // Verify change\n const updated = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n expect(updated.default_export_format).not.toBe(original.default_export_format);\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n\n it('should save AI template settings', async () => {\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n ai_template: {\n tone: 'professional',\n format: 'bullet_points',\n verbosity: 'balanced',\n },\n };\n await api.savePreferences(modified);\n const saved = await api.getPreferences();\n return { success: true, saved };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n if (result.saved?.ai_template) {\n expect(result.saved.ai_template.tone).toBe('professional');\n }\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n });\n});\n\ndescribe('Cloud Consent', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCloudConsentStatus', () => {\n it('should return consent status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getCloudConsentStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('consentGranted');\n expect(typeof result.status.consentGranted).toBe('boolean');\n });\n });\n\n describe('grantCloudConsent', () => {\n it('should grant cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.grantCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(true);\n });\n });\n\n describe('revokeCloudConsent', () => {\n it('should revoke cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.revokeCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(false);\n });\n });\n});\n\ndescribe('Trigger Settings', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getTriggerStatus', () => {\n it('should return trigger status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getTriggerStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('enabled');\n expect(result.status).toHaveProperty('is_snoozed');\n });\n });\n\n describe('setTriggerEnabled', () => {\n it('should enable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(true);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(true);\n });\n\n it('should disable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(false);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(false);\n });\n });\n\n describe('snoozeTriggers', () => {\n it('should snooze triggers for specified minutes', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.snoozeTriggers(15);\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(true);\n });\n });\n\n describe('resetSnooze', () => {\n it('should reset snooze', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.resetSnooze();\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(false);\n });\n });\n\n describe('dismissTrigger', () => {\n it('should dismiss active trigger', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.dismissTrigger();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should not throw even if no active trigger\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/settings.spec.ts"},"span":[8255,8258],"sourceCode":"/**\n * Settings & Preferences E2E Tests\n *\n * Tests for preferences, triggers, and cloud consent.\n */\n\nimport { waitForAppReady, navigateTo, waitForLoadingComplete } from './fixtures';\n\ndescribe('User Preferences', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getPreferences', () => {\n it('should retrieve user preferences', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const prefs = await api.getPreferences();\n return { success: true, prefs };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.prefs).toBeDefined();\n expect(result.prefs).toHaveProperty('default_export_format');\n });\n\n it('should have expected preference structure', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n const prefs = await api.getPreferences();\n return {\n hasAiConfig: 'ai_config' in prefs,\n hasAiTemplate: 'ai_template' in prefs,\n hasAudioDevices: 'audio_devices' in prefs,\n hasExportFormat: 'default_export_format' in prefs,\n hasIntegrations: 'integrations' in prefs,\n };\n });\n\n expect(result.hasAiConfig).toBe(true);\n expect(result.hasAiTemplate).toBe(true);\n expect(result.hasAudioDevices).toBe(true);\n expect(result.hasExportFormat).toBe(true);\n expect(result.hasIntegrations).toBe(true);\n });\n });\n\n describe('savePreferences', () => {\n it('should save and persist preferences', async () => {\n // Get current prefs\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n // Modify and save\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n default_export_format: prefs.default_export_format === 'markdown' ? 'html' : 'markdown',\n };\n await api.savePreferences(modified);\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n\n // Verify change\n const updated = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n expect(updated.default_export_format).not.toBe(original.default_export_format);\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n\n it('should save AI template settings', async () => {\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n ai_template: {\n tone: 'professional',\n format: 'bullet_points',\n verbosity: 'balanced',\n },\n };\n await api.savePreferences(modified);\n const saved = await api.getPreferences();\n return { success: true, saved };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n if (result.saved?.ai_template) {\n expect(result.saved.ai_template.tone).toBe('professional');\n }\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n });\n});\n\ndescribe('Cloud Consent', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCloudConsentStatus', () => {\n it('should return consent status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getCloudConsentStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('consentGranted');\n expect(typeof result.status.consentGranted).toBe('boolean');\n });\n });\n\n describe('grantCloudConsent', () => {\n it('should grant cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.grantCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(true);\n });\n });\n\n describe('revokeCloudConsent', () => {\n it('should revoke cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.revokeCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(false);\n });\n });\n});\n\ndescribe('Trigger Settings', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getTriggerStatus', () => {\n it('should return trigger status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getTriggerStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('enabled');\n expect(result.status).toHaveProperty('is_snoozed');\n });\n });\n\n describe('setTriggerEnabled', () => {\n it('should enable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(true);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(true);\n });\n\n it('should disable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(false);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(false);\n });\n });\n\n describe('snoozeTriggers', () => {\n it('should snooze triggers for specified minutes', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.snoozeTriggers(15);\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(true);\n });\n });\n\n describe('resetSnooze', () => {\n it('should reset snooze', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.resetSnooze();\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(false);\n });\n });\n\n describe('dismissTrigger', () => {\n it('should dismiss active trigger', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.dismissTrigger();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should not throw even if no active trigger\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/settings.spec.ts"},"span":[8845,8848],"sourceCode":"/**\n * Settings & Preferences E2E Tests\n *\n * Tests for preferences, triggers, and cloud consent.\n */\n\nimport { waitForAppReady, navigateTo, waitForLoadingComplete } from './fixtures';\n\ndescribe('User Preferences', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getPreferences', () => {\n it('should retrieve user preferences', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const prefs = await api.getPreferences();\n return { success: true, prefs };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.prefs).toBeDefined();\n expect(result.prefs).toHaveProperty('default_export_format');\n });\n\n it('should have expected preference structure', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n const prefs = await api.getPreferences();\n return {\n hasAiConfig: 'ai_config' in prefs,\n hasAiTemplate: 'ai_template' in prefs,\n hasAudioDevices: 'audio_devices' in prefs,\n hasExportFormat: 'default_export_format' in prefs,\n hasIntegrations: 'integrations' in prefs,\n };\n });\n\n expect(result.hasAiConfig).toBe(true);\n expect(result.hasAiTemplate).toBe(true);\n expect(result.hasAudioDevices).toBe(true);\n expect(result.hasExportFormat).toBe(true);\n expect(result.hasIntegrations).toBe(true);\n });\n });\n\n describe('savePreferences', () => {\n it('should save and persist preferences', async () => {\n // Get current prefs\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n // Modify and save\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n default_export_format: prefs.default_export_format === 'markdown' ? 'html' : 'markdown',\n };\n await api.savePreferences(modified);\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n\n // Verify change\n const updated = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n expect(updated.default_export_format).not.toBe(original.default_export_format);\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n\n it('should save AI template settings', async () => {\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n ai_template: {\n tone: 'professional',\n format: 'bullet_points',\n verbosity: 'balanced',\n },\n };\n await api.savePreferences(modified);\n const saved = await api.getPreferences();\n return { success: true, saved };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n if (result.saved?.ai_template) {\n expect(result.saved.ai_template.tone).toBe('professional');\n }\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n });\n});\n\ndescribe('Cloud Consent', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCloudConsentStatus', () => {\n it('should return consent status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getCloudConsentStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('consentGranted');\n expect(typeof result.status.consentGranted).toBe('boolean');\n });\n });\n\n describe('grantCloudConsent', () => {\n it('should grant cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.grantCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(true);\n });\n });\n\n describe('revokeCloudConsent', () => {\n it('should revoke cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.revokeCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(false);\n });\n });\n});\n\ndescribe('Trigger Settings', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getTriggerStatus', () => {\n it('should return trigger status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getTriggerStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('enabled');\n expect(result.status).toHaveProperty('is_snoozed');\n });\n });\n\n describe('setTriggerEnabled', () => {\n it('should enable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(true);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(true);\n });\n\n it('should disable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(false);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(false);\n });\n });\n\n describe('snoozeTriggers', () => {\n it('should snooze triggers for specified minutes', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.snoozeTriggers(15);\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(true);\n });\n });\n\n describe('resetSnooze', () => {\n it('should reset snooze', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.resetSnooze();\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(false);\n });\n });\n\n describe('dismissTrigger', () => {\n it('should dismiss active trigger', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.dismissTrigger();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should not throw even if no active trigger\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/settings.spec.ts"},"span":[9444,9447],"sourceCode":"/**\n * Settings & Preferences E2E Tests\n *\n * Tests for preferences, triggers, and cloud consent.\n */\n\nimport { waitForAppReady, navigateTo, waitForLoadingComplete } from './fixtures';\n\ndescribe('User Preferences', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getPreferences', () => {\n it('should retrieve user preferences', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const prefs = await api.getPreferences();\n return { success: true, prefs };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.prefs).toBeDefined();\n expect(result.prefs).toHaveProperty('default_export_format');\n });\n\n it('should have expected preference structure', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n const prefs = await api.getPreferences();\n return {\n hasAiConfig: 'ai_config' in prefs,\n hasAiTemplate: 'ai_template' in prefs,\n hasAudioDevices: 'audio_devices' in prefs,\n hasExportFormat: 'default_export_format' in prefs,\n hasIntegrations: 'integrations' in prefs,\n };\n });\n\n expect(result.hasAiConfig).toBe(true);\n expect(result.hasAiTemplate).toBe(true);\n expect(result.hasAudioDevices).toBe(true);\n expect(result.hasExportFormat).toBe(true);\n expect(result.hasIntegrations).toBe(true);\n });\n });\n\n describe('savePreferences', () => {\n it('should save and persist preferences', async () => {\n // Get current prefs\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n // Modify and save\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n default_export_format: prefs.default_export_format === 'markdown' ? 'html' : 'markdown',\n };\n await api.savePreferences(modified);\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n\n // Verify change\n const updated = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n expect(updated.default_export_format).not.toBe(original.default_export_format);\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n\n it('should save AI template settings', async () => {\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n ai_template: {\n tone: 'professional',\n format: 'bullet_points',\n verbosity: 'balanced',\n },\n };\n await api.savePreferences(modified);\n const saved = await api.getPreferences();\n return { success: true, saved };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n if (result.saved?.ai_template) {\n expect(result.saved.ai_template.tone).toBe('professional');\n }\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n });\n});\n\ndescribe('Cloud Consent', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCloudConsentStatus', () => {\n it('should return consent status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getCloudConsentStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('consentGranted');\n expect(typeof result.status.consentGranted).toBe('boolean');\n });\n });\n\n describe('grantCloudConsent', () => {\n it('should grant cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.grantCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(true);\n });\n });\n\n describe('revokeCloudConsent', () => {\n it('should revoke cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.revokeCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(false);\n });\n });\n});\n\ndescribe('Trigger Settings', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getTriggerStatus', () => {\n it('should return trigger status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getTriggerStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('enabled');\n expect(result.status).toHaveProperty('is_snoozed');\n });\n });\n\n describe('setTriggerEnabled', () => {\n it('should enable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(true);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(true);\n });\n\n it('should disable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(false);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(false);\n });\n });\n\n describe('snoozeTriggers', () => {\n it('should snooze triggers for specified minutes', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.snoozeTriggers(15);\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(true);\n });\n });\n\n describe('resetSnooze', () => {\n it('should reset snooze', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.resetSnooze();\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(false);\n });\n });\n\n describe('dismissTrigger', () => {\n it('should dismiss active trigger', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.dismissTrigger();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should not throw even if no active trigger\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/correctness/noUnusedImports","severity":"error","description":"Several of these imports are unused.","message":[{"elements":[],"content":"Several of these "},{"elements":["Emphasis"],"content":"imports"},{"elements":[],"content":" are unused."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"Unused imports might be the result of an incomplete refactoring."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove the unused imports."}]]},{"diff":{"dictionary":"/**\n * Settings & Preferences E2E Tests\n * */\n\nimport { waitForAppReady, navigateTo, waitForLoadingComplete } from './fixtures';\n\ndescribe('User Preferences', () => { });\n});\n","ops":[{"diffOp":{"equal":{"range":[0,42]}}},{"equalLines":{"line_count":1}},{"diffOp":{"equal":{"range":[42,73]}}},{"diffOp":{"delete":{"range":[73,83]}}},{"diffOp":{"delete":{"range":[83,85]}}},{"diffOp":{"delete":{"range":[85,108]}}},{"diffOp":{"equal":{"range":[108,166]}}},{"equalLines":{"line_count":289}},{"diffOp":{"equal":{"range":[166,176]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/settings.spec.ts"},"span":[129,163],"sourceCode":"/**\n * Settings & Preferences E2E Tests\n *\n * Tests for preferences, triggers, and cloud consent.\n */\n\nimport { waitForAppReady, navigateTo, waitForLoadingComplete } from './fixtures';\n\ndescribe('User Preferences', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getPreferences', () => {\n it('should retrieve user preferences', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const prefs = await api.getPreferences();\n return { success: true, prefs };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.prefs).toBeDefined();\n expect(result.prefs).toHaveProperty('default_export_format');\n });\n\n it('should have expected preference structure', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n const prefs = await api.getPreferences();\n return {\n hasAiConfig: 'ai_config' in prefs,\n hasAiTemplate: 'ai_template' in prefs,\n hasAudioDevices: 'audio_devices' in prefs,\n hasExportFormat: 'default_export_format' in prefs,\n hasIntegrations: 'integrations' in prefs,\n };\n });\n\n expect(result.hasAiConfig).toBe(true);\n expect(result.hasAiTemplate).toBe(true);\n expect(result.hasAudioDevices).toBe(true);\n expect(result.hasExportFormat).toBe(true);\n expect(result.hasIntegrations).toBe(true);\n });\n });\n\n describe('savePreferences', () => {\n it('should save and persist preferences', async () => {\n // Get current prefs\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n // Modify and save\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n default_export_format: prefs.default_export_format === 'markdown' ? 'html' : 'markdown',\n };\n await api.savePreferences(modified);\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n\n // Verify change\n const updated = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n expect(updated.default_export_format).not.toBe(original.default_export_format);\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n\n it('should save AI template settings', async () => {\n const original = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n return api.getPreferences();\n });\n\n const result = await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const modified = {\n ...prefs,\n ai_template: {\n tone: 'professional',\n format: 'bullet_points',\n verbosity: 'balanced',\n },\n };\n await api.savePreferences(modified);\n const saved = await api.getPreferences();\n return { success: true, saved };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, original);\n\n expect(result.success).toBe(true);\n if (result.saved?.ai_template) {\n expect(result.saved.ai_template.tone).toBe('professional');\n }\n\n // Restore original\n await browser.execute(async (prefs) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.savePreferences(prefs);\n }, original);\n });\n });\n});\n\ndescribe('Cloud Consent', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getCloudConsentStatus', () => {\n it('should return consent status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getCloudConsentStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('consentGranted');\n expect(typeof result.status.consentGranted).toBe('boolean');\n });\n });\n\n describe('grantCloudConsent', () => {\n it('should grant cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.grantCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(true);\n });\n });\n\n describe('revokeCloudConsent', () => {\n it('should revoke cloud consent', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.revokeCloudConsent();\n const status = await api.getCloudConsentStatus();\n return { success: true, granted: status.consentGranted };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.granted).toBe(false);\n });\n });\n});\n\ndescribe('Trigger Settings', () => {\n before(async () => {\n await waitForAppReady();\n });\n\n describe('getTriggerStatus', () => {\n it('should return trigger status', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const status = await api.getTriggerStatus();\n return { success: true, status };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.status).toHaveProperty('enabled');\n expect(result.status).toHaveProperty('is_snoozed');\n });\n });\n\n describe('setTriggerEnabled', () => {\n it('should enable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(true);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(true);\n });\n\n it('should disable triggers', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.setTriggerEnabled(false);\n const status = await api.getTriggerStatus();\n return { success: true, enabled: status.enabled };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.enabled).toBe(false);\n });\n });\n\n describe('snoozeTriggers', () => {\n it('should snooze triggers for specified minutes', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.snoozeTriggers(15);\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(true);\n });\n });\n\n describe('resetSnooze', () => {\n it('should reset snooze', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.resetSnooze();\n const status = await api.getTriggerStatus();\n return { success: true, snoozed: status.is_snoozed };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result.success).toBe(true);\n expect(result.snoozed).toBe(false);\n });\n });\n\n describe('dismissTrigger', () => {\n it('should dismiss active trigger', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n await api.dismissTrigger();\n return { success: true };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n // Should not throw even if no active trigger\n expect(result).toBeDefined();\n });\n });\n});\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/webhooks.spec.ts"},"span":[1025,1028],"sourceCode":"/**\n * Webhook E2E Tests\n *\n * Tests for webhook CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Webhook Operations', () => {\n let testWebhookId: string | null = null;\n let testWorkspaceId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Get a workspace ID for webhook operations\n const workspaces = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWorkspaces();\n return response?.workspaces ?? [];\n } catch {\n return [];\n }\n });\n // Only use workspace if it has a valid (non-null) ID\n const nullUuid = '00000000-0000-0000-0000-000000000000';\n if (workspaces.length > 0 && workspaces[0].id && workspaces[0].id !== nullUuid) {\n testWorkspaceId = workspaces[0].id;\n }\n });\n\n after(async () => {\n if (testWebhookId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(id);\n }, testWebhookId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('registerWebhook', () => {\n it('should register a new webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Test Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook).toHaveProperty('id');\n expect(result.webhook).toHaveProperty('url');\n expect(result.webhook).toHaveProperty('events');\n testWebhookId = result.webhook.id;\n }\n });\n\n it('should register webhook with multiple events', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed', 'summary.generated', 'recording.started'],\n name: `Multi-Event Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook.events.length).toBe(3);\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n\n it('should register webhook with secret', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Secret Webhook ${id}`,\n secret: 'my-webhook-secret',\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n });\n\n describe('listWebhooks', () => {\n it('should list all webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('webhooks');\n expect(Array.isArray(result.webhooks)).toBe(true);\n }\n });\n\n it('should list only enabled webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks(true);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success && result.webhooks) {\n for (const webhook of result.webhooks) {\n expect(webhook.enabled).toBe(true);\n }\n }\n });\n });\n\n describe('updateWebhook', () => {\n it('should update webhook name', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n name: 'Updated Webhook Name',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.name).toBe('Updated Webhook Name');\n }\n });\n\n it('should disable webhook', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n enabled: false,\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.enabled).toBe(false);\n }\n });\n });\n\n describe('getWebhookDeliveries', () => {\n it('should get delivery history', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getWebhookDeliveries(webhookId, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('deliveries');\n }\n });\n });\n\n describe('deleteWebhook', () => {\n it('should delete a webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n // Create a webhook to delete\n const testId = TestData.generateTestId();\n const createResult = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/delete/${id}`,\n events: ['meeting.completed'],\n name: `Delete Test ${id}`,\n });\n } catch {\n return null;\n }\n }, testId, testWorkspaceId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.deleteWebhook(webhookId);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/webhooks.spec.ts"},"span":[445,448],"sourceCode":"/**\n * Webhook E2E Tests\n *\n * Tests for webhook CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Webhook Operations', () => {\n let testWebhookId: string | null = null;\n let testWorkspaceId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Get a workspace ID for webhook operations\n const workspaces = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWorkspaces();\n return response?.workspaces ?? [];\n } catch {\n return [];\n }\n });\n // Only use workspace if it has a valid (non-null) ID\n const nullUuid = '00000000-0000-0000-0000-000000000000';\n if (workspaces.length > 0 && workspaces[0].id && workspaces[0].id !== nullUuid) {\n testWorkspaceId = workspaces[0].id;\n }\n });\n\n after(async () => {\n if (testWebhookId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(id);\n }, testWebhookId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('registerWebhook', () => {\n it('should register a new webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Test Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook).toHaveProperty('id');\n expect(result.webhook).toHaveProperty('url');\n expect(result.webhook).toHaveProperty('events');\n testWebhookId = result.webhook.id;\n }\n });\n\n it('should register webhook with multiple events', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed', 'summary.generated', 'recording.started'],\n name: `Multi-Event Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook.events.length).toBe(3);\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n\n it('should register webhook with secret', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Secret Webhook ${id}`,\n secret: 'my-webhook-secret',\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n });\n\n describe('listWebhooks', () => {\n it('should list all webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('webhooks');\n expect(Array.isArray(result.webhooks)).toBe(true);\n }\n });\n\n it('should list only enabled webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks(true);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success && result.webhooks) {\n for (const webhook of result.webhooks) {\n expect(webhook.enabled).toBe(true);\n }\n }\n });\n });\n\n describe('updateWebhook', () => {\n it('should update webhook name', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n name: 'Updated Webhook Name',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.name).toBe('Updated Webhook Name');\n }\n });\n\n it('should disable webhook', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n enabled: false,\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.enabled).toBe(false);\n }\n });\n });\n\n describe('getWebhookDeliveries', () => {\n it('should get delivery history', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getWebhookDeliveries(webhookId, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('deliveries');\n }\n });\n });\n\n describe('deleteWebhook', () => {\n it('should delete a webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n // Create a webhook to delete\n const testId = TestData.generateTestId();\n const createResult = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/delete/${id}`,\n events: ['meeting.completed'],\n name: `Delete Test ${id}`,\n });\n } catch {\n return null;\n }\n }, testId, testWorkspaceId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.deleteWebhook(webhookId);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/webhooks.spec.ts"},"span":[1540,1543],"sourceCode":"/**\n * Webhook E2E Tests\n *\n * Tests for webhook CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Webhook Operations', () => {\n let testWebhookId: string | null = null;\n let testWorkspaceId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Get a workspace ID for webhook operations\n const workspaces = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWorkspaces();\n return response?.workspaces ?? [];\n } catch {\n return [];\n }\n });\n // Only use workspace if it has a valid (non-null) ID\n const nullUuid = '00000000-0000-0000-0000-000000000000';\n if (workspaces.length > 0 && workspaces[0].id && workspaces[0].id !== nullUuid) {\n testWorkspaceId = workspaces[0].id;\n }\n });\n\n after(async () => {\n if (testWebhookId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(id);\n }, testWebhookId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('registerWebhook', () => {\n it('should register a new webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Test Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook).toHaveProperty('id');\n expect(result.webhook).toHaveProperty('url');\n expect(result.webhook).toHaveProperty('events');\n testWebhookId = result.webhook.id;\n }\n });\n\n it('should register webhook with multiple events', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed', 'summary.generated', 'recording.started'],\n name: `Multi-Event Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook.events.length).toBe(3);\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n\n it('should register webhook with secret', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Secret Webhook ${id}`,\n secret: 'my-webhook-secret',\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n });\n\n describe('listWebhooks', () => {\n it('should list all webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('webhooks');\n expect(Array.isArray(result.webhooks)).toBe(true);\n }\n });\n\n it('should list only enabled webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks(true);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success && result.webhooks) {\n for (const webhook of result.webhooks) {\n expect(webhook.enabled).toBe(true);\n }\n }\n });\n });\n\n describe('updateWebhook', () => {\n it('should update webhook name', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n name: 'Updated Webhook Name',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.name).toBe('Updated Webhook Name');\n }\n });\n\n it('should disable webhook', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n enabled: false,\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.enabled).toBe(false);\n }\n });\n });\n\n describe('getWebhookDeliveries', () => {\n it('should get delivery history', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getWebhookDeliveries(webhookId, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('deliveries');\n }\n });\n });\n\n describe('deleteWebhook', () => {\n it('should delete a webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n // Create a webhook to delete\n const testId = TestData.generateTestId();\n const createResult = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/delete/${id}`,\n events: ['meeting.completed'],\n name: `Delete Test ${id}`,\n });\n } catch {\n return null;\n }\n }, testId, testWorkspaceId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.deleteWebhook(webhookId);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/webhooks.spec.ts"},"span":[1886,1889],"sourceCode":"/**\n * Webhook E2E Tests\n *\n * Tests for webhook CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Webhook Operations', () => {\n let testWebhookId: string | null = null;\n let testWorkspaceId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Get a workspace ID for webhook operations\n const workspaces = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWorkspaces();\n return response?.workspaces ?? [];\n } catch {\n return [];\n }\n });\n // Only use workspace if it has a valid (non-null) ID\n const nullUuid = '00000000-0000-0000-0000-000000000000';\n if (workspaces.length > 0 && workspaces[0].id && workspaces[0].id !== nullUuid) {\n testWorkspaceId = workspaces[0].id;\n }\n });\n\n after(async () => {\n if (testWebhookId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(id);\n }, testWebhookId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('registerWebhook', () => {\n it('should register a new webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Test Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook).toHaveProperty('id');\n expect(result.webhook).toHaveProperty('url');\n expect(result.webhook).toHaveProperty('events');\n testWebhookId = result.webhook.id;\n }\n });\n\n it('should register webhook with multiple events', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed', 'summary.generated', 'recording.started'],\n name: `Multi-Event Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook.events.length).toBe(3);\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n\n it('should register webhook with secret', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Secret Webhook ${id}`,\n secret: 'my-webhook-secret',\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n });\n\n describe('listWebhooks', () => {\n it('should list all webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('webhooks');\n expect(Array.isArray(result.webhooks)).toBe(true);\n }\n });\n\n it('should list only enabled webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks(true);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success && result.webhooks) {\n for (const webhook of result.webhooks) {\n expect(webhook.enabled).toBe(true);\n }\n }\n });\n });\n\n describe('updateWebhook', () => {\n it('should update webhook name', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n name: 'Updated Webhook Name',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.name).toBe('Updated Webhook Name');\n }\n });\n\n it('should disable webhook', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n enabled: false,\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.enabled).toBe(false);\n }\n });\n });\n\n describe('getWebhookDeliveries', () => {\n it('should get delivery history', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getWebhookDeliveries(webhookId, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('deliveries');\n }\n });\n });\n\n describe('deleteWebhook', () => {\n it('should delete a webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n // Create a webhook to delete\n const testId = TestData.generateTestId();\n const createResult = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/delete/${id}`,\n events: ['meeting.completed'],\n name: `Delete Test ${id}`,\n });\n } catch {\n return null;\n }\n }, testId, testWorkspaceId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.deleteWebhook(webhookId);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/webhooks.spec.ts"},"span":[2724,2727],"sourceCode":"/**\n * Webhook E2E Tests\n *\n * Tests for webhook CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Webhook Operations', () => {\n let testWebhookId: string | null = null;\n let testWorkspaceId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Get a workspace ID for webhook operations\n const workspaces = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWorkspaces();\n return response?.workspaces ?? [];\n } catch {\n return [];\n }\n });\n // Only use workspace if it has a valid (non-null) ID\n const nullUuid = '00000000-0000-0000-0000-000000000000';\n if (workspaces.length > 0 && workspaces[0].id && workspaces[0].id !== nullUuid) {\n testWorkspaceId = workspaces[0].id;\n }\n });\n\n after(async () => {\n if (testWebhookId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(id);\n }, testWebhookId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('registerWebhook', () => {\n it('should register a new webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Test Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook).toHaveProperty('id');\n expect(result.webhook).toHaveProperty('url');\n expect(result.webhook).toHaveProperty('events');\n testWebhookId = result.webhook.id;\n }\n });\n\n it('should register webhook with multiple events', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed', 'summary.generated', 'recording.started'],\n name: `Multi-Event Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook.events.length).toBe(3);\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n\n it('should register webhook with secret', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Secret Webhook ${id}`,\n secret: 'my-webhook-secret',\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n });\n\n describe('listWebhooks', () => {\n it('should list all webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('webhooks');\n expect(Array.isArray(result.webhooks)).toBe(true);\n }\n });\n\n it('should list only enabled webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks(true);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success && result.webhooks) {\n for (const webhook of result.webhooks) {\n expect(webhook.enabled).toBe(true);\n }\n }\n });\n });\n\n describe('updateWebhook', () => {\n it('should update webhook name', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n name: 'Updated Webhook Name',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.name).toBe('Updated Webhook Name');\n }\n });\n\n it('should disable webhook', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n enabled: false,\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.enabled).toBe(false);\n }\n });\n });\n\n describe('getWebhookDeliveries', () => {\n it('should get delivery history', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getWebhookDeliveries(webhookId, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('deliveries');\n }\n });\n });\n\n describe('deleteWebhook', () => {\n it('should delete a webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n // Create a webhook to delete\n const testId = TestData.generateTestId();\n const createResult = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/delete/${id}`,\n events: ['meeting.completed'],\n name: `Delete Test ${id}`,\n });\n } catch {\n return null;\n }\n }, testId, testWorkspaceId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.deleteWebhook(webhookId);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/webhooks.spec.ts"},"span":[3119,3122],"sourceCode":"/**\n * Webhook E2E Tests\n *\n * Tests for webhook CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Webhook Operations', () => {\n let testWebhookId: string | null = null;\n let testWorkspaceId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Get a workspace ID for webhook operations\n const workspaces = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWorkspaces();\n return response?.workspaces ?? [];\n } catch {\n return [];\n }\n });\n // Only use workspace if it has a valid (non-null) ID\n const nullUuid = '00000000-0000-0000-0000-000000000000';\n if (workspaces.length > 0 && workspaces[0].id && workspaces[0].id !== nullUuid) {\n testWorkspaceId = workspaces[0].id;\n }\n });\n\n after(async () => {\n if (testWebhookId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(id);\n }, testWebhookId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('registerWebhook', () => {\n it('should register a new webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Test Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook).toHaveProperty('id');\n expect(result.webhook).toHaveProperty('url');\n expect(result.webhook).toHaveProperty('events');\n testWebhookId = result.webhook.id;\n }\n });\n\n it('should register webhook with multiple events', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed', 'summary.generated', 'recording.started'],\n name: `Multi-Event Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook.events.length).toBe(3);\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n\n it('should register webhook with secret', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Secret Webhook ${id}`,\n secret: 'my-webhook-secret',\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n });\n\n describe('listWebhooks', () => {\n it('should list all webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('webhooks');\n expect(Array.isArray(result.webhooks)).toBe(true);\n }\n });\n\n it('should list only enabled webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks(true);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success && result.webhooks) {\n for (const webhook of result.webhooks) {\n expect(webhook.enabled).toBe(true);\n }\n }\n });\n });\n\n describe('updateWebhook', () => {\n it('should update webhook name', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n name: 'Updated Webhook Name',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.name).toBe('Updated Webhook Name');\n }\n });\n\n it('should disable webhook', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n enabled: false,\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.enabled).toBe(false);\n }\n });\n });\n\n describe('getWebhookDeliveries', () => {\n it('should get delivery history', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getWebhookDeliveries(webhookId, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('deliveries');\n }\n });\n });\n\n describe('deleteWebhook', () => {\n it('should delete a webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n // Create a webhook to delete\n const testId = TestData.generateTestId();\n const createResult = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/delete/${id}`,\n events: ['meeting.completed'],\n name: `Delete Test ${id}`,\n });\n } catch {\n return null;\n }\n }, testId, testWorkspaceId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.deleteWebhook(webhookId);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/webhooks.spec.ts"},"span":[3559,3562],"sourceCode":"/**\n * Webhook E2E Tests\n *\n * Tests for webhook CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Webhook Operations', () => {\n let testWebhookId: string | null = null;\n let testWorkspaceId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Get a workspace ID for webhook operations\n const workspaces = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWorkspaces();\n return response?.workspaces ?? [];\n } catch {\n return [];\n }\n });\n // Only use workspace if it has a valid (non-null) ID\n const nullUuid = '00000000-0000-0000-0000-000000000000';\n if (workspaces.length > 0 && workspaces[0].id && workspaces[0].id !== nullUuid) {\n testWorkspaceId = workspaces[0].id;\n }\n });\n\n after(async () => {\n if (testWebhookId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(id);\n }, testWebhookId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('registerWebhook', () => {\n it('should register a new webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Test Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook).toHaveProperty('id');\n expect(result.webhook).toHaveProperty('url');\n expect(result.webhook).toHaveProperty('events');\n testWebhookId = result.webhook.id;\n }\n });\n\n it('should register webhook with multiple events', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed', 'summary.generated', 'recording.started'],\n name: `Multi-Event Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook.events.length).toBe(3);\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n\n it('should register webhook with secret', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Secret Webhook ${id}`,\n secret: 'my-webhook-secret',\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n });\n\n describe('listWebhooks', () => {\n it('should list all webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('webhooks');\n expect(Array.isArray(result.webhooks)).toBe(true);\n }\n });\n\n it('should list only enabled webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks(true);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success && result.webhooks) {\n for (const webhook of result.webhooks) {\n expect(webhook.enabled).toBe(true);\n }\n }\n });\n });\n\n describe('updateWebhook', () => {\n it('should update webhook name', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n name: 'Updated Webhook Name',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.name).toBe('Updated Webhook Name');\n }\n });\n\n it('should disable webhook', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n enabled: false,\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.enabled).toBe(false);\n }\n });\n });\n\n describe('getWebhookDeliveries', () => {\n it('should get delivery history', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getWebhookDeliveries(webhookId, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('deliveries');\n }\n });\n });\n\n describe('deleteWebhook', () => {\n it('should delete a webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n // Create a webhook to delete\n const testId = TestData.generateTestId();\n const createResult = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/delete/${id}`,\n events: ['meeting.completed'],\n name: `Delete Test ${id}`,\n });\n } catch {\n return null;\n }\n }, testId, testWorkspaceId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.deleteWebhook(webhookId);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/webhooks.spec.ts"},"span":[4000,4003],"sourceCode":"/**\n * Webhook E2E Tests\n *\n * Tests for webhook CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Webhook Operations', () => {\n let testWebhookId: string | null = null;\n let testWorkspaceId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Get a workspace ID for webhook operations\n const workspaces = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWorkspaces();\n return response?.workspaces ?? [];\n } catch {\n return [];\n }\n });\n // Only use workspace if it has a valid (non-null) ID\n const nullUuid = '00000000-0000-0000-0000-000000000000';\n if (workspaces.length > 0 && workspaces[0].id && workspaces[0].id !== nullUuid) {\n testWorkspaceId = workspaces[0].id;\n }\n });\n\n after(async () => {\n if (testWebhookId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(id);\n }, testWebhookId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('registerWebhook', () => {\n it('should register a new webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Test Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook).toHaveProperty('id');\n expect(result.webhook).toHaveProperty('url');\n expect(result.webhook).toHaveProperty('events');\n testWebhookId = result.webhook.id;\n }\n });\n\n it('should register webhook with multiple events', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed', 'summary.generated', 'recording.started'],\n name: `Multi-Event Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook.events.length).toBe(3);\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n\n it('should register webhook with secret', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Secret Webhook ${id}`,\n secret: 'my-webhook-secret',\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n });\n\n describe('listWebhooks', () => {\n it('should list all webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('webhooks');\n expect(Array.isArray(result.webhooks)).toBe(true);\n }\n });\n\n it('should list only enabled webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks(true);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success && result.webhooks) {\n for (const webhook of result.webhooks) {\n expect(webhook.enabled).toBe(true);\n }\n }\n });\n });\n\n describe('updateWebhook', () => {\n it('should update webhook name', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n name: 'Updated Webhook Name',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.name).toBe('Updated Webhook Name');\n }\n });\n\n it('should disable webhook', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n enabled: false,\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.enabled).toBe(false);\n }\n });\n });\n\n describe('getWebhookDeliveries', () => {\n it('should get delivery history', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getWebhookDeliveries(webhookId, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('deliveries');\n }\n });\n });\n\n describe('deleteWebhook', () => {\n it('should delete a webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n // Create a webhook to delete\n const testId = TestData.generateTestId();\n const createResult = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/delete/${id}`,\n events: ['meeting.completed'],\n name: `Delete Test ${id}`,\n });\n } catch {\n return null;\n }\n }, testId, testWorkspaceId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.deleteWebhook(webhookId);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/webhooks.spec.ts"},"span":[4389,4392],"sourceCode":"/**\n * Webhook E2E Tests\n *\n * Tests for webhook CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Webhook Operations', () => {\n let testWebhookId: string | null = null;\n let testWorkspaceId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Get a workspace ID for webhook operations\n const workspaces = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWorkspaces();\n return response?.workspaces ?? [];\n } catch {\n return [];\n }\n });\n // Only use workspace if it has a valid (non-null) ID\n const nullUuid = '00000000-0000-0000-0000-000000000000';\n if (workspaces.length > 0 && workspaces[0].id && workspaces[0].id !== nullUuid) {\n testWorkspaceId = workspaces[0].id;\n }\n });\n\n after(async () => {\n if (testWebhookId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(id);\n }, testWebhookId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('registerWebhook', () => {\n it('should register a new webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Test Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook).toHaveProperty('id');\n expect(result.webhook).toHaveProperty('url');\n expect(result.webhook).toHaveProperty('events');\n testWebhookId = result.webhook.id;\n }\n });\n\n it('should register webhook with multiple events', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed', 'summary.generated', 'recording.started'],\n name: `Multi-Event Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook.events.length).toBe(3);\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n\n it('should register webhook with secret', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Secret Webhook ${id}`,\n secret: 'my-webhook-secret',\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n });\n\n describe('listWebhooks', () => {\n it('should list all webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('webhooks');\n expect(Array.isArray(result.webhooks)).toBe(true);\n }\n });\n\n it('should list only enabled webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks(true);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success && result.webhooks) {\n for (const webhook of result.webhooks) {\n expect(webhook.enabled).toBe(true);\n }\n }\n });\n });\n\n describe('updateWebhook', () => {\n it('should update webhook name', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n name: 'Updated Webhook Name',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.name).toBe('Updated Webhook Name');\n }\n });\n\n it('should disable webhook', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n enabled: false,\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.enabled).toBe(false);\n }\n });\n });\n\n describe('getWebhookDeliveries', () => {\n it('should get delivery history', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getWebhookDeliveries(webhookId, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('deliveries');\n }\n });\n });\n\n describe('deleteWebhook', () => {\n it('should delete a webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n // Create a webhook to delete\n const testId = TestData.generateTestId();\n const createResult = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/delete/${id}`,\n events: ['meeting.completed'],\n name: `Delete Test ${id}`,\n });\n } catch {\n return null;\n }\n }, testId, testWorkspaceId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.deleteWebhook(webhookId);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/webhooks.spec.ts"},"span":[4775,4778],"sourceCode":"/**\n * Webhook E2E Tests\n *\n * Tests for webhook CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Webhook Operations', () => {\n let testWebhookId: string | null = null;\n let testWorkspaceId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Get a workspace ID for webhook operations\n const workspaces = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWorkspaces();\n return response?.workspaces ?? [];\n } catch {\n return [];\n }\n });\n // Only use workspace if it has a valid (non-null) ID\n const nullUuid = '00000000-0000-0000-0000-000000000000';\n if (workspaces.length > 0 && workspaces[0].id && workspaces[0].id !== nullUuid) {\n testWorkspaceId = workspaces[0].id;\n }\n });\n\n after(async () => {\n if (testWebhookId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(id);\n }, testWebhookId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('registerWebhook', () => {\n it('should register a new webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Test Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook).toHaveProperty('id');\n expect(result.webhook).toHaveProperty('url');\n expect(result.webhook).toHaveProperty('events');\n testWebhookId = result.webhook.id;\n }\n });\n\n it('should register webhook with multiple events', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed', 'summary.generated', 'recording.started'],\n name: `Multi-Event Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook.events.length).toBe(3);\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n\n it('should register webhook with secret', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Secret Webhook ${id}`,\n secret: 'my-webhook-secret',\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n });\n\n describe('listWebhooks', () => {\n it('should list all webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('webhooks');\n expect(Array.isArray(result.webhooks)).toBe(true);\n }\n });\n\n it('should list only enabled webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks(true);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success && result.webhooks) {\n for (const webhook of result.webhooks) {\n expect(webhook.enabled).toBe(true);\n }\n }\n });\n });\n\n describe('updateWebhook', () => {\n it('should update webhook name', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n name: 'Updated Webhook Name',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.name).toBe('Updated Webhook Name');\n }\n });\n\n it('should disable webhook', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n enabled: false,\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.enabled).toBe(false);\n }\n });\n });\n\n describe('getWebhookDeliveries', () => {\n it('should get delivery history', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getWebhookDeliveries(webhookId, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('deliveries');\n }\n });\n });\n\n describe('deleteWebhook', () => {\n it('should delete a webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n // Create a webhook to delete\n const testId = TestData.generateTestId();\n const createResult = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/delete/${id}`,\n events: ['meeting.completed'],\n name: `Delete Test ${id}`,\n });\n } catch {\n return null;\n }\n }, testId, testWorkspaceId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.deleteWebhook(webhookId);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/webhooks.spec.ts"},"span":[5070,5073],"sourceCode":"/**\n * Webhook E2E Tests\n *\n * Tests for webhook CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Webhook Operations', () => {\n let testWebhookId: string | null = null;\n let testWorkspaceId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Get a workspace ID for webhook operations\n const workspaces = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWorkspaces();\n return response?.workspaces ?? [];\n } catch {\n return [];\n }\n });\n // Only use workspace if it has a valid (non-null) ID\n const nullUuid = '00000000-0000-0000-0000-000000000000';\n if (workspaces.length > 0 && workspaces[0].id && workspaces[0].id !== nullUuid) {\n testWorkspaceId = workspaces[0].id;\n }\n });\n\n after(async () => {\n if (testWebhookId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(id);\n }, testWebhookId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('registerWebhook', () => {\n it('should register a new webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Test Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook).toHaveProperty('id');\n expect(result.webhook).toHaveProperty('url');\n expect(result.webhook).toHaveProperty('events');\n testWebhookId = result.webhook.id;\n }\n });\n\n it('should register webhook with multiple events', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed', 'summary.generated', 'recording.started'],\n name: `Multi-Event Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook.events.length).toBe(3);\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n\n it('should register webhook with secret', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Secret Webhook ${id}`,\n secret: 'my-webhook-secret',\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n });\n\n describe('listWebhooks', () => {\n it('should list all webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('webhooks');\n expect(Array.isArray(result.webhooks)).toBe(true);\n }\n });\n\n it('should list only enabled webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks(true);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success && result.webhooks) {\n for (const webhook of result.webhooks) {\n expect(webhook.enabled).toBe(true);\n }\n }\n });\n });\n\n describe('updateWebhook', () => {\n it('should update webhook name', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n name: 'Updated Webhook Name',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.name).toBe('Updated Webhook Name');\n }\n });\n\n it('should disable webhook', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n enabled: false,\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.enabled).toBe(false);\n }\n });\n });\n\n describe('getWebhookDeliveries', () => {\n it('should get delivery history', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getWebhookDeliveries(webhookId, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('deliveries');\n }\n });\n });\n\n describe('deleteWebhook', () => {\n it('should delete a webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n // Create a webhook to delete\n const testId = TestData.generateTestId();\n const createResult = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/delete/${id}`,\n events: ['meeting.completed'],\n name: `Delete Test ${id}`,\n });\n } catch {\n return null;\n }\n }, testId, testWorkspaceId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.deleteWebhook(webhookId);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/webhooks.spec.ts"},"span":[5677,5680],"sourceCode":"/**\n * Webhook E2E Tests\n *\n * Tests for webhook CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Webhook Operations', () => {\n let testWebhookId: string | null = null;\n let testWorkspaceId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Get a workspace ID for webhook operations\n const workspaces = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWorkspaces();\n return response?.workspaces ?? [];\n } catch {\n return [];\n }\n });\n // Only use workspace if it has a valid (non-null) ID\n const nullUuid = '00000000-0000-0000-0000-000000000000';\n if (workspaces.length > 0 && workspaces[0].id && workspaces[0].id !== nullUuid) {\n testWorkspaceId = workspaces[0].id;\n }\n });\n\n after(async () => {\n if (testWebhookId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(id);\n }, testWebhookId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('registerWebhook', () => {\n it('should register a new webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Test Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook).toHaveProperty('id');\n expect(result.webhook).toHaveProperty('url');\n expect(result.webhook).toHaveProperty('events');\n testWebhookId = result.webhook.id;\n }\n });\n\n it('should register webhook with multiple events', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed', 'summary.generated', 'recording.started'],\n name: `Multi-Event Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook.events.length).toBe(3);\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n\n it('should register webhook with secret', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Secret Webhook ${id}`,\n secret: 'my-webhook-secret',\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n });\n\n describe('listWebhooks', () => {\n it('should list all webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('webhooks');\n expect(Array.isArray(result.webhooks)).toBe(true);\n }\n });\n\n it('should list only enabled webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks(true);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success && result.webhooks) {\n for (const webhook of result.webhooks) {\n expect(webhook.enabled).toBe(true);\n }\n }\n });\n });\n\n describe('updateWebhook', () => {\n it('should update webhook name', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n name: 'Updated Webhook Name',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.name).toBe('Updated Webhook Name');\n }\n });\n\n it('should disable webhook', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n enabled: false,\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.enabled).toBe(false);\n }\n });\n });\n\n describe('getWebhookDeliveries', () => {\n it('should get delivery history', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getWebhookDeliveries(webhookId, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('deliveries');\n }\n });\n });\n\n describe('deleteWebhook', () => {\n it('should delete a webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n // Create a webhook to delete\n const testId = TestData.generateTestId();\n const createResult = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/delete/${id}`,\n events: ['meeting.completed'],\n name: `Delete Test ${id}`,\n });\n } catch {\n return null;\n }\n }, testId, testWorkspaceId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.deleteWebhook(webhookId);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/webhooks.spec.ts"},"span":[6457,6460],"sourceCode":"/**\n * Webhook E2E Tests\n *\n * Tests for webhook CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Webhook Operations', () => {\n let testWebhookId: string | null = null;\n let testWorkspaceId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Get a workspace ID for webhook operations\n const workspaces = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWorkspaces();\n return response?.workspaces ?? [];\n } catch {\n return [];\n }\n });\n // Only use workspace if it has a valid (non-null) ID\n const nullUuid = '00000000-0000-0000-0000-000000000000';\n if (workspaces.length > 0 && workspaces[0].id && workspaces[0].id !== nullUuid) {\n testWorkspaceId = workspaces[0].id;\n }\n });\n\n after(async () => {\n if (testWebhookId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(id);\n }, testWebhookId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('registerWebhook', () => {\n it('should register a new webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Test Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook).toHaveProperty('id');\n expect(result.webhook).toHaveProperty('url');\n expect(result.webhook).toHaveProperty('events');\n testWebhookId = result.webhook.id;\n }\n });\n\n it('should register webhook with multiple events', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed', 'summary.generated', 'recording.started'],\n name: `Multi-Event Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook.events.length).toBe(3);\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n\n it('should register webhook with secret', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Secret Webhook ${id}`,\n secret: 'my-webhook-secret',\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n });\n\n describe('listWebhooks', () => {\n it('should list all webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('webhooks');\n expect(Array.isArray(result.webhooks)).toBe(true);\n }\n });\n\n it('should list only enabled webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks(true);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success && result.webhooks) {\n for (const webhook of result.webhooks) {\n expect(webhook.enabled).toBe(true);\n }\n }\n });\n });\n\n describe('updateWebhook', () => {\n it('should update webhook name', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n name: 'Updated Webhook Name',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.name).toBe('Updated Webhook Name');\n }\n });\n\n it('should disable webhook', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n enabled: false,\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.enabled).toBe(false);\n }\n });\n });\n\n describe('getWebhookDeliveries', () => {\n it('should get delivery history', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getWebhookDeliveries(webhookId, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('deliveries');\n }\n });\n });\n\n describe('deleteWebhook', () => {\n it('should delete a webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n // Create a webhook to delete\n const testId = TestData.generateTestId();\n const createResult = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/delete/${id}`,\n events: ['meeting.completed'],\n name: `Delete Test ${id}`,\n });\n } catch {\n return null;\n }\n }, testId, testWorkspaceId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.deleteWebhook(webhookId);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/webhooks.spec.ts"},"span":[7230,7233],"sourceCode":"/**\n * Webhook E2E Tests\n *\n * Tests for webhook CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Webhook Operations', () => {\n let testWebhookId: string | null = null;\n let testWorkspaceId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Get a workspace ID for webhook operations\n const workspaces = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWorkspaces();\n return response?.workspaces ?? [];\n } catch {\n return [];\n }\n });\n // Only use workspace if it has a valid (non-null) ID\n const nullUuid = '00000000-0000-0000-0000-000000000000';\n if (workspaces.length > 0 && workspaces[0].id && workspaces[0].id !== nullUuid) {\n testWorkspaceId = workspaces[0].id;\n }\n });\n\n after(async () => {\n if (testWebhookId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(id);\n }, testWebhookId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('registerWebhook', () => {\n it('should register a new webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Test Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook).toHaveProperty('id');\n expect(result.webhook).toHaveProperty('url');\n expect(result.webhook).toHaveProperty('events');\n testWebhookId = result.webhook.id;\n }\n });\n\n it('should register webhook with multiple events', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed', 'summary.generated', 'recording.started'],\n name: `Multi-Event Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook.events.length).toBe(3);\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n\n it('should register webhook with secret', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Secret Webhook ${id}`,\n secret: 'my-webhook-secret',\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n });\n\n describe('listWebhooks', () => {\n it('should list all webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('webhooks');\n expect(Array.isArray(result.webhooks)).toBe(true);\n }\n });\n\n it('should list only enabled webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks(true);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success && result.webhooks) {\n for (const webhook of result.webhooks) {\n expect(webhook.enabled).toBe(true);\n }\n }\n });\n });\n\n describe('updateWebhook', () => {\n it('should update webhook name', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n name: 'Updated Webhook Name',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.name).toBe('Updated Webhook Name');\n }\n });\n\n it('should disable webhook', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n enabled: false,\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.enabled).toBe(false);\n }\n });\n });\n\n describe('getWebhookDeliveries', () => {\n it('should get delivery history', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getWebhookDeliveries(webhookId, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('deliveries');\n }\n });\n });\n\n describe('deleteWebhook', () => {\n it('should delete a webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n // Create a webhook to delete\n const testId = TestData.generateTestId();\n const createResult = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/delete/${id}`,\n events: ['meeting.completed'],\n name: `Delete Test ${id}`,\n });\n } catch {\n return null;\n }\n }, testId, testWorkspaceId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.deleteWebhook(webhookId);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/webhooks.spec.ts"},"span":[8029,8032],"sourceCode":"/**\n * Webhook E2E Tests\n *\n * Tests for webhook CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Webhook Operations', () => {\n let testWebhookId: string | null = null;\n let testWorkspaceId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Get a workspace ID for webhook operations\n const workspaces = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWorkspaces();\n return response?.workspaces ?? [];\n } catch {\n return [];\n }\n });\n // Only use workspace if it has a valid (non-null) ID\n const nullUuid = '00000000-0000-0000-0000-000000000000';\n if (workspaces.length > 0 && workspaces[0].id && workspaces[0].id !== nullUuid) {\n testWorkspaceId = workspaces[0].id;\n }\n });\n\n after(async () => {\n if (testWebhookId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(id);\n }, testWebhookId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('registerWebhook', () => {\n it('should register a new webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Test Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook).toHaveProperty('id');\n expect(result.webhook).toHaveProperty('url');\n expect(result.webhook).toHaveProperty('events');\n testWebhookId = result.webhook.id;\n }\n });\n\n it('should register webhook with multiple events', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed', 'summary.generated', 'recording.started'],\n name: `Multi-Event Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook.events.length).toBe(3);\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n\n it('should register webhook with secret', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Secret Webhook ${id}`,\n secret: 'my-webhook-secret',\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n });\n\n describe('listWebhooks', () => {\n it('should list all webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('webhooks');\n expect(Array.isArray(result.webhooks)).toBe(true);\n }\n });\n\n it('should list only enabled webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks(true);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success && result.webhooks) {\n for (const webhook of result.webhooks) {\n expect(webhook.enabled).toBe(true);\n }\n }\n });\n });\n\n describe('updateWebhook', () => {\n it('should update webhook name', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n name: 'Updated Webhook Name',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.name).toBe('Updated Webhook Name');\n }\n });\n\n it('should disable webhook', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n enabled: false,\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.enabled).toBe(false);\n }\n });\n });\n\n describe('getWebhookDeliveries', () => {\n it('should get delivery history', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getWebhookDeliveries(webhookId, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('deliveries');\n }\n });\n });\n\n describe('deleteWebhook', () => {\n it('should delete a webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n // Create a webhook to delete\n const testId = TestData.generateTestId();\n const createResult = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/delete/${id}`,\n events: ['meeting.completed'],\n name: `Delete Test ${id}`,\n });\n } catch {\n return null;\n }\n }, testId, testWorkspaceId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.deleteWebhook(webhookId);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/webhooks.spec.ts"},"span":[8864,8867],"sourceCode":"/**\n * Webhook E2E Tests\n *\n * Tests for webhook CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Webhook Operations', () => {\n let testWebhookId: string | null = null;\n let testWorkspaceId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Get a workspace ID for webhook operations\n const workspaces = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWorkspaces();\n return response?.workspaces ?? [];\n } catch {\n return [];\n }\n });\n // Only use workspace if it has a valid (non-null) ID\n const nullUuid = '00000000-0000-0000-0000-000000000000';\n if (workspaces.length > 0 && workspaces[0].id && workspaces[0].id !== nullUuid) {\n testWorkspaceId = workspaces[0].id;\n }\n });\n\n after(async () => {\n if (testWebhookId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(id);\n }, testWebhookId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('registerWebhook', () => {\n it('should register a new webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Test Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook).toHaveProperty('id');\n expect(result.webhook).toHaveProperty('url');\n expect(result.webhook).toHaveProperty('events');\n testWebhookId = result.webhook.id;\n }\n });\n\n it('should register webhook with multiple events', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed', 'summary.generated', 'recording.started'],\n name: `Multi-Event Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook.events.length).toBe(3);\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n\n it('should register webhook with secret', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Secret Webhook ${id}`,\n secret: 'my-webhook-secret',\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n });\n\n describe('listWebhooks', () => {\n it('should list all webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('webhooks');\n expect(Array.isArray(result.webhooks)).toBe(true);\n }\n });\n\n it('should list only enabled webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks(true);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success && result.webhooks) {\n for (const webhook of result.webhooks) {\n expect(webhook.enabled).toBe(true);\n }\n }\n });\n });\n\n describe('updateWebhook', () => {\n it('should update webhook name', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n name: 'Updated Webhook Name',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.name).toBe('Updated Webhook Name');\n }\n });\n\n it('should disable webhook', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n enabled: false,\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.enabled).toBe(false);\n }\n });\n });\n\n describe('getWebhookDeliveries', () => {\n it('should get delivery history', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getWebhookDeliveries(webhookId, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('deliveries');\n }\n });\n });\n\n describe('deleteWebhook', () => {\n it('should delete a webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n // Create a webhook to delete\n const testId = TestData.generateTestId();\n const createResult = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/delete/${id}`,\n events: ['meeting.completed'],\n name: `Delete Test ${id}`,\n });\n } catch {\n return null;\n }\n }, testId, testWorkspaceId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.deleteWebhook(webhookId);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noExplicitAny","severity":"error","description":"Unexpected any. Specify a different type.","message":[{"elements":[],"content":"Unexpected "},{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":". Specify a different type."}],"advices":{"advices":[{"log":["info",[{"elements":["Emphasis"],"content":"any"},{"elements":[],"content":" disables many type checking rules. Its use should be avoided."}]]}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/webhooks.spec.ts"},"span":[9358,9361],"sourceCode":"/**\n * Webhook E2E Tests\n *\n * Tests for webhook CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Webhook Operations', () => {\n let testWebhookId: string | null = null;\n let testWorkspaceId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Get a workspace ID for webhook operations\n const workspaces = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWorkspaces();\n return response?.workspaces ?? [];\n } catch {\n return [];\n }\n });\n // Only use workspace if it has a valid (non-null) ID\n const nullUuid = '00000000-0000-0000-0000-000000000000';\n if (workspaces.length > 0 && workspaces[0].id && workspaces[0].id !== nullUuid) {\n testWorkspaceId = workspaces[0].id;\n }\n });\n\n after(async () => {\n if (testWebhookId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(id);\n }, testWebhookId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('registerWebhook', () => {\n it('should register a new webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Test Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook).toHaveProperty('id');\n expect(result.webhook).toHaveProperty('url');\n expect(result.webhook).toHaveProperty('events');\n testWebhookId = result.webhook.id;\n }\n });\n\n it('should register webhook with multiple events', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed', 'summary.generated', 'recording.started'],\n name: `Multi-Event Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook.events.length).toBe(3);\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n\n it('should register webhook with secret', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Secret Webhook ${id}`,\n secret: 'my-webhook-secret',\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n });\n\n describe('listWebhooks', () => {\n it('should list all webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('webhooks');\n expect(Array.isArray(result.webhooks)).toBe(true);\n }\n });\n\n it('should list only enabled webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks(true);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success && result.webhooks) {\n for (const webhook of result.webhooks) {\n expect(webhook.enabled).toBe(true);\n }\n }\n });\n });\n\n describe('updateWebhook', () => {\n it('should update webhook name', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n name: 'Updated Webhook Name',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.name).toBe('Updated Webhook Name');\n }\n });\n\n it('should disable webhook', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n enabled: false,\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.enabled).toBe(false);\n }\n });\n });\n\n describe('getWebhookDeliveries', () => {\n it('should get delivery history', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getWebhookDeliveries(webhookId, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('deliveries');\n }\n });\n });\n\n describe('deleteWebhook', () => {\n it('should delete a webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n // Create a webhook to delete\n const testId = TestData.generateTestId();\n const createResult = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/delete/${id}`,\n events: ['meeting.completed'],\n name: `Delete Test ${id}`,\n });\n } catch {\n return null;\n }\n }, testId, testWorkspaceId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.deleteWebhook(webhookId);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":[],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * Webhook E2E Tests\n * describe('registerWebhook', () => {\n it('should register a new webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n } });\n});\n","ops":[{"diffOp":{"equal":{"range":[0,27]}}},{"equalLines":{"line_count":41}},{"diffOp":{"equal":{"range":[27,148]}}},{"diffOp":{"delete":{"range":[148,205]}}},{"diffOp":{"equal":{"range":[205,229]}}},{"equalLines":{"line_count":251}},{"diffOp":{"equal":{"range":[229,239]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/webhooks.spec.ts"},"span":[1314,1325],"sourceCode":"/**\n * Webhook E2E Tests\n *\n * Tests for webhook CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Webhook Operations', () => {\n let testWebhookId: string | null = null;\n let testWorkspaceId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Get a workspace ID for webhook operations\n const workspaces = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWorkspaces();\n return response?.workspaces ?? [];\n } catch {\n return [];\n }\n });\n // Only use workspace if it has a valid (non-null) ID\n const nullUuid = '00000000-0000-0000-0000-000000000000';\n if (workspaces.length > 0 && workspaces[0].id && workspaces[0].id !== nullUuid) {\n testWorkspaceId = workspaces[0].id;\n }\n });\n\n after(async () => {\n if (testWebhookId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(id);\n }, testWebhookId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('registerWebhook', () => {\n it('should register a new webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Test Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook).toHaveProperty('id');\n expect(result.webhook).toHaveProperty('url');\n expect(result.webhook).toHaveProperty('events');\n testWebhookId = result.webhook.id;\n }\n });\n\n it('should register webhook with multiple events', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed', 'summary.generated', 'recording.started'],\n name: `Multi-Event Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook.events.length).toBe(3);\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n\n it('should register webhook with secret', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Secret Webhook ${id}`,\n secret: 'my-webhook-secret',\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n });\n\n describe('listWebhooks', () => {\n it('should list all webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('webhooks');\n expect(Array.isArray(result.webhooks)).toBe(true);\n }\n });\n\n it('should list only enabled webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks(true);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success && result.webhooks) {\n for (const webhook of result.webhooks) {\n expect(webhook.enabled).toBe(true);\n }\n }\n });\n });\n\n describe('updateWebhook', () => {\n it('should update webhook name', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n name: 'Updated Webhook Name',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.name).toBe('Updated Webhook Name');\n }\n });\n\n it('should disable webhook', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n enabled: false,\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.enabled).toBe(false);\n }\n });\n });\n\n describe('getWebhookDeliveries', () => {\n it('should get delivery history', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getWebhookDeliveries(webhookId, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('deliveries');\n }\n });\n });\n\n describe('deleteWebhook', () => {\n it('should delete a webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n // Create a webhook to delete\n const testId = TestData.generateTestId();\n const createResult = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/delete/${id}`,\n events: ['meeting.completed'],\n name: `Delete Test ${id}`,\n });\n } catch {\n return null;\n }\n }, testId, testWorkspaceId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.deleteWebhook(webhookId);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * Webhook E2E Tests\n *\n it('should register webhook with multiple events', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n } });\n});\n","ops":[{"diffOp":{"equal":{"range":[0,27]}}},{"equalLines":{"line_count":74}},{"diffOp":{"equal":{"range":[27,126]}}},{"diffOp":{"delete":{"range":[126,183]}}},{"diffOp":{"equal":{"range":[183,207]}}},{"equalLines":{"line_count":218}},{"diffOp":{"equal":{"range":[207,217]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/webhooks.spec.ts"},"span":[2498,2509],"sourceCode":"/**\n * Webhook E2E Tests\n *\n * Tests for webhook CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Webhook Operations', () => {\n let testWebhookId: string | null = null;\n let testWorkspaceId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Get a workspace ID for webhook operations\n const workspaces = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWorkspaces();\n return response?.workspaces ?? [];\n } catch {\n return [];\n }\n });\n // Only use workspace if it has a valid (non-null) ID\n const nullUuid = '00000000-0000-0000-0000-000000000000';\n if (workspaces.length > 0 && workspaces[0].id && workspaces[0].id !== nullUuid) {\n testWorkspaceId = workspaces[0].id;\n }\n });\n\n after(async () => {\n if (testWebhookId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(id);\n }, testWebhookId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('registerWebhook', () => {\n it('should register a new webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Test Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook).toHaveProperty('id');\n expect(result.webhook).toHaveProperty('url');\n expect(result.webhook).toHaveProperty('events');\n testWebhookId = result.webhook.id;\n }\n });\n\n it('should register webhook with multiple events', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed', 'summary.generated', 'recording.started'],\n name: `Multi-Event Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook.events.length).toBe(3);\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n\n it('should register webhook with secret', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Secret Webhook ${id}`,\n secret: 'my-webhook-secret',\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n });\n\n describe('listWebhooks', () => {\n it('should list all webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('webhooks');\n expect(Array.isArray(result.webhooks)).toBe(true);\n }\n });\n\n it('should list only enabled webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks(true);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success && result.webhooks) {\n for (const webhook of result.webhooks) {\n expect(webhook.enabled).toBe(true);\n }\n }\n });\n });\n\n describe('updateWebhook', () => {\n it('should update webhook name', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n name: 'Updated Webhook Name',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.name).toBe('Updated Webhook Name');\n }\n });\n\n it('should disable webhook', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n enabled: false,\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.enabled).toBe(false);\n }\n });\n });\n\n describe('getWebhookDeliveries', () => {\n it('should get delivery history', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getWebhookDeliveries(webhookId, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('deliveries');\n }\n });\n });\n\n describe('deleteWebhook', () => {\n it('should delete a webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n // Create a webhook to delete\n const testId = TestData.generateTestId();\n const createResult = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/delete/${id}`,\n events: ['meeting.completed'],\n name: `Delete Test ${id}`,\n });\n } catch {\n return null;\n }\n }, testId, testWorkspaceId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.deleteWebhook(webhookId);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * Webhook E2E Tests\n *\n it('should register webhook with secret', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n } });\n});\n","ops":[{"diffOp":{"equal":{"range":[0,27]}}},{"equalLines":{"line_count":109}},{"diffOp":{"equal":{"range":[27,117]}}},{"diffOp":{"delete":{"range":[117,174]}}},{"diffOp":{"equal":{"range":[174,198]}}},{"equalLines":{"line_count":183}},{"diffOp":{"equal":{"range":[198,208]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/webhooks.spec.ts"},"span":[3774,3785],"sourceCode":"/**\n * Webhook E2E Tests\n *\n * Tests for webhook CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Webhook Operations', () => {\n let testWebhookId: string | null = null;\n let testWorkspaceId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Get a workspace ID for webhook operations\n const workspaces = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWorkspaces();\n return response?.workspaces ?? [];\n } catch {\n return [];\n }\n });\n // Only use workspace if it has a valid (non-null) ID\n const nullUuid = '00000000-0000-0000-0000-000000000000';\n if (workspaces.length > 0 && workspaces[0].id && workspaces[0].id !== nullUuid) {\n testWorkspaceId = workspaces[0].id;\n }\n });\n\n after(async () => {\n if (testWebhookId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(id);\n }, testWebhookId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('registerWebhook', () => {\n it('should register a new webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Test Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook).toHaveProperty('id');\n expect(result.webhook).toHaveProperty('url');\n expect(result.webhook).toHaveProperty('events');\n testWebhookId = result.webhook.id;\n }\n });\n\n it('should register webhook with multiple events', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed', 'summary.generated', 'recording.started'],\n name: `Multi-Event Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook.events.length).toBe(3);\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n\n it('should register webhook with secret', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Secret Webhook ${id}`,\n secret: 'my-webhook-secret',\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n });\n\n describe('listWebhooks', () => {\n it('should list all webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('webhooks');\n expect(Array.isArray(result.webhooks)).toBe(true);\n }\n });\n\n it('should list only enabled webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks(true);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success && result.webhooks) {\n for (const webhook of result.webhooks) {\n expect(webhook.enabled).toBe(true);\n }\n }\n });\n });\n\n describe('updateWebhook', () => {\n it('should update webhook name', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n name: 'Updated Webhook Name',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.name).toBe('Updated Webhook Name');\n }\n });\n\n it('should disable webhook', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n enabled: false,\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.enabled).toBe(false);\n }\n });\n });\n\n describe('getWebhookDeliveries', () => {\n it('should get delivery history', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getWebhookDeliveries(webhookId, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('deliveries');\n }\n });\n });\n\n describe('deleteWebhook', () => {\n it('should delete a webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n // Create a webhook to delete\n const testId = TestData.generateTestId();\n const createResult = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/delete/${id}`,\n events: ['meeting.completed'],\n name: `Delete Test ${id}`,\n });\n } catch {\n return null;\n }\n }, testId, testWorkspaceId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.deleteWebhook(webhookId);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * Webhook E2E Tests\n * describe('updateWebhook', () => {\n it('should update webhook name', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n } });\n});\n","ops":[{"diffOp":{"equal":{"range":[0,27]}}},{"equalLines":{"line_count":185}},{"diffOp":{"equal":{"range":[27,141]}}},{"diffOp":{"delete":{"range":[141,199]}}},{"diffOp":{"equal":{"range":[199,223]}}},{"equalLines":{"line_count":107}},{"diffOp":{"equal":{"range":[223,233]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/webhooks.spec.ts"},"span":[6285,6296],"sourceCode":"/**\n * Webhook E2E Tests\n *\n * Tests for webhook CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Webhook Operations', () => {\n let testWebhookId: string | null = null;\n let testWorkspaceId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Get a workspace ID for webhook operations\n const workspaces = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWorkspaces();\n return response?.workspaces ?? [];\n } catch {\n return [];\n }\n });\n // Only use workspace if it has a valid (non-null) ID\n const nullUuid = '00000000-0000-0000-0000-000000000000';\n if (workspaces.length > 0 && workspaces[0].id && workspaces[0].id !== nullUuid) {\n testWorkspaceId = workspaces[0].id;\n }\n });\n\n after(async () => {\n if (testWebhookId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(id);\n }, testWebhookId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('registerWebhook', () => {\n it('should register a new webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Test Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook).toHaveProperty('id');\n expect(result.webhook).toHaveProperty('url');\n expect(result.webhook).toHaveProperty('events');\n testWebhookId = result.webhook.id;\n }\n });\n\n it('should register webhook with multiple events', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed', 'summary.generated', 'recording.started'],\n name: `Multi-Event Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook.events.length).toBe(3);\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n\n it('should register webhook with secret', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Secret Webhook ${id}`,\n secret: 'my-webhook-secret',\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n });\n\n describe('listWebhooks', () => {\n it('should list all webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('webhooks');\n expect(Array.isArray(result.webhooks)).toBe(true);\n }\n });\n\n it('should list only enabled webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks(true);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success && result.webhooks) {\n for (const webhook of result.webhooks) {\n expect(webhook.enabled).toBe(true);\n }\n }\n });\n });\n\n describe('updateWebhook', () => {\n it('should update webhook name', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n name: 'Updated Webhook Name',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.name).toBe('Updated Webhook Name');\n }\n });\n\n it('should disable webhook', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n enabled: false,\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.enabled).toBe(false);\n }\n });\n });\n\n describe('getWebhookDeliveries', () => {\n it('should get delivery history', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getWebhookDeliveries(webhookId, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('deliveries');\n }\n });\n });\n\n describe('deleteWebhook', () => {\n it('should delete a webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n // Create a webhook to delete\n const testId = TestData.generateTestId();\n const createResult = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/delete/${id}`,\n events: ['meeting.completed'],\n name: `Delete Test ${id}`,\n });\n } catch {\n return null;\n }\n }, testId, testWorkspaceId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.deleteWebhook(webhookId);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * Webhook E2E Tests\n *\n it('should disable webhook', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n } });\n});\n","ops":[{"diffOp":{"equal":{"range":[0,27]}}},{"equalLines":{"line_count":210}},{"diffOp":{"equal":{"range":[27,102]}}},{"diffOp":{"delete":{"range":[102,160]}}},{"diffOp":{"equal":{"range":[160,184]}}},{"equalLines":{"line_count":82}},{"diffOp":{"equal":{"range":[184,194]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/webhooks.spec.ts"},"span":[7058,7069],"sourceCode":"/**\n * Webhook E2E Tests\n *\n * Tests for webhook CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Webhook Operations', () => {\n let testWebhookId: string | null = null;\n let testWorkspaceId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Get a workspace ID for webhook operations\n const workspaces = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWorkspaces();\n return response?.workspaces ?? [];\n } catch {\n return [];\n }\n });\n // Only use workspace if it has a valid (non-null) ID\n const nullUuid = '00000000-0000-0000-0000-000000000000';\n if (workspaces.length > 0 && workspaces[0].id && workspaces[0].id !== nullUuid) {\n testWorkspaceId = workspaces[0].id;\n }\n });\n\n after(async () => {\n if (testWebhookId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(id);\n }, testWebhookId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('registerWebhook', () => {\n it('should register a new webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Test Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook).toHaveProperty('id');\n expect(result.webhook).toHaveProperty('url');\n expect(result.webhook).toHaveProperty('events');\n testWebhookId = result.webhook.id;\n }\n });\n\n it('should register webhook with multiple events', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed', 'summary.generated', 'recording.started'],\n name: `Multi-Event Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook.events.length).toBe(3);\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n\n it('should register webhook with secret', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Secret Webhook ${id}`,\n secret: 'my-webhook-secret',\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n });\n\n describe('listWebhooks', () => {\n it('should list all webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('webhooks');\n expect(Array.isArray(result.webhooks)).toBe(true);\n }\n });\n\n it('should list only enabled webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks(true);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success && result.webhooks) {\n for (const webhook of result.webhooks) {\n expect(webhook.enabled).toBe(true);\n }\n }\n });\n });\n\n describe('updateWebhook', () => {\n it('should update webhook name', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n name: 'Updated Webhook Name',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.name).toBe('Updated Webhook Name');\n }\n });\n\n it('should disable webhook', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n enabled: false,\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.enabled).toBe(false);\n }\n });\n });\n\n describe('getWebhookDeliveries', () => {\n it('should get delivery history', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getWebhookDeliveries(webhookId, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('deliveries');\n }\n });\n });\n\n describe('deleteWebhook', () => {\n it('should delete a webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n // Create a webhook to delete\n const testId = TestData.generateTestId();\n const createResult = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/delete/${id}`,\n events: ['meeting.completed'],\n name: `Delete Test ${id}`,\n });\n } catch {\n return null;\n }\n }, testId, testWorkspaceId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.deleteWebhook(webhookId);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * Webhook E2E Tests\n * describe('getWebhookDeliveries', () => {\n it('should get delivery history', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n } });\n});\n","ops":[{"diffOp":{"equal":{"range":[0,27]}}},{"equalLines":{"line_count":237}},{"diffOp":{"equal":{"range":[27,149]}}},{"diffOp":{"delete":{"range":[149,207]}}},{"diffOp":{"equal":{"range":[207,231]}}},{"equalLines":{"line_count":55}},{"diffOp":{"equal":{"range":[231,241]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/webhooks.spec.ts"},"span":[7857,7868],"sourceCode":"/**\n * Webhook E2E Tests\n *\n * Tests for webhook CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Webhook Operations', () => {\n let testWebhookId: string | null = null;\n let testWorkspaceId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Get a workspace ID for webhook operations\n const workspaces = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWorkspaces();\n return response?.workspaces ?? [];\n } catch {\n return [];\n }\n });\n // Only use workspace if it has a valid (non-null) ID\n const nullUuid = '00000000-0000-0000-0000-000000000000';\n if (workspaces.length > 0 && workspaces[0].id && workspaces[0].id !== nullUuid) {\n testWorkspaceId = workspaces[0].id;\n }\n });\n\n after(async () => {\n if (testWebhookId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(id);\n }, testWebhookId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('registerWebhook', () => {\n it('should register a new webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Test Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook).toHaveProperty('id');\n expect(result.webhook).toHaveProperty('url');\n expect(result.webhook).toHaveProperty('events');\n testWebhookId = result.webhook.id;\n }\n });\n\n it('should register webhook with multiple events', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed', 'summary.generated', 'recording.started'],\n name: `Multi-Event Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook.events.length).toBe(3);\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n\n it('should register webhook with secret', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Secret Webhook ${id}`,\n secret: 'my-webhook-secret',\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n });\n\n describe('listWebhooks', () => {\n it('should list all webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('webhooks');\n expect(Array.isArray(result.webhooks)).toBe(true);\n }\n });\n\n it('should list only enabled webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks(true);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success && result.webhooks) {\n for (const webhook of result.webhooks) {\n expect(webhook.enabled).toBe(true);\n }\n }\n });\n });\n\n describe('updateWebhook', () => {\n it('should update webhook name', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n name: 'Updated Webhook Name',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.name).toBe('Updated Webhook Name');\n }\n });\n\n it('should disable webhook', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n enabled: false,\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.enabled).toBe(false);\n }\n });\n });\n\n describe('getWebhookDeliveries', () => {\n it('should get delivery history', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getWebhookDeliveries(webhookId, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('deliveries');\n }\n });\n });\n\n describe('deleteWebhook', () => {\n it('should delete a webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n // Create a webhook to delete\n const testId = TestData.generateTestId();\n const createResult = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/delete/${id}`,\n events: ['meeting.completed'],\n name: `Delete Test ${id}`,\n });\n } catch {\n return null;\n }\n }, testId, testWorkspaceId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.deleteWebhook(webhookId);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * Webhook E2E Tests\n * describe('deleteWebhook', () => {\n it('should delete a webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n } });\n});\n","ops":[{"diffOp":{"equal":{"range":[0,27]}}},{"equalLines":{"line_count":261}},{"diffOp":{"equal":{"range":[27,140]}}},{"diffOp":{"delete":{"range":[140,197]}}},{"diffOp":{"equal":{"range":[197,221]}}},{"equalLines":{"line_count":31}},{"diffOp":{"equal":{"range":[221,231]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"e2e-native/webhooks.spec.ts"},"span":[8597,8608],"sourceCode":"/**\n * Webhook E2E Tests\n *\n * Tests for webhook CRUD operations.\n */\n\nimport { waitForAppReady, TestData } from './fixtures';\n\ndescribe('Webhook Operations', () => {\n let testWebhookId: string | null = null;\n let testWorkspaceId: string | null = null;\n\n before(async () => {\n await waitForAppReady();\n // Get a workspace ID for webhook operations\n const workspaces = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWorkspaces();\n return response?.workspaces ?? [];\n } catch {\n return [];\n }\n });\n // Only use workspace if it has a valid (non-null) ID\n const nullUuid = '00000000-0000-0000-0000-000000000000';\n if (workspaces.length > 0 && workspaces[0].id && workspaces[0].id !== nullUuid) {\n testWorkspaceId = workspaces[0].id;\n }\n });\n\n after(async () => {\n if (testWebhookId) {\n try {\n await browser.execute(async (id) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(id);\n }, testWebhookId);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n\n describe('registerWebhook', () => {\n it('should register a new webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Test Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook).toHaveProperty('id');\n expect(result.webhook).toHaveProperty('url');\n expect(result.webhook).toHaveProperty('events');\n testWebhookId = result.webhook.id;\n }\n });\n\n it('should register webhook with multiple events', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed', 'summary.generated', 'recording.started'],\n name: `Multi-Event Webhook ${id}`,\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.webhook.events.length).toBe(3);\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n\n it('should register webhook with secret', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n const testId = TestData.generateTestId();\n\n const result = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const webhook = await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/webhook/${id}`,\n events: ['meeting.completed'],\n name: `Secret Webhook ${id}`,\n secret: 'my-webhook-secret',\n });\n return { success: true, webhook };\n } catch (e: any) {\n const errorMsg = e?.message || e?.error || (typeof e === 'object' ? JSON.stringify(e) : String(e));\n return { success: false, error: errorMsg };\n }\n }, testId, testWorkspaceId);\n\n expect(result).toBeDefined();\n if (result.success) {\n // Cleanup\n await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n await api.deleteWebhook(webhookId);\n }, result.webhook.id);\n }\n });\n });\n\n describe('listWebhooks', () => {\n it('should list all webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks();\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('webhooks');\n expect(Array.isArray(result.webhooks)).toBe(true);\n }\n });\n\n it('should list only enabled webhooks', async () => {\n const result = await browser.execute(async () => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.listWebhooks(true);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n });\n\n expect(result).toBeDefined();\n if (result.success && result.webhooks) {\n for (const webhook of result.webhooks) {\n expect(webhook.enabled).toBe(true);\n }\n }\n });\n });\n\n describe('updateWebhook', () => {\n it('should update webhook name', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n name: 'Updated Webhook Name',\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.name).toBe('Updated Webhook Name');\n }\n });\n\n it('should disable webhook', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const updated = await api.updateWebhook({\n webhook_id: webhookId,\n enabled: false,\n });\n return { success: true, updated };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result.updated.enabled).toBe(false);\n }\n });\n });\n\n describe('getWebhookDeliveries', () => {\n it('should get delivery history', async () => {\n if (!testWebhookId) {\n console.log('Skipping: no test webhook created');\n return;\n }\n\n const result = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.getWebhookDeliveries(webhookId, 10);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, testWebhookId);\n\n expect(result).toBeDefined();\n if (result.success) {\n expect(result).toHaveProperty('deliveries');\n }\n });\n });\n\n describe('deleteWebhook', () => {\n it('should delete a webhook', async () => {\n if (!testWorkspaceId) {\n console.log('Skipping: no workspace available');\n return;\n }\n\n // Create a webhook to delete\n const testId = TestData.generateTestId();\n const createResult = await browser.execute(async (id, workspaceId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n return await api.registerWebhook({\n workspace_id: workspaceId,\n url: `https://example.com/delete/${id}`,\n events: ['meeting.completed'],\n name: `Delete Test ${id}`,\n });\n } catch {\n return null;\n }\n }, testId, testWorkspaceId);\n\n if (createResult?.id) {\n const deleteResult = await browser.execute(async (webhookId) => {\n const api = (window as any).__NOTEFLOW_API__;\n try {\n const response = await api.deleteWebhook(webhookId);\n return { success: true, ...response };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : String(e) };\n }\n }, createResult.id);\n\n expect(deleteResult.success).toBe(true);\n }\n });\n });\n});\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"// Cached read-only API adapter for offline mode\n\nimport { startTauriEventBridge } from '@/lib/tauri-events'; setConnectionServerUrl(serverUrl ?? null);\n await preferences.initialize();\n await startTauriEventBridge().catch((err: unknown) => {\n console.warn('[CachedAdapter] Event bridge initialization failed:', err);\n });\n return info; },\n};\n","ops":[{"diffOp":{"equal":{"range":[0,109]}}},{"equalLines":{"line_count":128}},{"diffOp":{"equal":{"range":[109,245]}}},{"diffOp":{"delete":{"range":[245,323]}}},{"diffOp":{"equal":{"range":[323,344]}}},{"equalLines":{"line_count":429}},{"diffOp":{"equal":{"range":[344,352]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"src/api/cached-adapter.ts"},"span":[3718,3730],"sourceCode":"// Cached read-only API adapter for offline mode\n\nimport { startTauriEventBridge } from '@/lib/tauri-events';\nimport { preferences } from '@/lib/preferences';\nimport { meetingCache } from '@/lib/cache/meeting-cache';\nimport type { NoteFlowAPI, TranscriptionStream } from './interface';\nimport type {\n AddAnnotationRequest,\n AddProjectMemberRequest,\n Annotation,\n AudioDeviceInfo,\n CancelDiarizationResult,\n CompleteCalendarAuthResponse,\n CreateMeetingRequest,\n CreateProjectRequest,\n DeleteWebhookResponse,\n DiarizationJobStatus,\n DisconnectOAuthResponse,\n ExportFormat,\n ExportResult,\n ExtractEntitiesResponse,\n ExtractedEntity,\n GetCalendarProvidersResponse,\n GetCurrentUserResponse,\n GetMeetingRequest,\n GetOAuthConnectionStatusResponse,\n GetProjectBySlugRequest,\n GetProjectRequest,\n GetPerformanceMetricsRequest,\n GetPerformanceMetricsResponse,\n GetRecentLogsRequest,\n GetRecentLogsResponse,\n GetSyncStatusResponse,\n GetUserIntegrationsResponse,\n GetWebhookDeliveriesResponse,\n InitiateCalendarAuthResponse,\n ListWorkspacesResponse,\n ListCalendarEventsResponse,\n ListMeetingsRequest,\n ListMeetingsResponse,\n ListProjectMembersRequest,\n ListProjectMembersResponse,\n ListProjectsRequest,\n ListProjectsResponse,\n ListSyncHistoryResponse,\n ListWebhooksResponse,\n Meeting,\n PlaybackInfo,\n Project,\n ProjectMembership,\n RegisteredWebhook,\n RegisterWebhookRequest,\n RemoveProjectMemberRequest,\n RemoveProjectMemberResponse,\n ServerInfo,\n StartIntegrationSyncResponse,\n SwitchWorkspaceResponse,\n Summary,\n TriggerStatus,\n UpdateAnnotationRequest,\n UpdateProjectMemberRoleRequest,\n UpdateProjectRequest,\n UpdateWebhookRequest,\n UserPreferences,\n} from './types';\nimport { initializeTauriAPI, isTauriEnvironment } from './tauri-adapter';\nimport { setAPIInstance } from './interface';\nimport { setConnectionMode, setConnectionServerUrl } from './connection-state';\nimport { IdentityDefaults } from './constants';\n\nconst readOnlyError = () =>\n new Error('Cached read-only mode: reconnect to enable write operations.');\n\nconst rejectReadOnly = async <T>(): Promise<T> => {\n throw readOnlyError();\n};\n\nconst offlineServerInfo: ServerInfo = {\n version: 'offline',\n asr_model: 'unavailable',\n asr_ready: false,\n supported_sample_rates: [],\n max_chunk_size: 0,\n uptime_seconds: 0,\n active_meetings: 0,\n diarization_enabled: false,\n diarization_ready: false,\n};\n\nconst offlineUser: GetCurrentUserResponse = {\n user_id: IdentityDefaults.DEFAULT_USER_ID,\n display_name: IdentityDefaults.DEFAULT_USER_NAME,\n};\n\nconst offlineWorkspaces: ListWorkspacesResponse = {\n workspaces: [\n {\n id: IdentityDefaults.DEFAULT_WORKSPACE_ID,\n name: IdentityDefaults.DEFAULT_WORKSPACE_NAME,\n role: 'owner',\n is_default: true,\n },\n ],\n};\n\nconst offlineProjects: ListProjectsResponse = {\n projects: [\n {\n id: IdentityDefaults.DEFAULT_PROJECT_ID,\n workspace_id: IdentityDefaults.DEFAULT_WORKSPACE_ID,\n name: IdentityDefaults.DEFAULT_PROJECT_NAME,\n slug: 'general',\n description: 'Default project (offline).',\n is_default: true,\n is_archived: false,\n settings: {},\n created_at: 0,\n updated_at: 0,\n },\n ],\n total_count: 1,\n};\n\nasync function connectWithTauri(serverUrl?: string): Promise<ServerInfo> {\n if (!isTauriEnvironment()) {\n throw new Error('Tauri environment required to connect.');\n }\n const tauriAPI = await initializeTauriAPI();\n const info = await tauriAPI.connect(serverUrl);\n setAPIInstance(tauriAPI);\n setConnectionMode('connected');\n setConnectionServerUrl(serverUrl ?? null);\n await preferences.initialize();\n await startTauriEventBridge().catch((err: unknown) => {\n console.warn('[CachedAdapter] Event bridge initialization failed:', err);\n });\n return info;\n}\n\nexport const cachedAPI: NoteFlowAPI = {\n async getServerInfo(): Promise<ServerInfo> {\n return offlineServerInfo;\n },\n\n async connect(serverUrl?: string): Promise<ServerInfo> {\n try {\n return await connectWithTauri(serverUrl);\n } catch (error) {\n setConnectionMode('cached', error instanceof Error ? error.message : null);\n throw error;\n }\n },\n\n async disconnect(): Promise<void> {\n setConnectionMode('cached');\n },\n\n async isConnected(): Promise<boolean> {\n return false;\n },\n\n async getCurrentUser(): Promise<GetCurrentUserResponse> {\n return offlineUser;\n },\n\n async listWorkspaces(): Promise<ListWorkspacesResponse> {\n return offlineWorkspaces;\n },\n\n async switchWorkspace(workspaceId: string): Promise<SwitchWorkspaceResponse> {\n const workspace = offlineWorkspaces.workspaces.find((item) => item.id === workspaceId);\n return {\n success: Boolean(workspace),\n workspace,\n };\n },\n\n async createProject(_request: CreateProjectRequest): Promise<Project> {\n return rejectReadOnly();\n },\n\n async getProject(request: GetProjectRequest): Promise<Project> {\n const project = offlineProjects.projects.find((item) => item.id === request.project_id);\n if (!project) {\n throw new Error('Project not available in offline cache.');\n }\n return project;\n },\n\n async getProjectBySlug(request: GetProjectBySlugRequest): Promise<Project> {\n const project = offlineProjects.projects.find(\n (item) => item.workspace_id === request.workspace_id && item.slug === request.slug\n );\n if (!project) {\n throw new Error('Project not available in offline cache.');\n }\n return project;\n },\n\n async listProjects(request: ListProjectsRequest): Promise<ListProjectsResponse> {\n const projects = offlineProjects.projects.filter(\n (item) => item.workspace_id === request.workspace_id\n );\n return {\n projects,\n total_count: projects.length,\n };\n },\n\n async updateProject(_request: UpdateProjectRequest): Promise<Project> {\n return rejectReadOnly();\n },\n\n async archiveProject(_projectId: string): Promise<Project> {\n return rejectReadOnly();\n },\n\n async restoreProject(_projectId: string): Promise<Project> {\n return rejectReadOnly();\n },\n\n async deleteProject(_projectId: string): Promise<boolean> {\n return rejectReadOnly();\n },\n\n async setActiveProject(_request: { workspace_id: string; project_id?: string }): Promise<void> {\n return;\n },\n\n async getActiveProject(request: { workspace_id: string }): Promise<{ project_id?: string; project: Project }> {\n const project =\n offlineProjects.projects.find((item) => item.workspace_id === request.workspace_id) ??\n offlineProjects.projects[0];\n if (!project) {\n throw new Error('No project available in offline cache.');\n }\n return { project_id: project.id, project };\n },\n\n async addProjectMember(_request: AddProjectMemberRequest): Promise<ProjectMembership> {\n return rejectReadOnly();\n },\n\n async updateProjectMemberRole(\n _request: UpdateProjectMemberRoleRequest\n ): Promise<ProjectMembership> {\n return rejectReadOnly();\n },\n\n async removeProjectMember(\n _request: RemoveProjectMemberRequest\n ): Promise<RemoveProjectMemberResponse> {\n return rejectReadOnly();\n },\n\n async listProjectMembers(\n _request: ListProjectMembersRequest\n ): Promise<ListProjectMembersResponse> {\n return { members: [], total_count: 0 };\n },\n\n async createMeeting(_request: CreateMeetingRequest): Promise<Meeting> {\n return rejectReadOnly();\n },\n\n async listMeetings(request: ListMeetingsRequest): Promise<ListMeetingsResponse> {\n const meetings = meetingCache.listMeetings();\n let filtered = meetings;\n\n if (request.project_id) {\n filtered = filtered.filter((meeting) => meeting.project_id === request.project_id);\n }\n\n if (request.states?.length) {\n filtered = filtered.filter((meeting) => request.states?.includes(meeting.state));\n }\n\n const sortOrder = request.sort_order ?? 'newest';\n filtered = [...filtered].sort((a, b) => {\n const diff = a.created_at - b.created_at;\n return sortOrder === 'oldest' ? diff : -diff;\n });\n\n const offset = request.offset ?? 0;\n const limit = request.limit ?? 50;\n const paged = filtered.slice(offset, offset + limit);\n\n return {\n meetings: paged,\n total_count: filtered.length,\n };\n },\n\n async getMeeting(request: GetMeetingRequest): Promise<Meeting> {\n const cached = meetingCache.getMeeting(request.meeting_id);\n if (!cached) {\n throw new Error('Meeting not available in offline cache.');\n }\n return cached;\n },\n\n async stopMeeting(_meetingId: string): Promise<Meeting> {\n return rejectReadOnly();\n },\n\n async deleteMeeting(_meetingId: string): Promise<boolean> {\n return rejectReadOnly();\n },\n\n async startTranscription(_meetingId: string): Promise<TranscriptionStream> {\n return rejectReadOnly();\n },\n\n async generateSummary(_meetingId: string, _forceRegenerate?: boolean): Promise<Summary> {\n return rejectReadOnly();\n },\n\n async grantCloudConsent(): Promise<void> {\n return rejectReadOnly();\n },\n\n async revokeCloudConsent(): Promise<void> {\n return rejectReadOnly();\n },\n\n async getCloudConsentStatus(): Promise<{ consentGranted: boolean }> {\n return { consentGranted: false };\n },\n\n async listAnnotations(_meetingId: string): Promise<Annotation[]> {\n return [];\n },\n\n async addAnnotation(_request: AddAnnotationRequest): Promise<Annotation> {\n return rejectReadOnly();\n },\n\n async getAnnotation(_annotationId: string): Promise<Annotation> {\n return rejectReadOnly();\n },\n\n async updateAnnotation(_request: UpdateAnnotationRequest): Promise<Annotation> {\n return rejectReadOnly();\n },\n\n async deleteAnnotation(_annotationId: string): Promise<boolean> {\n return rejectReadOnly();\n },\n\n async exportTranscript(_meetingId: string, _format: ExportFormat): Promise<ExportResult> {\n return rejectReadOnly();\n },\n\n async saveExportFile(_content: string, _defaultName: string, _extension: string): Promise<boolean> {\n return rejectReadOnly();\n },\n\n async startPlayback(_meetingId: string, _startTime?: number): Promise<void> {\n return rejectReadOnly();\n },\n\n async pausePlayback(): Promise<void> {\n return rejectReadOnly();\n },\n\n async stopPlayback(): Promise<void> {\n return rejectReadOnly();\n },\n\n async seekPlayback(_position: number): Promise<PlaybackInfo> {\n return rejectReadOnly();\n },\n\n async getPlaybackState(): Promise<PlaybackInfo> {\n return rejectReadOnly();\n },\n\n async refineSpeakers(_meetingId: string, _numSpeakers?: number): Promise<DiarizationJobStatus> {\n return rejectReadOnly();\n },\n\n async getDiarizationJobStatus(_jobId: string): Promise<DiarizationJobStatus> {\n return rejectReadOnly();\n },\n\n async renameSpeaker(_meetingId: string, _oldSpeakerId: string, _newName: string): Promise<boolean> {\n return rejectReadOnly();\n },\n\n async cancelDiarization(_jobId: string): Promise<CancelDiarizationResult> {\n return rejectReadOnly();\n },\n\n async getPreferences(): Promise<UserPreferences> {\n return preferences.get();\n },\n\n async savePreferences(next: UserPreferences): Promise<void> {\n preferences.replace(next);\n },\n\n async listAudioDevices(): Promise<AudioDeviceInfo[]> {\n return [];\n },\n\n async getDefaultAudioDevice(_isInput: boolean): Promise<AudioDeviceInfo | null> {\n return null;\n },\n\n async selectAudioDevice(_deviceId: string, _isInput: boolean): Promise<void> {\n return rejectReadOnly();\n },\n\n async setTriggerEnabled(_enabled: boolean): Promise<void> {\n return rejectReadOnly();\n },\n\n async snoozeTriggers(_minutes?: number): Promise<void> {\n return rejectReadOnly();\n },\n\n async resetSnooze(): Promise<void> {\n return rejectReadOnly();\n },\n\n async getTriggerStatus(): Promise<TriggerStatus> {\n return {\n enabled: false,\n is_snoozed: false,\n };\n },\n\n async dismissTrigger(): Promise<void> {\n return rejectReadOnly();\n },\n\n async acceptTrigger(_title?: string): Promise<Meeting> {\n return rejectReadOnly();\n },\n\n async extractEntities(_meetingId: string, _forceRefresh?: boolean): Promise<ExtractEntitiesResponse> {\n return { entities: [], total_count: 0, cached: true };\n },\n\n async updateEntity(\n _meetingId: string,\n _entityId: string,\n _text?: string,\n _category?: string\n ): Promise<ExtractedEntity> {\n return rejectReadOnly();\n },\n\n async deleteEntity(_meetingId: string, _entityId: string): Promise<boolean> {\n return rejectReadOnly();\n },\n\n async listCalendarEvents(\n _hoursAhead?: number,\n _limit?: number,\n _provider?: string\n ): Promise<ListCalendarEventsResponse> {\n return { events: [] };\n },\n\n async getCalendarProviders(): Promise<GetCalendarProvidersResponse> {\n return { providers: [] };\n },\n\n async initiateCalendarAuth(\n _provider: string,\n _redirectUri?: string\n ): Promise<InitiateCalendarAuthResponse> {\n return rejectReadOnly();\n },\n\n async completeCalendarAuth(\n _provider: string,\n _code: string,\n _state: string\n ): Promise<CompleteCalendarAuthResponse> {\n return rejectReadOnly();\n },\n\n async getOAuthConnectionStatus(_provider: string): Promise<GetOAuthConnectionStatusResponse> {\n return {\n connection: {\n provider: _provider,\n status: 'disconnected',\n email: '',\n expires_at: 0,\n error_message: 'Offline',\n integration_type: 'calendar',\n },\n };\n },\n\n async disconnectCalendar(_provider: string): Promise<DisconnectOAuthResponse> {\n return rejectReadOnly();\n },\n\n async registerWebhook(_request: RegisterWebhookRequest): Promise<RegisteredWebhook> {\n return rejectReadOnly();\n },\n\n async listWebhooks(_enabledOnly?: boolean): Promise<ListWebhooksResponse> {\n return { webhooks: [], total_count: 0 };\n },\n\n async updateWebhook(_request: UpdateWebhookRequest): Promise<RegisteredWebhook> {\n return rejectReadOnly();\n },\n\n async deleteWebhook(_webhookId: string): Promise<DeleteWebhookResponse> {\n return rejectReadOnly();\n },\n\n async getWebhookDeliveries(\n _webhookId: string,\n _limit?: number\n ): Promise<GetWebhookDeliveriesResponse> {\n return { deliveries: [], total_count: 0 };\n },\n\n async startIntegrationSync(_integrationId: string): Promise<StartIntegrationSyncResponse> {\n return rejectReadOnly();\n },\n\n async getSyncStatus(_syncRunId: string): Promise<GetSyncStatusResponse> {\n return rejectReadOnly();\n },\n\n async listSyncHistory(\n _integrationId: string,\n _limit?: number,\n _offset?: number\n ): Promise<ListSyncHistoryResponse> {\n return { runs: [], total_count: 0 };\n },\n\n async getUserIntegrations(): Promise<GetUserIntegrationsResponse> {\n return { integrations: [] };\n },\n\n async getRecentLogs(_request?: GetRecentLogsRequest): Promise<GetRecentLogsResponse> {\n return { logs: [], total_count: 0 };\n },\n\n async getPerformanceMetrics(\n _request?: GetPerformanceMetricsRequest\n ): Promise<GetPerformanceMetricsResponse> {\n const now = Date.now() / 1000;\n return {\n current: {\n timestamp: now,\n cpu_percent: 0,\n memory_percent: 0,\n memory_mb: 0,\n disk_percent: 0,\n network_bytes_sent: 0,\n network_bytes_recv: 0,\n process_memory_mb: 0,\n active_connections: 0,\n },\n history: [],\n };\n },\n};\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * NoteFlow API - Main Export\n * setConnectionMode('connected');\n await preferences.initialize();\n await startTauriEventBridge().catch((error: unknown) => {\n console.warn('[API] Event bridge initialization failed:', error);\n });\n startReconnection(); window.__NOTEFLOW_CONNECTION__ = { getConnectionState };\n}\n","ops":[{"diffOp":{"equal":{"range":[0,36]}}},{"equalLines":{"line_count":45}},{"diffOp":{"equal":{"range":[36,175]}}},{"diffOp":{"delete":{"range":[175,249]}}},{"diffOp":{"equal":{"range":[249,286]}}},{"equalLines":{"line_count":48}},{"diffOp":{"equal":{"range":[286,347]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"src/api/index.ts"},"span":[2014,2026],"sourceCode":"/**\n * NoteFlow API - Main Export\n *\n * This module provides the main entry point for the NoteFlow API.\n * It automatically detects the runtime environment and initializes\n * the appropriate backend adapter:\n *\n * - Tauri Desktop: Uses TauriAdapter → Rust backend → gRPC server\n * - Web Browser: Uses MockAdapter with simulated data\n *\n * @see noteflow-api-spec-2.json for the complete gRPC API specification\n */\n\nexport * from './interface';\nexport { mockAPI } from './mock-adapter';\nexport { cachedAPI } from './cached-adapter';\nexport { createTauriAPI, initializeTauriAPI, isTauriEnvironment } from './tauri-adapter';\n// Re-export all types and interfaces\nexport * from './types';\n\nimport { preferences } from '@/lib/preferences';\nimport { startTauriEventBridge } from '@/lib/tauri-events';\nimport { type NoteFlowAPI, setAPIInstance } from './interface';\nimport { cachedAPI } from './cached-adapter';\nimport { getConnectionState, setConnectionMode, setConnectionServerUrl } from './connection-state';\nimport { mockAPI } from './mock-adapter';\nimport { startReconnection } from './reconnection';\nimport { initializeTauriAPI } from './tauri-adapter';\n\n// ============================================================================\n// API Initialization\n// ============================================================================\n\n/**\n * Initialize the API with the appropriate backend adapter\n *\n * This function is called automatically on module load,\n * but can also be called manually for testing or custom initialization.\n */\nexport async function initializeAPI(): Promise<NoteFlowAPI> {\n // Always try Tauri first - initializeTauriAPI tests the API and throws if unavailable\n try {\n const tauriAPI = await initializeTauriAPI();\n setAPIInstance(tauriAPI);\n\n // Attempt to connect to the gRPC server\n try {\n await tauriAPI.connect();\n setConnectionMode('connected');\n await preferences.initialize();\n await startTauriEventBridge().catch((error: unknown) => {\n console.warn('[API] Event bridge initialization failed:', error);\n });\n startReconnection();\n return tauriAPI;\n } catch (connectError) {\n // Connection failed - fall back to cached mode but keep Tauri adapter\n const message = connectError instanceof Error ? connectError.message : 'Connection failed';\n setConnectionMode('cached', message);\n await preferences.initialize();\n startReconnection();\n return tauriAPI; // Keep Tauri adapter for reconnection attempts\n }\n } catch (_tauriError) {\n // Tauri unavailable - use mock API (we're in a browser)\n setConnectionMode('mock');\n setAPIInstance(mockAPI);\n return mockAPI;\n }\n}\n\n// ============================================================================\n// Auto-initialization\n// ============================================================================\n\n/**\n * Auto-initialize with appropriate adapter based on environment\n *\n * Always tries Tauri first (sync detection is unreliable in Tauri 2.x),\n * falls back to mock if Tauri APIs are unavailable.\n */\nif (typeof window !== 'undefined') {\n // Start with cached mode while we try to initialize\n setAPIInstance(cachedAPI);\n setConnectionMode('cached');\n\n // Always attempt Tauri initialization - it will fail gracefully in browser\n initializeAPI()\n .then((api) => {\n // @ts-expect-error - exposing for e2e tests\n window.__NOTEFLOW_API__ = api;\n })\n .catch((_err) => {\n // Tauri unavailable - switch to mock mode\n setConnectionMode('mock');\n setConnectionServerUrl(null);\n setAPIInstance(mockAPI);\n // @ts-expect-error - exposing for e2e tests\n window.__NOTEFLOW_API__ = mockAPI;\n });\n\n // @ts-expect-error - exposing for e2e tests\n window.__NOTEFLOW_CONNECTION__ = { getConnectionState };\n}\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/** Tauri API adapter implementing NoteFlowAPI via Rust backend IPC. */\nimport type { NoteFlowAPI, TranscriptionStream } from './interface';\nimport { TauriCommands, TauriEvents } from './tauri-constants'; .catch((err: unknown) => {\n this.consecutiveFailures++;\n const message = err instanceof Error ? err.message : String(err);\n console.error(`[TauriTranscriptionStream] send_audio_chunk failed: ${message}`);\n\n // Emit error callback once after threshold consecutive failures }\n}\n","ops":[{"diffOp":{"equal":{"range":[0,204]}}},{"equalLines":{"line_count":153}},{"diffOp":{"equal":{"range":[204,346]}}},{"diffOp":{"delete":{"range":[346,435]}}},{"diffOp":{"equal":{"range":[435,509]}}},{"equalLines":{"line_count":649}},{"diffOp":{"equal":{"range":[509,515]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"src/api/tauri-adapter.ts"},"span":[4902,4915],"sourceCode":"/** Tauri API adapter implementing NoteFlowAPI via Rust backend IPC. */\nimport type { NoteFlowAPI, TranscriptionStream } from './interface';\nimport { TauriCommands, TauriEvents } from './tauri-constants';\n\n// Re-export TauriEvents for external consumers\nexport { TauriEvents } from './tauri-constants';\nimport {\n annotationTypeToGrpcEnum,\n formatToGrpcEnum,\n normalizeAnnotationList,\n normalizeSuccessResponse,\n sortOrderToGrpcEnum,\n stateToGrpcEnum,\n} from './tauri-helpers';\nimport { meetingCache } from '@/lib/cache/meeting-cache';\nimport type {\n AddAnnotationRequest,\n Annotation,\n AudioChunk,\n AudioDeviceInfo,\n AddProjectMemberRequest,\n CancelDiarizationResult,\n CompleteCalendarAuthResponse,\n CreateMeetingRequest,\n CreateProjectRequest,\n DeleteWebhookResponse,\n DiarizationJobStatus,\n DisconnectOAuthResponse,\n ExportFormat,\n ExportResult,\n ExtractEntitiesResponse,\n ExtractedEntity,\n GetCalendarProvidersResponse,\n GetCurrentUserResponse,\n GetActiveProjectRequest,\n GetActiveProjectResponse,\n GetMeetingRequest,\n GetOAuthConnectionStatusResponse,\n GetProjectBySlugRequest,\n GetProjectRequest,\n GetPerformanceMetricsRequest,\n GetPerformanceMetricsResponse,\n GetRecentLogsRequest,\n GetRecentLogsResponse,\n GetSyncStatusResponse,\n GetUserIntegrationsResponse,\n GetWebhookDeliveriesResponse,\n InitiateCalendarAuthResponse,\n ListWorkspacesResponse,\n ListCalendarEventsResponse,\n ListMeetingsRequest,\n ListMeetingsResponse,\n ListProjectMembersRequest,\n ListProjectMembersResponse,\n ListProjectsRequest,\n ListProjectsResponse,\n ListSyncHistoryResponse,\n ListWebhooksResponse,\n Meeting,\n PlaybackInfo,\n Project,\n ProjectMembership,\n RegisteredWebhook,\n RegisterWebhookRequest,\n RemoveProjectMemberRequest,\n RemoveProjectMemberResponse,\n ServerInfo,\n SetActiveProjectRequest,\n StartIntegrationSyncResponse,\n SwitchWorkspaceResponse,\n SummarizationOptions,\n Summary,\n TranscriptUpdate,\n TriggerStatus,\n UpdateAnnotationRequest,\n UpdateProjectMemberRoleRequest,\n UpdateProjectRequest,\n UpdateWebhookRequest,\n UserPreferences,\n} from './types';\n\n/** Type-safe wrapper for Tauri's invoke function. */\nexport type TauriInvoke = <T>(cmd: string, args?: Record<string, unknown>) => Promise<T>;\n/** Type-safe wrapper for Tauri's event system. */\nexport type TauriListen = <T>(\n event: string,\n handler: (event: { payload: T }) => void\n) => Promise<() => void>;\n\n/** Error callback type for stream errors. */\nexport type StreamErrorCallback = (error: { code: string; message: string }) => void;\n\n/** Congestion state for UI feedback. */\nexport interface CongestionState {\n /** Whether the stream is currently showing congestion to the user. */\n isBuffering: boolean;\n /** Duration of congestion in milliseconds. */\n duration: number;\n}\n\n/** Congestion callback type for stream health updates. */\nexport type CongestionCallback = (state: CongestionState) => void;\n\n/** Consecutive failure threshold before emitting stream error. */\nexport const CONSECUTIVE_FAILURE_THRESHOLD = 3;\n\n/** Threshold in milliseconds before showing buffering indicator (2 seconds). */\nexport const CONGESTION_DISPLAY_THRESHOLD_MS = 2000;\n\n/** Real-time transcription stream using Tauri events. */\nexport class TauriTranscriptionStream implements TranscriptionStream {\n private unlistenFn: (() => void) | null = null;\n private healthUnlistenFn: (() => void) | null = null;\n private errorCallback: StreamErrorCallback | null = null;\n private congestionCallback: CongestionCallback | null = null;\n private consecutiveFailures = 0;\n private hasEmittedError = false;\n\n /** Latest ack_sequence received from server (for debugging/monitoring). */\n private lastAckedSequence = 0;\n\n /** Timestamp when congestion started (null if not congested). */\n private congestionStartTime: number | null = null;\n\n /** Whether buffering indicator is currently shown. */\n private isShowingBuffering = false;\n\n constructor(\n private meetingId: string,\n private invoke: TauriInvoke,\n private listen: TauriListen\n ) {}\n\n /** Get the last acknowledged chunk sequence number. */\n getLastAckedSequence(): number {\n return this.lastAckedSequence;\n }\n\n send(chunk: AudioChunk): void {\n const args: Record<string, unknown> = {\n meeting_id: chunk.meeting_id,\n audio_data: Array.from(chunk.audio_data),\n timestamp: chunk.timestamp,\n };\n if (typeof chunk.sample_rate === 'number') {\n args.sample_rate = chunk.sample_rate;\n }\n if (typeof chunk.channels === 'number') {\n args.channels = chunk.channels;\n }\n\n this.invoke(TauriCommands.SEND_AUDIO_CHUNK, args)\n .then(() => {\n // Reset failure counter on success\n this.consecutiveFailures = 0;\n })\n .catch((err: unknown) => {\n this.consecutiveFailures++;\n const message = err instanceof Error ? err.message : String(err);\n console.error(`[TauriTranscriptionStream] send_audio_chunk failed: ${message}`);\n\n // Emit error callback once after threshold consecutive failures\n if (\n this.consecutiveFailures >= CONSECUTIVE_FAILURE_THRESHOLD &&\n !this.hasEmittedError &&\n this.errorCallback\n ) {\n this.hasEmittedError = true;\n this.errorCallback({\n code: 'stream_send_failed',\n message: `Audio streaming interrupted after ${this.consecutiveFailures} failures: ${message}`,\n });\n }\n });\n }\n\n async onUpdate(callback: (update: TranscriptUpdate) => void): Promise<void> {\n this.unlistenFn = await this.listen<TranscriptUpdate>(\n TauriEvents.TRANSCRIPT_UPDATE,\n (event) => {\n if (event.payload.meeting_id === this.meetingId) {\n // Track latest ack_sequence for monitoring\n if (\n typeof event.payload.ack_sequence === 'number' &&\n event.payload.ack_sequence > this.lastAckedSequence\n ) {\n this.lastAckedSequence = event.payload.ack_sequence;\n }\n callback(event.payload);\n }\n }\n );\n }\n\n /** Register callback for stream errors (connection failures, etc.). */\n onError(callback: StreamErrorCallback): void {\n this.errorCallback = callback;\n }\n\n /** Register callback for congestion state updates (buffering indicator). */\n onCongestion(callback: CongestionCallback): void {\n this.congestionCallback = callback;\n // Start listening for stream_health events\n this.startHealthListener();\n }\n\n /** Start listening for stream_health events from the Rust backend. */\n private startHealthListener(): void {\n if (this.healthUnlistenFn) {\n return;\n } // Already listening\n\n this.listen<{\n meeting_id: string;\n is_congested: boolean;\n processing_delay_ms: number;\n queue_depth: number;\n congested_duration_ms: number;\n }>(TauriEvents.STREAM_HEALTH, (event) => {\n if (event.payload.meeting_id !== this.meetingId) {\n return;\n }\n\n const { is_congested } = event.payload;\n\n if (is_congested) {\n // Start tracking congestion if not already\n this.congestionStartTime ??= Date.now();\n const duration = Date.now() - this.congestionStartTime;\n\n // Only show buffering after threshold is exceeded\n if (duration >= CONGESTION_DISPLAY_THRESHOLD_MS && !this.isShowingBuffering) {\n this.isShowingBuffering = true;\n this.congestionCallback?.({ isBuffering: true, duration });\n } else if (this.isShowingBuffering) {\n // Update duration while showing\n this.congestionCallback?.({ isBuffering: true, duration });\n }\n } else {\n // Congestion cleared\n if (this.isShowingBuffering) {\n this.isShowingBuffering = false;\n this.congestionCallback?.({ isBuffering: false, duration: 0 });\n }\n this.congestionStartTime = null;\n }\n })\n .then((unlisten) => {\n this.healthUnlistenFn = unlisten;\n })\n .catch((err: unknown) => {\n const message = err instanceof Error ? err.message : String(err);\n console.error(`[TauriTranscriptionStream] Failed to listen for stream_health: ${message}`);\n });\n }\n\n close(): void {\n if (this.unlistenFn) {\n this.unlistenFn();\n this.unlistenFn = null;\n }\n if (this.healthUnlistenFn) {\n this.healthUnlistenFn();\n this.healthUnlistenFn = null;\n }\n // Reset congestion state\n this.congestionStartTime = null;\n this.isShowingBuffering = false;\n\n this.invoke(TauriCommands.STOP_RECORDING, { meeting_id: this.meetingId })\n .catch((err: unknown) => {\n const message = err instanceof Error ? err.message : String(err);\n console.error(`[TauriTranscriptionStream] stop_recording failed: ${message}`);\n // Emit error so UI can show notification\n if (this.errorCallback) {\n this.errorCallback({\n code: 'stream_close_failed',\n message: `Failed to stop recording: ${message}`,\n });\n }\n });\n }\n}\n\n/** Creates a Tauri API adapter instance. */\nexport function createTauriAPI(invoke: TauriInvoke, listen: TauriListen): NoteFlowAPI {\n return {\n async getServerInfo(): Promise<ServerInfo> {\n return invoke<ServerInfo>(TauriCommands.GET_SERVER_INFO);\n },\n async connect(serverUrl?: string): Promise<ServerInfo> {\n return invoke<ServerInfo>(TauriCommands.CONNECT, { server_url: serverUrl });\n },\n async disconnect(): Promise<void> {\n await invoke(TauriCommands.DISCONNECT);\n },\n async isConnected(): Promise<boolean> {\n return invoke<boolean>(TauriCommands.IS_CONNECTED);\n },\n\n async getCurrentUser(): Promise<GetCurrentUserResponse> {\n return invoke<GetCurrentUserResponse>(TauriCommands.GET_CURRENT_USER);\n },\n\n async listWorkspaces(): Promise<ListWorkspacesResponse> {\n return invoke<ListWorkspacesResponse>(TauriCommands.LIST_WORKSPACES);\n },\n\n async switchWorkspace(workspaceId: string): Promise<SwitchWorkspaceResponse> {\n return invoke<SwitchWorkspaceResponse>(TauriCommands.SWITCH_WORKSPACE, {\n workspace_id: workspaceId,\n });\n },\n\n async createProject(request: CreateProjectRequest): Promise<Project> {\n return invoke<Project>(TauriCommands.CREATE_PROJECT, {\n request,\n });\n },\n\n async getProject(request: GetProjectRequest): Promise<Project> {\n return invoke<Project>(TauriCommands.GET_PROJECT, {\n project_id: request.project_id,\n });\n },\n\n async getProjectBySlug(request: GetProjectBySlugRequest): Promise<Project> {\n return invoke<Project>(TauriCommands.GET_PROJECT_BY_SLUG, {\n workspace_id: request.workspace_id,\n slug: request.slug,\n });\n },\n\n async listProjects(request: ListProjectsRequest): Promise<ListProjectsResponse> {\n return invoke<ListProjectsResponse>(TauriCommands.LIST_PROJECTS, {\n workspace_id: request.workspace_id,\n include_archived: request.include_archived ?? false,\n limit: request.limit,\n offset: request.offset,\n });\n },\n\n async updateProject(request: UpdateProjectRequest): Promise<Project> {\n return invoke<Project>(TauriCommands.UPDATE_PROJECT, {\n request,\n });\n },\n\n async archiveProject(projectId: string): Promise<Project> {\n return invoke<Project>(TauriCommands.ARCHIVE_PROJECT, {\n project_id: projectId,\n });\n },\n\n async restoreProject(projectId: string): Promise<Project> {\n return invoke<Project>(TauriCommands.RESTORE_PROJECT, {\n project_id: projectId,\n });\n },\n\n async deleteProject(projectId: string): Promise<boolean> {\n const response = await invoke<{ success: boolean }>(TauriCommands.DELETE_PROJECT, {\n project_id: projectId,\n });\n return normalizeSuccessResponse(response);\n },\n\n async setActiveProject(request: SetActiveProjectRequest): Promise<void> {\n await invoke(TauriCommands.SET_ACTIVE_PROJECT, {\n workspace_id: request.workspace_id,\n project_id: request.project_id ?? '',\n });\n },\n\n async getActiveProject(request: GetActiveProjectRequest): Promise<GetActiveProjectResponse> {\n return invoke<GetActiveProjectResponse>(TauriCommands.GET_ACTIVE_PROJECT, {\n workspace_id: request.workspace_id,\n });\n },\n\n async addProjectMember(request: AddProjectMemberRequest): Promise<ProjectMembership> {\n return invoke<ProjectMembership>(TauriCommands.ADD_PROJECT_MEMBER, {\n request,\n });\n },\n\n async updateProjectMemberRole(\n request: UpdateProjectMemberRoleRequest\n ): Promise<ProjectMembership> {\n return invoke<ProjectMembership>(TauriCommands.UPDATE_PROJECT_MEMBER_ROLE, {\n request,\n });\n },\n\n async removeProjectMember(\n request: RemoveProjectMemberRequest\n ): Promise<RemoveProjectMemberResponse> {\n return invoke<RemoveProjectMemberResponse>(TauriCommands.REMOVE_PROJECT_MEMBER, {\n request,\n });\n },\n\n async listProjectMembers(\n request: ListProjectMembersRequest\n ): Promise<ListProjectMembersResponse> {\n return invoke<ListProjectMembersResponse>(TauriCommands.LIST_PROJECT_MEMBERS, {\n project_id: request.project_id,\n limit: request.limit,\n offset: request.offset,\n });\n },\n\n async createMeeting(request: CreateMeetingRequest): Promise<Meeting> {\n const meeting = await invoke<Meeting>(TauriCommands.CREATE_MEETING, {\n title: request.title,\n metadata: request.metadata ?? {},\n project_id: request.project_id,\n });\n meetingCache.cacheMeeting(meeting);\n return meeting;\n },\n async listMeetings(request: ListMeetingsRequest): Promise<ListMeetingsResponse> {\n const response = await invoke<ListMeetingsResponse>(TauriCommands.LIST_MEETINGS, {\n states: request.states?.map(stateToGrpcEnum) ?? [],\n limit: request.limit ?? 50,\n offset: request.offset ?? 0,\n sort_order: sortOrderToGrpcEnum(request.sort_order),\n project_id: request.project_id,\n });\n if (response.meetings?.length) {\n meetingCache.cacheMeetings(response.meetings);\n }\n return response;\n },\n async getMeeting(request: GetMeetingRequest): Promise<Meeting> {\n const meeting = await invoke<Meeting>(TauriCommands.GET_MEETING, {\n meeting_id: request.meeting_id,\n include_segments: request.include_segments ?? false,\n include_summary: request.include_summary ?? false,\n });\n meetingCache.cacheMeeting(meeting);\n return meeting;\n },\n async stopMeeting(meetingId: string): Promise<Meeting> {\n const meeting = await invoke<Meeting>(TauriCommands.STOP_MEETING, {\n meeting_id: meetingId,\n });\n meetingCache.cacheMeeting(meeting);\n return meeting;\n },\n async deleteMeeting(meetingId: string): Promise<boolean> {\n const result = normalizeSuccessResponse(\n await invoke<boolean | { success: boolean }>(TauriCommands.DELETE_MEETING, {\n meeting_id: meetingId,\n })\n );\n if (result) {\n meetingCache.removeMeeting(meetingId);\n }\n return result;\n },\n\n async startTranscription(meetingId: string): Promise<TranscriptionStream> {\n await invoke(TauriCommands.START_RECORDING, { meeting_id: meetingId });\n return new TauriTranscriptionStream(meetingId, invoke, listen);\n },\n\n async generateSummary(meetingId: string, forceRegenerate?: boolean): Promise<Summary> {\n let options: SummarizationOptions | undefined;\n try {\n const prefs = await invoke<UserPreferences>(TauriCommands.GET_PREFERENCES);\n if (prefs?.ai_template) {\n options = {\n tone: prefs.ai_template.tone,\n format: prefs.ai_template.format,\n verbosity: prefs.ai_template.verbosity,\n };\n }\n } catch {\n /* Preferences unavailable */\n }\n return invoke<Summary>(TauriCommands.GENERATE_SUMMARY, {\n meeting_id: meetingId,\n force_regenerate: forceRegenerate ?? false,\n options,\n });\n },\n\n async grantCloudConsent(): Promise<void> {\n await invoke(TauriCommands.GRANT_CLOUD_CONSENT);\n },\n async revokeCloudConsent(): Promise<void> {\n await invoke(TauriCommands.REVOKE_CLOUD_CONSENT);\n },\n async getCloudConsentStatus(): Promise<{ consentGranted: boolean }> {\n return invoke<{ consent_granted: boolean }>(TauriCommands.GET_CLOUD_CONSENT_STATUS).then(\n (r) => ({ consentGranted: r.consent_granted })\n );\n },\n\n async listAnnotations(\n meetingId: string,\n startTime?: number,\n endTime?: number\n ): Promise<Annotation[]> {\n return normalizeAnnotationList(\n await invoke<Annotation[] | { annotations: Annotation[] }>(TauriCommands.LIST_ANNOTATIONS, {\n meeting_id: meetingId,\n start_time: startTime ?? 0,\n end_time: endTime ?? 0,\n })\n );\n },\n async addAnnotation(request: AddAnnotationRequest): Promise<Annotation> {\n return invoke<Annotation>(TauriCommands.ADD_ANNOTATION, {\n meeting_id: request.meeting_id,\n annotation_type: annotationTypeToGrpcEnum(request.annotation_type),\n text: request.text,\n start_time: request.start_time,\n end_time: request.end_time,\n segment_ids: request.segment_ids ?? [],\n });\n },\n async getAnnotation(annotationId: string): Promise<Annotation> {\n return invoke<Annotation>(TauriCommands.GET_ANNOTATION, { annotation_id: annotationId });\n },\n async updateAnnotation(request: UpdateAnnotationRequest): Promise<Annotation> {\n return invoke<Annotation>(TauriCommands.UPDATE_ANNOTATION, {\n annotation_id: request.annotation_id,\n annotation_type: request.annotation_type\n ? annotationTypeToGrpcEnum(request.annotation_type)\n : undefined,\n text: request.text,\n start_time: request.start_time,\n end_time: request.end_time,\n segment_ids: request.segment_ids,\n });\n },\n async deleteAnnotation(annotationId: string): Promise<boolean> {\n return normalizeSuccessResponse(\n await invoke<boolean | { success: boolean }>(TauriCommands.DELETE_ANNOTATION, {\n annotation_id: annotationId,\n })\n );\n },\n\n async exportTranscript(meetingId: string, format: ExportFormat): Promise<ExportResult> {\n return invoke<ExportResult>(TauriCommands.EXPORT_TRANSCRIPT, {\n meeting_id: meetingId,\n format: formatToGrpcEnum(format),\n });\n },\n async saveExportFile(\n content: string,\n defaultName: string,\n extension: string\n ): Promise<boolean> {\n return invoke<boolean>(TauriCommands.SAVE_EXPORT_FILE, {\n content,\n default_name: defaultName,\n extension,\n });\n },\n\n async startPlayback(meetingId: string, startTime?: number): Promise<void> {\n await invoke(TauriCommands.START_PLAYBACK, { meeting_id: meetingId, start_time: startTime });\n },\n async pausePlayback(): Promise<void> {\n await invoke(TauriCommands.PAUSE_PLAYBACK);\n },\n async stopPlayback(): Promise<void> {\n await invoke(TauriCommands.STOP_PLAYBACK);\n },\n async seekPlayback(position: number): Promise<PlaybackInfo> {\n return invoke<PlaybackInfo>(TauriCommands.SEEK_PLAYBACK, { position });\n },\n async getPlaybackState(): Promise<PlaybackInfo> {\n return invoke<PlaybackInfo>(TauriCommands.GET_PLAYBACK_STATE);\n },\n\n async refineSpeakers(meetingId: string, numSpeakers?: number): Promise<DiarizationJobStatus> {\n return invoke<DiarizationJobStatus>(TauriCommands.REFINE_SPEAKERS, {\n meeting_id: meetingId,\n num_speakers: numSpeakers ?? 0,\n });\n },\n async getDiarizationJobStatus(jobId: string): Promise<DiarizationJobStatus> {\n return invoke<DiarizationJobStatus>(TauriCommands.GET_DIARIZATION_STATUS, { job_id: jobId });\n },\n async renameSpeaker(\n meetingId: string,\n oldSpeakerId: string,\n newName: string\n ): Promise<boolean> {\n return (\n await invoke<{ success: boolean }>(TauriCommands.RENAME_SPEAKER, {\n meeting_id: meetingId,\n old_speaker_id: oldSpeakerId,\n new_speaker_name: newName,\n })\n ).success;\n },\n async cancelDiarization(jobId: string): Promise<CancelDiarizationResult> {\n return invoke<CancelDiarizationResult>(TauriCommands.CANCEL_DIARIZATION, { job_id: jobId });\n },\n\n async getPreferences(): Promise<UserPreferences> {\n return invoke<UserPreferences>(TauriCommands.GET_PREFERENCES);\n },\n async savePreferences(preferences: UserPreferences): Promise<void> {\n await invoke(TauriCommands.SAVE_PREFERENCES, { preferences });\n },\n\n async listAudioDevices(): Promise<AudioDeviceInfo[]> {\n return invoke<AudioDeviceInfo[]>(TauriCommands.LIST_AUDIO_DEVICES);\n },\n async getDefaultAudioDevice(isInput: boolean): Promise<AudioDeviceInfo | null> {\n return invoke<AudioDeviceInfo | null>(TauriCommands.GET_DEFAULT_AUDIO_DEVICE, {\n is_input: isInput,\n });\n },\n async selectAudioDevice(deviceId: string, isInput: boolean): Promise<void> {\n await invoke(TauriCommands.SELECT_AUDIO_DEVICE, { device_id: deviceId, is_input: isInput });\n },\n\n async setTriggerEnabled(enabled: boolean): Promise<void> {\n await invoke(TauriCommands.SET_TRIGGER_ENABLED, { enabled });\n },\n async snoozeTriggers(minutes?: number): Promise<void> {\n await invoke(TauriCommands.SNOOZE_TRIGGERS, { minutes });\n },\n async resetSnooze(): Promise<void> {\n await invoke(TauriCommands.RESET_SNOOZE);\n },\n async getTriggerStatus(): Promise<TriggerStatus> {\n return invoke<TriggerStatus>(TauriCommands.GET_TRIGGER_STATUS);\n },\n async dismissTrigger(): Promise<void> {\n await invoke(TauriCommands.DISMISS_TRIGGER);\n },\n async acceptTrigger(title?: string): Promise<Meeting> {\n return invoke<Meeting>(TauriCommands.ACCEPT_TRIGGER, { title });\n },\n\n async extractEntities(\n meetingId: string,\n forceRefresh?: boolean\n ): Promise<ExtractEntitiesResponse> {\n return invoke<ExtractEntitiesResponse>(TauriCommands.EXTRACT_ENTITIES, {\n meeting_id: meetingId,\n force_refresh: forceRefresh ?? false,\n });\n },\n async updateEntity(\n meetingId: string,\n entityId: string,\n text?: string,\n category?: string\n ): Promise<ExtractedEntity> {\n return invoke<ExtractedEntity>(TauriCommands.UPDATE_ENTITY, {\n meeting_id: meetingId,\n entity_id: entityId,\n text,\n category,\n });\n },\n async deleteEntity(meetingId: string, entityId: string): Promise<boolean> {\n return invoke<boolean>(TauriCommands.DELETE_ENTITY, {\n meeting_id: meetingId,\n entity_id: entityId,\n });\n },\n\n async listCalendarEvents(\n hoursAhead?: number,\n limit?: number,\n provider?: string\n ): Promise<ListCalendarEventsResponse> {\n return invoke<ListCalendarEventsResponse>(TauriCommands.LIST_CALENDAR_EVENTS, {\n hours_ahead: hoursAhead,\n limit,\n provider,\n });\n },\n async getCalendarProviders(): Promise<GetCalendarProvidersResponse> {\n return invoke<GetCalendarProvidersResponse>(TauriCommands.GET_CALENDAR_PROVIDERS);\n },\n async initiateCalendarAuth(\n provider: string,\n redirectUri?: string\n ): Promise<InitiateCalendarAuthResponse> {\n return invoke<InitiateCalendarAuthResponse>(TauriCommands.INITIATE_OAUTH, {\n provider,\n redirect_uri: redirectUri,\n });\n },\n async completeCalendarAuth(\n provider: string,\n code: string,\n state: string\n ): Promise<CompleteCalendarAuthResponse> {\n return invoke<CompleteCalendarAuthResponse>(TauriCommands.COMPLETE_OAUTH, {\n provider,\n code,\n state,\n });\n },\n async getOAuthConnectionStatus(provider: string): Promise<GetOAuthConnectionStatusResponse> {\n return invoke<GetOAuthConnectionStatusResponse>(TauriCommands.GET_OAUTH_CONNECTION_STATUS, {\n provider,\n });\n },\n async disconnectCalendar(provider: string): Promise<DisconnectOAuthResponse> {\n return invoke<DisconnectOAuthResponse>(TauriCommands.DISCONNECT_OAUTH, { provider });\n },\n\n async registerWebhook(r: RegisterWebhookRequest): Promise<RegisteredWebhook> {\n return invoke<RegisteredWebhook>(TauriCommands.REGISTER_WEBHOOK, { request: r });\n },\n async listWebhooks(enabledOnly?: boolean): Promise<ListWebhooksResponse> {\n return invoke<ListWebhooksResponse>(TauriCommands.LIST_WEBHOOKS, {\n enabled_only: enabledOnly ?? false,\n });\n },\n async updateWebhook(r: UpdateWebhookRequest): Promise<RegisteredWebhook> {\n return invoke<RegisteredWebhook>(TauriCommands.UPDATE_WEBHOOK, { request: r });\n },\n async deleteWebhook(webhookId: string): Promise<DeleteWebhookResponse> {\n return invoke<DeleteWebhookResponse>(TauriCommands.DELETE_WEBHOOK, { webhook_id: webhookId });\n },\n async getWebhookDeliveries(\n webhookId: string,\n limit?: number\n ): Promise<GetWebhookDeliveriesResponse> {\n return invoke<GetWebhookDeliveriesResponse>(TauriCommands.GET_WEBHOOK_DELIVERIES, {\n webhook_id: webhookId,\n limit: limit ?? 50,\n });\n },\n\n // Integration Sync (Sprint 9)\n async startIntegrationSync(integrationId: string): Promise<StartIntegrationSyncResponse> {\n return invoke<StartIntegrationSyncResponse>(TauriCommands.START_INTEGRATION_SYNC, {\n integration_id: integrationId,\n });\n },\n async getSyncStatus(syncRunId: string): Promise<GetSyncStatusResponse> {\n return invoke<GetSyncStatusResponse>(TauriCommands.GET_SYNC_STATUS, {\n sync_run_id: syncRunId,\n });\n },\n async listSyncHistory(\n integrationId: string,\n limit?: number,\n offset?: number\n ): Promise<ListSyncHistoryResponse> {\n return invoke<ListSyncHistoryResponse>(TauriCommands.LIST_SYNC_HISTORY, {\n integration_id: integrationId,\n limit,\n offset,\n });\n },\n async getUserIntegrations(): Promise<GetUserIntegrationsResponse> {\n return invoke<GetUserIntegrationsResponse>(TauriCommands.GET_USER_INTEGRATIONS);\n },\n\n // Observability (Sprint 9)\n async getRecentLogs(request?: GetRecentLogsRequest): Promise<GetRecentLogsResponse> {\n return invoke<GetRecentLogsResponse>(TauriCommands.GET_RECENT_LOGS, {\n limit: request?.limit,\n level: request?.level,\n source: request?.source,\n });\n },\n async getPerformanceMetrics(\n request?: GetPerformanceMetricsRequest\n ): Promise<GetPerformanceMetricsResponse> {\n return invoke<GetPerformanceMetricsResponse>(TauriCommands.GET_PERFORMANCE_METRICS, {\n history_limit: request?.history_limit,\n });\n },\n };\n}\n\n/** Check if running in a Tauri environment (synchronous hint). */\nexport function isTauriEnvironment(): boolean {\n if (typeof window === 'undefined') {\n return false;\n }\n // Tauri 2.x injects __TAURI_INTERNALS__ into the window\n // Check multiple possible indicators\n return (\n '__TAURI_INTERNALS__' in window ||\n '__TAURI__' in window ||\n 'isTauri' in window\n );\n}\n\n/** Dynamically import Tauri APIs and create the adapter. */\nexport async function initializeTauriAPI(): Promise<NoteFlowAPI> {\n // Try to import Tauri APIs - this will fail in browser but succeed in Tauri\n try {\n const { invoke } = await import('@tauri-apps/api/core');\n const { listen } = await import('@tauri-apps/api/event');\n // Test if invoke actually works by calling a simple command\n await invoke('is_connected');\n return createTauriAPI(invoke, listen);\n } catch (error) {\n throw new Error(\n `Not running in Tauri environment: ${error instanceof Error ? error.message : 'unknown error'}`\n );\n }\n}\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/** Tauri API adapter implementing NoteFlowAPI via Rust backend IPC. */\nimport type { NoteFlowAPI, TranscriptionStream } from './interface';\nimport { TauriCommands, TauriEvents } from './tauri-constants'; })\n .catch((err: unknown) => {\n const message = err instanceof Error ? err.message : String(err);\n console.error(`[TauriTranscriptionStream] Failed to listen for stream_health: ${message}`);\n });\n } }\n}\n","ops":[{"diffOp":{"equal":{"range":[0,204]}}},{"equalLines":{"line_count":246}},{"diffOp":{"equal":{"range":[204,319]}}},{"diffOp":{"delete":{"range":[319,419]}}},{"diffOp":{"equal":{"range":[419,433]}}},{"equalLines":{"line_count":556}},{"diffOp":{"equal":{"range":[433,439]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"src/api/tauri-adapter.ts"},"span":[8114,8127],"sourceCode":"/** Tauri API adapter implementing NoteFlowAPI via Rust backend IPC. */\nimport type { NoteFlowAPI, TranscriptionStream } from './interface';\nimport { TauriCommands, TauriEvents } from './tauri-constants';\n\n// Re-export TauriEvents for external consumers\nexport { TauriEvents } from './tauri-constants';\nimport {\n annotationTypeToGrpcEnum,\n formatToGrpcEnum,\n normalizeAnnotationList,\n normalizeSuccessResponse,\n sortOrderToGrpcEnum,\n stateToGrpcEnum,\n} from './tauri-helpers';\nimport { meetingCache } from '@/lib/cache/meeting-cache';\nimport type {\n AddAnnotationRequest,\n Annotation,\n AudioChunk,\n AudioDeviceInfo,\n AddProjectMemberRequest,\n CancelDiarizationResult,\n CompleteCalendarAuthResponse,\n CreateMeetingRequest,\n CreateProjectRequest,\n DeleteWebhookResponse,\n DiarizationJobStatus,\n DisconnectOAuthResponse,\n ExportFormat,\n ExportResult,\n ExtractEntitiesResponse,\n ExtractedEntity,\n GetCalendarProvidersResponse,\n GetCurrentUserResponse,\n GetActiveProjectRequest,\n GetActiveProjectResponse,\n GetMeetingRequest,\n GetOAuthConnectionStatusResponse,\n GetProjectBySlugRequest,\n GetProjectRequest,\n GetPerformanceMetricsRequest,\n GetPerformanceMetricsResponse,\n GetRecentLogsRequest,\n GetRecentLogsResponse,\n GetSyncStatusResponse,\n GetUserIntegrationsResponse,\n GetWebhookDeliveriesResponse,\n InitiateCalendarAuthResponse,\n ListWorkspacesResponse,\n ListCalendarEventsResponse,\n ListMeetingsRequest,\n ListMeetingsResponse,\n ListProjectMembersRequest,\n ListProjectMembersResponse,\n ListProjectsRequest,\n ListProjectsResponse,\n ListSyncHistoryResponse,\n ListWebhooksResponse,\n Meeting,\n PlaybackInfo,\n Project,\n ProjectMembership,\n RegisteredWebhook,\n RegisterWebhookRequest,\n RemoveProjectMemberRequest,\n RemoveProjectMemberResponse,\n ServerInfo,\n SetActiveProjectRequest,\n StartIntegrationSyncResponse,\n SwitchWorkspaceResponse,\n SummarizationOptions,\n Summary,\n TranscriptUpdate,\n TriggerStatus,\n UpdateAnnotationRequest,\n UpdateProjectMemberRoleRequest,\n UpdateProjectRequest,\n UpdateWebhookRequest,\n UserPreferences,\n} from './types';\n\n/** Type-safe wrapper for Tauri's invoke function. */\nexport type TauriInvoke = <T>(cmd: string, args?: Record<string, unknown>) => Promise<T>;\n/** Type-safe wrapper for Tauri's event system. */\nexport type TauriListen = <T>(\n event: string,\n handler: (event: { payload: T }) => void\n) => Promise<() => void>;\n\n/** Error callback type for stream errors. */\nexport type StreamErrorCallback = (error: { code: string; message: string }) => void;\n\n/** Congestion state for UI feedback. */\nexport interface CongestionState {\n /** Whether the stream is currently showing congestion to the user. */\n isBuffering: boolean;\n /** Duration of congestion in milliseconds. */\n duration: number;\n}\n\n/** Congestion callback type for stream health updates. */\nexport type CongestionCallback = (state: CongestionState) => void;\n\n/** Consecutive failure threshold before emitting stream error. */\nexport const CONSECUTIVE_FAILURE_THRESHOLD = 3;\n\n/** Threshold in milliseconds before showing buffering indicator (2 seconds). */\nexport const CONGESTION_DISPLAY_THRESHOLD_MS = 2000;\n\n/** Real-time transcription stream using Tauri events. */\nexport class TauriTranscriptionStream implements TranscriptionStream {\n private unlistenFn: (() => void) | null = null;\n private healthUnlistenFn: (() => void) | null = null;\n private errorCallback: StreamErrorCallback | null = null;\n private congestionCallback: CongestionCallback | null = null;\n private consecutiveFailures = 0;\n private hasEmittedError = false;\n\n /** Latest ack_sequence received from server (for debugging/monitoring). */\n private lastAckedSequence = 0;\n\n /** Timestamp when congestion started (null if not congested). */\n private congestionStartTime: number | null = null;\n\n /** Whether buffering indicator is currently shown. */\n private isShowingBuffering = false;\n\n constructor(\n private meetingId: string,\n private invoke: TauriInvoke,\n private listen: TauriListen\n ) {}\n\n /** Get the last acknowledged chunk sequence number. */\n getLastAckedSequence(): number {\n return this.lastAckedSequence;\n }\n\n send(chunk: AudioChunk): void {\n const args: Record<string, unknown> = {\n meeting_id: chunk.meeting_id,\n audio_data: Array.from(chunk.audio_data),\n timestamp: chunk.timestamp,\n };\n if (typeof chunk.sample_rate === 'number') {\n args.sample_rate = chunk.sample_rate;\n }\n if (typeof chunk.channels === 'number') {\n args.channels = chunk.channels;\n }\n\n this.invoke(TauriCommands.SEND_AUDIO_CHUNK, args)\n .then(() => {\n // Reset failure counter on success\n this.consecutiveFailures = 0;\n })\n .catch((err: unknown) => {\n this.consecutiveFailures++;\n const message = err instanceof Error ? err.message : String(err);\n console.error(`[TauriTranscriptionStream] send_audio_chunk failed: ${message}`);\n\n // Emit error callback once after threshold consecutive failures\n if (\n this.consecutiveFailures >= CONSECUTIVE_FAILURE_THRESHOLD &&\n !this.hasEmittedError &&\n this.errorCallback\n ) {\n this.hasEmittedError = true;\n this.errorCallback({\n code: 'stream_send_failed',\n message: `Audio streaming interrupted after ${this.consecutiveFailures} failures: ${message}`,\n });\n }\n });\n }\n\n async onUpdate(callback: (update: TranscriptUpdate) => void): Promise<void> {\n this.unlistenFn = await this.listen<TranscriptUpdate>(\n TauriEvents.TRANSCRIPT_UPDATE,\n (event) => {\n if (event.payload.meeting_id === this.meetingId) {\n // Track latest ack_sequence for monitoring\n if (\n typeof event.payload.ack_sequence === 'number' &&\n event.payload.ack_sequence > this.lastAckedSequence\n ) {\n this.lastAckedSequence = event.payload.ack_sequence;\n }\n callback(event.payload);\n }\n }\n );\n }\n\n /** Register callback for stream errors (connection failures, etc.). */\n onError(callback: StreamErrorCallback): void {\n this.errorCallback = callback;\n }\n\n /** Register callback for congestion state updates (buffering indicator). */\n onCongestion(callback: CongestionCallback): void {\n this.congestionCallback = callback;\n // Start listening for stream_health events\n this.startHealthListener();\n }\n\n /** Start listening for stream_health events from the Rust backend. */\n private startHealthListener(): void {\n if (this.healthUnlistenFn) {\n return;\n } // Already listening\n\n this.listen<{\n meeting_id: string;\n is_congested: boolean;\n processing_delay_ms: number;\n queue_depth: number;\n congested_duration_ms: number;\n }>(TauriEvents.STREAM_HEALTH, (event) => {\n if (event.payload.meeting_id !== this.meetingId) {\n return;\n }\n\n const { is_congested } = event.payload;\n\n if (is_congested) {\n // Start tracking congestion if not already\n this.congestionStartTime ??= Date.now();\n const duration = Date.now() - this.congestionStartTime;\n\n // Only show buffering after threshold is exceeded\n if (duration >= CONGESTION_DISPLAY_THRESHOLD_MS && !this.isShowingBuffering) {\n this.isShowingBuffering = true;\n this.congestionCallback?.({ isBuffering: true, duration });\n } else if (this.isShowingBuffering) {\n // Update duration while showing\n this.congestionCallback?.({ isBuffering: true, duration });\n }\n } else {\n // Congestion cleared\n if (this.isShowingBuffering) {\n this.isShowingBuffering = false;\n this.congestionCallback?.({ isBuffering: false, duration: 0 });\n }\n this.congestionStartTime = null;\n }\n })\n .then((unlisten) => {\n this.healthUnlistenFn = unlisten;\n })\n .catch((err: unknown) => {\n const message = err instanceof Error ? err.message : String(err);\n console.error(`[TauriTranscriptionStream] Failed to listen for stream_health: ${message}`);\n });\n }\n\n close(): void {\n if (this.unlistenFn) {\n this.unlistenFn();\n this.unlistenFn = null;\n }\n if (this.healthUnlistenFn) {\n this.healthUnlistenFn();\n this.healthUnlistenFn = null;\n }\n // Reset congestion state\n this.congestionStartTime = null;\n this.isShowingBuffering = false;\n\n this.invoke(TauriCommands.STOP_RECORDING, { meeting_id: this.meetingId })\n .catch((err: unknown) => {\n const message = err instanceof Error ? err.message : String(err);\n console.error(`[TauriTranscriptionStream] stop_recording failed: ${message}`);\n // Emit error so UI can show notification\n if (this.errorCallback) {\n this.errorCallback({\n code: 'stream_close_failed',\n message: `Failed to stop recording: ${message}`,\n });\n }\n });\n }\n}\n\n/** Creates a Tauri API adapter instance. */\nexport function createTauriAPI(invoke: TauriInvoke, listen: TauriListen): NoteFlowAPI {\n return {\n async getServerInfo(): Promise<ServerInfo> {\n return invoke<ServerInfo>(TauriCommands.GET_SERVER_INFO);\n },\n async connect(serverUrl?: string): Promise<ServerInfo> {\n return invoke<ServerInfo>(TauriCommands.CONNECT, { server_url: serverUrl });\n },\n async disconnect(): Promise<void> {\n await invoke(TauriCommands.DISCONNECT);\n },\n async isConnected(): Promise<boolean> {\n return invoke<boolean>(TauriCommands.IS_CONNECTED);\n },\n\n async getCurrentUser(): Promise<GetCurrentUserResponse> {\n return invoke<GetCurrentUserResponse>(TauriCommands.GET_CURRENT_USER);\n },\n\n async listWorkspaces(): Promise<ListWorkspacesResponse> {\n return invoke<ListWorkspacesResponse>(TauriCommands.LIST_WORKSPACES);\n },\n\n async switchWorkspace(workspaceId: string): Promise<SwitchWorkspaceResponse> {\n return invoke<SwitchWorkspaceResponse>(TauriCommands.SWITCH_WORKSPACE, {\n workspace_id: workspaceId,\n });\n },\n\n async createProject(request: CreateProjectRequest): Promise<Project> {\n return invoke<Project>(TauriCommands.CREATE_PROJECT, {\n request,\n });\n },\n\n async getProject(request: GetProjectRequest): Promise<Project> {\n return invoke<Project>(TauriCommands.GET_PROJECT, {\n project_id: request.project_id,\n });\n },\n\n async getProjectBySlug(request: GetProjectBySlugRequest): Promise<Project> {\n return invoke<Project>(TauriCommands.GET_PROJECT_BY_SLUG, {\n workspace_id: request.workspace_id,\n slug: request.slug,\n });\n },\n\n async listProjects(request: ListProjectsRequest): Promise<ListProjectsResponse> {\n return invoke<ListProjectsResponse>(TauriCommands.LIST_PROJECTS, {\n workspace_id: request.workspace_id,\n include_archived: request.include_archived ?? false,\n limit: request.limit,\n offset: request.offset,\n });\n },\n\n async updateProject(request: UpdateProjectRequest): Promise<Project> {\n return invoke<Project>(TauriCommands.UPDATE_PROJECT, {\n request,\n });\n },\n\n async archiveProject(projectId: string): Promise<Project> {\n return invoke<Project>(TauriCommands.ARCHIVE_PROJECT, {\n project_id: projectId,\n });\n },\n\n async restoreProject(projectId: string): Promise<Project> {\n return invoke<Project>(TauriCommands.RESTORE_PROJECT, {\n project_id: projectId,\n });\n },\n\n async deleteProject(projectId: string): Promise<boolean> {\n const response = await invoke<{ success: boolean }>(TauriCommands.DELETE_PROJECT, {\n project_id: projectId,\n });\n return normalizeSuccessResponse(response);\n },\n\n async setActiveProject(request: SetActiveProjectRequest): Promise<void> {\n await invoke(TauriCommands.SET_ACTIVE_PROJECT, {\n workspace_id: request.workspace_id,\n project_id: request.project_id ?? '',\n });\n },\n\n async getActiveProject(request: GetActiveProjectRequest): Promise<GetActiveProjectResponse> {\n return invoke<GetActiveProjectResponse>(TauriCommands.GET_ACTIVE_PROJECT, {\n workspace_id: request.workspace_id,\n });\n },\n\n async addProjectMember(request: AddProjectMemberRequest): Promise<ProjectMembership> {\n return invoke<ProjectMembership>(TauriCommands.ADD_PROJECT_MEMBER, {\n request,\n });\n },\n\n async updateProjectMemberRole(\n request: UpdateProjectMemberRoleRequest\n ): Promise<ProjectMembership> {\n return invoke<ProjectMembership>(TauriCommands.UPDATE_PROJECT_MEMBER_ROLE, {\n request,\n });\n },\n\n async removeProjectMember(\n request: RemoveProjectMemberRequest\n ): Promise<RemoveProjectMemberResponse> {\n return invoke<RemoveProjectMemberResponse>(TauriCommands.REMOVE_PROJECT_MEMBER, {\n request,\n });\n },\n\n async listProjectMembers(\n request: ListProjectMembersRequest\n ): Promise<ListProjectMembersResponse> {\n return invoke<ListProjectMembersResponse>(TauriCommands.LIST_PROJECT_MEMBERS, {\n project_id: request.project_id,\n limit: request.limit,\n offset: request.offset,\n });\n },\n\n async createMeeting(request: CreateMeetingRequest): Promise<Meeting> {\n const meeting = await invoke<Meeting>(TauriCommands.CREATE_MEETING, {\n title: request.title,\n metadata: request.metadata ?? {},\n project_id: request.project_id,\n });\n meetingCache.cacheMeeting(meeting);\n return meeting;\n },\n async listMeetings(request: ListMeetingsRequest): Promise<ListMeetingsResponse> {\n const response = await invoke<ListMeetingsResponse>(TauriCommands.LIST_MEETINGS, {\n states: request.states?.map(stateToGrpcEnum) ?? [],\n limit: request.limit ?? 50,\n offset: request.offset ?? 0,\n sort_order: sortOrderToGrpcEnum(request.sort_order),\n project_id: request.project_id,\n });\n if (response.meetings?.length) {\n meetingCache.cacheMeetings(response.meetings);\n }\n return response;\n },\n async getMeeting(request: GetMeetingRequest): Promise<Meeting> {\n const meeting = await invoke<Meeting>(TauriCommands.GET_MEETING, {\n meeting_id: request.meeting_id,\n include_segments: request.include_segments ?? false,\n include_summary: request.include_summary ?? false,\n });\n meetingCache.cacheMeeting(meeting);\n return meeting;\n },\n async stopMeeting(meetingId: string): Promise<Meeting> {\n const meeting = await invoke<Meeting>(TauriCommands.STOP_MEETING, {\n meeting_id: meetingId,\n });\n meetingCache.cacheMeeting(meeting);\n return meeting;\n },\n async deleteMeeting(meetingId: string): Promise<boolean> {\n const result = normalizeSuccessResponse(\n await invoke<boolean | { success: boolean }>(TauriCommands.DELETE_MEETING, {\n meeting_id: meetingId,\n })\n );\n if (result) {\n meetingCache.removeMeeting(meetingId);\n }\n return result;\n },\n\n async startTranscription(meetingId: string): Promise<TranscriptionStream> {\n await invoke(TauriCommands.START_RECORDING, { meeting_id: meetingId });\n return new TauriTranscriptionStream(meetingId, invoke, listen);\n },\n\n async generateSummary(meetingId: string, forceRegenerate?: boolean): Promise<Summary> {\n let options: SummarizationOptions | undefined;\n try {\n const prefs = await invoke<UserPreferences>(TauriCommands.GET_PREFERENCES);\n if (prefs?.ai_template) {\n options = {\n tone: prefs.ai_template.tone,\n format: prefs.ai_template.format,\n verbosity: prefs.ai_template.verbosity,\n };\n }\n } catch {\n /* Preferences unavailable */\n }\n return invoke<Summary>(TauriCommands.GENERATE_SUMMARY, {\n meeting_id: meetingId,\n force_regenerate: forceRegenerate ?? false,\n options,\n });\n },\n\n async grantCloudConsent(): Promise<void> {\n await invoke(TauriCommands.GRANT_CLOUD_CONSENT);\n },\n async revokeCloudConsent(): Promise<void> {\n await invoke(TauriCommands.REVOKE_CLOUD_CONSENT);\n },\n async getCloudConsentStatus(): Promise<{ consentGranted: boolean }> {\n return invoke<{ consent_granted: boolean }>(TauriCommands.GET_CLOUD_CONSENT_STATUS).then(\n (r) => ({ consentGranted: r.consent_granted })\n );\n },\n\n async listAnnotations(\n meetingId: string,\n startTime?: number,\n endTime?: number\n ): Promise<Annotation[]> {\n return normalizeAnnotationList(\n await invoke<Annotation[] | { annotations: Annotation[] }>(TauriCommands.LIST_ANNOTATIONS, {\n meeting_id: meetingId,\n start_time: startTime ?? 0,\n end_time: endTime ?? 0,\n })\n );\n },\n async addAnnotation(request: AddAnnotationRequest): Promise<Annotation> {\n return invoke<Annotation>(TauriCommands.ADD_ANNOTATION, {\n meeting_id: request.meeting_id,\n annotation_type: annotationTypeToGrpcEnum(request.annotation_type),\n text: request.text,\n start_time: request.start_time,\n end_time: request.end_time,\n segment_ids: request.segment_ids ?? [],\n });\n },\n async getAnnotation(annotationId: string): Promise<Annotation> {\n return invoke<Annotation>(TauriCommands.GET_ANNOTATION, { annotation_id: annotationId });\n },\n async updateAnnotation(request: UpdateAnnotationRequest): Promise<Annotation> {\n return invoke<Annotation>(TauriCommands.UPDATE_ANNOTATION, {\n annotation_id: request.annotation_id,\n annotation_type: request.annotation_type\n ? annotationTypeToGrpcEnum(request.annotation_type)\n : undefined,\n text: request.text,\n start_time: request.start_time,\n end_time: request.end_time,\n segment_ids: request.segment_ids,\n });\n },\n async deleteAnnotation(annotationId: string): Promise<boolean> {\n return normalizeSuccessResponse(\n await invoke<boolean | { success: boolean }>(TauriCommands.DELETE_ANNOTATION, {\n annotation_id: annotationId,\n })\n );\n },\n\n async exportTranscript(meetingId: string, format: ExportFormat): Promise<ExportResult> {\n return invoke<ExportResult>(TauriCommands.EXPORT_TRANSCRIPT, {\n meeting_id: meetingId,\n format: formatToGrpcEnum(format),\n });\n },\n async saveExportFile(\n content: string,\n defaultName: string,\n extension: string\n ): Promise<boolean> {\n return invoke<boolean>(TauriCommands.SAVE_EXPORT_FILE, {\n content,\n default_name: defaultName,\n extension,\n });\n },\n\n async startPlayback(meetingId: string, startTime?: number): Promise<void> {\n await invoke(TauriCommands.START_PLAYBACK, { meeting_id: meetingId, start_time: startTime });\n },\n async pausePlayback(): Promise<void> {\n await invoke(TauriCommands.PAUSE_PLAYBACK);\n },\n async stopPlayback(): Promise<void> {\n await invoke(TauriCommands.STOP_PLAYBACK);\n },\n async seekPlayback(position: number): Promise<PlaybackInfo> {\n return invoke<PlaybackInfo>(TauriCommands.SEEK_PLAYBACK, { position });\n },\n async getPlaybackState(): Promise<PlaybackInfo> {\n return invoke<PlaybackInfo>(TauriCommands.GET_PLAYBACK_STATE);\n },\n\n async refineSpeakers(meetingId: string, numSpeakers?: number): Promise<DiarizationJobStatus> {\n return invoke<DiarizationJobStatus>(TauriCommands.REFINE_SPEAKERS, {\n meeting_id: meetingId,\n num_speakers: numSpeakers ?? 0,\n });\n },\n async getDiarizationJobStatus(jobId: string): Promise<DiarizationJobStatus> {\n return invoke<DiarizationJobStatus>(TauriCommands.GET_DIARIZATION_STATUS, { job_id: jobId });\n },\n async renameSpeaker(\n meetingId: string,\n oldSpeakerId: string,\n newName: string\n ): Promise<boolean> {\n return (\n await invoke<{ success: boolean }>(TauriCommands.RENAME_SPEAKER, {\n meeting_id: meetingId,\n old_speaker_id: oldSpeakerId,\n new_speaker_name: newName,\n })\n ).success;\n },\n async cancelDiarization(jobId: string): Promise<CancelDiarizationResult> {\n return invoke<CancelDiarizationResult>(TauriCommands.CANCEL_DIARIZATION, { job_id: jobId });\n },\n\n async getPreferences(): Promise<UserPreferences> {\n return invoke<UserPreferences>(TauriCommands.GET_PREFERENCES);\n },\n async savePreferences(preferences: UserPreferences): Promise<void> {\n await invoke(TauriCommands.SAVE_PREFERENCES, { preferences });\n },\n\n async listAudioDevices(): Promise<AudioDeviceInfo[]> {\n return invoke<AudioDeviceInfo[]>(TauriCommands.LIST_AUDIO_DEVICES);\n },\n async getDefaultAudioDevice(isInput: boolean): Promise<AudioDeviceInfo | null> {\n return invoke<AudioDeviceInfo | null>(TauriCommands.GET_DEFAULT_AUDIO_DEVICE, {\n is_input: isInput,\n });\n },\n async selectAudioDevice(deviceId: string, isInput: boolean): Promise<void> {\n await invoke(TauriCommands.SELECT_AUDIO_DEVICE, { device_id: deviceId, is_input: isInput });\n },\n\n async setTriggerEnabled(enabled: boolean): Promise<void> {\n await invoke(TauriCommands.SET_TRIGGER_ENABLED, { enabled });\n },\n async snoozeTriggers(minutes?: number): Promise<void> {\n await invoke(TauriCommands.SNOOZE_TRIGGERS, { minutes });\n },\n async resetSnooze(): Promise<void> {\n await invoke(TauriCommands.RESET_SNOOZE);\n },\n async getTriggerStatus(): Promise<TriggerStatus> {\n return invoke<TriggerStatus>(TauriCommands.GET_TRIGGER_STATUS);\n },\n async dismissTrigger(): Promise<void> {\n await invoke(TauriCommands.DISMISS_TRIGGER);\n },\n async acceptTrigger(title?: string): Promise<Meeting> {\n return invoke<Meeting>(TauriCommands.ACCEPT_TRIGGER, { title });\n },\n\n async extractEntities(\n meetingId: string,\n forceRefresh?: boolean\n ): Promise<ExtractEntitiesResponse> {\n return invoke<ExtractEntitiesResponse>(TauriCommands.EXTRACT_ENTITIES, {\n meeting_id: meetingId,\n force_refresh: forceRefresh ?? false,\n });\n },\n async updateEntity(\n meetingId: string,\n entityId: string,\n text?: string,\n category?: string\n ): Promise<ExtractedEntity> {\n return invoke<ExtractedEntity>(TauriCommands.UPDATE_ENTITY, {\n meeting_id: meetingId,\n entity_id: entityId,\n text,\n category,\n });\n },\n async deleteEntity(meetingId: string, entityId: string): Promise<boolean> {\n return invoke<boolean>(TauriCommands.DELETE_ENTITY, {\n meeting_id: meetingId,\n entity_id: entityId,\n });\n },\n\n async listCalendarEvents(\n hoursAhead?: number,\n limit?: number,\n provider?: string\n ): Promise<ListCalendarEventsResponse> {\n return invoke<ListCalendarEventsResponse>(TauriCommands.LIST_CALENDAR_EVENTS, {\n hours_ahead: hoursAhead,\n limit,\n provider,\n });\n },\n async getCalendarProviders(): Promise<GetCalendarProvidersResponse> {\n return invoke<GetCalendarProvidersResponse>(TauriCommands.GET_CALENDAR_PROVIDERS);\n },\n async initiateCalendarAuth(\n provider: string,\n redirectUri?: string\n ): Promise<InitiateCalendarAuthResponse> {\n return invoke<InitiateCalendarAuthResponse>(TauriCommands.INITIATE_OAUTH, {\n provider,\n redirect_uri: redirectUri,\n });\n },\n async completeCalendarAuth(\n provider: string,\n code: string,\n state: string\n ): Promise<CompleteCalendarAuthResponse> {\n return invoke<CompleteCalendarAuthResponse>(TauriCommands.COMPLETE_OAUTH, {\n provider,\n code,\n state,\n });\n },\n async getOAuthConnectionStatus(provider: string): Promise<GetOAuthConnectionStatusResponse> {\n return invoke<GetOAuthConnectionStatusResponse>(TauriCommands.GET_OAUTH_CONNECTION_STATUS, {\n provider,\n });\n },\n async disconnectCalendar(provider: string): Promise<DisconnectOAuthResponse> {\n return invoke<DisconnectOAuthResponse>(TauriCommands.DISCONNECT_OAUTH, { provider });\n },\n\n async registerWebhook(r: RegisterWebhookRequest): Promise<RegisteredWebhook> {\n return invoke<RegisteredWebhook>(TauriCommands.REGISTER_WEBHOOK, { request: r });\n },\n async listWebhooks(enabledOnly?: boolean): Promise<ListWebhooksResponse> {\n return invoke<ListWebhooksResponse>(TauriCommands.LIST_WEBHOOKS, {\n enabled_only: enabledOnly ?? false,\n });\n },\n async updateWebhook(r: UpdateWebhookRequest): Promise<RegisteredWebhook> {\n return invoke<RegisteredWebhook>(TauriCommands.UPDATE_WEBHOOK, { request: r });\n },\n async deleteWebhook(webhookId: string): Promise<DeleteWebhookResponse> {\n return invoke<DeleteWebhookResponse>(TauriCommands.DELETE_WEBHOOK, { webhook_id: webhookId });\n },\n async getWebhookDeliveries(\n webhookId: string,\n limit?: number\n ): Promise<GetWebhookDeliveriesResponse> {\n return invoke<GetWebhookDeliveriesResponse>(TauriCommands.GET_WEBHOOK_DELIVERIES, {\n webhook_id: webhookId,\n limit: limit ?? 50,\n });\n },\n\n // Integration Sync (Sprint 9)\n async startIntegrationSync(integrationId: string): Promise<StartIntegrationSyncResponse> {\n return invoke<StartIntegrationSyncResponse>(TauriCommands.START_INTEGRATION_SYNC, {\n integration_id: integrationId,\n });\n },\n async getSyncStatus(syncRunId: string): Promise<GetSyncStatusResponse> {\n return invoke<GetSyncStatusResponse>(TauriCommands.GET_SYNC_STATUS, {\n sync_run_id: syncRunId,\n });\n },\n async listSyncHistory(\n integrationId: string,\n limit?: number,\n offset?: number\n ): Promise<ListSyncHistoryResponse> {\n return invoke<ListSyncHistoryResponse>(TauriCommands.LIST_SYNC_HISTORY, {\n integration_id: integrationId,\n limit,\n offset,\n });\n },\n async getUserIntegrations(): Promise<GetUserIntegrationsResponse> {\n return invoke<GetUserIntegrationsResponse>(TauriCommands.GET_USER_INTEGRATIONS);\n },\n\n // Observability (Sprint 9)\n async getRecentLogs(request?: GetRecentLogsRequest): Promise<GetRecentLogsResponse> {\n return invoke<GetRecentLogsResponse>(TauriCommands.GET_RECENT_LOGS, {\n limit: request?.limit,\n level: request?.level,\n source: request?.source,\n });\n },\n async getPerformanceMetrics(\n request?: GetPerformanceMetricsRequest\n ): Promise<GetPerformanceMetricsResponse> {\n return invoke<GetPerformanceMetricsResponse>(TauriCommands.GET_PERFORMANCE_METRICS, {\n history_limit: request?.history_limit,\n });\n },\n };\n}\n\n/** Check if running in a Tauri environment (synchronous hint). */\nexport function isTauriEnvironment(): boolean {\n if (typeof window === 'undefined') {\n return false;\n }\n // Tauri 2.x injects __TAURI_INTERNALS__ into the window\n // Check multiple possible indicators\n return (\n '__TAURI_INTERNALS__' in window ||\n '__TAURI__' in window ||\n 'isTauri' in window\n );\n}\n\n/** Dynamically import Tauri APIs and create the adapter. */\nexport async function initializeTauriAPI(): Promise<NoteFlowAPI> {\n // Try to import Tauri APIs - this will fail in browser but succeed in Tauri\n try {\n const { invoke } = await import('@tauri-apps/api/core');\n const { listen } = await import('@tauri-apps/api/event');\n // Test if invoke actually works by calling a simple command\n await invoke('is_connected');\n return createTauriAPI(invoke, listen);\n } catch (error) {\n throw new Error(\n `Not running in Tauri environment: ${error instanceof Error ? error.message : 'unknown error'}`\n );\n }\n}\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/** Tauri API adapter implementing NoteFlowAPI via Rust backend IPC. */\nimport type { NoteFlowAPI, TranscriptionStream } from './interface';\nimport { TauriCommands, TauriEvents } from './tauri-constants'; this.invoke(TauriCommands.STOP_RECORDING, { meeting_id: this.meetingId })\n .catch((err: unknown) => {\n const message = err instanceof Error ? err.message : String(err);\n console.error(`[TauriTranscriptionStream] stop_recording failed: ${message}`);\n // Emit error so UI can show notification\n if (this.errorCallback) { }\n}\n","ops":[{"diffOp":{"equal":{"range":[0,204]}}},{"equalLines":{"line_count":266}},{"diffOp":{"equal":{"range":[204,388]}}},{"diffOp":{"delete":{"range":[388,475]}}},{"diffOp":{"equal":{"range":[475,559]}}},{"equalLines":{"line_count":536}},{"diffOp":{"equal":{"range":[559,565]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"src/api/tauri-adapter.ts"},"span":[8731,8744],"sourceCode":"/** Tauri API adapter implementing NoteFlowAPI via Rust backend IPC. */\nimport type { NoteFlowAPI, TranscriptionStream } from './interface';\nimport { TauriCommands, TauriEvents } from './tauri-constants';\n\n// Re-export TauriEvents for external consumers\nexport { TauriEvents } from './tauri-constants';\nimport {\n annotationTypeToGrpcEnum,\n formatToGrpcEnum,\n normalizeAnnotationList,\n normalizeSuccessResponse,\n sortOrderToGrpcEnum,\n stateToGrpcEnum,\n} from './tauri-helpers';\nimport { meetingCache } from '@/lib/cache/meeting-cache';\nimport type {\n AddAnnotationRequest,\n Annotation,\n AudioChunk,\n AudioDeviceInfo,\n AddProjectMemberRequest,\n CancelDiarizationResult,\n CompleteCalendarAuthResponse,\n CreateMeetingRequest,\n CreateProjectRequest,\n DeleteWebhookResponse,\n DiarizationJobStatus,\n DisconnectOAuthResponse,\n ExportFormat,\n ExportResult,\n ExtractEntitiesResponse,\n ExtractedEntity,\n GetCalendarProvidersResponse,\n GetCurrentUserResponse,\n GetActiveProjectRequest,\n GetActiveProjectResponse,\n GetMeetingRequest,\n GetOAuthConnectionStatusResponse,\n GetProjectBySlugRequest,\n GetProjectRequest,\n GetPerformanceMetricsRequest,\n GetPerformanceMetricsResponse,\n GetRecentLogsRequest,\n GetRecentLogsResponse,\n GetSyncStatusResponse,\n GetUserIntegrationsResponse,\n GetWebhookDeliveriesResponse,\n InitiateCalendarAuthResponse,\n ListWorkspacesResponse,\n ListCalendarEventsResponse,\n ListMeetingsRequest,\n ListMeetingsResponse,\n ListProjectMembersRequest,\n ListProjectMembersResponse,\n ListProjectsRequest,\n ListProjectsResponse,\n ListSyncHistoryResponse,\n ListWebhooksResponse,\n Meeting,\n PlaybackInfo,\n Project,\n ProjectMembership,\n RegisteredWebhook,\n RegisterWebhookRequest,\n RemoveProjectMemberRequest,\n RemoveProjectMemberResponse,\n ServerInfo,\n SetActiveProjectRequest,\n StartIntegrationSyncResponse,\n SwitchWorkspaceResponse,\n SummarizationOptions,\n Summary,\n TranscriptUpdate,\n TriggerStatus,\n UpdateAnnotationRequest,\n UpdateProjectMemberRoleRequest,\n UpdateProjectRequest,\n UpdateWebhookRequest,\n UserPreferences,\n} from './types';\n\n/** Type-safe wrapper for Tauri's invoke function. */\nexport type TauriInvoke = <T>(cmd: string, args?: Record<string, unknown>) => Promise<T>;\n/** Type-safe wrapper for Tauri's event system. */\nexport type TauriListen = <T>(\n event: string,\n handler: (event: { payload: T }) => void\n) => Promise<() => void>;\n\n/** Error callback type for stream errors. */\nexport type StreamErrorCallback = (error: { code: string; message: string }) => void;\n\n/** Congestion state for UI feedback. */\nexport interface CongestionState {\n /** Whether the stream is currently showing congestion to the user. */\n isBuffering: boolean;\n /** Duration of congestion in milliseconds. */\n duration: number;\n}\n\n/** Congestion callback type for stream health updates. */\nexport type CongestionCallback = (state: CongestionState) => void;\n\n/** Consecutive failure threshold before emitting stream error. */\nexport const CONSECUTIVE_FAILURE_THRESHOLD = 3;\n\n/** Threshold in milliseconds before showing buffering indicator (2 seconds). */\nexport const CONGESTION_DISPLAY_THRESHOLD_MS = 2000;\n\n/** Real-time transcription stream using Tauri events. */\nexport class TauriTranscriptionStream implements TranscriptionStream {\n private unlistenFn: (() => void) | null = null;\n private healthUnlistenFn: (() => void) | null = null;\n private errorCallback: StreamErrorCallback | null = null;\n private congestionCallback: CongestionCallback | null = null;\n private consecutiveFailures = 0;\n private hasEmittedError = false;\n\n /** Latest ack_sequence received from server (for debugging/monitoring). */\n private lastAckedSequence = 0;\n\n /** Timestamp when congestion started (null if not congested). */\n private congestionStartTime: number | null = null;\n\n /** Whether buffering indicator is currently shown. */\n private isShowingBuffering = false;\n\n constructor(\n private meetingId: string,\n private invoke: TauriInvoke,\n private listen: TauriListen\n ) {}\n\n /** Get the last acknowledged chunk sequence number. */\n getLastAckedSequence(): number {\n return this.lastAckedSequence;\n }\n\n send(chunk: AudioChunk): void {\n const args: Record<string, unknown> = {\n meeting_id: chunk.meeting_id,\n audio_data: Array.from(chunk.audio_data),\n timestamp: chunk.timestamp,\n };\n if (typeof chunk.sample_rate === 'number') {\n args.sample_rate = chunk.sample_rate;\n }\n if (typeof chunk.channels === 'number') {\n args.channels = chunk.channels;\n }\n\n this.invoke(TauriCommands.SEND_AUDIO_CHUNK, args)\n .then(() => {\n // Reset failure counter on success\n this.consecutiveFailures = 0;\n })\n .catch((err: unknown) => {\n this.consecutiveFailures++;\n const message = err instanceof Error ? err.message : String(err);\n console.error(`[TauriTranscriptionStream] send_audio_chunk failed: ${message}`);\n\n // Emit error callback once after threshold consecutive failures\n if (\n this.consecutiveFailures >= CONSECUTIVE_FAILURE_THRESHOLD &&\n !this.hasEmittedError &&\n this.errorCallback\n ) {\n this.hasEmittedError = true;\n this.errorCallback({\n code: 'stream_send_failed',\n message: `Audio streaming interrupted after ${this.consecutiveFailures} failures: ${message}`,\n });\n }\n });\n }\n\n async onUpdate(callback: (update: TranscriptUpdate) => void): Promise<void> {\n this.unlistenFn = await this.listen<TranscriptUpdate>(\n TauriEvents.TRANSCRIPT_UPDATE,\n (event) => {\n if (event.payload.meeting_id === this.meetingId) {\n // Track latest ack_sequence for monitoring\n if (\n typeof event.payload.ack_sequence === 'number' &&\n event.payload.ack_sequence > this.lastAckedSequence\n ) {\n this.lastAckedSequence = event.payload.ack_sequence;\n }\n callback(event.payload);\n }\n }\n );\n }\n\n /** Register callback for stream errors (connection failures, etc.). */\n onError(callback: StreamErrorCallback): void {\n this.errorCallback = callback;\n }\n\n /** Register callback for congestion state updates (buffering indicator). */\n onCongestion(callback: CongestionCallback): void {\n this.congestionCallback = callback;\n // Start listening for stream_health events\n this.startHealthListener();\n }\n\n /** Start listening for stream_health events from the Rust backend. */\n private startHealthListener(): void {\n if (this.healthUnlistenFn) {\n return;\n } // Already listening\n\n this.listen<{\n meeting_id: string;\n is_congested: boolean;\n processing_delay_ms: number;\n queue_depth: number;\n congested_duration_ms: number;\n }>(TauriEvents.STREAM_HEALTH, (event) => {\n if (event.payload.meeting_id !== this.meetingId) {\n return;\n }\n\n const { is_congested } = event.payload;\n\n if (is_congested) {\n // Start tracking congestion if not already\n this.congestionStartTime ??= Date.now();\n const duration = Date.now() - this.congestionStartTime;\n\n // Only show buffering after threshold is exceeded\n if (duration >= CONGESTION_DISPLAY_THRESHOLD_MS && !this.isShowingBuffering) {\n this.isShowingBuffering = true;\n this.congestionCallback?.({ isBuffering: true, duration });\n } else if (this.isShowingBuffering) {\n // Update duration while showing\n this.congestionCallback?.({ isBuffering: true, duration });\n }\n } else {\n // Congestion cleared\n if (this.isShowingBuffering) {\n this.isShowingBuffering = false;\n this.congestionCallback?.({ isBuffering: false, duration: 0 });\n }\n this.congestionStartTime = null;\n }\n })\n .then((unlisten) => {\n this.healthUnlistenFn = unlisten;\n })\n .catch((err: unknown) => {\n const message = err instanceof Error ? err.message : String(err);\n console.error(`[TauriTranscriptionStream] Failed to listen for stream_health: ${message}`);\n });\n }\n\n close(): void {\n if (this.unlistenFn) {\n this.unlistenFn();\n this.unlistenFn = null;\n }\n if (this.healthUnlistenFn) {\n this.healthUnlistenFn();\n this.healthUnlistenFn = null;\n }\n // Reset congestion state\n this.congestionStartTime = null;\n this.isShowingBuffering = false;\n\n this.invoke(TauriCommands.STOP_RECORDING, { meeting_id: this.meetingId })\n .catch((err: unknown) => {\n const message = err instanceof Error ? err.message : String(err);\n console.error(`[TauriTranscriptionStream] stop_recording failed: ${message}`);\n // Emit error so UI can show notification\n if (this.errorCallback) {\n this.errorCallback({\n code: 'stream_close_failed',\n message: `Failed to stop recording: ${message}`,\n });\n }\n });\n }\n}\n\n/** Creates a Tauri API adapter instance. */\nexport function createTauriAPI(invoke: TauriInvoke, listen: TauriListen): NoteFlowAPI {\n return {\n async getServerInfo(): Promise<ServerInfo> {\n return invoke<ServerInfo>(TauriCommands.GET_SERVER_INFO);\n },\n async connect(serverUrl?: string): Promise<ServerInfo> {\n return invoke<ServerInfo>(TauriCommands.CONNECT, { server_url: serverUrl });\n },\n async disconnect(): Promise<void> {\n await invoke(TauriCommands.DISCONNECT);\n },\n async isConnected(): Promise<boolean> {\n return invoke<boolean>(TauriCommands.IS_CONNECTED);\n },\n\n async getCurrentUser(): Promise<GetCurrentUserResponse> {\n return invoke<GetCurrentUserResponse>(TauriCommands.GET_CURRENT_USER);\n },\n\n async listWorkspaces(): Promise<ListWorkspacesResponse> {\n return invoke<ListWorkspacesResponse>(TauriCommands.LIST_WORKSPACES);\n },\n\n async switchWorkspace(workspaceId: string): Promise<SwitchWorkspaceResponse> {\n return invoke<SwitchWorkspaceResponse>(TauriCommands.SWITCH_WORKSPACE, {\n workspace_id: workspaceId,\n });\n },\n\n async createProject(request: CreateProjectRequest): Promise<Project> {\n return invoke<Project>(TauriCommands.CREATE_PROJECT, {\n request,\n });\n },\n\n async getProject(request: GetProjectRequest): Promise<Project> {\n return invoke<Project>(TauriCommands.GET_PROJECT, {\n project_id: request.project_id,\n });\n },\n\n async getProjectBySlug(request: GetProjectBySlugRequest): Promise<Project> {\n return invoke<Project>(TauriCommands.GET_PROJECT_BY_SLUG, {\n workspace_id: request.workspace_id,\n slug: request.slug,\n });\n },\n\n async listProjects(request: ListProjectsRequest): Promise<ListProjectsResponse> {\n return invoke<ListProjectsResponse>(TauriCommands.LIST_PROJECTS, {\n workspace_id: request.workspace_id,\n include_archived: request.include_archived ?? false,\n limit: request.limit,\n offset: request.offset,\n });\n },\n\n async updateProject(request: UpdateProjectRequest): Promise<Project> {\n return invoke<Project>(TauriCommands.UPDATE_PROJECT, {\n request,\n });\n },\n\n async archiveProject(projectId: string): Promise<Project> {\n return invoke<Project>(TauriCommands.ARCHIVE_PROJECT, {\n project_id: projectId,\n });\n },\n\n async restoreProject(projectId: string): Promise<Project> {\n return invoke<Project>(TauriCommands.RESTORE_PROJECT, {\n project_id: projectId,\n });\n },\n\n async deleteProject(projectId: string): Promise<boolean> {\n const response = await invoke<{ success: boolean }>(TauriCommands.DELETE_PROJECT, {\n project_id: projectId,\n });\n return normalizeSuccessResponse(response);\n },\n\n async setActiveProject(request: SetActiveProjectRequest): Promise<void> {\n await invoke(TauriCommands.SET_ACTIVE_PROJECT, {\n workspace_id: request.workspace_id,\n project_id: request.project_id ?? '',\n });\n },\n\n async getActiveProject(request: GetActiveProjectRequest): Promise<GetActiveProjectResponse> {\n return invoke<GetActiveProjectResponse>(TauriCommands.GET_ACTIVE_PROJECT, {\n workspace_id: request.workspace_id,\n });\n },\n\n async addProjectMember(request: AddProjectMemberRequest): Promise<ProjectMembership> {\n return invoke<ProjectMembership>(TauriCommands.ADD_PROJECT_MEMBER, {\n request,\n });\n },\n\n async updateProjectMemberRole(\n request: UpdateProjectMemberRoleRequest\n ): Promise<ProjectMembership> {\n return invoke<ProjectMembership>(TauriCommands.UPDATE_PROJECT_MEMBER_ROLE, {\n request,\n });\n },\n\n async removeProjectMember(\n request: RemoveProjectMemberRequest\n ): Promise<RemoveProjectMemberResponse> {\n return invoke<RemoveProjectMemberResponse>(TauriCommands.REMOVE_PROJECT_MEMBER, {\n request,\n });\n },\n\n async listProjectMembers(\n request: ListProjectMembersRequest\n ): Promise<ListProjectMembersResponse> {\n return invoke<ListProjectMembersResponse>(TauriCommands.LIST_PROJECT_MEMBERS, {\n project_id: request.project_id,\n limit: request.limit,\n offset: request.offset,\n });\n },\n\n async createMeeting(request: CreateMeetingRequest): Promise<Meeting> {\n const meeting = await invoke<Meeting>(TauriCommands.CREATE_MEETING, {\n title: request.title,\n metadata: request.metadata ?? {},\n project_id: request.project_id,\n });\n meetingCache.cacheMeeting(meeting);\n return meeting;\n },\n async listMeetings(request: ListMeetingsRequest): Promise<ListMeetingsResponse> {\n const response = await invoke<ListMeetingsResponse>(TauriCommands.LIST_MEETINGS, {\n states: request.states?.map(stateToGrpcEnum) ?? [],\n limit: request.limit ?? 50,\n offset: request.offset ?? 0,\n sort_order: sortOrderToGrpcEnum(request.sort_order),\n project_id: request.project_id,\n });\n if (response.meetings?.length) {\n meetingCache.cacheMeetings(response.meetings);\n }\n return response;\n },\n async getMeeting(request: GetMeetingRequest): Promise<Meeting> {\n const meeting = await invoke<Meeting>(TauriCommands.GET_MEETING, {\n meeting_id: request.meeting_id,\n include_segments: request.include_segments ?? false,\n include_summary: request.include_summary ?? false,\n });\n meetingCache.cacheMeeting(meeting);\n return meeting;\n },\n async stopMeeting(meetingId: string): Promise<Meeting> {\n const meeting = await invoke<Meeting>(TauriCommands.STOP_MEETING, {\n meeting_id: meetingId,\n });\n meetingCache.cacheMeeting(meeting);\n return meeting;\n },\n async deleteMeeting(meetingId: string): Promise<boolean> {\n const result = normalizeSuccessResponse(\n await invoke<boolean | { success: boolean }>(TauriCommands.DELETE_MEETING, {\n meeting_id: meetingId,\n })\n );\n if (result) {\n meetingCache.removeMeeting(meetingId);\n }\n return result;\n },\n\n async startTranscription(meetingId: string): Promise<TranscriptionStream> {\n await invoke(TauriCommands.START_RECORDING, { meeting_id: meetingId });\n return new TauriTranscriptionStream(meetingId, invoke, listen);\n },\n\n async generateSummary(meetingId: string, forceRegenerate?: boolean): Promise<Summary> {\n let options: SummarizationOptions | undefined;\n try {\n const prefs = await invoke<UserPreferences>(TauriCommands.GET_PREFERENCES);\n if (prefs?.ai_template) {\n options = {\n tone: prefs.ai_template.tone,\n format: prefs.ai_template.format,\n verbosity: prefs.ai_template.verbosity,\n };\n }\n } catch {\n /* Preferences unavailable */\n }\n return invoke<Summary>(TauriCommands.GENERATE_SUMMARY, {\n meeting_id: meetingId,\n force_regenerate: forceRegenerate ?? false,\n options,\n });\n },\n\n async grantCloudConsent(): Promise<void> {\n await invoke(TauriCommands.GRANT_CLOUD_CONSENT);\n },\n async revokeCloudConsent(): Promise<void> {\n await invoke(TauriCommands.REVOKE_CLOUD_CONSENT);\n },\n async getCloudConsentStatus(): Promise<{ consentGranted: boolean }> {\n return invoke<{ consent_granted: boolean }>(TauriCommands.GET_CLOUD_CONSENT_STATUS).then(\n (r) => ({ consentGranted: r.consent_granted })\n );\n },\n\n async listAnnotations(\n meetingId: string,\n startTime?: number,\n endTime?: number\n ): Promise<Annotation[]> {\n return normalizeAnnotationList(\n await invoke<Annotation[] | { annotations: Annotation[] }>(TauriCommands.LIST_ANNOTATIONS, {\n meeting_id: meetingId,\n start_time: startTime ?? 0,\n end_time: endTime ?? 0,\n })\n );\n },\n async addAnnotation(request: AddAnnotationRequest): Promise<Annotation> {\n return invoke<Annotation>(TauriCommands.ADD_ANNOTATION, {\n meeting_id: request.meeting_id,\n annotation_type: annotationTypeToGrpcEnum(request.annotation_type),\n text: request.text,\n start_time: request.start_time,\n end_time: request.end_time,\n segment_ids: request.segment_ids ?? [],\n });\n },\n async getAnnotation(annotationId: string): Promise<Annotation> {\n return invoke<Annotation>(TauriCommands.GET_ANNOTATION, { annotation_id: annotationId });\n },\n async updateAnnotation(request: UpdateAnnotationRequest): Promise<Annotation> {\n return invoke<Annotation>(TauriCommands.UPDATE_ANNOTATION, {\n annotation_id: request.annotation_id,\n annotation_type: request.annotation_type\n ? annotationTypeToGrpcEnum(request.annotation_type)\n : undefined,\n text: request.text,\n start_time: request.start_time,\n end_time: request.end_time,\n segment_ids: request.segment_ids,\n });\n },\n async deleteAnnotation(annotationId: string): Promise<boolean> {\n return normalizeSuccessResponse(\n await invoke<boolean | { success: boolean }>(TauriCommands.DELETE_ANNOTATION, {\n annotation_id: annotationId,\n })\n );\n },\n\n async exportTranscript(meetingId: string, format: ExportFormat): Promise<ExportResult> {\n return invoke<ExportResult>(TauriCommands.EXPORT_TRANSCRIPT, {\n meeting_id: meetingId,\n format: formatToGrpcEnum(format),\n });\n },\n async saveExportFile(\n content: string,\n defaultName: string,\n extension: string\n ): Promise<boolean> {\n return invoke<boolean>(TauriCommands.SAVE_EXPORT_FILE, {\n content,\n default_name: defaultName,\n extension,\n });\n },\n\n async startPlayback(meetingId: string, startTime?: number): Promise<void> {\n await invoke(TauriCommands.START_PLAYBACK, { meeting_id: meetingId, start_time: startTime });\n },\n async pausePlayback(): Promise<void> {\n await invoke(TauriCommands.PAUSE_PLAYBACK);\n },\n async stopPlayback(): Promise<void> {\n await invoke(TauriCommands.STOP_PLAYBACK);\n },\n async seekPlayback(position: number): Promise<PlaybackInfo> {\n return invoke<PlaybackInfo>(TauriCommands.SEEK_PLAYBACK, { position });\n },\n async getPlaybackState(): Promise<PlaybackInfo> {\n return invoke<PlaybackInfo>(TauriCommands.GET_PLAYBACK_STATE);\n },\n\n async refineSpeakers(meetingId: string, numSpeakers?: number): Promise<DiarizationJobStatus> {\n return invoke<DiarizationJobStatus>(TauriCommands.REFINE_SPEAKERS, {\n meeting_id: meetingId,\n num_speakers: numSpeakers ?? 0,\n });\n },\n async getDiarizationJobStatus(jobId: string): Promise<DiarizationJobStatus> {\n return invoke<DiarizationJobStatus>(TauriCommands.GET_DIARIZATION_STATUS, { job_id: jobId });\n },\n async renameSpeaker(\n meetingId: string,\n oldSpeakerId: string,\n newName: string\n ): Promise<boolean> {\n return (\n await invoke<{ success: boolean }>(TauriCommands.RENAME_SPEAKER, {\n meeting_id: meetingId,\n old_speaker_id: oldSpeakerId,\n new_speaker_name: newName,\n })\n ).success;\n },\n async cancelDiarization(jobId: string): Promise<CancelDiarizationResult> {\n return invoke<CancelDiarizationResult>(TauriCommands.CANCEL_DIARIZATION, { job_id: jobId });\n },\n\n async getPreferences(): Promise<UserPreferences> {\n return invoke<UserPreferences>(TauriCommands.GET_PREFERENCES);\n },\n async savePreferences(preferences: UserPreferences): Promise<void> {\n await invoke(TauriCommands.SAVE_PREFERENCES, { preferences });\n },\n\n async listAudioDevices(): Promise<AudioDeviceInfo[]> {\n return invoke<AudioDeviceInfo[]>(TauriCommands.LIST_AUDIO_DEVICES);\n },\n async getDefaultAudioDevice(isInput: boolean): Promise<AudioDeviceInfo | null> {\n return invoke<AudioDeviceInfo | null>(TauriCommands.GET_DEFAULT_AUDIO_DEVICE, {\n is_input: isInput,\n });\n },\n async selectAudioDevice(deviceId: string, isInput: boolean): Promise<void> {\n await invoke(TauriCommands.SELECT_AUDIO_DEVICE, { device_id: deviceId, is_input: isInput });\n },\n\n async setTriggerEnabled(enabled: boolean): Promise<void> {\n await invoke(TauriCommands.SET_TRIGGER_ENABLED, { enabled });\n },\n async snoozeTriggers(minutes?: number): Promise<void> {\n await invoke(TauriCommands.SNOOZE_TRIGGERS, { minutes });\n },\n async resetSnooze(): Promise<void> {\n await invoke(TauriCommands.RESET_SNOOZE);\n },\n async getTriggerStatus(): Promise<TriggerStatus> {\n return invoke<TriggerStatus>(TauriCommands.GET_TRIGGER_STATUS);\n },\n async dismissTrigger(): Promise<void> {\n await invoke(TauriCommands.DISMISS_TRIGGER);\n },\n async acceptTrigger(title?: string): Promise<Meeting> {\n return invoke<Meeting>(TauriCommands.ACCEPT_TRIGGER, { title });\n },\n\n async extractEntities(\n meetingId: string,\n forceRefresh?: boolean\n ): Promise<ExtractEntitiesResponse> {\n return invoke<ExtractEntitiesResponse>(TauriCommands.EXTRACT_ENTITIES, {\n meeting_id: meetingId,\n force_refresh: forceRefresh ?? false,\n });\n },\n async updateEntity(\n meetingId: string,\n entityId: string,\n text?: string,\n category?: string\n ): Promise<ExtractedEntity> {\n return invoke<ExtractedEntity>(TauriCommands.UPDATE_ENTITY, {\n meeting_id: meetingId,\n entity_id: entityId,\n text,\n category,\n });\n },\n async deleteEntity(meetingId: string, entityId: string): Promise<boolean> {\n return invoke<boolean>(TauriCommands.DELETE_ENTITY, {\n meeting_id: meetingId,\n entity_id: entityId,\n });\n },\n\n async listCalendarEvents(\n hoursAhead?: number,\n limit?: number,\n provider?: string\n ): Promise<ListCalendarEventsResponse> {\n return invoke<ListCalendarEventsResponse>(TauriCommands.LIST_CALENDAR_EVENTS, {\n hours_ahead: hoursAhead,\n limit,\n provider,\n });\n },\n async getCalendarProviders(): Promise<GetCalendarProvidersResponse> {\n return invoke<GetCalendarProvidersResponse>(TauriCommands.GET_CALENDAR_PROVIDERS);\n },\n async initiateCalendarAuth(\n provider: string,\n redirectUri?: string\n ): Promise<InitiateCalendarAuthResponse> {\n return invoke<InitiateCalendarAuthResponse>(TauriCommands.INITIATE_OAUTH, {\n provider,\n redirect_uri: redirectUri,\n });\n },\n async completeCalendarAuth(\n provider: string,\n code: string,\n state: string\n ): Promise<CompleteCalendarAuthResponse> {\n return invoke<CompleteCalendarAuthResponse>(TauriCommands.COMPLETE_OAUTH, {\n provider,\n code,\n state,\n });\n },\n async getOAuthConnectionStatus(provider: string): Promise<GetOAuthConnectionStatusResponse> {\n return invoke<GetOAuthConnectionStatusResponse>(TauriCommands.GET_OAUTH_CONNECTION_STATUS, {\n provider,\n });\n },\n async disconnectCalendar(provider: string): Promise<DisconnectOAuthResponse> {\n return invoke<DisconnectOAuthResponse>(TauriCommands.DISCONNECT_OAUTH, { provider });\n },\n\n async registerWebhook(r: RegisterWebhookRequest): Promise<RegisteredWebhook> {\n return invoke<RegisteredWebhook>(TauriCommands.REGISTER_WEBHOOK, { request: r });\n },\n async listWebhooks(enabledOnly?: boolean): Promise<ListWebhooksResponse> {\n return invoke<ListWebhooksResponse>(TauriCommands.LIST_WEBHOOKS, {\n enabled_only: enabledOnly ?? false,\n });\n },\n async updateWebhook(r: UpdateWebhookRequest): Promise<RegisteredWebhook> {\n return invoke<RegisteredWebhook>(TauriCommands.UPDATE_WEBHOOK, { request: r });\n },\n async deleteWebhook(webhookId: string): Promise<DeleteWebhookResponse> {\n return invoke<DeleteWebhookResponse>(TauriCommands.DELETE_WEBHOOK, { webhook_id: webhookId });\n },\n async getWebhookDeliveries(\n webhookId: string,\n limit?: number\n ): Promise<GetWebhookDeliveriesResponse> {\n return invoke<GetWebhookDeliveriesResponse>(TauriCommands.GET_WEBHOOK_DELIVERIES, {\n webhook_id: webhookId,\n limit: limit ?? 50,\n });\n },\n\n // Integration Sync (Sprint 9)\n async startIntegrationSync(integrationId: string): Promise<StartIntegrationSyncResponse> {\n return invoke<StartIntegrationSyncResponse>(TauriCommands.START_INTEGRATION_SYNC, {\n integration_id: integrationId,\n });\n },\n async getSyncStatus(syncRunId: string): Promise<GetSyncStatusResponse> {\n return invoke<GetSyncStatusResponse>(TauriCommands.GET_SYNC_STATUS, {\n sync_run_id: syncRunId,\n });\n },\n async listSyncHistory(\n integrationId: string,\n limit?: number,\n offset?: number\n ): Promise<ListSyncHistoryResponse> {\n return invoke<ListSyncHistoryResponse>(TauriCommands.LIST_SYNC_HISTORY, {\n integration_id: integrationId,\n limit,\n offset,\n });\n },\n async getUserIntegrations(): Promise<GetUserIntegrationsResponse> {\n return invoke<GetUserIntegrationsResponse>(TauriCommands.GET_USER_INTEGRATIONS);\n },\n\n // Observability (Sprint 9)\n async getRecentLogs(request?: GetRecentLogsRequest): Promise<GetRecentLogsResponse> {\n return invoke<GetRecentLogsResponse>(TauriCommands.GET_RECENT_LOGS, {\n limit: request?.limit,\n level: request?.level,\n source: request?.source,\n });\n },\n async getPerformanceMetrics(\n request?: GetPerformanceMetricsRequest\n ): Promise<GetPerformanceMetricsResponse> {\n return invoke<GetPerformanceMetricsResponse>(TauriCommands.GET_PERFORMANCE_METRICS, {\n history_limit: request?.history_limit,\n });\n },\n };\n}\n\n/** Check if running in a Tauri environment (synchronous hint). */\nexport function isTauriEnvironment(): boolean {\n if (typeof window === 'undefined') {\n return false;\n }\n // Tauri 2.x injects __TAURI_INTERNALS__ into the window\n // Check multiple possible indicators\n return (\n '__TAURI_INTERNALS__' in window ||\n '__TAURI__' in window ||\n 'isTauri' in window\n );\n}\n\n/** Dynamically import Tauri APIs and create the adapter. */\nexport async function initializeTauriAPI(): Promise<NoteFlowAPI> {\n // Try to import Tauri APIs - this will fail in browser but succeed in Tauri\n try {\n const { invoke } = await import('@tauri-apps/api/core');\n const { listen } = await import('@tauri-apps/api/event');\n // Test if invoke actually works by calling a simple command\n await invoke('is_connected');\n return createTauriAPI(invoke, listen);\n } catch (error) {\n throw new Error(\n `Not running in Tauri environment: ${error instanceof Error ? error.message : 'unknown error'}`\n );\n }\n}\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"// Project context for managing active project selection and project data\n\nimport { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'; void getAPI()\n .setActiveProject({ workspace_id: currentWorkspace.id, project_id: projectId })\n .catch((err: unknown) => {\n console.warn('[ProjectContext] Failed to set active project:', err);\n });\n }, return context;\n}\n","ops":[{"diffOp":{"equal":{"range":[0,168]}}},{"equalLines":{"line_count":136}},{"diffOp":{"equal":{"range":[168,310]}}},{"diffOp":{"delete":{"range":[310,389]}}},{"diffOp":{"equal":{"range":[389,408]}}},{"equalLines":{"line_count":110}},{"diffOp":{"equal":{"range":[408,428]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"src/contexts/project-context.tsx"},"span":[4790,4802],"sourceCode":"// Project context for managing active project selection and project data\n\nimport { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';\nimport { IdentityDefaults } from '@/api/constants';\nimport { getAPI } from '@/api/interface';\nimport type { CreateProjectRequest, Project, UpdateProjectRequest } from '@/api/types';\nimport { useWorkspace } from '@/contexts/workspace-context';\n\ninterface ProjectContextValue {\n projects: Project[];\n activeProject: Project | null;\n switchProject: (projectId: string) => void;\n refreshProjects: () => Promise<void>;\n createProject: (request: Omit<CreateProjectRequest, 'workspace_id'> & { workspace_id?: string }) => Promise<Project>;\n updateProject: (request: UpdateProjectRequest) => Promise<Project>;\n archiveProject: (projectId: string) => Promise<Project>;\n restoreProject: (projectId: string) => Promise<Project>;\n deleteProject: (projectId: string) => Promise<boolean>;\n isLoading: boolean;\n error: string | null;\n}\n\nconst STORAGE_KEY_PREFIX = 'noteflow_active_project_id';\n\nconst ProjectContext = createContext<ProjectContextValue | null>(null);\n\nfunction storageKey(workspaceId: string): string {\n return `${STORAGE_KEY_PREFIX}:${workspaceId}`;\n}\n\nfunction readStoredProjectId(workspaceId: string): string | null {\n try {\n return localStorage.getItem(storageKey(workspaceId));\n } catch {\n return null;\n }\n}\n\nfunction persistProjectId(workspaceId: string, projectId: string): void {\n try {\n localStorage.setItem(storageKey(workspaceId), projectId);\n } catch {\n // Ignore storage failures\n }\n}\n\nfunction resolveActiveProject(projects: Project[], preferredId: string | null): Project | null {\n if (!projects.length) {\n return null;\n }\n const activeCandidates = projects.filter((project) => !project.is_archived);\n if (preferredId) {\n const match = activeCandidates.find((project) => project.id === preferredId);\n if (match) {\n return match;\n }\n }\n const defaultProject = activeCandidates.find((project) => project.is_default);\n return defaultProject ?? activeCandidates[0] ?? null;\n}\n\nfunction fallbackProject(workspaceId: string): Project {\n return {\n id: IdentityDefaults.DEFAULT_PROJECT_ID,\n workspace_id: workspaceId,\n name: IdentityDefaults.DEFAULT_PROJECT_NAME,\n slug: 'general',\n description: 'Default project',\n is_default: true,\n is_archived: false,\n settings: {},\n created_at: 0,\n updated_at: 0,\n };\n}\n\nexport function ProjectProvider({ children }: { children: React.ReactNode }) {\n const { currentWorkspace } = useWorkspace();\n const [projects, setProjects] = useState<Project[]>([]);\n const [activeProjectId, setActiveProjectId] = useState<string | null>(null);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const loadProjects = useCallback(async () => {\n if (!currentWorkspace) {\n return;\n }\n setIsLoading(true);\n setError(null);\n try {\n const response = await getAPI().listProjects({\n workspace_id: currentWorkspace.id,\n include_archived: true,\n limit: 200,\n offset: 0,\n });\n let preferredId = readStoredProjectId(currentWorkspace.id);\n try {\n const activeResponse = await getAPI().getActiveProject({\n workspace_id: currentWorkspace.id,\n });\n const activeId = activeResponse.project_id ?? activeResponse.project?.id;\n if (activeId) {\n preferredId = activeId;\n }\n } catch {\n // Ignore active project lookup failures (offline or unsupported)\n }\n const available = response.projects.length\n ? response.projects\n : [fallbackProject(currentWorkspace.id)];\n setProjects(available);\n const resolved = resolveActiveProject(available, preferredId);\n setActiveProjectId(resolved?.id ?? null);\n if (resolved) {\n persistProjectId(currentWorkspace.id, resolved.id);\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load projects');\n const fallback = fallbackProject(currentWorkspace.id);\n setProjects([fallback]);\n setActiveProjectId(fallback.id);\n persistProjectId(currentWorkspace.id, fallback.id);\n } finally {\n setIsLoading(false);\n }\n }, [currentWorkspace]);\n\n useEffect(() => {\n void loadProjects();\n }, [loadProjects]);\n\n const switchProject = useCallback(\n (projectId: string) => {\n if (!currentWorkspace) {\n return;\n }\n setActiveProjectId(projectId);\n persistProjectId(currentWorkspace.id, projectId);\n void getAPI()\n .setActiveProject({ workspace_id: currentWorkspace.id, project_id: projectId })\n .catch((err: unknown) => {\n console.warn('[ProjectContext] Failed to set active project:', err);\n });\n },\n [currentWorkspace]\n );\n\n const createProject = useCallback(\n async (\n request: Omit<CreateProjectRequest, 'workspace_id'> & { workspace_id?: string }\n ): Promise<Project> => {\n const workspaceId = request.workspace_id ?? currentWorkspace?.id;\n if (!workspaceId) {\n throw new Error('Workspace is required to create a project');\n }\n const project = await getAPI().createProject({ ...request, workspace_id: workspaceId });\n setProjects((prev) => [project, ...prev]);\n switchProject(project.id);\n return project;\n },\n [currentWorkspace, switchProject]\n );\n\n const updateProject = useCallback(async (request: UpdateProjectRequest): Promise<Project> => {\n const updated = await getAPI().updateProject(request);\n setProjects((prev) => prev.map((project) => (project.id === updated.id ? updated : project)));\n return updated;\n }, []);\n\n const archiveProject = useCallback(\n async (projectId: string): Promise<Project> => {\n const updated = await getAPI().archiveProject(projectId);\n const nextProjects = projects.map((project) =>\n project.id === updated.id ? updated : project\n );\n setProjects(nextProjects);\n if (activeProjectId === projectId && currentWorkspace) {\n const nextActive = resolveActiveProject(nextProjects, null);\n if (nextActive) {\n switchProject(nextActive.id);\n }\n }\n return updated;\n },\n [activeProjectId, currentWorkspace, projects, switchProject]\n );\n\n const restoreProject = useCallback(async (projectId: string): Promise<Project> => {\n const updated = await getAPI().restoreProject(projectId);\n setProjects((prev) => prev.map((project) => (project.id === updated.id ? updated : project)));\n return updated;\n }, []);\n\n const deleteProject = useCallback(async (projectId: string): Promise<boolean> => {\n const deleted = await getAPI().deleteProject(projectId);\n if (deleted) {\n setProjects((prev) => prev.filter((project) => project.id !== projectId));\n if (activeProjectId === projectId && currentWorkspace) {\n const next = resolveActiveProject(\n projects.filter((project) => project.id !== projectId),\n null\n );\n if (next) {\n switchProject(next.id);\n }\n }\n }\n return deleted;\n }, [activeProjectId, currentWorkspace, projects, switchProject]);\n\n const activeProject = useMemo(() => {\n if (!activeProjectId) {\n return null;\n }\n return projects.find((project) => project.id === activeProjectId) ?? null;\n }, [activeProjectId, projects]);\n\n const value = useMemo<ProjectContextValue>(\n () => ({\n projects,\n activeProject,\n switchProject,\n refreshProjects: loadProjects,\n createProject,\n updateProject,\n archiveProject,\n restoreProject,\n deleteProject,\n isLoading,\n error,\n }),\n [\n projects,\n activeProject,\n switchProject,\n loadProjects,\n createProject,\n updateProject,\n archiveProject,\n restoreProject,\n deleteProject,\n isLoading,\n error,\n ]\n );\n\n return <ProjectContext.Provider value={value}>{children}</ProjectContext.Provider>;\n}\n\nexport function useProjects(): ProjectContextValue {\n const context = useContext(ProjectContext);\n if (!context) {\n throw new Error('useProjects must be used within ProjectProvider');\n }\n return context;\n}\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"// User preferences store with localStorage persistence\n\nimport { isRecord } from '@/api/helpers'; // Validate cached integrations against server (Sprint 18.1)\n // Run in background - don't block startup\n validateCachedIntegrations().catch((err: unknown) => {\n console.warn('[Preferences] Integration cache validation failed:', err);\n });\n }, },\n};\n","ops":[{"diffOp":{"equal":{"range":[0,98]}}},{"equalLines":{"line_count":228}},{"diffOp":{"equal":{"range":[98,268]}}},{"diffOp":{"delete":{"range":[268,347]}}},{"diffOp":{"equal":{"range":[347,360]}}},{"equalLines":{"line_count":318}},{"diffOp":{"equal":{"range":[360,368]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"src/lib/preferences.ts"},"span":[8118,8130],"sourceCode":"// User preferences store with localStorage persistence\n\nimport { isRecord } from '@/api/helpers';\nimport { generateId } from '@/api/mock-data';\nimport type {\n AIProviderConfig,\n AITemplate,\n ExportFormat,\n Integration,\n SyncHistoryEvent,\n SyncNotificationPreferences,\n Tag,\n TaskCompletion,\n TranscriptionProviderConfig,\n UserPreferences,\n} from '@/api/types';\nimport {\n DEFAULT_AI_TEMPLATE,\n DEFAULT_AUDIO_DEVICES,\n DEFAULT_EMBEDDING_CONFIG,\n DEFAULT_EXPORT_LOCATION,\n DEFAULT_SUMMARY_CONFIG,\n DEFAULT_TRANSCRIPTION_CONFIG,\n} from '@/lib/config';\nimport { DEFAULT_INTEGRATIONS } from '@/lib/default-integrations';\n\n// ============================================================================\n// TYPE DEFINITIONS\n// ============================================================================\n\nexport type AIConfigType = 'transcription' | 'summary' | 'embedding';\nexport type AudioDeviceType = 'input' | 'output';\n\nexport type ConfigForType<T extends AIConfigType> = T extends 'transcription'\n ? TranscriptionProviderConfig\n : AIProviderConfig;\n\nexport interface UpdateAIConfigOptions {\n resetTestStatus?: boolean;\n}\n\n// ============================================================================\n// CONSTANTS & STATE\n// ============================================================================\n\nconst STORAGE_KEY = 'noteflow_preferences';\nlet hasHydratedFromTauri = false;\nconst listeners = new Set<(prefs: UserPreferences) => void>();\n\nconst defaultPreferences: UserPreferences = {\n simulate_transcription: false,\n default_export_format: 'markdown',\n default_export_location: DEFAULT_EXPORT_LOCATION,\n completed_tasks: [],\n speaker_names: [],\n tags: [\n { id: generateId(), name: 'Important', color: 'primary', meeting_ids: [] },\n { id: generateId(), name: 'Follow-up', color: 'warning', meeting_ids: [] },\n { id: generateId(), name: 'Personal', color: 'info', meeting_ids: [] },\n ],\n ai_config: {\n transcription: DEFAULT_TRANSCRIPTION_CONFIG,\n summary: DEFAULT_SUMMARY_CONFIG,\n embedding: DEFAULT_EMBEDDING_CONFIG,\n },\n audio_devices: DEFAULT_AUDIO_DEVICES,\n ai_template: DEFAULT_AI_TEMPLATE,\n integrations: DEFAULT_INTEGRATIONS,\n sync_notifications: {\n enabled: true,\n notify_on_success: false,\n notify_on_error: true,\n notify_via_toast: true,\n notify_via_email: false,\n quiet_hours_enabled: false,\n },\n sync_scheduler_paused: false,\n sync_history: [],\n};\n\n// ============================================================================\n// CORE HELPERS\n// ============================================================================\n\nfunction clonePreferences(prefs: UserPreferences): UserPreferences {\n if (typeof structuredClone === 'function') {\n return structuredClone(prefs);\n }\n return JSON.parse(JSON.stringify(prefs)) as UserPreferences;\n}\n\nfunction isTauriRuntime(): boolean {\n return typeof window !== 'undefined' && '__TAURI__' in window;\n}\n\nfunction loadPreferences(): UserPreferences {\n try {\n const stored = localStorage.getItem(STORAGE_KEY);\n if (stored) {\n const parsed: unknown = JSON.parse(stored);\n if (!isRecord(parsed)) {\n return clonePreferences(defaultPreferences);\n }\n const parsedPrefs = parsed as Partial<UserPreferences>;\n\n // Merge integrations with defaults to ensure config objects exist\n const mergedIntegrations = defaultPreferences.integrations.map((defaultInt) => {\n const storedIntegrations = Array.isArray(parsedPrefs.integrations)\n ? parsedPrefs.integrations\n : [];\n const storedInt = storedIntegrations.find((i: Integration) => i.name === defaultInt.name);\n if (storedInt) {\n return {\n ...defaultInt,\n ...storedInt,\n oauth_config: storedInt.oauth_config || defaultInt.oauth_config,\n email_config: storedInt.email_config || defaultInt.email_config,\n calendar_config: storedInt.calendar_config || defaultInt.calendar_config,\n pkm_config: storedInt.pkm_config || defaultInt.pkm_config,\n webhook_config: storedInt.webhook_config || defaultInt.webhook_config,\n };\n }\n return defaultInt;\n });\n\n // Add any custom integrations that aren't in defaults\n const customIntegrations = (\n Array.isArray(parsedPrefs.integrations) ? parsedPrefs.integrations : []\n ).filter((i: Integration) => !defaultPreferences.integrations.some((d) => d.name === i.name));\n\n return {\n ...defaultPreferences,\n ...parsedPrefs,\n integrations: [...mergedIntegrations, ...customIntegrations],\n ai_config: {\n transcription: {\n ...DEFAULT_TRANSCRIPTION_CONFIG,\n ...(parsedPrefs.ai_config?.transcription ?? {}),\n },\n summary: { ...DEFAULT_SUMMARY_CONFIG, ...(parsedPrefs.ai_config?.summary ?? {}) },\n embedding: { ...DEFAULT_EMBEDDING_CONFIG, ...(parsedPrefs.ai_config?.embedding ?? {}) },\n },\n };\n }\n } catch (_e) {}\n return clonePreferences(defaultPreferences);\n}\n\nfunction savePreferences(prefs: UserPreferences): void {\n try {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(prefs));\n if (isTauriRuntime()) {\n void persistPreferencesToTauri(prefs);\n }\n for (const listener of listeners) {\n listener(prefs);\n }\n } catch (_e) {}\n}\n\nasync function persistPreferencesToTauri(prefs: UserPreferences): Promise<void> {\n try {\n const { invoke } = await import('@tauri-apps/api/core');\n await invoke('save_preferences', { preferences: prefs });\n } catch (_e) {}\n}\n\nasync function hydratePreferencesFromTauri(): Promise<void> {\n if (hasHydratedFromTauri || !isTauriRuntime()) {\n return;\n }\n hasHydratedFromTauri = true;\n try {\n const { invoke } = await import('@tauri-apps/api/core');\n const prefs = await invoke<UserPreferences>('get_preferences');\n preferences.replace(prefs);\n } catch (_e) {}\n}\n\n/**\n * Validate cached integration IDs against server and remove stale ones.\n * Prevents infinite retry loops when integrations are deleted server-side.\n * (Sprint 18.1: Integration Cache Resilience)\n */\nasync function validateCachedIntegrations(): Promise<void> {\n if (!isTauriRuntime()) {\n return;\n }\n try {\n const { invoke } = await import('@tauri-apps/api/core');\n const response = await invoke<{ integrations: Array<{ id: string }> }>('get_user_integrations');\n const serverIntegrationIds = new Set(response.integrations.map((i) => i.id));\n\n // Remove integrations that no longer exist on the server\n const currentPrefs = loadPreferences();\n const validIntegrations = currentPrefs.integrations.filter((integration) => {\n // Keep static/default integrations without server IDs (they're not server-synced)\n if (!integration.id || integration.id.startsWith('default-')) {\n return true;\n }\n // Keep integrations that exist on the server\n return serverIntegrationIds.has(integration.id);\n });\n\n // Only update if we removed something\n if (validIntegrations.length !== currentPrefs.integrations.length) {\n preferences.replace({ ...currentPrefs, integrations: validIntegrations });\n }\n } catch (_e) {\n // Silently fail - validation is best-effort\n // Server might be unavailable, in which case we'll validate on next startup\n }\n}\n\n/**\n * Core helper that eliminates load/mutate/save repetition.\n * All preference mutations should use this function.\n */\nfunction withPreferences(updater: (prefs: UserPreferences) => void): void {\n const prefs = loadPreferences();\n updater(prefs);\n savePreferences(prefs);\n}\n\n// ============================================================================\n// PREFERENCES API\n// ============================================================================\n\nexport const preferences = {\n async initialize(): Promise<void> {\n await hydratePreferencesFromTauri();\n // Validate cached integrations against server (Sprint 18.1)\n // Run in background - don't block startup\n validateCachedIntegrations().catch((err: unknown) => {\n console.warn('[Preferences] Integration cache validation failed:', err);\n });\n },\n\n get(): UserPreferences {\n return loadPreferences();\n },\n\n replace(prefs: UserPreferences): void {\n savePreferences(prefs);\n },\n\n subscribe(listener: (prefs: UserPreferences) => void): () => void {\n listeners.add(listener);\n listener(loadPreferences());\n return () => listeners.delete(listener);\n },\n\n // AI CONFIG (consolidated from 18 methods to 2)\n /**\n * Update any AI configuration (transcription, summary, embedding).\n * Replaces: setXxxProvider, setXxxApiKey, setXxxModel, setXxxModels, updateXxxConfig\n */\n updateAIConfig<T extends AIConfigType>(\n configType: T,\n updates: Partial<ConfigForType<T>>,\n options: UpdateAIConfigOptions = {}\n ): void {\n withPreferences((prefs) => {\n Object.assign(prefs.ai_config[configType], updates);\n if (options.resetTestStatus) {\n prefs.ai_config[configType].test_status = 'untested';\n }\n });\n },\n\n /**\n * Set test status and timestamp for any AI configuration.\n * Replaces: setXxxTestStatus\n */\n setAIConfigTestStatus(configType: AIConfigType, status: 'untested' | 'success' | 'error'): void {\n withPreferences((prefs) => {\n prefs.ai_config[configType].test_status = status;\n prefs.ai_config[configType].last_tested = Date.now();\n });\n },\n\n // AI TEMPLATE (consolidated from 3 methods to 1)\n /**\n * Set any AI template field (tone, format, verbosity).\n * Replaces: setAITone, setAIFormat, setAIVerbosity\n */\n setAITemplate<K extends keyof AITemplate>(field: K, value: AITemplate[K]): void {\n withPreferences((prefs) => {\n prefs.ai_template[field] = value;\n });\n },\n\n // AUDIO DEVICES (consolidated from 2 methods to 1)\n /**\n * Set audio device by type (input or output).\n * Replaces: setInputDevice, setOutputDevice\n */\n setAudioDevice(type: AudioDeviceType, deviceId: string): void {\n withPreferences((prefs) => {\n const key = type === 'input' ? 'input_device_id' : 'output_device_id';\n prefs.audio_devices[key] = deviceId;\n });\n },\n\n // SIMPLE TOP-LEVEL SETTERS\n setSimulateTranscription(enabled: boolean): void {\n withPreferences((prefs) => {\n prefs.simulate_transcription = enabled;\n });\n },\n\n setDefaultExportFormat(format: ExportFormat): void {\n withPreferences((prefs) => {\n prefs.default_export_format = format;\n });\n },\n\n setDefaultExportLocation(location: string): void {\n withPreferences((prefs) => {\n prefs.default_export_location = location;\n });\n },\n\n\n // INTEGRATIONS (consolidated updateIntegrationStatus + updateIntegrationConfig)\n\n\n getIntegrations(): Integration[] {\n return loadPreferences().integrations;\n },\n\n /**\n * Update any integration fields by ID.\n * Replaces: updateIntegrationStatus, updateIntegrationConfig, updateIntegrationLastSync\n */\n updateIntegration(integrationId: string, updates: Partial<Integration>): void {\n withPreferences((prefs) => {\n const index = prefs.integrations.findIndex((i) => i.id === integrationId);\n if (index >= 0) {\n prefs.integrations[index] = { ...prefs.integrations[index], ...updates };\n }\n });\n },\n\n addCustomIntegration(\n name: string,\n webhookConfig: {\n url: string;\n method?: 'GET' | 'POST' | 'PUT';\n auth_type?: 'none' | 'bearer' | 'basic' | 'api_key';\n auth_value?: string;\n }\n ): Integration {\n const prefs = loadPreferences();\n const integration: Integration = {\n id: generateId(),\n name,\n type: 'custom',\n status: 'disconnected',\n webhook_config: {\n url: webhookConfig.url,\n method: webhookConfig.method || 'POST',\n auth_type: webhookConfig.auth_type || 'none',\n auth_value: webhookConfig.auth_value || '',\n },\n };\n prefs.integrations.push(integration);\n savePreferences(prefs);\n return integration;\n },\n\n removeIntegration(integrationId: string): void {\n withPreferences((prefs) => {\n prefs.integrations = prefs.integrations.filter((i) => i.id !== integrationId);\n });\n },\n\n\n // TASK COMPLETION\n\n\n isTaskCompleted(meetingId: string, taskText: string): boolean {\n const prefs = loadPreferences();\n return prefs.completed_tasks.some(\n (t) => t.meeting_id === meetingId && t.task_text === taskText\n );\n },\n\n toggleTaskCompletion(meetingId: string, taskText: string): boolean {\n const prefs = loadPreferences();\n const index = prefs.completed_tasks.findIndex(\n (t) => t.meeting_id === meetingId && t.task_text === taskText\n );\n\n if (index >= 0) {\n prefs.completed_tasks.splice(index, 1);\n savePreferences(prefs);\n return false;\n } else {\n prefs.completed_tasks.push({\n meeting_id: meetingId,\n task_text: taskText,\n completed_at: Date.now() / 1000,\n });\n savePreferences(prefs);\n return true;\n }\n },\n\n getCompletedTasks(): TaskCompletion[] {\n return loadPreferences().completed_tasks;\n },\n\n\n // SPEAKER NAMES\n\n getSpeakerName(meetingId: string, speakerId: string): string | undefined {\n const prefs = loadPreferences();\n const entry = prefs.speaker_names.find(\n (s) => s.meeting_id === meetingId && s.speaker_id === speakerId\n );\n if (entry) {\n return entry.name;\n }\n // Fall back to global if not found for specific meeting\n if (meetingId !== '__global__') {\n return prefs.speaker_names.find(\n (s) => s.meeting_id === '__global__' && s.speaker_id === speakerId\n )?.name;\n }\n return undefined;\n },\n setSpeakerName(meetingId: string, speakerId: string, name: string): void {\n withPreferences((prefs) => {\n const index = prefs.speaker_names.findIndex(\n (s) => s.meeting_id === meetingId && s.speaker_id === speakerId\n );\n if (index >= 0) {\n prefs.speaker_names[index].name = name;\n } else {\n prefs.speaker_names.push({ meeting_id: meetingId, speaker_id: speakerId, name });\n }\n });\n },\n // Global speaker name wrappers (inline to avoid `this` typing issues)\n getGlobalSpeakerName(speakerId: string): string | undefined {\n return loadPreferences().speaker_names.find(\n (s) => s.meeting_id === '__global__' && s.speaker_id === speakerId\n )?.name;\n },\n setGlobalSpeakerName(speakerId: string, name: string): void {\n withPreferences((prefs) => {\n const i = prefs.speaker_names.findIndex(\n (s) => s.meeting_id === '__global__' && s.speaker_id === speakerId\n );\n if (i >= 0) {\n prefs.speaker_names[i].name = name;\n } else {\n prefs.speaker_names.push({ meeting_id: '__global__', speaker_id: speakerId, name });\n }\n });\n },\n\n\n // TAGS\n\n\n getTags(): Tag[] {\n return loadPreferences().tags;\n },\n\n addTag(name: string, color: string): Tag {\n const prefs = loadPreferences();\n const tag: Tag = { id: generateId(), name, color, meeting_ids: [] };\n prefs.tags.push(tag);\n savePreferences(prefs);\n return tag;\n },\n\n deleteTag(tagId: string): void {\n withPreferences((prefs) => {\n prefs.tags = prefs.tags.filter((t) => t.id !== tagId);\n });\n },\n\n addMeetingToTag(tagId: string, meetingId: string): void {\n withPreferences((prefs) => {\n const tag = prefs.tags.find((t) => t.id === tagId);\n if (tag && !tag.meeting_ids.includes(meetingId)) {\n tag.meeting_ids.push(meetingId);\n }\n });\n },\n\n removeMeetingFromTag(tagId: string, meetingId: string): void {\n withPreferences((prefs) => {\n const tag = prefs.tags.find((t) => t.id === tagId);\n if (tag) {\n tag.meeting_ids = tag.meeting_ids.filter((id) => id !== meetingId);\n }\n });\n },\n\n getMeetingTags(meetingId: string): Tag[] {\n return loadPreferences().tags.filter((t) => t.meeting_ids.includes(meetingId));\n },\n\n\n // SYNC NOTIFICATIONS\n\n\n getSyncNotifications(): SyncNotificationPreferences {\n return loadPreferences().sync_notifications;\n },\n\n updateSyncNotifications(updates: Partial<SyncNotificationPreferences>): void {\n withPreferences((prefs) => {\n prefs.sync_notifications = { ...prefs.sync_notifications, ...updates };\n });\n },\n\n\n // SYNC SCHEDULER\n\n\n isSyncSchedulerPaused(): boolean {\n return loadPreferences().sync_scheduler_paused;\n },\n\n setSyncSchedulerPaused(paused: boolean): void {\n withPreferences((prefs) => {\n prefs.sync_scheduler_paused = paused;\n });\n },\n\n\n // SYNC HISTORY\n\n\n getSyncHistory(): SyncHistoryEvent[] {\n return loadPreferences().sync_history || [];\n },\n\n addSyncHistoryEvent(event: SyncHistoryEvent): void {\n withPreferences((prefs) => {\n // Keep only last 100 events\n const history = [event, ...(prefs.sync_history || [])].slice(0, 100);\n prefs.sync_history = history;\n });\n },\n\n clearSyncHistory(): void {\n withPreferences((prefs) => {\n prefs.sync_history = [];\n });\n },\n};\n"},"tags":["fixable"],"source":null},{"category":"lint/correctness/noUnusedImports","severity":"error","description":"Several of these imports are unused.","message":[{"elements":[],"content":"Several of these "},{"elements":["Emphasis"],"content":"imports"},{"elements":[],"content":" are unused."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"Unused imports might be the result of an incomplete refactoring."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove the unused imports."}]]},{"diff":{"dictionary":"import { render, screen } from '@testing-library/react';\nimport { createMemoryRouter, RouterProvider } from 'react-router-dom';\nimport { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';\n\nimport { getAPI } from '@/api/interface'; });\n});\n","ops":[{"diffOp":{"equal":{"range":[0,148]}}},{"diffOp":{"delete":{"range":[148,158]}}},{"diffOp":{"delete":{"range":[158,160]}}},{"diffOp":{"equal":{"range":[160,244]}}},{"equalLines":{"line_count":78}},{"diffOp":{"equal":{"range":[244,254]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"src/pages/Recording.test.tsx"},"span":[148,158],"sourceCode":"import { render, screen } from '@testing-library/react';\nimport { createMemoryRouter, RouterProvider } from 'react-router-dom';\nimport { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';\n\nimport { getAPI } from '@/api/interface';\nimport { ConnectionProvider } from '@/contexts/connection-context';\nimport { ProjectProvider } from '@/contexts/project-context';\nimport { WorkspaceProvider } from '@/contexts/workspace-context';\nimport RecordingPage from '@/pages/Recording';\n\nvi.mock('@/api/interface', async (importOriginal) => {\n const actual = await importOriginal<typeof import('@/api/interface')>();\n return {\n ...actual,\n getAPI: vi.fn(() => ({\n listWorkspaces: vi.fn().mockResolvedValue({ workspaces: [] }),\n listProjects: vi.fn().mockResolvedValue({ projects: [], total_count: 0 }),\n getActiveProject: vi.fn().mockResolvedValue({ project_id: '' }),\n setActiveProject: vi.fn().mockResolvedValue(undefined),\n })),\n };\n});\n\nfunction Wrapper({ children }: { children: React.ReactNode }) {\n return (\n <ConnectionProvider>\n <WorkspaceProvider>\n <ProjectProvider>{children}</ProjectProvider>\n </WorkspaceProvider>\n </ConnectionProvider>\n );\n}\n\ndescribe('RecordingPage', () => {\n afterEach(() => {\n localStorage.clear();\n });\n\n it('shows desktop-only message when not running in Tauri', () => {\n localStorage.setItem('noteflow_preferences', JSON.stringify({ simulate_transcription: false }));\n const router = createMemoryRouter([{ path: '/recording/:id', element: <RecordingPage /> }], {\n initialEntries: ['/recording/new'],\n future: {\n v7_startTransition: true,\n v7_relativeSplatPath: true,\n },\n });\n\n render(\n <Wrapper>\n <RouterProvider\n router={router}\n future={{ v7_startTransition: true, v7_relativeSplatPath: true }}\n />\n </Wrapper>\n );\n\n expect(screen.getByText('Desktop recording only')).toBeInTheDocument();\n expect(\n screen.getByText(/Recording and live transcription are available in the desktop app/i)\n ).toBeInTheDocument();\n });\n\n it('allows simulated recording when enabled in preferences', () => {\n localStorage.setItem('noteflow_preferences', JSON.stringify({ simulate_transcription: true }));\n const router = createMemoryRouter([{ path: '/recording/:id', element: <RecordingPage /> }], {\n initialEntries: ['/recording/new'],\n future: {\n v7_startTransition: true,\n v7_relativeSplatPath: true,\n },\n });\n\n render(\n <Wrapper>\n <RouterProvider\n router={router}\n future={{ v7_startTransition: true, v7_relativeSplatPath: true }}\n />\n </Wrapper>\n );\n\n expect(screen.getByRole('button', { name: /Start Recording/i })).toBeInTheDocument();\n });\n});\n"},"tags":["fixable"],"source":null},{"category":"lint/correctness/noUnusedImports","severity":"error","description":"This import is unused.","message":[{"elements":[],"content":"This "},{"elements":["Emphasis"],"content":"import"},{"elements":[],"content":" is unused."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"Unused imports might be the result of an incomplete refactoring."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove the unused imports."}]]},{"diff":{"dictionary":"import { render, screen } from '@testing-library/react';\nimport { createMemoryRouter, RouterProvider } from 'react-router-dom';\nimport { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';\n\nimport { getAPI } from '@/api/interface';\nimport { ConnectionProvider } from '@/contexts/connection-context';\nimport { ProjectProvider } from '@/contexts/project-context'; });\n});\n","ops":[{"diffOp":{"equal":{"range":[0,127]}}},{"diffOp":{"equal":{"range":[127,201]}}},{"diffOp":{"insert":{"range":[127,128]}}},{"diffOp":{"delete":{"range":[201,244]}}},{"diffOp":{"equal":{"range":[244,374]}}},{"equalLines":{"line_count":76}},{"diffOp":{"equal":{"range":[374,384]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"src/pages/Recording.test.tsx"},"span":[210,220],"sourceCode":"import { render, screen } from '@testing-library/react';\nimport { createMemoryRouter, RouterProvider } from 'react-router-dom';\nimport { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';\n\nimport { getAPI } from '@/api/interface';\nimport { ConnectionProvider } from '@/contexts/connection-context';\nimport { ProjectProvider } from '@/contexts/project-context';\nimport { WorkspaceProvider } from '@/contexts/workspace-context';\nimport RecordingPage from '@/pages/Recording';\n\nvi.mock('@/api/interface', async (importOriginal) => {\n const actual = await importOriginal<typeof import('@/api/interface')>();\n return {\n ...actual,\n getAPI: vi.fn(() => ({\n listWorkspaces: vi.fn().mockResolvedValue({ workspaces: [] }),\n listProjects: vi.fn().mockResolvedValue({ projects: [], total_count: 0 }),\n getActiveProject: vi.fn().mockResolvedValue({ project_id: '' }),\n setActiveProject: vi.fn().mockResolvedValue(undefined),\n })),\n };\n});\n\nfunction Wrapper({ children }: { children: React.ReactNode }) {\n return (\n <ConnectionProvider>\n <WorkspaceProvider>\n <ProjectProvider>{children}</ProjectProvider>\n </WorkspaceProvider>\n </ConnectionProvider>\n );\n}\n\ndescribe('RecordingPage', () => {\n afterEach(() => {\n localStorage.clear();\n });\n\n it('shows desktop-only message when not running in Tauri', () => {\n localStorage.setItem('noteflow_preferences', JSON.stringify({ simulate_transcription: false }));\n const router = createMemoryRouter([{ path: '/recording/:id', element: <RecordingPage /> }], {\n initialEntries: ['/recording/new'],\n future: {\n v7_startTransition: true,\n v7_relativeSplatPath: true,\n },\n });\n\n render(\n <Wrapper>\n <RouterProvider\n router={router}\n future={{ v7_startTransition: true, v7_relativeSplatPath: true }}\n />\n </Wrapper>\n );\n\n expect(screen.getByText('Desktop recording only')).toBeInTheDocument();\n expect(\n screen.getByText(/Recording and live transcription are available in the desktop app/i)\n ).toBeInTheDocument();\n });\n\n it('allows simulated recording when enabled in preferences', () => {\n localStorage.setItem('noteflow_preferences', JSON.stringify({ simulate_transcription: true }));\n const router = createMemoryRouter([{ path: '/recording/:id', element: <RecordingPage /> }], {\n initialEntries: ['/recording/new'],\n future: {\n v7_startTransition: true,\n v7_relativeSplatPath: true,\n },\n });\n\n render(\n <Wrapper>\n <RouterProvider\n router={router}\n future={{ v7_startTransition: true, v7_relativeSplatPath: true }}\n />\n </Wrapper>\n );\n\n expect(screen.getByRole('button', { name: /Start Recording/i })).toBeInTheDocument();\n });\n});\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n * onComplete: async function () {\n // Stop tauri-driver\n if (tauriDriverProcess) {\n console.log('Stopping tauri-driver');\n tauriDriverProcess.kill();\n tauriDriverProcess = null; },\n};\n","ops":[{"diffOp":{"equal":{"range":[0,60]}}},{"equalLines":{"line_count":237}},{"diffOp":{"equal":{"range":[60,148]}}},{"diffOp":{"delete":{"range":[148,192]}}},{"diffOp":{"equal":{"range":[192,258]}}},{"equalLines":{"line_count":22}},{"diffOp":{"equal":{"range":[258,266]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"wdio.conf.ts"},"span":[6782,6793],"sourceCode":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n *\n * This config runs tests against the actual Tauri desktop app using tauri-driver.\n * Requires: cargo install tauri-driver\n *\n * Usage:\n * 1. Build the app: npm run tauri:build\n * 2. Run tests: npm run test:native\n */\n\nimport type { Options } from '@wdio/types';\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport { fileURLToPath } from 'url';\nimport { spawn, type ChildProcess } from 'child_process';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// Track tauri-driver process\nlet tauriDriverProcess: ChildProcess | null = null;\n\n// Detect the built Tauri binary path based on platform\nfunction getTauriBinaryPath(): string {\n const projectRoot = path.resolve(__dirname, 'src-tauri');\n\n if (process.platform === 'win32') {\n // Windows: look for .exe in release or debug\n // Binary name comes from Cargo.toml package name\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri.exe');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri.exe');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n\n // Fallback to release path (will error if not built)\n return releasePath;\n } else if (process.platform === 'darwin') {\n // macOS: .app bundle\n const releasePath = path.join(\n projectRoot,\n 'target',\n 'release',\n 'bundle',\n 'macos',\n 'NoteFlow.app',\n 'Contents',\n 'MacOS',\n 'noteflow-tauri'\n );\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n } else {\n // Linux: AppImage or direct binary\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n }\n}\n\n// Get tauri-driver path\nfunction getTauriDriverPath(): string {\n if (process.platform === 'win32') {\n // On Windows, tauri-driver is in cargo bin\n const cargoHome = process.env.CARGO_HOME || path.join(process.env.USERPROFILE || '', '.cargo');\n return path.join(cargoHome, 'bin', 'tauri-driver.exe');\n }\n return 'tauri-driver';\n}\n\n// Get msedgedriver path (Windows only)\nasync function getMsEdgeDriverPath(): Promise<string | null> {\n if (process.platform !== 'win32') {\n return null;\n }\n\n // Try edgedriver npm package first\n try {\n const edgedriver = await import('edgedriver');\n // The package provides a function to get/download the binary\n if (typeof edgedriver.default === 'string') {\n return edgedriver.default;\n }\n if (edgedriver.binPath && fs.existsSync(edgedriver.binPath)) {\n return edgedriver.binPath;\n }\n } catch {\n // Package not available or failed\n }\n\n // Check common locations\n const possiblePaths = [\n // Custom env var\n process.env.MSEDGEDRIVER_PATH,\n // Common install locations\n 'C:\\\\Program Files\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n 'C:\\\\Program Files (x86)\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n path.join(process.env.USERPROFILE || '', 'msedgedriver.exe'),\n path.join(process.env.USERPROFILE || '', '.webdrivers', 'msedgedriver.exe'),\n ];\n\n for (const p of possiblePaths) {\n if (p && fs.existsSync(p)) {\n return p;\n }\n }\n\n return null;\n}\n\nexport const config: Options.Testrunner = {\n // Test specs\n specs: ['./e2e-native/**/*.spec.ts'],\n exclude: [],\n\n // Capabilities\n maxInstances: 1, // Tauri apps should run one at a time\n capabilities: [\n {\n // Use tauri-driver as the WebDriver server\n 'tauri:options': {\n application: getTauriBinaryPath(),\n },\n },\n ],\n\n // Test framework\n framework: 'mocha',\n mochaOpts: {\n ui: 'bdd',\n timeout: 60000,\n },\n\n // Reporters\n reporters: ['spec'],\n\n // Log level\n logLevel: 'info',\n\n // Connection settings for tauri-driver\n hostname: '127.0.0.1',\n port: 4444,\n\n // No built-in service - tauri-driver started via onPrepare hook\n services: [],\n\n // Timeouts\n connectionRetryTimeout: 120000,\n connectionRetryCount: 3,\n\n // Hooks\n onPrepare: async function () {\n const driverPath = getTauriDriverPath();\n console.log(`Starting tauri-driver: ${driverPath}`);\n\n // Check if tauri-driver exists\n if (!fs.existsSync(driverPath)) {\n throw new Error(\n `tauri-driver not found at: ${driverPath}\\n` +\n 'Install it with: cargo install tauri-driver'\n );\n }\n\n // On Windows, check for msedgedriver\n const edgeDriverPath = await getMsEdgeDriverPath();\n if (process.platform === 'win32' && !edgeDriverPath) {\n console.warn(\n '\\n⚠ msedgedriver.exe not found in common locations.\\n' +\n ' Download from: https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/\\n' +\n ' Then either:\\n' +\n ' - Add to PATH\\n' +\n ' - Set MSEDGEDRIVER_PATH environment variable\\n' +\n ' - Place in your home directory\\n'\n );\n }\n\n // Build args\n const args = ['--port', '4444'];\n if (edgeDriverPath) {\n args.push('--native-driver', edgeDriverPath);\n console.log(`Using msedgedriver: ${edgeDriverPath}`);\n }\n\n // Start tauri-driver\n tauriDriverProcess = spawn(driverPath, args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n tauriDriverProcess.stdout?.on('data', (data) => {\n console.log(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n tauriDriverProcess.stderr?.on('data', (data) => {\n console.error(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n // Wait for tauri-driver to be ready\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error('tauri-driver failed to start within 10s'));\n }, 10000);\n\n const checkReady = async () => {\n try {\n const response = await fetch('http://127.0.0.1:4444/status');\n if (response.ok) {\n clearTimeout(timeout);\n console.log('tauri-driver is ready');\n resolve();\n }\n } catch {\n // Not ready yet, retry\n setTimeout(checkReady, 200);\n }\n };\n\n // Start checking after a brief delay\n setTimeout(checkReady, 500);\n });\n },\n\n onComplete: async function () {\n // Stop tauri-driver\n if (tauriDriverProcess) {\n console.log('Stopping tauri-driver');\n tauriDriverProcess.kill();\n tauriDriverProcess = null;\n }\n },\n\n beforeSession: async function () {\n const binaryPath = getTauriBinaryPath();\n if (!fs.existsSync(binaryPath)) {\n throw new Error(\n `Tauri binary not found at: ${binaryPath}\\n` +\n 'Please build the app first with: npm run tauri:build'\n );\n }\n console.log(`Using Tauri binary: ${binaryPath}`);\n },\n\n afterTest: async function (test, _context, { error }) {\n if (error) {\n // Take screenshot on failure\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const screenshotPath = `./e2e-native/screenshots/${test.title}-${timestamp}.png`;\n await browser.saveScreenshot(screenshotPath);\n console.log(`Screenshot saved: ${screenshotPath}`);\n }\n },\n};\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n * // Hooks\n onPrepare: async function () {\n const driverPath = getTauriDriverPath();\n console.log(`Starting tauri-driver: ${driverPath}`);\n\n // Check if tauri-driver exists },\n};\n","ops":[{"diffOp":{"equal":{"range":[0,60]}}},{"equalLines":{"line_count":166}},{"diffOp":{"equal":{"range":[60,148]}}},{"diffOp":{"delete":{"range":[148,205]}}},{"diffOp":{"equal":{"range":[205,242]}}},{"equalLines":{"line_count":93}},{"diffOp":{"equal":{"range":[242,250]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"wdio.conf.ts"},"span":[4583,4594],"sourceCode":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n *\n * This config runs tests against the actual Tauri desktop app using tauri-driver.\n * Requires: cargo install tauri-driver\n *\n * Usage:\n * 1. Build the app: npm run tauri:build\n * 2. Run tests: npm run test:native\n */\n\nimport type { Options } from '@wdio/types';\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport { fileURLToPath } from 'url';\nimport { spawn, type ChildProcess } from 'child_process';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// Track tauri-driver process\nlet tauriDriverProcess: ChildProcess | null = null;\n\n// Detect the built Tauri binary path based on platform\nfunction getTauriBinaryPath(): string {\n const projectRoot = path.resolve(__dirname, 'src-tauri');\n\n if (process.platform === 'win32') {\n // Windows: look for .exe in release or debug\n // Binary name comes from Cargo.toml package name\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri.exe');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri.exe');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n\n // Fallback to release path (will error if not built)\n return releasePath;\n } else if (process.platform === 'darwin') {\n // macOS: .app bundle\n const releasePath = path.join(\n projectRoot,\n 'target',\n 'release',\n 'bundle',\n 'macos',\n 'NoteFlow.app',\n 'Contents',\n 'MacOS',\n 'noteflow-tauri'\n );\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n } else {\n // Linux: AppImage or direct binary\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n }\n}\n\n// Get tauri-driver path\nfunction getTauriDriverPath(): string {\n if (process.platform === 'win32') {\n // On Windows, tauri-driver is in cargo bin\n const cargoHome = process.env.CARGO_HOME || path.join(process.env.USERPROFILE || '', '.cargo');\n return path.join(cargoHome, 'bin', 'tauri-driver.exe');\n }\n return 'tauri-driver';\n}\n\n// Get msedgedriver path (Windows only)\nasync function getMsEdgeDriverPath(): Promise<string | null> {\n if (process.platform !== 'win32') {\n return null;\n }\n\n // Try edgedriver npm package first\n try {\n const edgedriver = await import('edgedriver');\n // The package provides a function to get/download the binary\n if (typeof edgedriver.default === 'string') {\n return edgedriver.default;\n }\n if (edgedriver.binPath && fs.existsSync(edgedriver.binPath)) {\n return edgedriver.binPath;\n }\n } catch {\n // Package not available or failed\n }\n\n // Check common locations\n const possiblePaths = [\n // Custom env var\n process.env.MSEDGEDRIVER_PATH,\n // Common install locations\n 'C:\\\\Program Files\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n 'C:\\\\Program Files (x86)\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n path.join(process.env.USERPROFILE || '', 'msedgedriver.exe'),\n path.join(process.env.USERPROFILE || '', '.webdrivers', 'msedgedriver.exe'),\n ];\n\n for (const p of possiblePaths) {\n if (p && fs.existsSync(p)) {\n return p;\n }\n }\n\n return null;\n}\n\nexport const config: Options.Testrunner = {\n // Test specs\n specs: ['./e2e-native/**/*.spec.ts'],\n exclude: [],\n\n // Capabilities\n maxInstances: 1, // Tauri apps should run one at a time\n capabilities: [\n {\n // Use tauri-driver as the WebDriver server\n 'tauri:options': {\n application: getTauriBinaryPath(),\n },\n },\n ],\n\n // Test framework\n framework: 'mocha',\n mochaOpts: {\n ui: 'bdd',\n timeout: 60000,\n },\n\n // Reporters\n reporters: ['spec'],\n\n // Log level\n logLevel: 'info',\n\n // Connection settings for tauri-driver\n hostname: '127.0.0.1',\n port: 4444,\n\n // No built-in service - tauri-driver started via onPrepare hook\n services: [],\n\n // Timeouts\n connectionRetryTimeout: 120000,\n connectionRetryCount: 3,\n\n // Hooks\n onPrepare: async function () {\n const driverPath = getTauriDriverPath();\n console.log(`Starting tauri-driver: ${driverPath}`);\n\n // Check if tauri-driver exists\n if (!fs.existsSync(driverPath)) {\n throw new Error(\n `tauri-driver not found at: ${driverPath}\\n` +\n 'Install it with: cargo install tauri-driver'\n );\n }\n\n // On Windows, check for msedgedriver\n const edgeDriverPath = await getMsEdgeDriverPath();\n if (process.platform === 'win32' && !edgeDriverPath) {\n console.warn(\n '\\n⚠ msedgedriver.exe not found in common locations.\\n' +\n ' Download from: https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/\\n' +\n ' Then either:\\n' +\n ' - Add to PATH\\n' +\n ' - Set MSEDGEDRIVER_PATH environment variable\\n' +\n ' - Place in your home directory\\n'\n );\n }\n\n // Build args\n const args = ['--port', '4444'];\n if (edgeDriverPath) {\n args.push('--native-driver', edgeDriverPath);\n console.log(`Using msedgedriver: ${edgeDriverPath}`);\n }\n\n // Start tauri-driver\n tauriDriverProcess = spawn(driverPath, args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n tauriDriverProcess.stdout?.on('data', (data) => {\n console.log(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n tauriDriverProcess.stderr?.on('data', (data) => {\n console.error(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n // Wait for tauri-driver to be ready\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error('tauri-driver failed to start within 10s'));\n }, 10000);\n\n const checkReady = async () => {\n try {\n const response = await fetch('http://127.0.0.1:4444/status');\n if (response.ok) {\n clearTimeout(timeout);\n console.log('tauri-driver is ready');\n resolve();\n }\n } catch {\n // Not ready yet, retry\n setTimeout(checkReady, 200);\n }\n };\n\n // Start checking after a brief delay\n setTimeout(checkReady, 500);\n });\n },\n\n onComplete: async function () {\n // Stop tauri-driver\n if (tauriDriverProcess) {\n console.log('Stopping tauri-driver');\n tauriDriverProcess.kill();\n tauriDriverProcess = null;\n }\n },\n\n beforeSession: async function () {\n const binaryPath = getTauriBinaryPath();\n if (!fs.existsSync(binaryPath)) {\n throw new Error(\n `Tauri binary not found at: ${binaryPath}\\n` +\n 'Please build the app first with: npm run tauri:build'\n );\n }\n console.log(`Using Tauri binary: ${binaryPath}`);\n },\n\n afterTest: async function (test, _context, { error }) {\n if (error) {\n // Take screenshot on failure\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const screenshotPath = `./e2e-native/screenshots/${test.title}-${timestamp}.png`;\n await browser.saveScreenshot(screenshotPath);\n console.log(`Screenshot saved: ${screenshotPath}`);\n }\n },\n};\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n * // On Windows, check for msedgedriver\n const edgeDriverPath = await getMsEdgeDriverPath();\n if (process.platform === 'win32' && !edgeDriverPath) {\n console.warn(\n '\\n⚠ msedgedriver.exe not found in common locations.\\n' +\n ' Download from: https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/\\n' +\n ' Then either:\\n' +\n ' - Add to PATH\\n' +\n ' - Set MSEDGEDRIVER_PATH environment variable\\n' +\n ' - Place in your home directory\\n'\n );\n }\n },\n};\n","ops":[{"diffOp":{"equal":{"range":[0,60]}}},{"equalLines":{"line_count":179}},{"diffOp":{"equal":{"range":[60,216]}}},{"diffOp":{"delete":{"range":[216,598]}}},{"diffOp":{"equal":{"range":[598,605]}}},{"equalLines":{"line_count":73}},{"diffOp":{"equal":{"range":[605,613]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"wdio.conf.ts"},"span":[5024,5036],"sourceCode":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n *\n * This config runs tests against the actual Tauri desktop app using tauri-driver.\n * Requires: cargo install tauri-driver\n *\n * Usage:\n * 1. Build the app: npm run tauri:build\n * 2. Run tests: npm run test:native\n */\n\nimport type { Options } from '@wdio/types';\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport { fileURLToPath } from 'url';\nimport { spawn, type ChildProcess } from 'child_process';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// Track tauri-driver process\nlet tauriDriverProcess: ChildProcess | null = null;\n\n// Detect the built Tauri binary path based on platform\nfunction getTauriBinaryPath(): string {\n const projectRoot = path.resolve(__dirname, 'src-tauri');\n\n if (process.platform === 'win32') {\n // Windows: look for .exe in release or debug\n // Binary name comes from Cargo.toml package name\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri.exe');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri.exe');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n\n // Fallback to release path (will error if not built)\n return releasePath;\n } else if (process.platform === 'darwin') {\n // macOS: .app bundle\n const releasePath = path.join(\n projectRoot,\n 'target',\n 'release',\n 'bundle',\n 'macos',\n 'NoteFlow.app',\n 'Contents',\n 'MacOS',\n 'noteflow-tauri'\n );\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n } else {\n // Linux: AppImage or direct binary\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n }\n}\n\n// Get tauri-driver path\nfunction getTauriDriverPath(): string {\n if (process.platform === 'win32') {\n // On Windows, tauri-driver is in cargo bin\n const cargoHome = process.env.CARGO_HOME || path.join(process.env.USERPROFILE || '', '.cargo');\n return path.join(cargoHome, 'bin', 'tauri-driver.exe');\n }\n return 'tauri-driver';\n}\n\n// Get msedgedriver path (Windows only)\nasync function getMsEdgeDriverPath(): Promise<string | null> {\n if (process.platform !== 'win32') {\n return null;\n }\n\n // Try edgedriver npm package first\n try {\n const edgedriver = await import('edgedriver');\n // The package provides a function to get/download the binary\n if (typeof edgedriver.default === 'string') {\n return edgedriver.default;\n }\n if (edgedriver.binPath && fs.existsSync(edgedriver.binPath)) {\n return edgedriver.binPath;\n }\n } catch {\n // Package not available or failed\n }\n\n // Check common locations\n const possiblePaths = [\n // Custom env var\n process.env.MSEDGEDRIVER_PATH,\n // Common install locations\n 'C:\\\\Program Files\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n 'C:\\\\Program Files (x86)\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n path.join(process.env.USERPROFILE || '', 'msedgedriver.exe'),\n path.join(process.env.USERPROFILE || '', '.webdrivers', 'msedgedriver.exe'),\n ];\n\n for (const p of possiblePaths) {\n if (p && fs.existsSync(p)) {\n return p;\n }\n }\n\n return null;\n}\n\nexport const config: Options.Testrunner = {\n // Test specs\n specs: ['./e2e-native/**/*.spec.ts'],\n exclude: [],\n\n // Capabilities\n maxInstances: 1, // Tauri apps should run one at a time\n capabilities: [\n {\n // Use tauri-driver as the WebDriver server\n 'tauri:options': {\n application: getTauriBinaryPath(),\n },\n },\n ],\n\n // Test framework\n framework: 'mocha',\n mochaOpts: {\n ui: 'bdd',\n timeout: 60000,\n },\n\n // Reporters\n reporters: ['spec'],\n\n // Log level\n logLevel: 'info',\n\n // Connection settings for tauri-driver\n hostname: '127.0.0.1',\n port: 4444,\n\n // No built-in service - tauri-driver started via onPrepare hook\n services: [],\n\n // Timeouts\n connectionRetryTimeout: 120000,\n connectionRetryCount: 3,\n\n // Hooks\n onPrepare: async function () {\n const driverPath = getTauriDriverPath();\n console.log(`Starting tauri-driver: ${driverPath}`);\n\n // Check if tauri-driver exists\n if (!fs.existsSync(driverPath)) {\n throw new Error(\n `tauri-driver not found at: ${driverPath}\\n` +\n 'Install it with: cargo install tauri-driver'\n );\n }\n\n // On Windows, check for msedgedriver\n const edgeDriverPath = await getMsEdgeDriverPath();\n if (process.platform === 'win32' && !edgeDriverPath) {\n console.warn(\n '\\n⚠ msedgedriver.exe not found in common locations.\\n' +\n ' Download from: https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/\\n' +\n ' Then either:\\n' +\n ' - Add to PATH\\n' +\n ' - Set MSEDGEDRIVER_PATH environment variable\\n' +\n ' - Place in your home directory\\n'\n );\n }\n\n // Build args\n const args = ['--port', '4444'];\n if (edgeDriverPath) {\n args.push('--native-driver', edgeDriverPath);\n console.log(`Using msedgedriver: ${edgeDriverPath}`);\n }\n\n // Start tauri-driver\n tauriDriverProcess = spawn(driverPath, args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n tauriDriverProcess.stdout?.on('data', (data) => {\n console.log(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n tauriDriverProcess.stderr?.on('data', (data) => {\n console.error(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n // Wait for tauri-driver to be ready\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error('tauri-driver failed to start within 10s'));\n }, 10000);\n\n const checkReady = async () => {\n try {\n const response = await fetch('http://127.0.0.1:4444/status');\n if (response.ok) {\n clearTimeout(timeout);\n console.log('tauri-driver is ready');\n resolve();\n }\n } catch {\n // Not ready yet, retry\n setTimeout(checkReady, 200);\n }\n };\n\n // Start checking after a brief delay\n setTimeout(checkReady, 500);\n });\n },\n\n onComplete: async function () {\n // Stop tauri-driver\n if (tauriDriverProcess) {\n console.log('Stopping tauri-driver');\n tauriDriverProcess.kill();\n tauriDriverProcess = null;\n }\n },\n\n beforeSession: async function () {\n const binaryPath = getTauriBinaryPath();\n if (!fs.existsSync(binaryPath)) {\n throw new Error(\n `Tauri binary not found at: ${binaryPath}\\n` +\n 'Please build the app first with: npm run tauri:build'\n );\n }\n console.log(`Using Tauri binary: ${binaryPath}`);\n },\n\n afterTest: async function (test, _context, { error }) {\n if (error) {\n // Take screenshot on failure\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const screenshotPath = `./e2e-native/screenshots/${test.title}-${timestamp}.png`;\n await browser.saveScreenshot(screenshotPath);\n console.log(`Screenshot saved: ${screenshotPath}`);\n }\n },\n};\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n * 'Please build the app first with: npm run tauri:build'\n );\n }\n console.log(`Using Tauri binary: ${binaryPath}`);\n },\n },\n};\n","ops":[{"diffOp":{"equal":{"range":[0,60]}}},{"equalLines":{"line_count":251}},{"diffOp":{"equal":{"range":[60,139]}}},{"diffOp":{"delete":{"range":[139,193]}}},{"diffOp":{"equal":{"range":[193,199]}}},{"equalLines":{"line_count":8}},{"diffOp":{"equal":{"range":[199,207]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"wdio.conf.ts"},"span":[7180,7191],"sourceCode":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n *\n * This config runs tests against the actual Tauri desktop app using tauri-driver.\n * Requires: cargo install tauri-driver\n *\n * Usage:\n * 1. Build the app: npm run tauri:build\n * 2. Run tests: npm run test:native\n */\n\nimport type { Options } from '@wdio/types';\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport { fileURLToPath } from 'url';\nimport { spawn, type ChildProcess } from 'child_process';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// Track tauri-driver process\nlet tauriDriverProcess: ChildProcess | null = null;\n\n// Detect the built Tauri binary path based on platform\nfunction getTauriBinaryPath(): string {\n const projectRoot = path.resolve(__dirname, 'src-tauri');\n\n if (process.platform === 'win32') {\n // Windows: look for .exe in release or debug\n // Binary name comes from Cargo.toml package name\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri.exe');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri.exe');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n\n // Fallback to release path (will error if not built)\n return releasePath;\n } else if (process.platform === 'darwin') {\n // macOS: .app bundle\n const releasePath = path.join(\n projectRoot,\n 'target',\n 'release',\n 'bundle',\n 'macos',\n 'NoteFlow.app',\n 'Contents',\n 'MacOS',\n 'noteflow-tauri'\n );\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n } else {\n // Linux: AppImage or direct binary\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n }\n}\n\n// Get tauri-driver path\nfunction getTauriDriverPath(): string {\n if (process.platform === 'win32') {\n // On Windows, tauri-driver is in cargo bin\n const cargoHome = process.env.CARGO_HOME || path.join(process.env.USERPROFILE || '', '.cargo');\n return path.join(cargoHome, 'bin', 'tauri-driver.exe');\n }\n return 'tauri-driver';\n}\n\n// Get msedgedriver path (Windows only)\nasync function getMsEdgeDriverPath(): Promise<string | null> {\n if (process.platform !== 'win32') {\n return null;\n }\n\n // Try edgedriver npm package first\n try {\n const edgedriver = await import('edgedriver');\n // The package provides a function to get/download the binary\n if (typeof edgedriver.default === 'string') {\n return edgedriver.default;\n }\n if (edgedriver.binPath && fs.existsSync(edgedriver.binPath)) {\n return edgedriver.binPath;\n }\n } catch {\n // Package not available or failed\n }\n\n // Check common locations\n const possiblePaths = [\n // Custom env var\n process.env.MSEDGEDRIVER_PATH,\n // Common install locations\n 'C:\\\\Program Files\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n 'C:\\\\Program Files (x86)\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n path.join(process.env.USERPROFILE || '', 'msedgedriver.exe'),\n path.join(process.env.USERPROFILE || '', '.webdrivers', 'msedgedriver.exe'),\n ];\n\n for (const p of possiblePaths) {\n if (p && fs.existsSync(p)) {\n return p;\n }\n }\n\n return null;\n}\n\nexport const config: Options.Testrunner = {\n // Test specs\n specs: ['./e2e-native/**/*.spec.ts'],\n exclude: [],\n\n // Capabilities\n maxInstances: 1, // Tauri apps should run one at a time\n capabilities: [\n {\n // Use tauri-driver as the WebDriver server\n 'tauri:options': {\n application: getTauriBinaryPath(),\n },\n },\n ],\n\n // Test framework\n framework: 'mocha',\n mochaOpts: {\n ui: 'bdd',\n timeout: 60000,\n },\n\n // Reporters\n reporters: ['spec'],\n\n // Log level\n logLevel: 'info',\n\n // Connection settings for tauri-driver\n hostname: '127.0.0.1',\n port: 4444,\n\n // No built-in service - tauri-driver started via onPrepare hook\n services: [],\n\n // Timeouts\n connectionRetryTimeout: 120000,\n connectionRetryCount: 3,\n\n // Hooks\n onPrepare: async function () {\n const driverPath = getTauriDriverPath();\n console.log(`Starting tauri-driver: ${driverPath}`);\n\n // Check if tauri-driver exists\n if (!fs.existsSync(driverPath)) {\n throw new Error(\n `tauri-driver not found at: ${driverPath}\\n` +\n 'Install it with: cargo install tauri-driver'\n );\n }\n\n // On Windows, check for msedgedriver\n const edgeDriverPath = await getMsEdgeDriverPath();\n if (process.platform === 'win32' && !edgeDriverPath) {\n console.warn(\n '\\n⚠ msedgedriver.exe not found in common locations.\\n' +\n ' Download from: https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/\\n' +\n ' Then either:\\n' +\n ' - Add to PATH\\n' +\n ' - Set MSEDGEDRIVER_PATH environment variable\\n' +\n ' - Place in your home directory\\n'\n );\n }\n\n // Build args\n const args = ['--port', '4444'];\n if (edgeDriverPath) {\n args.push('--native-driver', edgeDriverPath);\n console.log(`Using msedgedriver: ${edgeDriverPath}`);\n }\n\n // Start tauri-driver\n tauriDriverProcess = spawn(driverPath, args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n tauriDriverProcess.stdout?.on('data', (data) => {\n console.log(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n tauriDriverProcess.stderr?.on('data', (data) => {\n console.error(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n // Wait for tauri-driver to be ready\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error('tauri-driver failed to start within 10s'));\n }, 10000);\n\n const checkReady = async () => {\n try {\n const response = await fetch('http://127.0.0.1:4444/status');\n if (response.ok) {\n clearTimeout(timeout);\n console.log('tauri-driver is ready');\n resolve();\n }\n } catch {\n // Not ready yet, retry\n setTimeout(checkReady, 200);\n }\n };\n\n // Start checking after a brief delay\n setTimeout(checkReady, 500);\n });\n },\n\n onComplete: async function () {\n // Stop tauri-driver\n if (tauriDriverProcess) {\n console.log('Stopping tauri-driver');\n tauriDriverProcess.kill();\n tauriDriverProcess = null;\n }\n },\n\n beforeSession: async function () {\n const binaryPath = getTauriBinaryPath();\n if (!fs.existsSync(binaryPath)) {\n throw new Error(\n `Tauri binary not found at: ${binaryPath}\\n` +\n 'Please build the app first with: npm run tauri:build'\n );\n }\n console.log(`Using Tauri binary: ${binaryPath}`);\n },\n\n afterTest: async function (test, _context, { error }) {\n if (error) {\n // Take screenshot on failure\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const screenshotPath = `./e2e-native/screenshots/${test.title}-${timestamp}.png`;\n await browser.saveScreenshot(screenshotPath);\n console.log(`Screenshot saved: ${screenshotPath}`);\n }\n },\n};\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n * const args = ['--port', '4444'];\n if (edgeDriverPath) {\n args.push('--native-driver', edgeDriverPath);\n console.log(`Using msedgedriver: ${edgeDriverPath}`);\n }\n },\n};\n","ops":[{"diffOp":{"equal":{"range":[0,60]}}},{"equalLines":{"line_count":193}},{"diffOp":{"equal":{"range":[60,174]}}},{"diffOp":{"delete":{"range":[174,234]}}},{"diffOp":{"equal":{"range":[234,241]}}},{"equalLines":{"line_count":66}},{"diffOp":{"equal":{"range":[241,249]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"wdio.conf.ts"},"span":[5546,5557],"sourceCode":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n *\n * This config runs tests against the actual Tauri desktop app using tauri-driver.\n * Requires: cargo install tauri-driver\n *\n * Usage:\n * 1. Build the app: npm run tauri:build\n * 2. Run tests: npm run test:native\n */\n\nimport type { Options } from '@wdio/types';\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport { fileURLToPath } from 'url';\nimport { spawn, type ChildProcess } from 'child_process';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// Track tauri-driver process\nlet tauriDriverProcess: ChildProcess | null = null;\n\n// Detect the built Tauri binary path based on platform\nfunction getTauriBinaryPath(): string {\n const projectRoot = path.resolve(__dirname, 'src-tauri');\n\n if (process.platform === 'win32') {\n // Windows: look for .exe in release or debug\n // Binary name comes from Cargo.toml package name\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri.exe');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri.exe');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n\n // Fallback to release path (will error if not built)\n return releasePath;\n } else if (process.platform === 'darwin') {\n // macOS: .app bundle\n const releasePath = path.join(\n projectRoot,\n 'target',\n 'release',\n 'bundle',\n 'macos',\n 'NoteFlow.app',\n 'Contents',\n 'MacOS',\n 'noteflow-tauri'\n );\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n } else {\n // Linux: AppImage or direct binary\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n }\n}\n\n// Get tauri-driver path\nfunction getTauriDriverPath(): string {\n if (process.platform === 'win32') {\n // On Windows, tauri-driver is in cargo bin\n const cargoHome = process.env.CARGO_HOME || path.join(process.env.USERPROFILE || '', '.cargo');\n return path.join(cargoHome, 'bin', 'tauri-driver.exe');\n }\n return 'tauri-driver';\n}\n\n// Get msedgedriver path (Windows only)\nasync function getMsEdgeDriverPath(): Promise<string | null> {\n if (process.platform !== 'win32') {\n return null;\n }\n\n // Try edgedriver npm package first\n try {\n const edgedriver = await import('edgedriver');\n // The package provides a function to get/download the binary\n if (typeof edgedriver.default === 'string') {\n return edgedriver.default;\n }\n if (edgedriver.binPath && fs.existsSync(edgedriver.binPath)) {\n return edgedriver.binPath;\n }\n } catch {\n // Package not available or failed\n }\n\n // Check common locations\n const possiblePaths = [\n // Custom env var\n process.env.MSEDGEDRIVER_PATH,\n // Common install locations\n 'C:\\\\Program Files\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n 'C:\\\\Program Files (x86)\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n path.join(process.env.USERPROFILE || '', 'msedgedriver.exe'),\n path.join(process.env.USERPROFILE || '', '.webdrivers', 'msedgedriver.exe'),\n ];\n\n for (const p of possiblePaths) {\n if (p && fs.existsSync(p)) {\n return p;\n }\n }\n\n return null;\n}\n\nexport const config: Options.Testrunner = {\n // Test specs\n specs: ['./e2e-native/**/*.spec.ts'],\n exclude: [],\n\n // Capabilities\n maxInstances: 1, // Tauri apps should run one at a time\n capabilities: [\n {\n // Use tauri-driver as the WebDriver server\n 'tauri:options': {\n application: getTauriBinaryPath(),\n },\n },\n ],\n\n // Test framework\n framework: 'mocha',\n mochaOpts: {\n ui: 'bdd',\n timeout: 60000,\n },\n\n // Reporters\n reporters: ['spec'],\n\n // Log level\n logLevel: 'info',\n\n // Connection settings for tauri-driver\n hostname: '127.0.0.1',\n port: 4444,\n\n // No built-in service - tauri-driver started via onPrepare hook\n services: [],\n\n // Timeouts\n connectionRetryTimeout: 120000,\n connectionRetryCount: 3,\n\n // Hooks\n onPrepare: async function () {\n const driverPath = getTauriDriverPath();\n console.log(`Starting tauri-driver: ${driverPath}`);\n\n // Check if tauri-driver exists\n if (!fs.existsSync(driverPath)) {\n throw new Error(\n `tauri-driver not found at: ${driverPath}\\n` +\n 'Install it with: cargo install tauri-driver'\n );\n }\n\n // On Windows, check for msedgedriver\n const edgeDriverPath = await getMsEdgeDriverPath();\n if (process.platform === 'win32' && !edgeDriverPath) {\n console.warn(\n '\\n⚠ msedgedriver.exe not found in common locations.\\n' +\n ' Download from: https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/\\n' +\n ' Then either:\\n' +\n ' - Add to PATH\\n' +\n ' - Set MSEDGEDRIVER_PATH environment variable\\n' +\n ' - Place in your home directory\\n'\n );\n }\n\n // Build args\n const args = ['--port', '4444'];\n if (edgeDriverPath) {\n args.push('--native-driver', edgeDriverPath);\n console.log(`Using msedgedriver: ${edgeDriverPath}`);\n }\n\n // Start tauri-driver\n tauriDriverProcess = spawn(driverPath, args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n tauriDriverProcess.stdout?.on('data', (data) => {\n console.log(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n tauriDriverProcess.stderr?.on('data', (data) => {\n console.error(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n // Wait for tauri-driver to be ready\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error('tauri-driver failed to start within 10s'));\n }, 10000);\n\n const checkReady = async () => {\n try {\n const response = await fetch('http://127.0.0.1:4444/status');\n if (response.ok) {\n clearTimeout(timeout);\n console.log('tauri-driver is ready');\n resolve();\n }\n } catch {\n // Not ready yet, retry\n setTimeout(checkReady, 200);\n }\n };\n\n // Start checking after a brief delay\n setTimeout(checkReady, 500);\n });\n },\n\n onComplete: async function () {\n // Stop tauri-driver\n if (tauriDriverProcess) {\n console.log('Stopping tauri-driver');\n tauriDriverProcess.kill();\n tauriDriverProcess = null;\n }\n },\n\n beforeSession: async function () {\n const binaryPath = getTauriBinaryPath();\n if (!fs.existsSync(binaryPath)) {\n throw new Error(\n `Tauri binary not found at: ${binaryPath}\\n` +\n 'Please build the app first with: npm run tauri:build'\n );\n }\n console.log(`Using Tauri binary: ${binaryPath}`);\n },\n\n afterTest: async function (test, _context, { error }) {\n if (error) {\n // Take screenshot on failure\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const screenshotPath = `./e2e-native/screenshots/${test.title}-${timestamp}.png`;\n await browser.saveScreenshot(screenshotPath);\n console.log(`Screenshot saved: ${screenshotPath}`);\n }\n },\n};\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n * const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const screenshotPath = `./e2e-native/screenshots/${test.title}-${timestamp}.png`;\n await browser.saveScreenshot(screenshotPath);\n console.log(`Screenshot saved: ${screenshotPath}`);\n }\n },\n};\n","ops":[{"diffOp":{"equal":{"range":[0,60]}}},{"equalLines":{"line_count":260}},{"diffOp":{"equal":{"range":[60,271]}}},{"diffOp":{"delete":{"range":[271,329]}}},{"diffOp":{"equal":{"range":[329,344]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"wdio.conf.ts"},"span":[7565,7576],"sourceCode":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n *\n * This config runs tests against the actual Tauri desktop app using tauri-driver.\n * Requires: cargo install tauri-driver\n *\n * Usage:\n * 1. Build the app: npm run tauri:build\n * 2. Run tests: npm run test:native\n */\n\nimport type { Options } from '@wdio/types';\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport { fileURLToPath } from 'url';\nimport { spawn, type ChildProcess } from 'child_process';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// Track tauri-driver process\nlet tauriDriverProcess: ChildProcess | null = null;\n\n// Detect the built Tauri binary path based on platform\nfunction getTauriBinaryPath(): string {\n const projectRoot = path.resolve(__dirname, 'src-tauri');\n\n if (process.platform === 'win32') {\n // Windows: look for .exe in release or debug\n // Binary name comes from Cargo.toml package name\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri.exe');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri.exe');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n\n // Fallback to release path (will error if not built)\n return releasePath;\n } else if (process.platform === 'darwin') {\n // macOS: .app bundle\n const releasePath = path.join(\n projectRoot,\n 'target',\n 'release',\n 'bundle',\n 'macos',\n 'NoteFlow.app',\n 'Contents',\n 'MacOS',\n 'noteflow-tauri'\n );\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n } else {\n // Linux: AppImage or direct binary\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n }\n}\n\n// Get tauri-driver path\nfunction getTauriDriverPath(): string {\n if (process.platform === 'win32') {\n // On Windows, tauri-driver is in cargo bin\n const cargoHome = process.env.CARGO_HOME || path.join(process.env.USERPROFILE || '', '.cargo');\n return path.join(cargoHome, 'bin', 'tauri-driver.exe');\n }\n return 'tauri-driver';\n}\n\n// Get msedgedriver path (Windows only)\nasync function getMsEdgeDriverPath(): Promise<string | null> {\n if (process.platform !== 'win32') {\n return null;\n }\n\n // Try edgedriver npm package first\n try {\n const edgedriver = await import('edgedriver');\n // The package provides a function to get/download the binary\n if (typeof edgedriver.default === 'string') {\n return edgedriver.default;\n }\n if (edgedriver.binPath && fs.existsSync(edgedriver.binPath)) {\n return edgedriver.binPath;\n }\n } catch {\n // Package not available or failed\n }\n\n // Check common locations\n const possiblePaths = [\n // Custom env var\n process.env.MSEDGEDRIVER_PATH,\n // Common install locations\n 'C:\\\\Program Files\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n 'C:\\\\Program Files (x86)\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n path.join(process.env.USERPROFILE || '', 'msedgedriver.exe'),\n path.join(process.env.USERPROFILE || '', '.webdrivers', 'msedgedriver.exe'),\n ];\n\n for (const p of possiblePaths) {\n if (p && fs.existsSync(p)) {\n return p;\n }\n }\n\n return null;\n}\n\nexport const config: Options.Testrunner = {\n // Test specs\n specs: ['./e2e-native/**/*.spec.ts'],\n exclude: [],\n\n // Capabilities\n maxInstances: 1, // Tauri apps should run one at a time\n capabilities: [\n {\n // Use tauri-driver as the WebDriver server\n 'tauri:options': {\n application: getTauriBinaryPath(),\n },\n },\n ],\n\n // Test framework\n framework: 'mocha',\n mochaOpts: {\n ui: 'bdd',\n timeout: 60000,\n },\n\n // Reporters\n reporters: ['spec'],\n\n // Log level\n logLevel: 'info',\n\n // Connection settings for tauri-driver\n hostname: '127.0.0.1',\n port: 4444,\n\n // No built-in service - tauri-driver started via onPrepare hook\n services: [],\n\n // Timeouts\n connectionRetryTimeout: 120000,\n connectionRetryCount: 3,\n\n // Hooks\n onPrepare: async function () {\n const driverPath = getTauriDriverPath();\n console.log(`Starting tauri-driver: ${driverPath}`);\n\n // Check if tauri-driver exists\n if (!fs.existsSync(driverPath)) {\n throw new Error(\n `tauri-driver not found at: ${driverPath}\\n` +\n 'Install it with: cargo install tauri-driver'\n );\n }\n\n // On Windows, check for msedgedriver\n const edgeDriverPath = await getMsEdgeDriverPath();\n if (process.platform === 'win32' && !edgeDriverPath) {\n console.warn(\n '\\n⚠ msedgedriver.exe not found in common locations.\\n' +\n ' Download from: https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/\\n' +\n ' Then either:\\n' +\n ' - Add to PATH\\n' +\n ' - Set MSEDGEDRIVER_PATH environment variable\\n' +\n ' - Place in your home directory\\n'\n );\n }\n\n // Build args\n const args = ['--port', '4444'];\n if (edgeDriverPath) {\n args.push('--native-driver', edgeDriverPath);\n console.log(`Using msedgedriver: ${edgeDriverPath}`);\n }\n\n // Start tauri-driver\n tauriDriverProcess = spawn(driverPath, args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n tauriDriverProcess.stdout?.on('data', (data) => {\n console.log(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n tauriDriverProcess.stderr?.on('data', (data) => {\n console.error(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n // Wait for tauri-driver to be ready\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error('tauri-driver failed to start within 10s'));\n }, 10000);\n\n const checkReady = async () => {\n try {\n const response = await fetch('http://127.0.0.1:4444/status');\n if (response.ok) {\n clearTimeout(timeout);\n console.log('tauri-driver is ready');\n resolve();\n }\n } catch {\n // Not ready yet, retry\n setTimeout(checkReady, 200);\n }\n };\n\n // Start checking after a brief delay\n setTimeout(checkReady, 500);\n });\n },\n\n onComplete: async function () {\n // Stop tauri-driver\n if (tauriDriverProcess) {\n console.log('Stopping tauri-driver');\n tauriDriverProcess.kill();\n tauriDriverProcess = null;\n }\n },\n\n beforeSession: async function () {\n const binaryPath = getTauriBinaryPath();\n if (!fs.existsSync(binaryPath)) {\n throw new Error(\n `Tauri binary not found at: ${binaryPath}\\n` +\n 'Please build the app first with: npm run tauri:build'\n );\n }\n console.log(`Using Tauri binary: ${binaryPath}`);\n },\n\n afterTest: async function (test, _context, { error }) {\n if (error) {\n // Take screenshot on failure\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const screenshotPath = `./e2e-native/screenshots/${test.title}-${timestamp}.png`;\n await browser.saveScreenshot(screenshotPath);\n console.log(`Screenshot saved: ${screenshotPath}`);\n }\n },\n};\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n * });\n\n tauriDriverProcess.stdout?.on('data', (data) => {\n console.log(`[tauri-driver] ${data.toString().trim()}`);\n });\n },\n};\n","ops":[{"diffOp":{"equal":{"range":[0,60]}}},{"equalLines":{"line_count":202}},{"diffOp":{"equal":{"range":[60,122]}}},{"diffOp":{"delete":{"range":[122,185]}}},{"diffOp":{"equal":{"range":[185,194]}}},{"equalLines":{"line_count":57}},{"diffOp":{"equal":{"range":[194,202]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"wdio.conf.ts"},"span":[5794,5805],"sourceCode":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n *\n * This config runs tests against the actual Tauri desktop app using tauri-driver.\n * Requires: cargo install tauri-driver\n *\n * Usage:\n * 1. Build the app: npm run tauri:build\n * 2. Run tests: npm run test:native\n */\n\nimport type { Options } from '@wdio/types';\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport { fileURLToPath } from 'url';\nimport { spawn, type ChildProcess } from 'child_process';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// Track tauri-driver process\nlet tauriDriverProcess: ChildProcess | null = null;\n\n// Detect the built Tauri binary path based on platform\nfunction getTauriBinaryPath(): string {\n const projectRoot = path.resolve(__dirname, 'src-tauri');\n\n if (process.platform === 'win32') {\n // Windows: look for .exe in release or debug\n // Binary name comes from Cargo.toml package name\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri.exe');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri.exe');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n\n // Fallback to release path (will error if not built)\n return releasePath;\n } else if (process.platform === 'darwin') {\n // macOS: .app bundle\n const releasePath = path.join(\n projectRoot,\n 'target',\n 'release',\n 'bundle',\n 'macos',\n 'NoteFlow.app',\n 'Contents',\n 'MacOS',\n 'noteflow-tauri'\n );\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n } else {\n // Linux: AppImage or direct binary\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n }\n}\n\n// Get tauri-driver path\nfunction getTauriDriverPath(): string {\n if (process.platform === 'win32') {\n // On Windows, tauri-driver is in cargo bin\n const cargoHome = process.env.CARGO_HOME || path.join(process.env.USERPROFILE || '', '.cargo');\n return path.join(cargoHome, 'bin', 'tauri-driver.exe');\n }\n return 'tauri-driver';\n}\n\n// Get msedgedriver path (Windows only)\nasync function getMsEdgeDriverPath(): Promise<string | null> {\n if (process.platform !== 'win32') {\n return null;\n }\n\n // Try edgedriver npm package first\n try {\n const edgedriver = await import('edgedriver');\n // The package provides a function to get/download the binary\n if (typeof edgedriver.default === 'string') {\n return edgedriver.default;\n }\n if (edgedriver.binPath && fs.existsSync(edgedriver.binPath)) {\n return edgedriver.binPath;\n }\n } catch {\n // Package not available or failed\n }\n\n // Check common locations\n const possiblePaths = [\n // Custom env var\n process.env.MSEDGEDRIVER_PATH,\n // Common install locations\n 'C:\\\\Program Files\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n 'C:\\\\Program Files (x86)\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n path.join(process.env.USERPROFILE || '', 'msedgedriver.exe'),\n path.join(process.env.USERPROFILE || '', '.webdrivers', 'msedgedriver.exe'),\n ];\n\n for (const p of possiblePaths) {\n if (p && fs.existsSync(p)) {\n return p;\n }\n }\n\n return null;\n}\n\nexport const config: Options.Testrunner = {\n // Test specs\n specs: ['./e2e-native/**/*.spec.ts'],\n exclude: [],\n\n // Capabilities\n maxInstances: 1, // Tauri apps should run one at a time\n capabilities: [\n {\n // Use tauri-driver as the WebDriver server\n 'tauri:options': {\n application: getTauriBinaryPath(),\n },\n },\n ],\n\n // Test framework\n framework: 'mocha',\n mochaOpts: {\n ui: 'bdd',\n timeout: 60000,\n },\n\n // Reporters\n reporters: ['spec'],\n\n // Log level\n logLevel: 'info',\n\n // Connection settings for tauri-driver\n hostname: '127.0.0.1',\n port: 4444,\n\n // No built-in service - tauri-driver started via onPrepare hook\n services: [],\n\n // Timeouts\n connectionRetryTimeout: 120000,\n connectionRetryCount: 3,\n\n // Hooks\n onPrepare: async function () {\n const driverPath = getTauriDriverPath();\n console.log(`Starting tauri-driver: ${driverPath}`);\n\n // Check if tauri-driver exists\n if (!fs.existsSync(driverPath)) {\n throw new Error(\n `tauri-driver not found at: ${driverPath}\\n` +\n 'Install it with: cargo install tauri-driver'\n );\n }\n\n // On Windows, check for msedgedriver\n const edgeDriverPath = await getMsEdgeDriverPath();\n if (process.platform === 'win32' && !edgeDriverPath) {\n console.warn(\n '\\n⚠ msedgedriver.exe not found in common locations.\\n' +\n ' Download from: https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/\\n' +\n ' Then either:\\n' +\n ' - Add to PATH\\n' +\n ' - Set MSEDGEDRIVER_PATH environment variable\\n' +\n ' - Place in your home directory\\n'\n );\n }\n\n // Build args\n const args = ['--port', '4444'];\n if (edgeDriverPath) {\n args.push('--native-driver', edgeDriverPath);\n console.log(`Using msedgedriver: ${edgeDriverPath}`);\n }\n\n // Start tauri-driver\n tauriDriverProcess = spawn(driverPath, args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n tauriDriverProcess.stdout?.on('data', (data) => {\n console.log(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n tauriDriverProcess.stderr?.on('data', (data) => {\n console.error(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n // Wait for tauri-driver to be ready\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error('tauri-driver failed to start within 10s'));\n }, 10000);\n\n const checkReady = async () => {\n try {\n const response = await fetch('http://127.0.0.1:4444/status');\n if (response.ok) {\n clearTimeout(timeout);\n console.log('tauri-driver is ready');\n resolve();\n }\n } catch {\n // Not ready yet, retry\n setTimeout(checkReady, 200);\n }\n };\n\n // Start checking after a brief delay\n setTimeout(checkReady, 500);\n });\n },\n\n onComplete: async function () {\n // Stop tauri-driver\n if (tauriDriverProcess) {\n console.log('Stopping tauri-driver');\n tauriDriverProcess.kill();\n tauriDriverProcess = null;\n }\n },\n\n beforeSession: async function () {\n const binaryPath = getTauriBinaryPath();\n if (!fs.existsSync(binaryPath)) {\n throw new Error(\n `Tauri binary not found at: ${binaryPath}\\n` +\n 'Please build the app first with: npm run tauri:build'\n );\n }\n console.log(`Using Tauri binary: ${binaryPath}`);\n },\n\n afterTest: async function (test, _context, { error }) {\n if (error) {\n // Take screenshot on failure\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const screenshotPath = `./e2e-native/screenshots/${test.title}-${timestamp}.png`;\n await browser.saveScreenshot(screenshotPath);\n console.log(`Screenshot saved: ${screenshotPath}`);\n }\n },\n};\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n * });\n\n tauriDriverProcess.stderr?.on('data', (data) => {\n console.error(`[tauri-driver] ${data.toString().trim()}`);\n });\n },\n};\n","ops":[{"diffOp":{"equal":{"range":[0,60]}}},{"equalLines":{"line_count":206}},{"diffOp":{"equal":{"range":[60,122]}}},{"diffOp":{"delete":{"range":[122,187]}}},{"diffOp":{"equal":{"range":[187,196]}}},{"equalLines":{"line_count":53}},{"diffOp":{"equal":{"range":[196,204]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"wdio.conf.ts"},"span":[5920,5933],"sourceCode":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n *\n * This config runs tests against the actual Tauri desktop app using tauri-driver.\n * Requires: cargo install tauri-driver\n *\n * Usage:\n * 1. Build the app: npm run tauri:build\n * 2. Run tests: npm run test:native\n */\n\nimport type { Options } from '@wdio/types';\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport { fileURLToPath } from 'url';\nimport { spawn, type ChildProcess } from 'child_process';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// Track tauri-driver process\nlet tauriDriverProcess: ChildProcess | null = null;\n\n// Detect the built Tauri binary path based on platform\nfunction getTauriBinaryPath(): string {\n const projectRoot = path.resolve(__dirname, 'src-tauri');\n\n if (process.platform === 'win32') {\n // Windows: look for .exe in release or debug\n // Binary name comes from Cargo.toml package name\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri.exe');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri.exe');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n\n // Fallback to release path (will error if not built)\n return releasePath;\n } else if (process.platform === 'darwin') {\n // macOS: .app bundle\n const releasePath = path.join(\n projectRoot,\n 'target',\n 'release',\n 'bundle',\n 'macos',\n 'NoteFlow.app',\n 'Contents',\n 'MacOS',\n 'noteflow-tauri'\n );\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n } else {\n // Linux: AppImage or direct binary\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n }\n}\n\n// Get tauri-driver path\nfunction getTauriDriverPath(): string {\n if (process.platform === 'win32') {\n // On Windows, tauri-driver is in cargo bin\n const cargoHome = process.env.CARGO_HOME || path.join(process.env.USERPROFILE || '', '.cargo');\n return path.join(cargoHome, 'bin', 'tauri-driver.exe');\n }\n return 'tauri-driver';\n}\n\n// Get msedgedriver path (Windows only)\nasync function getMsEdgeDriverPath(): Promise<string | null> {\n if (process.platform !== 'win32') {\n return null;\n }\n\n // Try edgedriver npm package first\n try {\n const edgedriver = await import('edgedriver');\n // The package provides a function to get/download the binary\n if (typeof edgedriver.default === 'string') {\n return edgedriver.default;\n }\n if (edgedriver.binPath && fs.existsSync(edgedriver.binPath)) {\n return edgedriver.binPath;\n }\n } catch {\n // Package not available or failed\n }\n\n // Check common locations\n const possiblePaths = [\n // Custom env var\n process.env.MSEDGEDRIVER_PATH,\n // Common install locations\n 'C:\\\\Program Files\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n 'C:\\\\Program Files (x86)\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n path.join(process.env.USERPROFILE || '', 'msedgedriver.exe'),\n path.join(process.env.USERPROFILE || '', '.webdrivers', 'msedgedriver.exe'),\n ];\n\n for (const p of possiblePaths) {\n if (p && fs.existsSync(p)) {\n return p;\n }\n }\n\n return null;\n}\n\nexport const config: Options.Testrunner = {\n // Test specs\n specs: ['./e2e-native/**/*.spec.ts'],\n exclude: [],\n\n // Capabilities\n maxInstances: 1, // Tauri apps should run one at a time\n capabilities: [\n {\n // Use tauri-driver as the WebDriver server\n 'tauri:options': {\n application: getTauriBinaryPath(),\n },\n },\n ],\n\n // Test framework\n framework: 'mocha',\n mochaOpts: {\n ui: 'bdd',\n timeout: 60000,\n },\n\n // Reporters\n reporters: ['spec'],\n\n // Log level\n logLevel: 'info',\n\n // Connection settings for tauri-driver\n hostname: '127.0.0.1',\n port: 4444,\n\n // No built-in service - tauri-driver started via onPrepare hook\n services: [],\n\n // Timeouts\n connectionRetryTimeout: 120000,\n connectionRetryCount: 3,\n\n // Hooks\n onPrepare: async function () {\n const driverPath = getTauriDriverPath();\n console.log(`Starting tauri-driver: ${driverPath}`);\n\n // Check if tauri-driver exists\n if (!fs.existsSync(driverPath)) {\n throw new Error(\n `tauri-driver not found at: ${driverPath}\\n` +\n 'Install it with: cargo install tauri-driver'\n );\n }\n\n // On Windows, check for msedgedriver\n const edgeDriverPath = await getMsEdgeDriverPath();\n if (process.platform === 'win32' && !edgeDriverPath) {\n console.warn(\n '\\n⚠ msedgedriver.exe not found in common locations.\\n' +\n ' Download from: https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/\\n' +\n ' Then either:\\n' +\n ' - Add to PATH\\n' +\n ' - Set MSEDGEDRIVER_PATH environment variable\\n' +\n ' - Place in your home directory\\n'\n );\n }\n\n // Build args\n const args = ['--port', '4444'];\n if (edgeDriverPath) {\n args.push('--native-driver', edgeDriverPath);\n console.log(`Using msedgedriver: ${edgeDriverPath}`);\n }\n\n // Start tauri-driver\n tauriDriverProcess = spawn(driverPath, args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n tauriDriverProcess.stdout?.on('data', (data) => {\n console.log(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n tauriDriverProcess.stderr?.on('data', (data) => {\n console.error(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n // Wait for tauri-driver to be ready\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error('tauri-driver failed to start within 10s'));\n }, 10000);\n\n const checkReady = async () => {\n try {\n const response = await fetch('http://127.0.0.1:4444/status');\n if (response.ok) {\n clearTimeout(timeout);\n console.log('tauri-driver is ready');\n resolve();\n }\n } catch {\n // Not ready yet, retry\n setTimeout(checkReady, 200);\n }\n };\n\n // Start checking after a brief delay\n setTimeout(checkReady, 500);\n });\n },\n\n onComplete: async function () {\n // Stop tauri-driver\n if (tauriDriverProcess) {\n console.log('Stopping tauri-driver');\n tauriDriverProcess.kill();\n tauriDriverProcess = null;\n }\n },\n\n beforeSession: async function () {\n const binaryPath = getTauriBinaryPath();\n if (!fs.existsSync(binaryPath)) {\n throw new Error(\n `Tauri binary not found at: ${binaryPath}\\n` +\n 'Please build the app first with: npm run tauri:build'\n );\n }\n console.log(`Using Tauri binary: ${binaryPath}`);\n },\n\n afterTest: async function (test, _context, { error }) {\n if (error) {\n // Take screenshot on failure\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const screenshotPath = `./e2e-native/screenshots/${test.title}-${timestamp}.png`;\n await browser.saveScreenshot(screenshotPath);\n console.log(`Screenshot saved: ${screenshotPath}`);\n }\n },\n};\n"},"tags":["fixable"],"source":null},{"category":"lint/suspicious/noConsole","severity":"error","description":"Don't use console.","message":[{"elements":[],"content":"Don't use "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}],"advices":{"advices":[{"log":["info",[{"elements":[],"content":"The use of "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":" is often reserved for debugging."}]]},{"log":["info",[{"elements":[],"content":"Unsafe fix: Remove "},{"elements":["Emphasis"],"content":"console"},{"elements":[],"content":"."}]]},{"diff":{"dictionary":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n * const response = await fetch('http://127.0.0.1:4444/status');\n if (response.ok) {\n clearTimeout(timeout);\n console.log('tauri-driver is ready');\n resolve();\n } },\n};\n","ops":[{"diffOp":{"equal":{"range":[0,60]}}},{"equalLines":{"line_count":220}},{"diffOp":{"equal":{"range":[60,195]}}},{"diffOp":{"delete":{"range":[195,245]}}},{"diffOp":{"equal":{"range":[245,280]}}},{"equalLines":{"line_count":39}},{"diffOp":{"equal":{"range":[280,288]}}}]}}]},"verboseAdvices":{"advices":[]},"location":{"path":{"file":"wdio.conf.ts"},"span":[6410,6421],"sourceCode":"/**\n * WebdriverIO Configuration for Native Tauri Testing\n *\n * This config runs tests against the actual Tauri desktop app using tauri-driver.\n * Requires: cargo install tauri-driver\n *\n * Usage:\n * 1. Build the app: npm run tauri:build\n * 2. Run tests: npm run test:native\n */\n\nimport type { Options } from '@wdio/types';\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport { fileURLToPath } from 'url';\nimport { spawn, type ChildProcess } from 'child_process';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// Track tauri-driver process\nlet tauriDriverProcess: ChildProcess | null = null;\n\n// Detect the built Tauri binary path based on platform\nfunction getTauriBinaryPath(): string {\n const projectRoot = path.resolve(__dirname, 'src-tauri');\n\n if (process.platform === 'win32') {\n // Windows: look for .exe in release or debug\n // Binary name comes from Cargo.toml package name\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri.exe');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri.exe');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n\n // Fallback to release path (will error if not built)\n return releasePath;\n } else if (process.platform === 'darwin') {\n // macOS: .app bundle\n const releasePath = path.join(\n projectRoot,\n 'target',\n 'release',\n 'bundle',\n 'macos',\n 'NoteFlow.app',\n 'Contents',\n 'MacOS',\n 'noteflow-tauri'\n );\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n } else {\n // Linux: AppImage or direct binary\n const releasePath = path.join(projectRoot, 'target', 'release', 'noteflow-tauri');\n const debugPath = path.join(projectRoot, 'target', 'debug', 'noteflow-tauri');\n\n if (fs.existsSync(releasePath)) {\n return releasePath;\n }\n if (fs.existsSync(debugPath)) {\n return debugPath;\n }\n return releasePath;\n }\n}\n\n// Get tauri-driver path\nfunction getTauriDriverPath(): string {\n if (process.platform === 'win32') {\n // On Windows, tauri-driver is in cargo bin\n const cargoHome = process.env.CARGO_HOME || path.join(process.env.USERPROFILE || '', '.cargo');\n return path.join(cargoHome, 'bin', 'tauri-driver.exe');\n }\n return 'tauri-driver';\n}\n\n// Get msedgedriver path (Windows only)\nasync function getMsEdgeDriverPath(): Promise<string | null> {\n if (process.platform !== 'win32') {\n return null;\n }\n\n // Try edgedriver npm package first\n try {\n const edgedriver = await import('edgedriver');\n // The package provides a function to get/download the binary\n if (typeof edgedriver.default === 'string') {\n return edgedriver.default;\n }\n if (edgedriver.binPath && fs.existsSync(edgedriver.binPath)) {\n return edgedriver.binPath;\n }\n } catch {\n // Package not available or failed\n }\n\n // Check common locations\n const possiblePaths = [\n // Custom env var\n process.env.MSEDGEDRIVER_PATH,\n // Common install locations\n 'C:\\\\Program Files\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n 'C:\\\\Program Files (x86)\\\\Microsoft\\\\Edge\\\\msedgedriver.exe',\n path.join(process.env.USERPROFILE || '', 'msedgedriver.exe'),\n path.join(process.env.USERPROFILE || '', '.webdrivers', 'msedgedriver.exe'),\n ];\n\n for (const p of possiblePaths) {\n if (p && fs.existsSync(p)) {\n return p;\n }\n }\n\n return null;\n}\n\nexport const config: Options.Testrunner = {\n // Test specs\n specs: ['./e2e-native/**/*.spec.ts'],\n exclude: [],\n\n // Capabilities\n maxInstances: 1, // Tauri apps should run one at a time\n capabilities: [\n {\n // Use tauri-driver as the WebDriver server\n 'tauri:options': {\n application: getTauriBinaryPath(),\n },\n },\n ],\n\n // Test framework\n framework: 'mocha',\n mochaOpts: {\n ui: 'bdd',\n timeout: 60000,\n },\n\n // Reporters\n reporters: ['spec'],\n\n // Log level\n logLevel: 'info',\n\n // Connection settings for tauri-driver\n hostname: '127.0.0.1',\n port: 4444,\n\n // No built-in service - tauri-driver started via onPrepare hook\n services: [],\n\n // Timeouts\n connectionRetryTimeout: 120000,\n connectionRetryCount: 3,\n\n // Hooks\n onPrepare: async function () {\n const driverPath = getTauriDriverPath();\n console.log(`Starting tauri-driver: ${driverPath}`);\n\n // Check if tauri-driver exists\n if (!fs.existsSync(driverPath)) {\n throw new Error(\n `tauri-driver not found at: ${driverPath}\\n` +\n 'Install it with: cargo install tauri-driver'\n );\n }\n\n // On Windows, check for msedgedriver\n const edgeDriverPath = await getMsEdgeDriverPath();\n if (process.platform === 'win32' && !edgeDriverPath) {\n console.warn(\n '\\n⚠ msedgedriver.exe not found in common locations.\\n' +\n ' Download from: https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/\\n' +\n ' Then either:\\n' +\n ' - Add to PATH\\n' +\n ' - Set MSEDGEDRIVER_PATH environment variable\\n' +\n ' - Place in your home directory\\n'\n );\n }\n\n // Build args\n const args = ['--port', '4444'];\n if (edgeDriverPath) {\n args.push('--native-driver', edgeDriverPath);\n console.log(`Using msedgedriver: ${edgeDriverPath}`);\n }\n\n // Start tauri-driver\n tauriDriverProcess = spawn(driverPath, args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n tauriDriverProcess.stdout?.on('data', (data) => {\n console.log(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n tauriDriverProcess.stderr?.on('data', (data) => {\n console.error(`[tauri-driver] ${data.toString().trim()}`);\n });\n\n // Wait for tauri-driver to be ready\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error('tauri-driver failed to start within 10s'));\n }, 10000);\n\n const checkReady = async () => {\n try {\n const response = await fetch('http://127.0.0.1:4444/status');\n if (response.ok) {\n clearTimeout(timeout);\n console.log('tauri-driver is ready');\n resolve();\n }\n } catch {\n // Not ready yet, retry\n setTimeout(checkReady, 200);\n }\n };\n\n // Start checking after a brief delay\n setTimeout(checkReady, 500);\n });\n },\n\n onComplete: async function () {\n // Stop tauri-driver\n if (tauriDriverProcess) {\n console.log('Stopping tauri-driver');\n tauriDriverProcess.kill();\n tauriDriverProcess = null;\n }\n },\n\n beforeSession: async function () {\n const binaryPath = getTauriBinaryPath();\n if (!fs.existsSync(binaryPath)) {\n throw new Error(\n `Tauri binary not found at: ${binaryPath}\\n` +\n 'Please build the app first with: npm run tauri:build'\n );\n }\n console.log(`Using Tauri binary: ${binaryPath}`);\n },\n\n afterTest: async function (test, _context, { error }) {\n if (error) {\n // Take screenshot on failure\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const screenshotPath = `./e2e-native/screenshots/${test.title}-${timestamp}.png`;\n await browser.saveScreenshot(screenshotPath);\n console.log(`Screenshot saved: ${screenshotPath}`);\n }\n },\n};\n"},"tags":["fixable"],"source":null}],"command":"lint"}