@xiuchang-midscene/shared 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (382) hide show
  1. package/README.md +9 -0
  2. package/dist/es/baseDB.mjs +109 -0
  3. package/dist/es/baseDB.mjs.bak +109 -0
  4. package/dist/es/build/copy-static.mjs +31 -0
  5. package/dist/es/build/copy-static.mjs.bak +31 -0
  6. package/dist/es/build/rspack-config.mjs +4 -0
  7. package/dist/es/build/rspack-config.mjs.bak +4 -0
  8. package/dist/es/cli/cli-runner.mjs +140 -0
  9. package/dist/es/cli/cli-runner.mjs.bak +140 -0
  10. package/dist/es/cli/index.mjs +2 -0
  11. package/dist/es/cli/index.mjs.bak +2 -0
  12. package/dist/es/common.mjs +37 -0
  13. package/dist/es/common.mjs.bak +37 -0
  14. package/dist/es/constants/example-code.mjs +223 -0
  15. package/dist/es/constants/example-code.mjs.bak +223 -0
  16. package/dist/es/constants/index.mjs +23 -0
  17. package/dist/es/constants/index.mjs.bak +23 -0
  18. package/dist/es/env/basic.mjs +6 -0
  19. package/dist/es/env/basic.mjs.bak +6 -0
  20. package/dist/es/env/constants.mjs +70 -0
  21. package/dist/es/env/constants.mjs.bak +70 -0
  22. package/dist/es/env/global-config-manager.mjs +94 -0
  23. package/dist/es/env/global-config-manager.mjs.bak +94 -0
  24. package/dist/es/env/helper.mjs +43 -0
  25. package/dist/es/env/helper.mjs.bak +43 -0
  26. package/dist/es/env/index.mjs +5 -0
  27. package/dist/es/env/index.mjs.bak +5 -0
  28. package/dist/es/env/init-debug.mjs +18 -0
  29. package/dist/es/env/init-debug.mjs.bak +18 -0
  30. package/dist/es/env/model-config-manager.mjs +79 -0
  31. package/dist/es/env/model-config-manager.mjs.bak +79 -0
  32. package/dist/es/env/parse-model-config.mjs +132 -0
  33. package/dist/es/env/parse-model-config.mjs.bak +132 -0
  34. package/dist/es/env/types.mjs +220 -0
  35. package/dist/es/env/types.mjs.bak +220 -0
  36. package/dist/es/env/utils.mjs +26 -0
  37. package/dist/es/env/utils.mjs.bak +26 -0
  38. package/dist/es/extractor/constants.mjs +2 -0
  39. package/dist/es/extractor/constants.mjs.bak +2 -0
  40. package/dist/es/extractor/debug.mjs +6 -0
  41. package/dist/es/extractor/debug.mjs.bak +6 -0
  42. package/dist/es/extractor/dom-util.mjs +92 -0
  43. package/dist/es/extractor/dom-util.mjs.bak +92 -0
  44. package/dist/es/extractor/index.mjs +5 -0
  45. package/dist/es/extractor/index.mjs.bak +5 -0
  46. package/dist/es/extractor/locator.mjs +250 -0
  47. package/dist/es/extractor/locator.mjs.bak +250 -0
  48. package/dist/es/extractor/tree.mjs +78 -0
  49. package/dist/es/extractor/tree.mjs.bak +78 -0
  50. package/dist/es/extractor/util.mjs +245 -0
  51. package/dist/es/extractor/util.mjs.bak +245 -0
  52. package/dist/es/extractor/web-extractor.mjs +303 -0
  53. package/dist/es/extractor/web-extractor.mjs.bak +303 -0
  54. package/dist/es/img/box-select.mjs +824 -0
  55. package/dist/es/img/box-select.mjs.bak +824 -0
  56. package/dist/es/img/canvas-fallback.mjs +238 -0
  57. package/dist/es/img/canvas-fallback.mjs.bak +238 -0
  58. package/dist/es/img/get-photon.mjs +45 -0
  59. package/dist/es/img/get-photon.mjs.bak +45 -0
  60. package/dist/es/img/get-sharp.mjs +11 -0
  61. package/dist/es/img/get-sharp.mjs.bak +11 -0
  62. package/dist/es/img/index.mjs +4 -0
  63. package/dist/es/img/index.mjs.bak +4 -0
  64. package/dist/es/img/info.mjs +29 -0
  65. package/dist/es/img/info.mjs.bak +29 -0
  66. package/dist/es/img/transform.mjs +295 -0
  67. package/dist/es/img/transform.mjs.bak +295 -0
  68. package/dist/es/index.mjs +4 -0
  69. package/dist/es/index.mjs.bak +4 -0
  70. package/dist/es/logger.mjs +64 -0
  71. package/dist/es/logger.mjs.bak +64 -0
  72. package/dist/es/mcp/base-server.mjs +281 -0
  73. package/dist/es/mcp/base-server.mjs.bak +281 -0
  74. package/dist/es/mcp/base-tools.mjs +91 -0
  75. package/dist/es/mcp/base-tools.mjs.bak +91 -0
  76. package/dist/es/mcp/chrome-path.mjs +35 -0
  77. package/dist/es/mcp/chrome-path.mjs.bak +35 -0
  78. package/dist/es/mcp/index.mjs +7 -0
  79. package/dist/es/mcp/index.mjs.bak +7 -0
  80. package/dist/es/mcp/inject-report-html-plugin.mjs +53 -0
  81. package/dist/es/mcp/inject-report-html-plugin.mjs.bak +53 -0
  82. package/dist/es/mcp/launcher-helper.mjs +52 -0
  83. package/dist/es/mcp/launcher-helper.mjs.bak +52 -0
  84. package/dist/es/mcp/tool-generator.mjs +297 -0
  85. package/dist/es/mcp/tool-generator.mjs.bak +297 -0
  86. package/dist/es/mcp/types.mjs +3 -0
  87. package/dist/es/mcp/types.mjs.bak +3 -0
  88. package/dist/es/node/fs.mjs +44 -0
  89. package/dist/es/node/fs.mjs.bak +44 -0
  90. package/dist/es/node/index.mjs +2 -0
  91. package/dist/es/node/index.mjs.bak +2 -0
  92. package/dist/es/node/port.mjs +24 -0
  93. package/dist/es/node/port.mjs.bak +24 -0
  94. package/dist/es/oss/demo.mjs +30 -0
  95. package/dist/es/oss/demo.mjs.bak +30 -0
  96. package/dist/es/oss/index.mjs +90 -0
  97. package/dist/es/oss/index.mjs.bak +90 -0
  98. package/dist/es/polyfills/async-hooks.mjs +2 -0
  99. package/dist/es/polyfills/async-hooks.mjs.bak +2 -0
  100. package/dist/es/polyfills/index.mjs +1 -0
  101. package/dist/es/polyfills/index.mjs.bak +1 -0
  102. package/dist/es/types/index.mjs +3 -0
  103. package/dist/es/types/index.mjs.bak +3 -0
  104. package/dist/es/us-keyboard-layout.mjs +1414 -0
  105. package/dist/es/us-keyboard-layout.mjs.LICENSE.txt +5 -0
  106. package/dist/es/us-keyboard-layout.mjs.bak +1414 -0
  107. package/dist/es/utils.mjs +72 -0
  108. package/dist/es/utils.mjs.bak +72 -0
  109. package/dist/es/zod-schema-utils.mjs +54 -0
  110. package/dist/es/zod-schema-utils.mjs.bak +54 -0
  111. package/dist/lib/baseDB.js +149 -0
  112. package/dist/lib/baseDB.js.bak +149 -0
  113. package/dist/lib/build/copy-static.js +79 -0
  114. package/dist/lib/build/copy-static.js.bak +79 -0
  115. package/dist/lib/build/rspack-config.js +38 -0
  116. package/dist/lib/build/rspack-config.js.bak +38 -0
  117. package/dist/lib/cli/cli-runner.js +196 -0
  118. package/dist/lib/cli/cli-runner.js.bak +196 -0
  119. package/dist/lib/cli/index.js +48 -0
  120. package/dist/lib/cli/index.js.bak +48 -0
  121. package/dist/lib/common.js +93 -0
  122. package/dist/lib/common.js.bak +93 -0
  123. package/dist/lib/constants/example-code.js +260 -0
  124. package/dist/lib/constants/example-code.js.bak +260 -0
  125. package/dist/lib/constants/index.js +96 -0
  126. package/dist/lib/constants/index.js.bak +96 -0
  127. package/dist/lib/env/basic.js +40 -0
  128. package/dist/lib/env/basic.js.bak +40 -0
  129. package/dist/lib/env/constants.js +113 -0
  130. package/dist/lib/env/constants.js.bak +113 -0
  131. package/dist/lib/env/global-config-manager.js +128 -0
  132. package/dist/lib/env/global-config-manager.js.bak +128 -0
  133. package/dist/lib/env/helper.js +80 -0
  134. package/dist/lib/env/helper.js.bak +80 -0
  135. package/dist/lib/env/index.js +90 -0
  136. package/dist/lib/env/index.js.bak +90 -0
  137. package/dist/lib/env/init-debug.js +52 -0
  138. package/dist/lib/env/init-debug.js.bak +52 -0
  139. package/dist/lib/env/model-config-manager.js +113 -0
  140. package/dist/lib/env/model-config-manager.js.bak +113 -0
  141. package/dist/lib/env/parse-model-config.js +178 -0
  142. package/dist/lib/env/parse-model-config.js.bak +178 -0
  143. package/dist/lib/env/types.js +554 -0
  144. package/dist/lib/env/types.js.bak +554 -0
  145. package/dist/lib/env/utils.js +72 -0
  146. package/dist/lib/env/utils.js.bak +72 -0
  147. package/dist/lib/extractor/constants.js +42 -0
  148. package/dist/lib/extractor/constants.js.bak +42 -0
  149. package/dist/lib/extractor/debug.js +12 -0
  150. package/dist/lib/extractor/debug.js.bak +12 -0
  151. package/dist/lib/extractor/dom-util.js +153 -0
  152. package/dist/lib/extractor/dom-util.js.bak +153 -0
  153. package/dist/lib/extractor/index.js +81 -0
  154. package/dist/lib/extractor/index.js.bak +81 -0
  155. package/dist/lib/extractor/locator.js +296 -0
  156. package/dist/lib/extractor/locator.js.bak +296 -0
  157. package/dist/lib/extractor/tree.js +124 -0
  158. package/dist/lib/extractor/tree.js.bak +124 -0
  159. package/dist/lib/extractor/util.js +336 -0
  160. package/dist/lib/extractor/util.js.bak +336 -0
  161. package/dist/lib/extractor/web-extractor.js +349 -0
  162. package/dist/lib/extractor/web-extractor.js.bak +349 -0
  163. package/dist/lib/img/box-select.js +875 -0
  164. package/dist/lib/img/box-select.js.bak +875 -0
  165. package/dist/lib/img/canvas-fallback.js +305 -0
  166. package/dist/lib/img/canvas-fallback.js.bak +305 -0
  167. package/dist/lib/img/get-photon.js +82 -0
  168. package/dist/lib/img/get-photon.js.bak +82 -0
  169. package/dist/lib/img/get-sharp.js +45 -0
  170. package/dist/lib/img/get-sharp.js.bak +45 -0
  171. package/dist/lib/img/index.js +95 -0
  172. package/dist/lib/img/index.js.bak +95 -0
  173. package/dist/lib/img/info.js +83 -0
  174. package/dist/lib/img/info.js.bak +83 -0
  175. package/dist/lib/img/transform.js +387 -0
  176. package/dist/lib/img/transform.js.bak +387 -0
  177. package/dist/lib/index.js +47 -0
  178. package/dist/lib/index.js.bak +47 -0
  179. package/dist/lib/logger.js +114 -0
  180. package/dist/lib/logger.js.bak +114 -0
  181. package/dist/lib/mcp/base-server.js +331 -0
  182. package/dist/lib/mcp/base-server.js.bak +331 -0
  183. package/dist/lib/mcp/base-tools.js +125 -0
  184. package/dist/lib/mcp/base-tools.js.bak +125 -0
  185. package/dist/lib/mcp/chrome-path.js +72 -0
  186. package/dist/lib/mcp/chrome-path.js.bak +72 -0
  187. package/dist/lib/mcp/index.js +100 -0
  188. package/dist/lib/mcp/index.js.bak +100 -0
  189. package/dist/lib/mcp/inject-report-html-plugin.js +98 -0
  190. package/dist/lib/mcp/inject-report-html-plugin.js.bak +98 -0
  191. package/dist/lib/mcp/launcher-helper.js +86 -0
  192. package/dist/lib/mcp/launcher-helper.js.bak +86 -0
  193. package/dist/lib/mcp/tool-generator.js +334 -0
  194. package/dist/lib/mcp/tool-generator.js.bak +334 -0
  195. package/dist/lib/mcp/types.js +40 -0
  196. package/dist/lib/mcp/types.js.bak +40 -0
  197. package/dist/lib/node/fs.js +97 -0
  198. package/dist/lib/node/fs.js.bak +97 -0
  199. package/dist/lib/node/index.js +65 -0
  200. package/dist/lib/node/index.js.bak +65 -0
  201. package/dist/lib/node/port.js +61 -0
  202. package/dist/lib/node/port.js.bak +61 -0
  203. package/dist/lib/oss/demo.js +36 -0
  204. package/dist/lib/oss/demo.js.bak +36 -0
  205. package/dist/lib/oss/index.js +138 -0
  206. package/dist/lib/oss/index.js.bak +138 -0
  207. package/dist/lib/polyfills/async-hooks.js +36 -0
  208. package/dist/lib/polyfills/async-hooks.js.bak +36 -0
  209. package/dist/lib/polyfills/index.js +58 -0
  210. package/dist/lib/polyfills/index.js.bak +58 -0
  211. package/dist/lib/types/index.js +37 -0
  212. package/dist/lib/types/index.js.bak +37 -0
  213. package/dist/lib/us-keyboard-layout.js +1457 -0
  214. package/dist/lib/us-keyboard-layout.js.LICENSE.txt +5 -0
  215. package/dist/lib/us-keyboard-layout.js.bak +1457 -0
  216. package/dist/lib/utils.js +148 -0
  217. package/dist/lib/utils.js.bak +148 -0
  218. package/dist/lib/zod-schema-utils.js +97 -0
  219. package/dist/lib/zod-schema-utils.js.bak +97 -0
  220. package/dist/types/baseDB.d.ts +25 -0
  221. package/dist/types/baseDB.d.ts.bak +25 -0
  222. package/dist/types/build/copy-static.d.ts +31 -0
  223. package/dist/types/build/copy-static.d.ts.bak +31 -0
  224. package/dist/types/build/rspack-config.d.ts +8 -0
  225. package/dist/types/build/rspack-config.d.ts.bak +8 -0
  226. package/dist/types/cli/cli-runner.d.ts +14 -0
  227. package/dist/types/cli/cli-runner.d.ts.bak +14 -0
  228. package/dist/types/cli/index.d.ts +2 -0
  229. package/dist/types/cli/index.d.ts.bak +2 -0
  230. package/dist/types/common.d.ts +12 -0
  231. package/dist/types/common.d.ts.bak +12 -0
  232. package/dist/types/constants/example-code.d.ts +2 -0
  233. package/dist/types/constants/example-code.d.ts.bak +2 -0
  234. package/dist/types/constants/index.d.ts +21 -0
  235. package/dist/types/constants/index.d.ts.bak +21 -0
  236. package/dist/types/env/basic.d.ts +6 -0
  237. package/dist/types/env/basic.d.ts.bak +6 -0
  238. package/dist/types/env/constants.d.ts +40 -0
  239. package/dist/types/env/constants.d.ts.bak +40 -0
  240. package/dist/types/env/global-config-manager.d.ts +32 -0
  241. package/dist/types/env/global-config-manager.d.ts.bak +32 -0
  242. package/dist/types/env/helper.d.ts +4 -0
  243. package/dist/types/env/helper.d.ts.bak +4 -0
  244. package/dist/types/env/index.d.ts +4 -0
  245. package/dist/types/env/index.d.ts.bak +4 -0
  246. package/dist/types/env/init-debug.d.ts +1 -0
  247. package/dist/types/env/init-debug.d.ts.bak +1 -0
  248. package/dist/types/env/model-config-manager.d.ts +25 -0
  249. package/dist/types/env/model-config-manager.d.ts.bak +25 -0
  250. package/dist/types/env/parse-model-config.d.ts +31 -0
  251. package/dist/types/env/parse-model-config.d.ts.bak +31 -0
  252. package/dist/types/env/types.d.ts +318 -0
  253. package/dist/types/env/types.d.ts.bak +318 -0
  254. package/dist/types/env/utils.d.ts +38 -0
  255. package/dist/types/env/utils.d.ts.bak +38 -0
  256. package/dist/types/extractor/constants.d.ts +1 -0
  257. package/dist/types/extractor/constants.d.ts.bak +1 -0
  258. package/dist/types/extractor/debug.d.ts +1 -0
  259. package/dist/types/extractor/debug.d.ts.bak +1 -0
  260. package/dist/types/extractor/dom-util.d.ts +56 -0
  261. package/dist/types/extractor/dom-util.d.ts.bak +56 -0
  262. package/dist/types/extractor/index.d.ts +32 -0
  263. package/dist/types/extractor/index.d.ts.bak +32 -0
  264. package/dist/types/extractor/locator.d.ts +9 -0
  265. package/dist/types/extractor/locator.d.ts.bak +9 -0
  266. package/dist/types/extractor/tree.d.ts +6 -0
  267. package/dist/types/extractor/tree.d.ts.bak +6 -0
  268. package/dist/types/extractor/util.d.ts +47 -0
  269. package/dist/types/extractor/util.d.ts.bak +47 -0
  270. package/dist/types/extractor/web-extractor.d.ts +19 -0
  271. package/dist/types/extractor/web-extractor.d.ts.bak +19 -0
  272. package/dist/types/img/box-select.d.ts +26 -0
  273. package/dist/types/img/box-select.d.ts.bak +26 -0
  274. package/dist/types/img/canvas-fallback.d.ts +105 -0
  275. package/dist/types/img/canvas-fallback.d.ts.bak +105 -0
  276. package/dist/types/img/get-photon.d.ts +19 -0
  277. package/dist/types/img/get-photon.d.ts.bak +19 -0
  278. package/dist/types/img/get-sharp.d.ts +3 -0
  279. package/dist/types/img/get-sharp.d.ts.bak +3 -0
  280. package/dist/types/img/index.d.ts +3 -0
  281. package/dist/types/img/index.d.ts.bak +3 -0
  282. package/dist/types/img/info.d.ts +29 -0
  283. package/dist/types/img/info.d.ts.bak +29 -0
  284. package/dist/types/img/transform.d.ts +107 -0
  285. package/dist/types/img/transform.d.ts.bak +107 -0
  286. package/dist/types/index.d.ts +4 -0
  287. package/dist/types/index.d.ts.bak +4 -0
  288. package/dist/types/logger.d.ts +5 -0
  289. package/dist/types/logger.d.ts.bak +5 -0
  290. package/dist/types/mcp/base-server.d.ts +93 -0
  291. package/dist/types/mcp/base-server.d.ts.bak +93 -0
  292. package/dist/types/mcp/base-tools.d.ts +79 -0
  293. package/dist/types/mcp/base-tools.d.ts.bak +79 -0
  294. package/dist/types/mcp/chrome-path.d.ts +2 -0
  295. package/dist/types/mcp/chrome-path.d.ts.bak +2 -0
  296. package/dist/types/mcp/index.d.ts +7 -0
  297. package/dist/types/mcp/index.d.ts.bak +7 -0
  298. package/dist/types/mcp/inject-report-html-plugin.d.ts +18 -0
  299. package/dist/types/mcp/inject-report-html-plugin.d.ts.bak +18 -0
  300. package/dist/types/mcp/launcher-helper.d.ts +94 -0
  301. package/dist/types/mcp/launcher-helper.d.ts.bak +94 -0
  302. package/dist/types/mcp/tool-generator.d.ts +10 -0
  303. package/dist/types/mcp/tool-generator.d.ts.bak +10 -0
  304. package/dist/types/mcp/types.d.ts +103 -0
  305. package/dist/types/mcp/types.d.ts.bak +103 -0
  306. package/dist/types/node/fs.d.ts +15 -0
  307. package/dist/types/node/fs.d.ts.bak +15 -0
  308. package/dist/types/node/index.d.ts +2 -0
  309. package/dist/types/node/index.d.ts.bak +2 -0
  310. package/dist/types/node/port.d.ts +8 -0
  311. package/dist/types/node/port.d.ts.bak +8 -0
  312. package/dist/types/oss/demo.d.ts +1 -0
  313. package/dist/types/oss/demo.d.ts.bak +1 -0
  314. package/dist/types/oss/index.d.ts +34 -0
  315. package/dist/types/oss/index.d.ts.bak +34 -0
  316. package/dist/types/polyfills/async-hooks.d.ts +6 -0
  317. package/dist/types/polyfills/async-hooks.d.ts.bak +6 -0
  318. package/dist/types/polyfills/index.d.ts +4 -0
  319. package/dist/types/polyfills/index.d.ts.bak +4 -0
  320. package/dist/types/types/index.d.ts +34 -0
  321. package/dist/types/types/index.d.ts.bak +34 -0
  322. package/dist/types/us-keyboard-layout.d.ts +32 -0
  323. package/dist/types/us-keyboard-layout.d.ts.bak +32 -0
  324. package/dist/types/utils.d.ts +34 -0
  325. package/dist/types/utils.d.ts.bak +34 -0
  326. package/dist/types/zod-schema-utils.d.ts +23 -0
  327. package/dist/types/zod-schema-utils.d.ts.bak +23 -0
  328. package/package.json +132 -0
  329. package/src/baseDB.ts +158 -0
  330. package/src/build/copy-static.ts +68 -0
  331. package/src/build/rspack-config.ts +12 -0
  332. package/src/cli/cli-runner.ts +224 -0
  333. package/src/cli/index.ts +8 -0
  334. package/src/common.ts +67 -0
  335. package/src/constants/example-code.ts +223 -0
  336. package/src/constants/index.ts +29 -0
  337. package/src/env/basic.ts +12 -0
  338. package/src/env/constants.ts +234 -0
  339. package/src/env/global-config-manager.ts +191 -0
  340. package/src/env/helper.ts +58 -0
  341. package/src/env/index.ts +4 -0
  342. package/src/env/init-debug.ts +34 -0
  343. package/src/env/model-config-manager.ts +149 -0
  344. package/src/env/parse-model-config.ts +294 -0
  345. package/src/env/types.ts +547 -0
  346. package/src/env/utils.ts +89 -0
  347. package/src/extractor/constants.ts +5 -0
  348. package/src/extractor/debug.ts +10 -0
  349. package/src/extractor/dom-util.ts +226 -0
  350. package/src/extractor/index.ts +48 -0
  351. package/src/extractor/locator.ts +469 -0
  352. package/src/extractor/tree.ts +179 -0
  353. package/src/extractor/util.ts +482 -0
  354. package/src/extractor/web-extractor.ts +481 -0
  355. package/src/img/box-select.ts +588 -0
  356. package/src/img/canvas-fallback.ts +393 -0
  357. package/src/img/get-photon.ts +108 -0
  358. package/src/img/get-sharp.ts +18 -0
  359. package/src/img/index.ts +26 -0
  360. package/src/img/info.ts +75 -0
  361. package/src/img/transform.ts +594 -0
  362. package/src/index.ts +8 -0
  363. package/src/logger.ts +96 -0
  364. package/src/mcp/base-server.ts +502 -0
  365. package/src/mcp/base-tools.ts +185 -0
  366. package/src/mcp/chrome-path.ts +48 -0
  367. package/src/mcp/index.ts +7 -0
  368. package/src/mcp/inject-report-html-plugin.ts +119 -0
  369. package/src/mcp/launcher-helper.ts +200 -0
  370. package/src/mcp/tool-generator.ts +429 -0
  371. package/src/mcp/types.ts +112 -0
  372. package/src/node/fs.ts +84 -0
  373. package/src/node/index.ts +2 -0
  374. package/src/node/port.ts +37 -0
  375. package/src/oss/demo.ts +61 -0
  376. package/src/oss/index.ts +187 -0
  377. package/src/polyfills/async-hooks.ts +6 -0
  378. package/src/polyfills/index.ts +4 -0
  379. package/src/types/index.ts +52 -0
  380. package/src/us-keyboard-layout.ts +723 -0
  381. package/src/utils.ts +149 -0
  382. package/src/zod-schema-utils.ts +133 -0
@@ -0,0 +1,469 @@
1
+ import type { ElementInfo } from '.';
2
+ import type { Point } from '../types';
3
+ import { isSvgElement } from './dom-util';
4
+ import {
5
+ getNodeFromCacheList,
6
+ getRect,
7
+ isElementPartiallyInViewport,
8
+ logger,
9
+ } from './util';
10
+ import { collectElementInfo } from './web-extractor';
11
+
12
+ /** Separator for compound XPath across iframes (e.g. "iframePath|>>|/html/body/div") */
13
+ const SUB_XPATH_SEPARATOR = '|>>|';
14
+
15
+ /** Parse the non-standard `zoom` CSS property (Chromium-only) with fallback to 1 */
16
+ function parseCSSZoom(style: CSSStyleDeclaration): number {
17
+ return (
18
+ Number.parseFloat(
19
+ (style as CSSStyleDeclaration & { zoom?: string }).zoom ?? '1',
20
+ ) || 1
21
+ );
22
+ }
23
+
24
+ /**
25
+ * Calculate the accumulated offset from an iframe-nested node's document
26
+ * up to the top-level document, accounting for border, padding, and zoom at each level.
27
+ */
28
+ function calculateIframeOffset(
29
+ nodeOwnerDoc: Document | null,
30
+ rootDoc: Document | null,
31
+ ): { left: number; top: number } {
32
+ let leftOffset = 0;
33
+ let topOffset = 0;
34
+ let iterDoc = nodeOwnerDoc;
35
+
36
+ while (iterDoc && iterDoc !== rootDoc) {
37
+ try {
38
+ const frameElement = iterDoc.defaultView?.frameElement;
39
+ if (!frameElement) break;
40
+
41
+ const rect = (frameElement as Element).getBoundingClientRect();
42
+ const parentWin = iterDoc.defaultView?.parent;
43
+
44
+ let borderLeft = 0;
45
+ let borderTop = 0;
46
+ let zoom = 1;
47
+ try {
48
+ if (parentWin) {
49
+ const style = parentWin.getComputedStyle(frameElement as Element);
50
+ borderLeft = Number.parseFloat(style.borderLeftWidth) || 0;
51
+ borderTop = Number.parseFloat(style.borderTopWidth) || 0;
52
+ zoom = parseCSSZoom(style);
53
+ }
54
+ } catch {
55
+ // cross-origin iframe style access may fail, use defaults
56
+ }
57
+
58
+ leftOffset = leftOffset / zoom + rect.left + borderLeft;
59
+ topOffset = topOffset / zoom + rect.top + borderTop;
60
+ iterDoc = (frameElement as Element).ownerDocument;
61
+ } catch {
62
+ break;
63
+ }
64
+ }
65
+
66
+ return { left: leftOffset, top: topOffset };
67
+ }
68
+
69
+ /**
70
+ * Translate a point from the parent window coordinate space into
71
+ * the iframe's local coordinate space.
72
+ */
73
+ function translatePointToIframeCoordinates(
74
+ point: { left: number; top: number },
75
+ iframeElement: Element,
76
+ parentWindow: Window,
77
+ ): { left: number; top: number } {
78
+ const rect = iframeElement.getBoundingClientRect();
79
+ const style = parentWindow.getComputedStyle(iframeElement);
80
+ const clientLeft = iframeElement.clientLeft;
81
+ const clientTop = iframeElement.clientTop;
82
+ const paddingLeft = Number.parseFloat(style.paddingLeft) || 0;
83
+ const paddingTop = Number.parseFloat(style.paddingTop) || 0;
84
+ const zoom = parseCSSZoom(style);
85
+
86
+ return {
87
+ left: (point.left - rect.left - clientLeft - paddingLeft) / zoom,
88
+ top: (point.top - rect.top - clientTop - paddingTop) / zoom,
89
+ };
90
+ }
91
+
92
+ const getElementXpathIndex = (element: Element): number => {
93
+ let index = 1;
94
+ let prev = element.previousElementSibling;
95
+
96
+ while (prev) {
97
+ if (prev.nodeName.toLowerCase() === element.nodeName.toLowerCase()) {
98
+ index++;
99
+ }
100
+ prev = prev.previousElementSibling;
101
+ }
102
+
103
+ return index;
104
+ };
105
+
106
+ const normalizeXpathText = (text: string): string => {
107
+ if (typeof text !== 'string') {
108
+ return '';
109
+ }
110
+
111
+ return text.replace(/\s+/g, ' ').trim();
112
+ };
113
+
114
+ const buildCurrentElementXpath = (
115
+ element: Element,
116
+ isOrderSensitive: boolean,
117
+ isLeafElement: boolean,
118
+ limitToCurrentDocument = false,
119
+ ): string => {
120
+ // Build parent path - inline the buildParentXpath logic
121
+ const parentPath = element.parentNode
122
+ ? getElementXpath(
123
+ element.parentNode,
124
+ isOrderSensitive,
125
+ false,
126
+ limitToCurrentDocument,
127
+ )
128
+ : '';
129
+ const prefix = parentPath ? `${parentPath}/` : '/';
130
+ const tagName = element.nodeName.toLowerCase();
131
+ const textContent = element.textContent?.trim();
132
+
133
+ // Check if this is an SVG element (has SVG namespace)
134
+ const isSVGNamespace = element.namespaceURI === 'http://www.w3.org/2000/svg';
135
+ // For SVG elements, we need to use *[name()="tagname"] syntax because
136
+ // XPath's element matching doesn't work with namespaced elements in HTML documents
137
+ const tagSelector = isSVGNamespace ? `*[name()="${tagName}"]` : tagName;
138
+
139
+ // Order-sensitive mode: always use index
140
+ if (isOrderSensitive) {
141
+ const index = getElementXpathIndex(element);
142
+ return `${prefix}${tagSelector}[${index}]`;
143
+ }
144
+
145
+ // Order-insensitive mode:
146
+ // - Leaf elements: try text first, fallback to index if no text
147
+ // - Non-leaf elements: always use index
148
+ if (isLeafElement && textContent) {
149
+ return `${prefix}${tagSelector}[normalize-space()="${normalizeXpathText(textContent)}"]`;
150
+ }
151
+
152
+ // Fallback to index (for non-leaf elements or leaf elements without text)
153
+ const index = getElementXpathIndex(element);
154
+ return `${prefix}${tagSelector}[${index}]`;
155
+ };
156
+
157
+ export const getElementXpath = (
158
+ element: Node,
159
+ isOrderSensitive = false,
160
+ isLeafElement = false,
161
+ limitToCurrentDocument = false,
162
+ ): string => {
163
+ // process text node
164
+ if (element.nodeType === Node.TEXT_NODE) {
165
+ const parentNode = element.parentNode;
166
+ if (parentNode && parentNode.nodeType === Node.ELEMENT_NODE) {
167
+ const parentXPath = getElementXpath(
168
+ parentNode,
169
+ isOrderSensitive,
170
+ true,
171
+ limitToCurrentDocument,
172
+ );
173
+ const textContent = element.textContent?.trim();
174
+ if (textContent) {
175
+ return `${parentXPath}/text()[normalize-space()="${normalizeXpathText(textContent)}"]`;
176
+ }
177
+ return `${parentXPath}/text()`;
178
+ }
179
+ return '';
180
+ }
181
+
182
+ if (element.nodeType !== Node.ELEMENT_NODE) return '';
183
+
184
+ const el = element as Element;
185
+
186
+ // special element handling (iframe-aware: prefix with frame path when not limitToCurrentDocument)
187
+ try {
188
+ const nodeName = el.nodeName.toLowerCase();
189
+ if (el === el.ownerDocument?.documentElement || nodeName === 'html') {
190
+ if (!limitToCurrentDocument) {
191
+ const frameElement = el.ownerDocument?.defaultView?.frameElement;
192
+ if (frameElement) {
193
+ const framePath = getElementXpath(
194
+ frameElement as Element,
195
+ isOrderSensitive,
196
+ false,
197
+ limitToCurrentDocument,
198
+ );
199
+ return `${framePath}${SUB_XPATH_SEPARATOR}/html`;
200
+ }
201
+ }
202
+ return '/html';
203
+ }
204
+ if (el === el.ownerDocument?.body || nodeName === 'body') {
205
+ if (!limitToCurrentDocument) {
206
+ const frameElement = el.ownerDocument?.defaultView?.frameElement;
207
+ if (frameElement) {
208
+ const framePath = getElementXpath(
209
+ frameElement as Element,
210
+ isOrderSensitive,
211
+ false,
212
+ limitToCurrentDocument,
213
+ );
214
+ return `${framePath}${SUB_XPATH_SEPARATOR}/html/body`;
215
+ }
216
+ }
217
+ return '/html/body';
218
+ }
219
+ } catch (error) {
220
+ logger('[midscene:locator] ownerDocument access failed:', error);
221
+ if (el.nodeName.toLowerCase() === 'html') return '/html';
222
+ if (el.nodeName.toLowerCase() === 'body') return '/html/body';
223
+ }
224
+
225
+ if (isSvgElement(el)) {
226
+ const tagName = el.nodeName.toLowerCase();
227
+ if (tagName === 'svg') {
228
+ return buildCurrentElementXpath(
229
+ el,
230
+ isOrderSensitive,
231
+ isLeafElement,
232
+ limitToCurrentDocument,
233
+ );
234
+ }
235
+ let parent = el.parentNode;
236
+ while (parent && parent.nodeType === Node.ELEMENT_NODE) {
237
+ const parentEl = parent as Element;
238
+ if (!isSvgElement(parentEl)) {
239
+ return getElementXpath(
240
+ parentEl,
241
+ isOrderSensitive,
242
+ isLeafElement,
243
+ limitToCurrentDocument,
244
+ );
245
+ }
246
+ const parentTag = parentEl.nodeName.toLowerCase();
247
+ if (parentTag === 'svg') {
248
+ return getElementXpath(
249
+ parentEl,
250
+ isOrderSensitive,
251
+ isLeafElement,
252
+ limitToCurrentDocument,
253
+ );
254
+ }
255
+ parent = parent.parentNode;
256
+ }
257
+ const fallbackParent = el.parentNode;
258
+ if (fallbackParent && fallbackParent.nodeType === Node.ELEMENT_NODE) {
259
+ return getElementXpath(
260
+ fallbackParent as Element,
261
+ isOrderSensitive,
262
+ isLeafElement,
263
+ limitToCurrentDocument,
264
+ );
265
+ }
266
+ return '';
267
+ }
268
+
269
+ return buildCurrentElementXpath(
270
+ el,
271
+ isOrderSensitive,
272
+ isLeafElement,
273
+ limitToCurrentDocument,
274
+ );
275
+ };
276
+
277
+ /** Retrieve XPath for a previously cached node by its hash ID.
278
+ * Returns a local xpath within the node's own document (limitToCurrentDocument=true). */
279
+ export function getXpathsById(id: string): string[] | null {
280
+ const node = getNodeFromCacheList(id);
281
+ if (!node) return null;
282
+ const fullXPath = getElementXpath(node, false, true, true);
283
+ return [fullXPath];
284
+ }
285
+
286
+ export function getXpathsByPoint(
287
+ point: Point,
288
+ isOrderSensitive: boolean,
289
+ ): string[] | null {
290
+ let currentWindow: Window =
291
+ typeof window !== 'undefined' ? window : (undefined as any);
292
+ let currentDocument: Document =
293
+ typeof document !== 'undefined' ? document : (undefined as any);
294
+ let { left, top } = point;
295
+ let depth = 0;
296
+ const MAX_DEPTH = 10;
297
+ let xpathPrefix = '';
298
+ let lastFoundElement: Element | null = null;
299
+
300
+ while (depth < MAX_DEPTH) {
301
+ depth++;
302
+ const element = currentDocument.elementFromPoint(left, top);
303
+
304
+ if (!element) {
305
+ if (lastFoundElement) {
306
+ const fullXPath = getElementXpath(
307
+ lastFoundElement,
308
+ isOrderSensitive,
309
+ true,
310
+ true,
311
+ );
312
+ return [xpathPrefix + fullXPath];
313
+ }
314
+ return null;
315
+ }
316
+
317
+ lastFoundElement = element;
318
+
319
+ const tag = element.tagName.toLowerCase();
320
+ if (tag === 'iframe' || tag === 'frame') {
321
+ try {
322
+ const contentWindow = (element as HTMLIFrameElement).contentWindow;
323
+ const contentDocument = (element as HTMLIFrameElement).contentDocument;
324
+
325
+ if (contentWindow && contentDocument) {
326
+ const localPoint = translatePointToIframeCoordinates(
327
+ { left, top },
328
+ element,
329
+ currentWindow,
330
+ );
331
+ const currentIframeXpath = getElementXpath(
332
+ element,
333
+ isOrderSensitive,
334
+ false,
335
+ true,
336
+ );
337
+ xpathPrefix += currentIframeXpath + SUB_XPATH_SEPARATOR;
338
+ currentWindow = contentWindow;
339
+ currentDocument = contentDocument;
340
+ left = localPoint.left;
341
+ top = localPoint.top;
342
+ continue;
343
+ }
344
+ } catch (error) {
345
+ logger(
346
+ '[midscene:locator] iframe penetration failed (cross-origin?):',
347
+ error,
348
+ );
349
+ }
350
+ }
351
+
352
+ const fullXPath = getElementXpath(element, isOrderSensitive, true, true);
353
+ return [xpathPrefix + fullXPath];
354
+ }
355
+
356
+ if (lastFoundElement) {
357
+ const fullXPath = getElementXpath(
358
+ lastFoundElement,
359
+ isOrderSensitive,
360
+ true,
361
+ true,
362
+ );
363
+ return [xpathPrefix + fullXPath];
364
+ }
365
+ return null;
366
+ }
367
+
368
+ export function getNodeInfoByXpath(xpath: string): Node | null {
369
+ const parts = xpath
370
+ .split(SUB_XPATH_SEPARATOR)
371
+ .map((p) => p.trim())
372
+ .filter(Boolean);
373
+ if (parts.length === 0) return null;
374
+
375
+ let currentDocument: Document =
376
+ typeof document !== 'undefined' ? document : (undefined as any);
377
+ let node: Node | null = null;
378
+
379
+ for (let i = 0; i < parts.length; i++) {
380
+ const currentXpath = parts[i];
381
+ const xpathResult = currentDocument.evaluate(
382
+ currentXpath,
383
+ currentDocument,
384
+ null,
385
+ XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
386
+ null,
387
+ );
388
+
389
+ if (xpathResult.snapshotLength !== 1) {
390
+ logger(
391
+ `[midscene:locator] XPath "${currentXpath}" matched ${xpathResult.snapshotLength} elements (expected 1), discarding.`,
392
+ );
393
+ return null;
394
+ }
395
+
396
+ node = xpathResult.snapshotItem(0);
397
+
398
+ if (i < parts.length - 1) {
399
+ if (
400
+ node &&
401
+ node.nodeType === Node.ELEMENT_NODE &&
402
+ (node as Element).tagName.toLowerCase() === 'iframe'
403
+ ) {
404
+ try {
405
+ const contentDocument = (node as HTMLIFrameElement).contentDocument;
406
+ if (contentDocument) {
407
+ currentDocument = contentDocument;
408
+ } else {
409
+ logger(
410
+ '[midscene:locator] iframe contentDocument is null (cross-origin?)',
411
+ );
412
+ return null;
413
+ }
414
+ } catch (error) {
415
+ logger(
416
+ '[midscene:locator] iframe contentDocument access failed:',
417
+ error,
418
+ );
419
+ return null;
420
+ }
421
+ } else {
422
+ return null;
423
+ }
424
+ }
425
+ }
426
+
427
+ return node;
428
+ }
429
+
430
+ export function getElementInfoByXpath(xpath: string): ElementInfo | null {
431
+ const node = getNodeInfoByXpath(xpath);
432
+ if (!node) return null;
433
+
434
+ let targetWindow: Window =
435
+ typeof window !== 'undefined' ? window : (undefined as any);
436
+ let targetDocument: Document =
437
+ typeof document !== 'undefined' ? document : (undefined as any);
438
+
439
+ if (node.ownerDocument?.defaultView) {
440
+ targetWindow = node.ownerDocument.defaultView;
441
+ targetDocument = node.ownerDocument;
442
+ }
443
+
444
+ const rootDoc = typeof document !== 'undefined' ? document : null;
445
+ const iframeOffset = calculateIframeOffset(
446
+ node.ownerDocument ?? null,
447
+ rootDoc,
448
+ );
449
+
450
+ const targetWin = targetWindow as typeof globalThis.window;
451
+ const targetDoc = targetDocument as typeof globalThis.document;
452
+ if (node instanceof (targetWin as any).HTMLElement) {
453
+ const rect = getRect(node, 1, targetWin);
454
+ const isVisible = isElementPartiallyInViewport(
455
+ rect,
456
+ targetWin,
457
+ targetDoc,
458
+ 1,
459
+ );
460
+ if (!isVisible) {
461
+ (node as HTMLElement).scrollIntoView({
462
+ behavior: 'instant',
463
+ block: 'center',
464
+ });
465
+ }
466
+ }
467
+
468
+ return collectElementInfo(node, targetWin, targetDoc, 1, iframeOffset, true);
469
+ }
@@ -0,0 +1,179 @@
1
+ import type { BaseElement, ElementTreeNode } from '../types';
2
+
3
+ export function truncateText(
4
+ text: string | number | object | undefined,
5
+ maxLength = 150,
6
+ ) {
7
+ if (typeof text === 'undefined') {
8
+ return '';
9
+ }
10
+
11
+ if (typeof text === 'object') {
12
+ text = JSON.stringify(text);
13
+ }
14
+
15
+ if (typeof text === 'number') {
16
+ return text.toString();
17
+ }
18
+
19
+ if (typeof text === 'string' && text.length > maxLength) {
20
+ return `${text.slice(0, maxLength)}...`;
21
+ }
22
+
23
+ if (typeof text === 'string') {
24
+ return text.trim();
25
+ }
26
+
27
+ return '';
28
+ }
29
+
30
+ export function trimAttributes(
31
+ attributes: Record<string, any>,
32
+ truncateTextLength?: number,
33
+ ) {
34
+ const tailorAttributes = Object.keys(attributes).reduce(
35
+ (res, currentKey: string) => {
36
+ const attributeVal = (attributes as any)[currentKey];
37
+ if (
38
+ currentKey === 'style' ||
39
+ currentKey === 'htmlTagName' ||
40
+ currentKey === 'nodeType'
41
+ ) {
42
+ return res;
43
+ }
44
+
45
+ res[currentKey] = truncateText(attributeVal, truncateTextLength);
46
+ return res;
47
+ },
48
+ {} as Record<string, string>,
49
+ );
50
+ return tailorAttributes;
51
+ }
52
+
53
+ const nodeSizeThreshold = 4;
54
+ export function descriptionOfTree<
55
+ ElementType extends BaseElement = BaseElement,
56
+ >(
57
+ tree: ElementTreeNode<ElementType>,
58
+ truncateTextLength?: number,
59
+ filterNonTextContent = false,
60
+ visibleOnly = true,
61
+ ) {
62
+ const attributesString = (kv: Record<string, any>) => {
63
+ return Object.entries(kv)
64
+ .map(
65
+ ([key, value]) => `${key}="${truncateText(value, truncateTextLength)}"`,
66
+ )
67
+ .join(' ');
68
+ };
69
+
70
+ function buildContentTree(
71
+ node: ElementTreeNode<ElementType>,
72
+ indent = 0,
73
+ visibleOnly = true,
74
+ ): string {
75
+ let before = '';
76
+ let contentWithIndent = '';
77
+ let after = '';
78
+ let emptyNode = true;
79
+ const indentStr = ' '.repeat(indent);
80
+
81
+ let children = '';
82
+ for (let i = 0; i < (node.children || []).length; i++) {
83
+ const childContent = buildContentTree(
84
+ node.children[i],
85
+ indent + 1,
86
+ visibleOnly,
87
+ );
88
+ if (childContent) {
89
+ children += `\n${childContent}`;
90
+ }
91
+ }
92
+
93
+ if (
94
+ node.node &&
95
+ node.node.rect.width > nodeSizeThreshold &&
96
+ node.node.rect.height > nodeSizeThreshold &&
97
+ (!filterNonTextContent || (filterNonTextContent && node.node.content)) &&
98
+ (!visibleOnly || (visibleOnly && node.node.isVisible))
99
+ ) {
100
+ emptyNode = false;
101
+ let nodeTypeString: string;
102
+ if (node.node.attributes?.htmlTagName) {
103
+ nodeTypeString = node.node.attributes.htmlTagName.replace(/[<>]/g, '');
104
+ } else {
105
+ nodeTypeString = node.node.attributes.nodeType
106
+ .replace(/\sNode$/, '')
107
+ .toLowerCase();
108
+ }
109
+ // const markerId = node.node.indexId;
110
+ // const markerIdString = markerId ? `markerId="${markerId}"` : '';
111
+ const rectAttribute = node.node.rect
112
+ ? {
113
+ left: node.node.rect.left,
114
+ top: node.node.rect.top,
115
+ width: node.node.rect.width,
116
+ height: node.node.rect.height,
117
+ }
118
+ : {};
119
+ before = `<${nodeTypeString} id="${node.node.id}" ${attributesString(trimAttributes(node.node.attributes || {}, truncateTextLength))} ${attributesString(rectAttribute)}>`;
120
+ const content = truncateText(node.node.content, truncateTextLength);
121
+ contentWithIndent = content ? `\n${indentStr} ${content}` : '';
122
+ after = `</${nodeTypeString}>`;
123
+ } else if (!filterNonTextContent) {
124
+ if (!children.trim().startsWith('<>')) {
125
+ before = '<>';
126
+ contentWithIndent = '';
127
+ after = '</>';
128
+ }
129
+ }
130
+
131
+ if (emptyNode && !children.trim()) {
132
+ return '';
133
+ }
134
+
135
+ const result = `${indentStr}${before}${contentWithIndent}${children}\n${indentStr}${after}`;
136
+ if (result.trim()) {
137
+ return result;
138
+ }
139
+ return '';
140
+ }
141
+
142
+ const result = buildContentTree(tree, 0, visibleOnly);
143
+ return result.replace(/^\s*\n/gm, '');
144
+ }
145
+
146
+ export function treeToList<T extends BaseElement>(
147
+ tree: ElementTreeNode<T>,
148
+ ): T[] {
149
+ const result: T[] = [];
150
+ function dfs(node: ElementTreeNode<T>) {
151
+ if (node.node) {
152
+ result.push(node.node);
153
+ }
154
+ for (const child of node.children) {
155
+ dfs(child);
156
+ }
157
+ }
158
+ dfs(tree);
159
+ return result;
160
+ }
161
+
162
+ export function traverseTree<
163
+ T extends BaseElement,
164
+ ReturnNodeType extends BaseElement,
165
+ >(
166
+ tree: ElementTreeNode<T>,
167
+ onNode: (node: T) => ReturnNodeType,
168
+ ): ElementTreeNode<ReturnNodeType> {
169
+ function dfs(node: ElementTreeNode<T>) {
170
+ if (node.node) {
171
+ node.node = onNode(node.node) as any;
172
+ }
173
+ for (const child of node.children) {
174
+ dfs(child);
175
+ }
176
+ }
177
+ dfs(tree);
178
+ return tree as any;
179
+ }