@midscene/core 0.16.3-beta-20250428133510.0 → 0.16.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +0,0 @@
1
- {"version":3,"mappings":"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","names":[],"ignoreList":[],"sources":["../../src/utils.ts"],"sourcesContent":["import { execSync } from 'node:child_process';\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport * as path from 'node:path';\nimport { dirname } from 'node:path';\nimport {\n defaultRunDirName,\n getMidsceneRunSubDir,\n logDir,\n} from '@midscene/shared/common';\nimport {\n MIDSCENE_DEBUG_MODE,\n MIDSCENE_OPENAI_INIT_CONFIG_JSON,\n getAIConfig,\n getAIConfigInJson,\n} from '@midscene/shared/env';\nimport { getRunningPkgInfo } from '@midscene/shared/fs';\nimport { assert, getGlobalScope } from '@midscene/shared/utils';\nimport { ifInBrowser, uuid } from '@midscene/shared/utils';\nimport type { Rect, ReportDumpWithAttributes } from './types';\n\nlet logEnvReady = false;\n\nexport const groupedActionDumpFileExt = 'web-dump.json';\n\nexport function getLogDir() {\n return logDir;\n}\n\nlet reportTpl: string | null = null;\nexport function setReportTpl(tpl: string) {\n reportTpl = tpl;\n}\n\nfunction getReportTpl() {\n const globalScope = getGlobalScope();\n if (\n !reportTpl &&\n globalScope &&\n typeof (globalScope as any).get_midscene_report_tpl === 'function'\n ) {\n reportTpl = (globalScope as any).get_midscene_report_tpl();\n return reportTpl;\n }\n\n const __dirname = dirname(__filename);\n if (!reportTpl && !ifInBrowser) {\n const possiblePaths = [\n path.join(__dirname, '../../report/index.html'),\n path.join(__dirname, '../report/index.html'),\n ];\n\n for (const reportPath of possiblePaths) {\n if (existsSync(reportPath)) {\n reportTpl = readFileSync(reportPath, 'utf-8');\n break;\n }\n }\n }\n return reportTpl;\n}\n\nexport function replaceStringWithFirstAppearance(\n str: string,\n target: string,\n replacement: string,\n) {\n const index = str.indexOf(target);\n return str.slice(0, index) + replacement + str.slice(index + target.length);\n}\n\nexport function reportHTMLContent(\n dumpData: string | ReportDumpWithAttributes[],\n): string {\n const tpl = getReportTpl();\n if (!tpl) {\n console.warn('reportTpl is not set, will not write report');\n return '';\n }\n\n let reportContent: string;\n if (\n (Array.isArray(dumpData) && dumpData.length === 0) ||\n typeof dumpData === 'undefined'\n ) {\n reportContent = replaceStringWithFirstAppearance(\n tpl,\n '{{dump}}',\n '<script type=\"midscene_web_dump\" type=\"application/json\"></script>',\n );\n } else if (typeof dumpData === 'string') {\n reportContent = replaceStringWithFirstAppearance(\n tpl,\n '{{dump}}',\n // biome-ignore lint/style/useTemplate: <explanation>\n '<script type=\"midscene_web_dump\" type=\"application/json\">\\n' +\n dumpData +\n '\\n</script>',\n );\n } else {\n const dumps = dumpData.map(({ dumpString, attributes }) => {\n const attributesArr = Object.keys(attributes || {}).map((key) => {\n return `${key}=\"${encodeURIComponent(attributes![key])}\"`;\n });\n return (\n // biome-ignore lint/style/useTemplate: <explanation>\n '<script type=\"midscene_web_dump\" type=\"application/json\" ' +\n attributesArr.join(' ') +\n '>\\n' +\n dumpString +\n '\\n</script>'\n );\n });\n reportContent = replaceStringWithFirstAppearance(\n tpl,\n '{{dump}}',\n dumps.join('\\n'),\n );\n }\n return reportContent;\n}\n\nexport function writeDumpReport(\n fileName: string,\n dumpData: string | ReportDumpWithAttributes[],\n): string | null {\n if (ifInBrowser) {\n console.log('will not write report in browser');\n return null;\n }\n\n const __dirname = dirname(__filename);\n const midscenePkgInfo = getRunningPkgInfo(__dirname);\n if (!midscenePkgInfo) {\n console.warn('midscenePkgInfo not found, will not write report');\n return null;\n }\n\n const reportPath = path.join(\n getMidsceneRunSubDir('report'),\n `${fileName}.html`,\n );\n const reportContent = reportHTMLContent(dumpData);\n if (!reportContent) {\n console.warn('reportContent is empty, will not write report');\n return null;\n }\n writeFileSync(reportPath, reportContent);\n if (process.env.MIDSCENE_DEBUG_LOG_JSON) {\n writeFileSync(\n `${reportPath}.json`,\n typeof dumpData === 'string'\n ? dumpData\n : JSON.stringify(dumpData, null, 2),\n );\n }\n\n return reportPath;\n}\n\nexport function writeLogFile(opts: {\n fileName: string;\n fileExt: string;\n fileContent: string;\n type: 'dump' | 'cache' | 'report' | 'tmp';\n generateReport?: boolean;\n}) {\n if (ifInBrowser) {\n return '/mock/report.html';\n }\n const { fileName, fileExt, fileContent, type = 'dump' } = opts;\n const targetDir = getMidsceneRunSubDir(type);\n // Ensure directory exists\n if (!logEnvReady) {\n assert(targetDir, 'logDir should be set before writing dump file');\n\n // gitIgnore in the parent directory\n const gitIgnorePath = path.join(targetDir, '../../.gitignore');\n const gitPath = path.join(targetDir, '../../.git');\n let gitIgnoreContent = '';\n\n if (existsSync(gitPath)) {\n // if the git path exists, we need to add the log folder to the git ignore file\n if (existsSync(gitIgnorePath)) {\n gitIgnoreContent = readFileSync(gitIgnorePath, 'utf-8');\n }\n\n // ignore the log folder\n if (!gitIgnoreContent.includes(`${defaultRunDirName}/`)) {\n writeFileSync(\n gitIgnorePath,\n `${gitIgnoreContent}\\n# Midscene.js dump files\\n${defaultRunDirName}/dump\\n${defaultRunDirName}/report\\n${defaultRunDirName}/tmp\\n${defaultRunDirName}/log\\n`,\n 'utf-8',\n );\n }\n }\n\n logEnvReady = true;\n }\n\n const filePath = path.join(targetDir, `${fileName}.${fileExt}`);\n\n if (type !== 'dump') {\n // do not write dump file any more\n writeFileSync(filePath, fileContent);\n }\n\n if (opts?.generateReport) {\n return writeDumpReport(fileName, fileContent);\n }\n\n return filePath;\n}\n\nexport function getTmpDir(): string | null {\n try {\n const runningPkgInfo = getRunningPkgInfo();\n if (!runningPkgInfo) {\n return null;\n }\n const { name } = runningPkgInfo;\n const tmpPath = path.join(tmpdir(), name);\n mkdirSync(tmpPath, { recursive: true });\n return tmpPath;\n } catch (e) {\n return null;\n }\n}\n\nexport function getTmpFile(fileExtWithoutDot: string): string | null {\n if (ifInBrowser) {\n return null;\n }\n const tmpDir = getTmpDir();\n const filename = `${uuid()}.${fileExtWithoutDot}`;\n return path.join(tmpDir!, filename);\n}\n\nexport function overlapped(container: Rect, target: Rect) {\n // container and the target have some part overlapped\n return (\n container.left < target.left + target.width &&\n container.left + container.width > target.left &&\n container.top < target.top + target.height &&\n container.top + container.height > target.top\n );\n}\n\nexport async function sleep(ms: number) {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport function replacerForPageObject(key: string, value: any) {\n if (value && value.constructor?.name === 'Page') {\n return '[Page object]';\n }\n if (value && value.constructor?.name === 'Browser') {\n return '[Browser object]';\n }\n return value;\n}\n\nexport function stringifyDumpData(data: any, indents?: number) {\n return JSON.stringify(data, replacerForPageObject, indents);\n}\n\ndeclare const __VERSION__: string;\n\nexport function getVersion() {\n return __VERSION__;\n}\n\nfunction debugLog(...message: any[]) {\n const debugMode = getAIConfig(MIDSCENE_DEBUG_MODE);\n if (debugMode) {\n console.log('[Midscene]', ...message);\n }\n}\n\nlet lastReportedRepoUrl = '';\nexport function uploadTestInfoToServer({ testUrl }: { testUrl: string }) {\n let repoUrl = '';\n let userEmail = '';\n\n const extraConfig = getAIConfigInJson(MIDSCENE_OPENAI_INIT_CONFIG_JSON);\n const serverUrl = extraConfig?.REPORT_SERVER_URL;\n\n try {\n repoUrl = execSync('git config --get remote.origin.url').toString().trim();\n userEmail = execSync('git config --get user.email').toString().trim();\n } catch (error) {\n debugLog('Failed to get git info:', error);\n }\n\n // Only upload test info if:\n // 1. Server URL is configured AND\n // 2. Either:\n // - We have a repo URL that's different from last reported one (to avoid duplicate reports)\n // - OR we don't have a repo URL but have a test URL (for non-git environments)\n if (\n serverUrl &&\n ((repoUrl && repoUrl !== lastReportedRepoUrl) || (!repoUrl && testUrl))\n ) {\n debugLog('Uploading test info to server', {\n serverUrl,\n repoUrl,\n testUrl,\n userEmail,\n });\n\n fetch(serverUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n repo_url: repoUrl,\n test_url: testUrl,\n user_email: userEmail,\n }),\n })\n .then((response) => response.json())\n .then((data) => {\n debugLog('Successfully uploaded test info to server:', data);\n })\n .catch((error) =>\n debugLog('Failed to upload test info to server:', error),\n );\n lastReportedRepoUrl = repoUrl;\n }\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"mappings":"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","names":[],"ignoreList":[],"sources":["../../src/utils.ts"],"sourcesContent":["import { execSync } from 'node:child_process';\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport * as path from 'node:path';\nimport { dirname } from 'node:path';\nimport {\n defaultRunDirName,\n getMidsceneRunSubDir,\n logDir,\n} from '@midscene/shared/common';\nimport {\n MIDSCENE_DEBUG_MODE,\n MIDSCENE_OPENAI_INIT_CONFIG_JSON,\n getAIConfig,\n getAIConfigInJson,\n} from '@midscene/shared/env';\nimport { getRunningPkgInfo } from '@midscene/shared/fs';\nimport { assert, getGlobalScope } from '@midscene/shared/utils';\nimport { ifInBrowser, uuid } from '@midscene/shared/utils';\nimport type { Rect, ReportDumpWithAttributes } from './types';\n\nlet logEnvReady = false;\n\nexport const groupedActionDumpFileExt = 'web-dump.json';\n\nexport function getLogDir() {\n return logDir;\n}\n\nlet reportTpl: string | null = null;\nexport function setReportTpl(tpl: string) {\n reportTpl = tpl;\n}\n\nfunction getReportTpl() {\n const globalScope = getGlobalScope();\n if (\n !reportTpl &&\n globalScope &&\n typeof (globalScope as any).get_midscene_report_tpl === 'function'\n ) {\n reportTpl = (globalScope as any).get_midscene_report_tpl();\n return reportTpl;\n }\n\n const __dirname = dirname(__filename);\n if (!reportTpl && !ifInBrowser) {\n const possiblePaths = [\n path.join(__dirname, '../../report/index.html'),\n path.join(__dirname, '../report/index.html'),\n ];\n\n for (const reportPath of possiblePaths) {\n if (existsSync(reportPath)) {\n reportTpl = readFileSync(reportPath, 'utf-8');\n break;\n }\n }\n }\n return reportTpl;\n}\n\nexport function replaceStringWithFirstAppearance(\n str: string,\n target: string,\n replacement: string,\n) {\n const index = str.indexOf(target);\n return str.slice(0, index) + replacement + str.slice(index + target.length);\n}\n\nexport function reportHTMLContent(\n dumpData: string | ReportDumpWithAttributes[],\n): string {\n const tpl = getReportTpl();\n if (!tpl) {\n console.warn('reportTpl is not set, will not write report');\n return '';\n }\n\n let reportContent: string;\n if (\n (Array.isArray(dumpData) && dumpData.length === 0) ||\n typeof dumpData === 'undefined'\n ) {\n reportContent = replaceStringWithFirstAppearance(\n tpl,\n '{{dump}}',\n '<script type=\"midscene_web_dump\" type=\"application/json\"></script>',\n );\n } else if (typeof dumpData === 'string') {\n reportContent = replaceStringWithFirstAppearance(\n tpl,\n '{{dump}}',\n // biome-ignore lint/style/useTemplate: <explanation>\n '<script type=\"midscene_web_dump\" type=\"application/json\">\\n' +\n dumpData +\n '\\n</script>',\n );\n } else {\n const dumps = dumpData.map(({ dumpString, attributes }) => {\n const attributesArr = Object.keys(attributes || {}).map((key) => {\n return `${key}=\"${encodeURIComponent(attributes![key])}\"`;\n });\n return (\n // biome-ignore lint/style/useTemplate: <explanation>\n '<script type=\"midscene_web_dump\" type=\"application/json\" ' +\n attributesArr.join(' ') +\n '>\\n' +\n dumpString +\n '\\n</script>'\n );\n });\n reportContent = replaceStringWithFirstAppearance(\n tpl,\n '{{dump}}',\n dumps.join('\\n'),\n );\n }\n return reportContent;\n}\n\nexport function writeDumpReport(\n fileName: string,\n dumpData: string | ReportDumpWithAttributes[],\n): string | null {\n if (ifInBrowser) {\n console.log('will not write report in browser');\n return null;\n }\n\n const __dirname = dirname(__filename);\n const midscenePkgInfo = getRunningPkgInfo(__dirname);\n if (!midscenePkgInfo) {\n console.warn('midscenePkgInfo not found, will not write report');\n return null;\n }\n\n const reportPath = path.join(\n getMidsceneRunSubDir('report'),\n `${fileName}.html`,\n );\n const reportContent = reportHTMLContent(dumpData);\n if (!reportContent) {\n console.warn('reportContent is empty, will not write report');\n return null;\n }\n writeFileSync(reportPath, reportContent);\n if (process.env.MIDSCENE_DEBUG_LOG_JSON) {\n writeFileSync(\n `${reportPath}.json`,\n typeof dumpData === 'string'\n ? dumpData\n : JSON.stringify(dumpData, null, 2),\n );\n }\n\n return reportPath;\n}\n\nexport function writeLogFile(opts: {\n fileName: string;\n fileExt: string;\n fileContent: string;\n type: 'dump' | 'cache' | 'report' | 'tmp';\n generateReport?: boolean;\n}) {\n if (ifInBrowser) {\n return '/mock/report.html';\n }\n const { fileName, fileExt, fileContent, type = 'dump' } = opts;\n const targetDir = getMidsceneRunSubDir(type);\n // Ensure directory exists\n if (!logEnvReady) {\n assert(targetDir, 'logDir should be set before writing dump file');\n\n // gitIgnore in the parent directory\n const gitIgnorePath = path.join(targetDir, '../../.gitignore');\n const gitPath = path.join(targetDir, '../../.git');\n let gitIgnoreContent = '';\n\n if (existsSync(gitPath)) {\n // if the git path exists, we need to add the log folder to the git ignore file\n if (existsSync(gitIgnorePath)) {\n gitIgnoreContent = readFileSync(gitIgnorePath, 'utf-8');\n }\n\n // ignore the log folder\n if (!gitIgnoreContent.includes(`${defaultRunDirName}/`)) {\n writeFileSync(\n gitIgnorePath,\n `${gitIgnoreContent}\\n# Midscene.js dump files\\n${defaultRunDirName}/dump\\n${defaultRunDirName}/report\\n${defaultRunDirName}/tmp\\n${defaultRunDirName}/log\\n`,\n 'utf-8',\n );\n }\n }\n\n logEnvReady = true;\n }\n\n const filePath = path.join(targetDir, `${fileName}.${fileExt}`);\n\n if (type !== 'dump') {\n // do not write dump file any more\n writeFileSync(filePath, fileContent);\n }\n\n if (opts?.generateReport) {\n return writeDumpReport(fileName, fileContent);\n }\n\n return filePath;\n}\n\nexport function getTmpDir(): string | null {\n try {\n const runningPkgInfo = getRunningPkgInfo();\n if (!runningPkgInfo) {\n return null;\n }\n const { name } = runningPkgInfo;\n const tmpPath = path.join(tmpdir(), name);\n mkdirSync(tmpPath, { recursive: true });\n return tmpPath;\n } catch (e) {\n return null;\n }\n}\n\nexport function getTmpFile(fileExtWithoutDot: string): string | null {\n if (ifInBrowser) {\n return null;\n }\n const tmpDir = getTmpDir();\n const filename = `${uuid()}.${fileExtWithoutDot}`;\n return path.join(tmpDir!, filename);\n}\n\nexport function overlapped(container: Rect, target: Rect) {\n // container and the target have some part overlapped\n return (\n container.left < target.left + target.width &&\n container.left + container.width > target.left &&\n container.top < target.top + target.height &&\n container.top + container.height > target.top\n );\n}\n\nexport async function sleep(ms: number) {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport function replacerForPageObject(key: string, value: any) {\n if (value && value.constructor?.name === 'Page') {\n return '[Page object]';\n }\n if (value && value.constructor?.name === 'Browser') {\n return '[Browser object]';\n }\n return value;\n}\n\nexport function stringifyDumpData(data: any, indents?: number) {\n return JSON.stringify(data, replacerForPageObject, indents);\n}\n\ndeclare const __VERSION__: string;\n\nexport function getVersion() {\n return __VERSION__;\n}\n\nfunction debugLog(...message: any[]) {\n const debugMode = getAIConfig(MIDSCENE_DEBUG_MODE);\n if (debugMode) {\n console.log('[Midscene]', ...message);\n }\n}\n\nlet lastReportedRepoUrl = '';\nexport function uploadTestInfoToServer({ testUrl }: { testUrl: string }) {\n let repoUrl = '';\n let userEmail = '';\n\n const extraConfig = getAIConfigInJson(MIDSCENE_OPENAI_INIT_CONFIG_JSON);\n const serverUrl = extraConfig?.REPORT_SERVER_URL;\n\n try {\n repoUrl = execSync('git config --get remote.origin.url').toString().trim();\n userEmail = execSync('git config --get user.email').toString().trim();\n } catch (error) {\n debugLog('Failed to get git info:', error);\n }\n\n // Only upload test info if:\n // 1. Server URL is configured AND\n // 2. Either:\n // - We have a repo URL that's different from last reported one (to avoid duplicate reports)\n // - OR we don't have a repo URL but have a test URL (for non-git environments)\n if (\n serverUrl &&\n ((repoUrl && repoUrl !== lastReportedRepoUrl) || (!repoUrl && testUrl))\n ) {\n debugLog('Uploading test info to server', {\n serverUrl,\n repoUrl,\n testUrl,\n userEmail,\n });\n\n fetch(serverUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n repo_url: repoUrl,\n test_url: testUrl,\n user_email: userEmail,\n }),\n })\n .then((response) => response.json())\n .then((data) => {\n debugLog('Successfully uploaded test info to server:', data);\n })\n .catch((error) =>\n debugLog('Failed to upload test info to server:', error),\n );\n lastReportedRepoUrl = repoUrl;\n }\n}\n"]}