@midscene/core 0.16.3-beta-20250428131551.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.
@@ -52,43 +52,66 @@ function replaceStringWithFirstAppearance(str, target, replacement) {
52
52
  const index = str.indexOf(target);
53
53
  return str.slice(0, index) + replacement + str.slice(index + target.length);
54
54
  }
55
- function reportHTMLContent(dumpData) {
55
+ function reportHTMLContent(dumpData, reportPath) {
56
56
  const tpl = getReportTpl();
57
57
  if (!tpl) {
58
58
  console.warn("reportTpl is not set, will not write report");
59
59
  return "";
60
60
  }
61
- let reportContent;
61
+ const dumpPlaceholder = "{{dump}}";
62
+ if (!tpl.includes(dumpPlaceholder)) {
63
+ console.warn("Template does not contain {{dump}} placeholder");
64
+ return "";
65
+ }
66
+ const placeholderIndex = tpl.indexOf(dumpPlaceholder);
67
+ const firstPart = tpl.substring(0, placeholderIndex);
68
+ const secondPart = tpl.substring(placeholderIndex + dumpPlaceholder.length);
69
+ const writeToFile = reportPath && !ifInBrowser;
70
+ let resultContent = "";
71
+ const appendOrWrite = (content) => {
72
+ if (writeToFile) {
73
+ writeFileSync(reportPath, `${content}
74
+ `, {
75
+ flag: "a"
76
+ });
77
+ } else {
78
+ resultContent += `${content}
79
+ `;
80
+ }
81
+ };
82
+ if (writeToFile) {
83
+ writeFileSync(reportPath, firstPart, { flag: "w" });
84
+ } else {
85
+ resultContent = firstPart;
86
+ }
62
87
  if (Array.isArray(dumpData) && dumpData.length === 0 || typeof dumpData === "undefined") {
63
- reportContent = replaceStringWithFirstAppearance(
64
- tpl,
65
- "{{dump}}",
66
- '<script type="midscene_web_dump" type="application/json"></script>'
67
- );
88
+ const dumpContent = '<script type="midscene_web_dump" type="application/json"></script>';
89
+ appendOrWrite(dumpContent);
68
90
  } else if (typeof dumpData === "string") {
69
- reportContent = replaceStringWithFirstAppearance(
70
- tpl,
71
- "{{dump}}",
72
- // biome-ignore lint/style/useTemplate: <explanation>
91
+ const dumpContent = (
92
+ // biome-ignore lint/style/useTemplate: <explanation> do not use template string here, will cause bundle error
73
93
  '<script type="midscene_web_dump" type="application/json">\n' + dumpData + "\n</script>"
74
94
  );
95
+ appendOrWrite(dumpContent);
75
96
  } else {
76
- const dumps = dumpData.map(({ dumpString, attributes }) => {
97
+ for (let i = 0; i < dumpData.length; i++) {
98
+ const { dumpString, attributes } = dumpData[i];
77
99
  const attributesArr = Object.keys(attributes || {}).map((key) => {
78
100
  return `${key}="${encodeURIComponent(attributes[key])}"`;
79
101
  });
80
- return (
81
- // biome-ignore lint/style/useTemplate: <explanation>
102
+ const dumpContent = (
103
+ // biome-ignore lint/style/useTemplate: <explanation> do not use template string here, will cause bundle error
82
104
  '<script type="midscene_web_dump" type="application/json" ' + attributesArr.join(" ") + ">\n" + dumpString + "\n</script>"
83
105
  );
84
- });
85
- reportContent = replaceStringWithFirstAppearance(
86
- tpl,
87
- "{{dump}}",
88
- dumps.join("\n")
89
- );
106
+ appendOrWrite(dumpContent);
107
+ }
90
108
  }
91
- return reportContent;
109
+ if (writeToFile) {
110
+ writeFileSync(reportPath, secondPart, { flag: "a" });
111
+ return reportPath;
112
+ }
113
+ resultContent += secondPart;
114
+ return resultContent;
92
115
  }
93
116
  function writeDumpReport(fileName, dumpData) {
94
117
  if (ifInBrowser) {
@@ -105,12 +128,7 @@ function writeDumpReport(fileName, dumpData) {
105
128
  getMidsceneRunSubDir("report"),
106
129
  `${fileName}.html`
107
130
  );
108
- const reportContent = reportHTMLContent(dumpData);
109
- if (!reportContent) {
110
- console.warn("reportContent is empty, will not write report");
111
- return null;
112
- }
113
- writeFileSync(reportPath, reportContent);
131
+ reportHTMLContent(dumpData, reportPath);
114
132
  if (process.env.MIDSCENE_DEBUG_LOG_JSON) {
115
133
  writeFileSync(
116
134
  `${reportPath}.json`,
@@ -200,7 +218,7 @@ function stringifyDumpData(data, indents) {
200
218
  return JSON.stringify(data, replacerForPageObject, indents);
201
219
  }
202
220
  function getVersion() {
203
- return "0.16.3-beta-20250428131551.0";
221
+ return "0.16.3";
204
222
  }
205
223
  function debugLog(...message) {
206
224
  const debugMode = getAIConfig(MIDSCENE_DEBUG_MODE);
@@ -264,4 +282,4 @@ export {
264
282
  uploadTestInfoToServer
265
283
  };
266
284
 
267
- //# sourceMappingURL=chunk-SMFK23VO.js.map
285
+ //# sourceMappingURL=chunk-6KQY52HD.js.map
@@ -0,0 +1 @@
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 reportPath?: string,\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 const dumpPlaceholder = '{{dump}}';\n\n // verify the template contains the placeholder\n if (!tpl.includes(dumpPlaceholder)) {\n console.warn('Template does not contain {{dump}} placeholder');\n return '';\n }\n\n // find the first placeholder position\n const placeholderIndex = tpl.indexOf(dumpPlaceholder);\n\n // split the template into two parts before and after the placeholder\n const firstPart = tpl.substring(0, placeholderIndex);\n const secondPart = tpl.substring(placeholderIndex + dumpPlaceholder.length);\n\n // if reportPath is set, it means we are in write to file mode\n const writeToFile = reportPath && !ifInBrowser;\n let resultContent = '';\n\n // helper function: decide to write to file or append to resultContent\n const appendOrWrite = (content: string): void => {\n if (writeToFile) {\n writeFileSync(reportPath!, `${content}\\n`, {\n flag: 'a',\n });\n } else {\n resultContent += `${content}\\n`;\n }\n };\n\n // if writeToFile is true, write the first part to file, otherwise set the first part to the initial value of resultContent\n if (writeToFile) {\n writeFileSync(reportPath!, firstPart, { flag: 'w' }); // use 'w' flag to overwrite the existing file\n } else {\n resultContent = firstPart;\n }\n\n // generate dump content\n // handle empty data or undefined\n if (\n (Array.isArray(dumpData) && dumpData.length === 0) ||\n typeof dumpData === 'undefined'\n ) {\n const dumpContent =\n '<script type=\"midscene_web_dump\" type=\"application/json\"></script>';\n appendOrWrite(dumpContent);\n }\n // handle string type dumpData\n else if (typeof dumpData === 'string') {\n const dumpContent =\n // biome-ignore lint/style/useTemplate: <explanation> do not use template string here, will cause bundle error\n '<script type=\"midscene_web_dump\" type=\"application/json\">\\n' +\n dumpData +\n '\\n</script>';\n appendOrWrite(dumpContent);\n }\n // handle array type dumpData\n else {\n // for array, handle each item\n for (let i = 0; i < dumpData.length; i++) {\n const { dumpString, attributes } = dumpData[i];\n const attributesArr = Object.keys(attributes || {}).map((key) => {\n return `${key}=\"${encodeURIComponent(attributes![key])}\"`;\n });\n\n const dumpContent =\n // biome-ignore lint/style/useTemplate: <explanation> do not use template string here, will cause bundle error\n '<script type=\"midscene_web_dump\" type=\"application/json\" ' +\n attributesArr.join(' ') +\n '>\\n' +\n dumpString +\n '\\n</script>';\n appendOrWrite(dumpContent);\n }\n }\n\n // add the second part\n if (writeToFile) {\n writeFileSync(reportPath!, secondPart, { flag: 'a' });\n return reportPath!;\n }\n\n resultContent += secondPart;\n return resultContent;\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\n reportHTMLContent(dumpData, reportPath);\n\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"]}
package/dist/es/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getVersion
3
- } from "./chunk-SMFK23VO.js";
3
+ } from "./chunk-6KQY52HD.js";
4
4
  import {
5
5
  AiAssert,
6
6
  AiExtractElementInfo,
@@ -6,7 +6,7 @@ declare const groupedActionDumpFileExt = "web-dump.json";
6
6
  declare function getLogDir(): string;
7
7
  declare function setReportTpl(tpl: string): void;
8
8
  declare function replaceStringWithFirstAppearance(str: string, target: string, replacement: string): string;
9
- declare function reportHTMLContent(dumpData: string | ReportDumpWithAttributes[]): string;
9
+ declare function reportHTMLContent(dumpData: string | ReportDumpWithAttributes[], reportPath?: string): string;
10
10
  declare function writeDumpReport(fileName: string, dumpData: string | ReportDumpWithAttributes[]): string | null;
11
11
  declare function writeLogFile(opts: {
12
12
  fileName: string;
package/dist/es/utils.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  uploadTestInfoToServer,
15
15
  writeDumpReport,
16
16
  writeLogFile
17
- } from "./chunk-SMFK23VO.js";
17
+ } from "./chunk-6KQY52HD.js";
18
18
  export {
19
19
  getLogDir,
20
20
  getTmpDir,
@@ -52,43 +52,66 @@ function replaceStringWithFirstAppearance(str, target, replacement) {
52
52
  const index = str.indexOf(target);
53
53
  return str.slice(0, index) + replacement + str.slice(index + target.length);
54
54
  }
55
- function reportHTMLContent(dumpData) {
55
+ function reportHTMLContent(dumpData, reportPath) {
56
56
  const tpl = getReportTpl();
57
57
  if (!tpl) {
58
58
  console.warn("reportTpl is not set, will not write report");
59
59
  return "";
60
60
  }
61
- let reportContent;
61
+ const dumpPlaceholder = "{{dump}}";
62
+ if (!tpl.includes(dumpPlaceholder)) {
63
+ console.warn("Template does not contain {{dump}} placeholder");
64
+ return "";
65
+ }
66
+ const placeholderIndex = tpl.indexOf(dumpPlaceholder);
67
+ const firstPart = tpl.substring(0, placeholderIndex);
68
+ const secondPart = tpl.substring(placeholderIndex + dumpPlaceholder.length);
69
+ const writeToFile = reportPath && !_utils.ifInBrowser;
70
+ let resultContent = "";
71
+ const appendOrWrite = (content) => {
72
+ if (writeToFile) {
73
+ _fs.writeFileSync.call(void 0, reportPath, `${content}
74
+ `, {
75
+ flag: "a"
76
+ });
77
+ } else {
78
+ resultContent += `${content}
79
+ `;
80
+ }
81
+ };
82
+ if (writeToFile) {
83
+ _fs.writeFileSync.call(void 0, reportPath, firstPart, { flag: "w" });
84
+ } else {
85
+ resultContent = firstPart;
86
+ }
62
87
  if (Array.isArray(dumpData) && dumpData.length === 0 || typeof dumpData === "undefined") {
63
- reportContent = replaceStringWithFirstAppearance(
64
- tpl,
65
- "{{dump}}",
66
- '<script type="midscene_web_dump" type="application/json"></script>'
67
- );
88
+ const dumpContent = '<script type="midscene_web_dump" type="application/json"></script>';
89
+ appendOrWrite(dumpContent);
68
90
  } else if (typeof dumpData === "string") {
69
- reportContent = replaceStringWithFirstAppearance(
70
- tpl,
71
- "{{dump}}",
72
- // biome-ignore lint/style/useTemplate: <explanation>
91
+ const dumpContent = (
92
+ // biome-ignore lint/style/useTemplate: <explanation> do not use template string here, will cause bundle error
73
93
  '<script type="midscene_web_dump" type="application/json">\n' + dumpData + "\n</script>"
74
94
  );
95
+ appendOrWrite(dumpContent);
75
96
  } else {
76
- const dumps = dumpData.map(({ dumpString, attributes }) => {
97
+ for (let i = 0; i < dumpData.length; i++) {
98
+ const { dumpString, attributes } = dumpData[i];
77
99
  const attributesArr = Object.keys(attributes || {}).map((key) => {
78
100
  return `${key}="${encodeURIComponent(attributes[key])}"`;
79
101
  });
80
- return (
81
- // biome-ignore lint/style/useTemplate: <explanation>
102
+ const dumpContent = (
103
+ // biome-ignore lint/style/useTemplate: <explanation> do not use template string here, will cause bundle error
82
104
  '<script type="midscene_web_dump" type="application/json" ' + attributesArr.join(" ") + ">\n" + dumpString + "\n</script>"
83
105
  );
84
- });
85
- reportContent = replaceStringWithFirstAppearance(
86
- tpl,
87
- "{{dump}}",
88
- dumps.join("\n")
89
- );
106
+ appendOrWrite(dumpContent);
107
+ }
90
108
  }
91
- return reportContent;
109
+ if (writeToFile) {
110
+ _fs.writeFileSync.call(void 0, reportPath, secondPart, { flag: "a" });
111
+ return reportPath;
112
+ }
113
+ resultContent += secondPart;
114
+ return resultContent;
92
115
  }
93
116
  function writeDumpReport(fileName, dumpData) {
94
117
  if (_utils.ifInBrowser) {
@@ -105,12 +128,7 @@ function writeDumpReport(fileName, dumpData) {
105
128
  _common.getMidsceneRunSubDir.call(void 0, "report"),
106
129
  `${fileName}.html`
107
130
  );
108
- const reportContent = reportHTMLContent(dumpData);
109
- if (!reportContent) {
110
- console.warn("reportContent is empty, will not write report");
111
- return null;
112
- }
113
- _fs.writeFileSync.call(void 0, reportPath, reportContent);
131
+ reportHTMLContent(dumpData, reportPath);
114
132
  if (process.env.MIDSCENE_DEBUG_LOG_JSON) {
115
133
  _fs.writeFileSync.call(void 0,
116
134
  `${reportPath}.json`,
@@ -200,7 +218,7 @@ function stringifyDumpData(data, indents) {
200
218
  return JSON.stringify(data, replacerForPageObject, indents);
201
219
  }
202
220
  function getVersion() {
203
- return "0.16.3-beta-20250428131551.0";
221
+ return "0.16.3";
204
222
  }
205
223
  function debugLog(...message) {
206
224
  const debugMode = _env.getAIConfig.call(void 0, _env.MIDSCENE_DEBUG_MODE);
@@ -264,4 +282,4 @@ function uploadTestInfoToServer({ testUrl }) {
264
282
 
265
283
  exports.groupedActionDumpFileExt = groupedActionDumpFileExt; exports.getLogDir = getLogDir; exports.setReportTpl = setReportTpl; exports.replaceStringWithFirstAppearance = replaceStringWithFirstAppearance; exports.reportHTMLContent = reportHTMLContent; exports.writeDumpReport = writeDumpReport; exports.writeLogFile = writeLogFile; exports.getTmpDir = getTmpDir; exports.getTmpFile = getTmpFile; exports.overlapped = overlapped; exports.sleep = sleep; exports.replacerForPageObject = replacerForPageObject; exports.stringifyDumpData = stringifyDumpData; exports.getVersion = getVersion; exports.uploadTestInfoToServer = uploadTestInfoToServer;
266
284
 
267
- //# sourceMappingURL=chunk-SMFK23VO.js.map
285
+ //# sourceMappingURL=chunk-6KQY52HD.js.map
@@ -0,0 +1 @@
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 reportPath?: string,\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 const dumpPlaceholder = '{{dump}}';\n\n // verify the template contains the placeholder\n if (!tpl.includes(dumpPlaceholder)) {\n console.warn('Template does not contain {{dump}} placeholder');\n return '';\n }\n\n // find the first placeholder position\n const placeholderIndex = tpl.indexOf(dumpPlaceholder);\n\n // split the template into two parts before and after the placeholder\n const firstPart = tpl.substring(0, placeholderIndex);\n const secondPart = tpl.substring(placeholderIndex + dumpPlaceholder.length);\n\n // if reportPath is set, it means we are in write to file mode\n const writeToFile = reportPath && !ifInBrowser;\n let resultContent = '';\n\n // helper function: decide to write to file or append to resultContent\n const appendOrWrite = (content: string): void => {\n if (writeToFile) {\n writeFileSync(reportPath!, `${content}\\n`, {\n flag: 'a',\n });\n } else {\n resultContent += `${content}\\n`;\n }\n };\n\n // if writeToFile is true, write the first part to file, otherwise set the first part to the initial value of resultContent\n if (writeToFile) {\n writeFileSync(reportPath!, firstPart, { flag: 'w' }); // use 'w' flag to overwrite the existing file\n } else {\n resultContent = firstPart;\n }\n\n // generate dump content\n // handle empty data or undefined\n if (\n (Array.isArray(dumpData) && dumpData.length === 0) ||\n typeof dumpData === 'undefined'\n ) {\n const dumpContent =\n '<script type=\"midscene_web_dump\" type=\"application/json\"></script>';\n appendOrWrite(dumpContent);\n }\n // handle string type dumpData\n else if (typeof dumpData === 'string') {\n const dumpContent =\n // biome-ignore lint/style/useTemplate: <explanation> do not use template string here, will cause bundle error\n '<script type=\"midscene_web_dump\" type=\"application/json\">\\n' +\n dumpData +\n '\\n</script>';\n appendOrWrite(dumpContent);\n }\n // handle array type dumpData\n else {\n // for array, handle each item\n for (let i = 0; i < dumpData.length; i++) {\n const { dumpString, attributes } = dumpData[i];\n const attributesArr = Object.keys(attributes || {}).map((key) => {\n return `${key}=\"${encodeURIComponent(attributes![key])}\"`;\n });\n\n const dumpContent =\n // biome-ignore lint/style/useTemplate: <explanation> do not use template string here, will cause bundle error\n '<script type=\"midscene_web_dump\" type=\"application/json\" ' +\n attributesArr.join(' ') +\n '>\\n' +\n dumpString +\n '\\n</script>';\n appendOrWrite(dumpContent);\n }\n }\n\n // add the second part\n if (writeToFile) {\n writeFileSync(reportPath!, secondPart, { flag: 'a' });\n return reportPath!;\n }\n\n resultContent += secondPart;\n return resultContent;\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\n reportHTMLContent(dumpData, reportPath);\n\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"]}
package/dist/lib/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
2
 
3
- var _chunkSMFK23VOjs = require('./chunk-SMFK23VO.js');
3
+ var _chunk6KQY52HDjs = require('./chunk-6KQY52HD.js');
4
4
 
5
5
 
6
6
 
@@ -156,7 +156,7 @@ ${_optionalChain([this, 'access', _7 => _7.latestErrorTask, 'call', _8 => _8(),
156
156
  }
157
157
  dump() {
158
158
  const dumpData = {
159
- sdkVersion: _chunkSMFK23VOjs.getVersion.call(void 0, ),
159
+ sdkVersion: _chunk6KQY52HDjs.getVersion.call(void 0, ),
160
160
  model_name: _env.getAIConfig.call(void 0, _env.MIDSCENE_MODEL_NAME) || "",
161
161
  logTime: Date.now(),
162
162
  name: this.name,
@@ -194,7 +194,7 @@ function emitInsightDump(data, dumpSubscriber) {
194
194
  }
195
195
  }
196
196
  const baseData = {
197
- sdkVersion: _chunkSMFK23VOjs.getVersion.call(void 0, ),
197
+ sdkVersion: _chunk6KQY52HDjs.getVersion.call(void 0, ),
198
198
  logTime: Date.now(),
199
199
  model_name: _env.getAIConfig.call(void 0, _env.MIDSCENE_MODEL_NAME) || "",
200
200
  model_description: modelDescription
@@ -455,6 +455,6 @@ var src_default = Insight;
455
455
 
456
456
 
457
457
 
458
- exports.AiAssert = _chunkQOGJ5YDSjs.AiAssert; exports.AiLocateElement = _chunkQOGJ5YDSjs.AiLocateElement; exports.Executor = Executor; exports.Insight = Insight; exports.MIDSCENE_MODEL_NAME = _env.MIDSCENE_MODEL_NAME; exports.default = src_default; exports.describeUserPage = _chunkQOGJ5YDSjs.describeUserPage; exports.getAIConfig = _env.getAIConfig; exports.getVersion = _chunkSMFK23VOjs.getVersion; exports.plan = _chunkQOGJ5YDSjs.plan;
458
+ exports.AiAssert = _chunkQOGJ5YDSjs.AiAssert; exports.AiLocateElement = _chunkQOGJ5YDSjs.AiLocateElement; exports.Executor = Executor; exports.Insight = Insight; exports.MIDSCENE_MODEL_NAME = _env.MIDSCENE_MODEL_NAME; exports.default = src_default; exports.describeUserPage = _chunkQOGJ5YDSjs.describeUserPage; exports.getAIConfig = _env.getAIConfig; exports.getVersion = _chunk6KQY52HDjs.getVersion; exports.plan = _chunkQOGJ5YDSjs.plan;
459
459
 
460
460
  //# sourceMappingURL=index.js.map
@@ -6,7 +6,7 @@ declare const groupedActionDumpFileExt = "web-dump.json";
6
6
  declare function getLogDir(): string;
7
7
  declare function setReportTpl(tpl: string): void;
8
8
  declare function replaceStringWithFirstAppearance(str: string, target: string, replacement: string): string;
9
- declare function reportHTMLContent(dumpData: string | ReportDumpWithAttributes[]): string;
9
+ declare function reportHTMLContent(dumpData: string | ReportDumpWithAttributes[], reportPath?: string): string;
10
10
  declare function writeDumpReport(fileName: string, dumpData: string | ReportDumpWithAttributes[]): string | null;
11
11
  declare function writeLogFile(opts: {
12
12
  fileName: string;
package/dist/lib/utils.js CHANGED
@@ -14,7 +14,7 @@
14
14
 
15
15
 
16
16
 
17
- var _chunkSMFK23VOjs = require('./chunk-SMFK23VO.js');
17
+ var _chunk6KQY52HDjs = require('./chunk-6KQY52HD.js');
18
18
 
19
19
 
20
20
 
@@ -31,4 +31,4 @@ var _chunkSMFK23VOjs = require('./chunk-SMFK23VO.js');
31
31
 
32
32
 
33
33
 
34
- exports.getLogDir = _chunkSMFK23VOjs.getLogDir; exports.getTmpDir = _chunkSMFK23VOjs.getTmpDir; exports.getTmpFile = _chunkSMFK23VOjs.getTmpFile; exports.getVersion = _chunkSMFK23VOjs.getVersion; exports.groupedActionDumpFileExt = _chunkSMFK23VOjs.groupedActionDumpFileExt; exports.overlapped = _chunkSMFK23VOjs.overlapped; exports.replaceStringWithFirstAppearance = _chunkSMFK23VOjs.replaceStringWithFirstAppearance; exports.replacerForPageObject = _chunkSMFK23VOjs.replacerForPageObject; exports.reportHTMLContent = _chunkSMFK23VOjs.reportHTMLContent; exports.setReportTpl = _chunkSMFK23VOjs.setReportTpl; exports.sleep = _chunkSMFK23VOjs.sleep; exports.stringifyDumpData = _chunkSMFK23VOjs.stringifyDumpData; exports.uploadTestInfoToServer = _chunkSMFK23VOjs.uploadTestInfoToServer; exports.writeDumpReport = _chunkSMFK23VOjs.writeDumpReport; exports.writeLogFile = _chunkSMFK23VOjs.writeLogFile;
34
+ exports.getLogDir = _chunk6KQY52HDjs.getLogDir; exports.getTmpDir = _chunk6KQY52HDjs.getTmpDir; exports.getTmpFile = _chunk6KQY52HDjs.getTmpFile; exports.getVersion = _chunk6KQY52HDjs.getVersion; exports.groupedActionDumpFileExt = _chunk6KQY52HDjs.groupedActionDumpFileExt; exports.overlapped = _chunk6KQY52HDjs.overlapped; exports.replaceStringWithFirstAppearance = _chunk6KQY52HDjs.replaceStringWithFirstAppearance; exports.replacerForPageObject = _chunk6KQY52HDjs.replacerForPageObject; exports.reportHTMLContent = _chunk6KQY52HDjs.reportHTMLContent; exports.setReportTpl = _chunk6KQY52HDjs.setReportTpl; exports.sleep = _chunk6KQY52HDjs.sleep; exports.stringifyDumpData = _chunk6KQY52HDjs.stringifyDumpData; exports.uploadTestInfoToServer = _chunk6KQY52HDjs.uploadTestInfoToServer; exports.writeDumpReport = _chunk6KQY52HDjs.writeDumpReport; exports.writeLogFile = _chunk6KQY52HDjs.writeLogFile;
@@ -6,7 +6,7 @@ declare const groupedActionDumpFileExt = "web-dump.json";
6
6
  declare function getLogDir(): string;
7
7
  declare function setReportTpl(tpl: string): void;
8
8
  declare function replaceStringWithFirstAppearance(str: string, target: string, replacement: string): string;
9
- declare function reportHTMLContent(dumpData: string | ReportDumpWithAttributes[]): string;
9
+ declare function reportHTMLContent(dumpData: string | ReportDumpWithAttributes[], reportPath?: string): string;
10
10
  declare function writeDumpReport(fileName: string, dumpData: string | ReportDumpWithAttributes[]): string | null;
11
11
  declare function writeLogFile(opts: {
12
12
  fileName: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@midscene/core",
3
3
  "description": "Automate browser actions, extract data, and perform assertions using AI. It offers JavaScript SDK, Chrome extension, and support for scripting in YAML. See https://midscenejs.com/ for details.",
4
- "version": "0.16.3-beta-20250428131551.0",
4
+ "version": "0.16.3",
5
5
  "repository": "https://github.com/web-infra-dev/midscene",
6
6
  "homepage": "https://midscenejs.com/",
7
7
  "jsnext:source": "./src/index.ts",
@@ -44,7 +44,7 @@
44
44
  "dirty-json": "0.9.2",
45
45
  "dotenv": "16.4.5",
46
46
  "langsmith": "0.3.7",
47
- "@midscene/shared": "0.16.3-beta-20250428131551.0"
47
+ "@midscene/shared": "0.16.3"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@modern-js/module-tools": "2.60.6",