@sudobility/testomniac_runner_service 0.1.24

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 (323) hide show
  1. package/CLAUDE.md +338 -0
  2. package/README.md +15 -0
  3. package/dist/adapter.d.ts +56 -0
  4. package/dist/adapter.d.ts.map +1 -0
  5. package/dist/adapter.js +1 -0
  6. package/dist/adapter.js.map +1 -0
  7. package/dist/ai/analyzer.d.ts +16 -0
  8. package/dist/ai/analyzer.d.ts.map +1 -0
  9. package/dist/ai/analyzer.js +55 -0
  10. package/dist/ai/analyzer.js.map +1 -0
  11. package/dist/ai/input-generator.d.ts +9 -0
  12. package/dist/ai/input-generator.d.ts.map +1 -0
  13. package/dist/ai/input-generator.js +20 -0
  14. package/dist/ai/input-generator.js.map +1 -0
  15. package/dist/ai/persona-generator.d.ts +7 -0
  16. package/dist/ai/persona-generator.d.ts.map +1 -0
  17. package/dist/ai/persona-generator.js +23 -0
  18. package/dist/ai/persona-generator.js.map +1 -0
  19. package/dist/ai/use-case-generator.d.ts +7 -0
  20. package/dist/ai/use-case-generator.d.ts.map +1 -0
  21. package/dist/ai/use-case-generator.js +23 -0
  22. package/dist/ai/use-case-generator.js.map +1 -0
  23. package/dist/api/client.d.ts +69 -0
  24. package/dist/api/client.d.ts.map +1 -0
  25. package/dist/api/client.js +327 -0
  26. package/dist/api/client.js.map +1 -0
  27. package/dist/browser/dom-snapshot.d.ts +4 -0
  28. package/dist/browser/dom-snapshot.d.ts.map +1 -0
  29. package/dist/browser/dom-snapshot.js +382 -0
  30. package/dist/browser/dom-snapshot.js.map +1 -0
  31. package/dist/browser/page-utils.d.ts +9 -0
  32. package/dist/browser/page-utils.d.ts.map +1 -0
  33. package/dist/browser/page-utils.js +63 -0
  34. package/dist/browser/page-utils.js.map +1 -0
  35. package/dist/config/constants.d.ts +28 -0
  36. package/dist/config/constants.d.ts.map +1 -0
  37. package/dist/config/constants.js +99 -0
  38. package/dist/config/constants.js.map +1 -0
  39. package/dist/detectors/action-description.d.ts +4 -0
  40. package/dist/detectors/action-description.d.ts.map +1 -0
  41. package/dist/detectors/action-description.js +25 -0
  42. package/dist/detectors/action-description.js.map +1 -0
  43. package/dist/detectors/bug-detector.d.ts +23 -0
  44. package/dist/detectors/bug-detector.d.ts.map +1 -0
  45. package/dist/detectors/bug-detector.js +214 -0
  46. package/dist/detectors/bug-detector.js.map +1 -0
  47. package/dist/detectors/content-checker.d.ts +10 -0
  48. package/dist/detectors/content-checker.d.ts.map +1 -0
  49. package/dist/detectors/content-checker.js +76 -0
  50. package/dist/detectors/content-checker.js.map +1 -0
  51. package/dist/detectors/detection-rule.d.ts +28 -0
  52. package/dist/detectors/detection-rule.d.ts.map +1 -0
  53. package/dist/detectors/detection-rule.js +1 -0
  54. package/dist/detectors/detection-rule.js.map +1 -0
  55. package/dist/detectors/functional-checker.d.ts +22 -0
  56. package/dist/detectors/functional-checker.d.ts.map +1 -0
  57. package/dist/detectors/functional-checker.js +88 -0
  58. package/dist/detectors/functional-checker.js.map +1 -0
  59. package/dist/detectors/index.d.ts +10 -0
  60. package/dist/detectors/index.d.ts.map +1 -0
  61. package/dist/detectors/index.js +9 -0
  62. package/dist/detectors/index.js.map +1 -0
  63. package/dist/detectors/link-checker.d.ts +24 -0
  64. package/dist/detectors/link-checker.d.ts.map +1 -0
  65. package/dist/detectors/link-checker.js +85 -0
  66. package/dist/detectors/link-checker.js.map +1 -0
  67. package/dist/detectors/modal-handler.d.ts +7 -0
  68. package/dist/detectors/modal-handler.d.ts.map +1 -0
  69. package/dist/detectors/modal-handler.js +60 -0
  70. package/dist/detectors/modal-handler.js.map +1 -0
  71. package/dist/detectors/rules/blank-page-rule.d.ts +3 -0
  72. package/dist/detectors/rules/blank-page-rule.d.ts.map +1 -0
  73. package/dist/detectors/rules/blank-page-rule.js +21 -0
  74. package/dist/detectors/rules/blank-page-rule.js.map +1 -0
  75. package/dist/detectors/rules/broken-image-rule.d.ts +3 -0
  76. package/dist/detectors/rules/broken-image-rule.d.ts.map +1 -0
  77. package/dist/detectors/rules/broken-image-rule.js +34 -0
  78. package/dist/detectors/rules/broken-image-rule.js.map +1 -0
  79. package/dist/detectors/rules/broken-link-rule.d.ts +3 -0
  80. package/dist/detectors/rules/broken-link-rule.d.ts.map +1 -0
  81. package/dist/detectors/rules/broken-link-rule.js +55 -0
  82. package/dist/detectors/rules/broken-link-rule.js.map +1 -0
  83. package/dist/detectors/rules/broken-media-rule.d.ts +3 -0
  84. package/dist/detectors/rules/broken-media-rule.d.ts.map +1 -0
  85. package/dist/detectors/rules/broken-media-rule.js +46 -0
  86. package/dist/detectors/rules/broken-media-rule.js.map +1 -0
  87. package/dist/detectors/rules/console-error-rule.d.ts +3 -0
  88. package/dist/detectors/rules/console-error-rule.d.ts.map +1 -0
  89. package/dist/detectors/rules/console-error-rule.js +9 -0
  90. package/dist/detectors/rules/console-error-rule.js.map +1 -0
  91. package/dist/detectors/rules/dead-click-rule.d.ts +3 -0
  92. package/dist/detectors/rules/dead-click-rule.d.ts.map +1 -0
  93. package/dist/detectors/rules/dead-click-rule.js +9 -0
  94. package/dist/detectors/rules/dead-click-rule.js.map +1 -0
  95. package/dist/detectors/rules/duplicate-heading-rule.d.ts +3 -0
  96. package/dist/detectors/rules/duplicate-heading-rule.d.ts.map +1 -0
  97. package/dist/detectors/rules/duplicate-heading-rule.js +35 -0
  98. package/dist/detectors/rules/duplicate-heading-rule.js.map +1 -0
  99. package/dist/detectors/rules/duplicate-id-rule.d.ts +3 -0
  100. package/dist/detectors/rules/duplicate-id-rule.d.ts.map +1 -0
  101. package/dist/detectors/rules/duplicate-id-rule.js +33 -0
  102. package/dist/detectors/rules/duplicate-id-rule.js.map +1 -0
  103. package/dist/detectors/rules/empty-link-rule.d.ts +3 -0
  104. package/dist/detectors/rules/empty-link-rule.d.ts.map +1 -0
  105. package/dist/detectors/rules/empty-link-rule.js +35 -0
  106. package/dist/detectors/rules/empty-link-rule.js.map +1 -0
  107. package/dist/detectors/rules/error-page-rule.d.ts +3 -0
  108. package/dist/detectors/rules/error-page-rule.d.ts.map +1 -0
  109. package/dist/detectors/rules/error-page-rule.js +40 -0
  110. package/dist/detectors/rules/error-page-rule.js.map +1 -0
  111. package/dist/detectors/rules/index.d.ts +16 -0
  112. package/dist/detectors/rules/index.d.ts.map +1 -0
  113. package/dist/detectors/rules/index.js +32 -0
  114. package/dist/detectors/rules/index.js.map +1 -0
  115. package/dist/detectors/rules/network-error-rule.d.ts +3 -0
  116. package/dist/detectors/rules/network-error-rule.d.ts.map +1 -0
  117. package/dist/detectors/rules/network-error-rule.js +9 -0
  118. package/dist/detectors/rules/network-error-rule.js.map +1 -0
  119. package/dist/detectors/rules/placeholder-text-rule.d.ts +3 -0
  120. package/dist/detectors/rules/placeholder-text-rule.d.ts.map +1 -0
  121. package/dist/detectors/rules/placeholder-text-rule.js +34 -0
  122. package/dist/detectors/rules/placeholder-text-rule.js.map +1 -0
  123. package/dist/detectors/visual-checker.d.ts +11 -0
  124. package/dist/detectors/visual-checker.d.ts.map +1 -0
  125. package/dist/detectors/visual-checker.js +78 -0
  126. package/dist/detectors/visual-checker.js.map +1 -0
  127. package/dist/domain/types.d.ts +3 -0
  128. package/dist/domain/types.d.ts.map +1 -0
  129. package/dist/domain/types.js +3 -0
  130. package/dist/domain/types.js.map +1 -0
  131. package/dist/domain/url-ownership.d.ts +7 -0
  132. package/dist/domain/url-ownership.d.ts.map +1 -0
  133. package/dist/domain/url-ownership.js +49 -0
  134. package/dist/domain/url-ownership.js.map +1 -0
  135. package/dist/extractors/buttons.d.ts +3 -0
  136. package/dist/extractors/buttons.d.ts.map +1 -0
  137. package/dist/extractors/buttons.js +30 -0
  138. package/dist/extractors/buttons.js.map +1 -0
  139. package/dist/extractors/clickables.d.ts +3 -0
  140. package/dist/extractors/clickables.d.ts.map +1 -0
  141. package/dist/extractors/clickables.js +35 -0
  142. package/dist/extractors/clickables.js.map +1 -0
  143. package/dist/extractors/form-extractor.d.ts +4 -0
  144. package/dist/extractors/form-extractor.d.ts.map +1 -0
  145. package/dist/extractors/form-extractor.js +47 -0
  146. package/dist/extractors/form-extractor.js.map +1 -0
  147. package/dist/extractors/helpers.d.ts +7 -0
  148. package/dist/extractors/helpers.d.ts.map +1 -0
  149. package/dist/extractors/helpers.js +43 -0
  150. package/dist/extractors/helpers.js.map +1 -0
  151. package/dist/extractors/index.d.ts +5 -0
  152. package/dist/extractors/index.d.ts.map +1 -0
  153. package/dist/extractors/index.js +25 -0
  154. package/dist/extractors/index.js.map +1 -0
  155. package/dist/extractors/product-actions.d.ts +3 -0
  156. package/dist/extractors/product-actions.d.ts.map +1 -0
  157. package/dist/extractors/product-actions.js +23 -0
  158. package/dist/extractors/product-actions.js.map +1 -0
  159. package/dist/extractors/selectors.d.ts +3 -0
  160. package/dist/extractors/selectors.d.ts.map +1 -0
  161. package/dist/extractors/selectors.js +42 -0
  162. package/dist/extractors/selectors.js.map +1 -0
  163. package/dist/extractors/selects.d.ts +3 -0
  164. package/dist/extractors/selects.d.ts.map +1 -0
  165. package/dist/extractors/selects.js +13 -0
  166. package/dist/extractors/selects.js.map +1 -0
  167. package/dist/extractors/text-inputs.d.ts +3 -0
  168. package/dist/extractors/text-inputs.d.ts.map +1 -0
  169. package/dist/extractors/text-inputs.js +35 -0
  170. package/dist/extractors/text-inputs.js.map +1 -0
  171. package/dist/extractors/toggles.d.ts +3 -0
  172. package/dist/extractors/toggles.d.ts.map +1 -0
  173. package/dist/extractors/toggles.js +14 -0
  174. package/dist/extractors/toggles.js.map +1 -0
  175. package/dist/extractors/types.d.ts +36 -0
  176. package/dist/extractors/types.d.ts.map +1 -0
  177. package/dist/extractors/types.js +1 -0
  178. package/dist/extractors/types.js.map +1 -0
  179. package/dist/generation/e2e.d.ts +15 -0
  180. package/dist/generation/e2e.d.ts.map +1 -0
  181. package/dist/generation/e2e.js +59 -0
  182. package/dist/generation/e2e.js.map +1 -0
  183. package/dist/generation/form-negative.d.ts +14 -0
  184. package/dist/generation/form-negative.d.ts.map +1 -0
  185. package/dist/generation/form-negative.js +36 -0
  186. package/dist/generation/form-negative.js.map +1 -0
  187. package/dist/generation/form.d.ts +23 -0
  188. package/dist/generation/form.d.ts.map +1 -0
  189. package/dist/generation/form.js +48 -0
  190. package/dist/generation/form.js.map +1 -0
  191. package/dist/generation/generator.d.ts +13 -0
  192. package/dist/generation/generator.d.ts.map +1 -0
  193. package/dist/generation/generator.js +38 -0
  194. package/dist/generation/generator.js.map +1 -0
  195. package/dist/generation/interaction.d.ts +14 -0
  196. package/dist/generation/interaction.d.ts.map +1 -0
  197. package/dist/generation/interaction.js +26 -0
  198. package/dist/generation/interaction.js.map +1 -0
  199. package/dist/generation/navigation.d.ts +14 -0
  200. package/dist/generation/navigation.d.ts.map +1 -0
  201. package/dist/generation/navigation.js +21 -0
  202. package/dist/generation/navigation.js.map +1 -0
  203. package/dist/generation/password.d.ts +20 -0
  204. package/dist/generation/password.d.ts.map +1 -0
  205. package/dist/generation/password.js +45 -0
  206. package/dist/generation/password.js.map +1 -0
  207. package/dist/generation/playwright-export.d.ts +3 -0
  208. package/dist/generation/playwright-export.d.ts.map +1 -0
  209. package/dist/generation/playwright-export.js +22 -0
  210. package/dist/generation/playwright-export.js.map +1 -0
  211. package/dist/generation/render.d.ts +29 -0
  212. package/dist/generation/render.d.ts.map +1 -0
  213. package/dist/generation/render.js +94 -0
  214. package/dist/generation/render.js.map +1 -0
  215. package/dist/generation/suite-tagger.d.ts +4 -0
  216. package/dist/generation/suite-tagger.d.ts.map +1 -0
  217. package/dist/generation/suite-tagger.js +20 -0
  218. package/dist/generation/suite-tagger.js.map +1 -0
  219. package/dist/identity/element-matcher.d.ts +23 -0
  220. package/dist/identity/element-matcher.d.ts.map +1 -0
  221. package/dist/identity/element-matcher.js +60 -0
  222. package/dist/identity/element-matcher.js.map +1 -0
  223. package/dist/identity/identity-cache.d.ts +12 -0
  224. package/dist/identity/identity-cache.d.ts.map +1 -0
  225. package/dist/identity/identity-cache.js +25 -0
  226. package/dist/identity/identity-cache.js.map +1 -0
  227. package/dist/identity/playwright-locator.d.ts +4 -0
  228. package/dist/identity/playwright-locator.d.ts.map +1 -0
  229. package/dist/identity/playwright-locator.js +67 -0
  230. package/dist/identity/playwright-locator.js.map +1 -0
  231. package/dist/index.d.ts +47 -0
  232. package/dist/index.d.ts.map +1 -0
  233. package/dist/index.js +56 -0
  234. package/dist/index.js.map +1 -0
  235. package/dist/orchestrator/decomposition.d.ts +6 -0
  236. package/dist/orchestrator/decomposition.d.ts.map +1 -0
  237. package/dist/orchestrator/decomposition.js +55 -0
  238. package/dist/orchestrator/decomposition.js.map +1 -0
  239. package/dist/orchestrator/orchestrator.d.ts +5 -0
  240. package/dist/orchestrator/orchestrator.d.ts.map +1 -0
  241. package/dist/orchestrator/orchestrator.js +122 -0
  242. package/dist/orchestrator/orchestrator.js.map +1 -0
  243. package/dist/orchestrator/test-execution.d.ts +5 -0
  244. package/dist/orchestrator/test-execution.d.ts.map +1 -0
  245. package/dist/orchestrator/test-execution.js +205 -0
  246. package/dist/orchestrator/test-execution.js.map +1 -0
  247. package/dist/orchestrator/types.d.ts +69 -0
  248. package/dist/orchestrator/types.d.ts.map +1 -0
  249. package/dist/orchestrator/types.js +1 -0
  250. package/dist/orchestrator/types.js.map +1 -0
  251. package/dist/planners/fill-value-planner.d.ts +9 -0
  252. package/dist/planners/fill-value-planner.d.ts.map +1 -0
  253. package/dist/planners/fill-value-planner.js +166 -0
  254. package/dist/planners/fill-value-planner.js.map +1 -0
  255. package/dist/plugins/registry.d.ts +6 -0
  256. package/dist/plugins/registry.d.ts.map +1 -0
  257. package/dist/plugins/registry.js +16 -0
  258. package/dist/plugins/registry.js.map +1 -0
  259. package/dist/plugins/types.d.ts +42 -0
  260. package/dist/plugins/types.d.ts.map +1 -0
  261. package/dist/plugins/types.js +1 -0
  262. package/dist/plugins/types.js.map +1 -0
  263. package/dist/scanner/action-classifier.d.ts +7 -0
  264. package/dist/scanner/action-classifier.d.ts.map +1 -0
  265. package/dist/scanner/action-classifier.js +64 -0
  266. package/dist/scanner/action-classifier.js.map +1 -0
  267. package/dist/scanner/action-queue.d.ts +18 -0
  268. package/dist/scanner/action-queue.d.ts.map +1 -0
  269. package/dist/scanner/action-queue.js +20 -0
  270. package/dist/scanner/action-queue.js.map +1 -0
  271. package/dist/scanner/component-detector.d.ts +43 -0
  272. package/dist/scanner/component-detector.d.ts.map +1 -0
  273. package/dist/scanner/component-detector.js +231 -0
  274. package/dist/scanner/component-detector.js.map +1 -0
  275. package/dist/scanner/email-detector.d.ts +3 -0
  276. package/dist/scanner/email-detector.d.ts.map +1 -0
  277. package/dist/scanner/email-detector.js +12 -0
  278. package/dist/scanner/email-detector.js.map +1 -0
  279. package/dist/scanner/html-decomposer.d.ts +19 -0
  280. package/dist/scanner/html-decomposer.d.ts.map +1 -0
  281. package/dist/scanner/html-decomposer.js +41 -0
  282. package/dist/scanner/html-decomposer.js.map +1 -0
  283. package/dist/scanner/issue-detector.d.ts +8 -0
  284. package/dist/scanner/issue-detector.d.ts.map +1 -0
  285. package/dist/scanner/issue-detector.js +71 -0
  286. package/dist/scanner/issue-detector.js.map +1 -0
  287. package/dist/scanner/loop-guard.d.ts +18 -0
  288. package/dist/scanner/loop-guard.d.ts.map +1 -0
  289. package/dist/scanner/loop-guard.js +33 -0
  290. package/dist/scanner/loop-guard.js.map +1 -0
  291. package/dist/scanner/navigator.d.ts +25 -0
  292. package/dist/scanner/navigator.d.ts.map +1 -0
  293. package/dist/scanner/navigator.js +56 -0
  294. package/dist/scanner/navigator.js.map +1 -0
  295. package/dist/scanner/page-cache.d.ts +22 -0
  296. package/dist/scanner/page-cache.d.ts.map +1 -0
  297. package/dist/scanner/page-cache.js +42 -0
  298. package/dist/scanner/page-cache.js.map +1 -0
  299. package/dist/scanner/pairwise.d.ts +7 -0
  300. package/dist/scanner/pairwise.d.ts.map +1 -0
  301. package/dist/scanner/pairwise.js +64 -0
  302. package/dist/scanner/pairwise.js.map +1 -0
  303. package/dist/scanner/pattern-detector.d.ts +22 -0
  304. package/dist/scanner/pattern-detector.d.ts.map +1 -0
  305. package/dist/scanner/pattern-detector.js +108 -0
  306. package/dist/scanner/pattern-detector.js.map +1 -0
  307. package/dist/scanner/phase-timer.d.ts +9 -0
  308. package/dist/scanner/phase-timer.d.ts.map +1 -0
  309. package/dist/scanner/phase-timer.js +27 -0
  310. package/dist/scanner/phase-timer.js.map +1 -0
  311. package/dist/scanner/reusable-element-cache.d.ts +13 -0
  312. package/dist/scanner/reusable-element-cache.d.ts.map +1 -0
  313. package/dist/scanner/reusable-element-cache.js +37 -0
  314. package/dist/scanner/reusable-element-cache.js.map +1 -0
  315. package/dist/scanner/scroll-scanner.d.ts +3 -0
  316. package/dist/scanner/scroll-scanner.d.ts.map +1 -0
  317. package/dist/scanner/scroll-scanner.js +19 -0
  318. package/dist/scanner/scroll-scanner.js.map +1 -0
  319. package/dist/scanner/state-manager.d.ts +11 -0
  320. package/dist/scanner/state-manager.d.ts.map +1 -0
  321. package/dist/scanner/state-manager.js +24 -0
  322. package/dist/scanner/state-manager.js.map +1 -0
  323. package/package.json +77 -0
@@ -0,0 +1,42 @@
1
+ /**
2
+ * In-memory cache of pages for an app, backed by the API.
3
+ * Key is the normalized URL (stripped of fragment).
4
+ * Avoids redundant API calls for the same URL during a scan.
5
+ */
6
+ export class PageCache {
7
+ cache = new Map();
8
+ appId;
9
+ api;
10
+ constructor(appId, api) {
11
+ this.appId = appId;
12
+ this.api = api;
13
+ }
14
+ /** Preload all existing pages for this app from the API. */
15
+ async preload() {
16
+ const pages = await this.api.getPagesByApp(this.appId);
17
+ for (const page of pages) {
18
+ this.cache.set(this.normalizeUrl(page.relativePath), page);
19
+ }
20
+ }
21
+ /** Find or create a page by URL. Returns cached result if available. */
22
+ async findOrCreate(url) {
23
+ const key = this.normalizeUrl(url);
24
+ const cached = this.cache.get(key);
25
+ if (cached)
26
+ return cached;
27
+ const page = await this.api.findOrCreatePage(this.appId, url);
28
+ this.cache.set(key, page);
29
+ return page;
30
+ }
31
+ /** Check if a URL has already been seen (without API call). */
32
+ has(url) {
33
+ return this.cache.has(this.normalizeUrl(url));
34
+ }
35
+ get size() {
36
+ return this.cache.size;
37
+ }
38
+ normalizeUrl(url) {
39
+ return url.split("#")[0];
40
+ }
41
+ }
42
+ //# sourceMappingURL=page-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-cache.js","sourceRoot":"","sources":["../../src/scanner/page-cache.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,MAAM,OAAO,SAAS;IACZ,KAAK,GAAG,IAAI,GAAG,EAAwB,CAAC;IACxC,KAAK,CAAS;IACd,GAAG,CAAY;IAEvB,YAAY,KAAa,EAAE,GAAc;QACvC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;IACjB,CAAC;IAED,4DAA4D;IAC5D,KAAK,CAAC,OAAO;QACX,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,IAAI,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED,wEAAwE;IACxE,KAAK,CAAC,YAAY,CAAC,GAAW;QAC5B,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QACnC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACnC,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC;QAE1B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAC9D,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,+DAA+D;IAC/D,GAAG,CAAC,GAAW;QACb,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;IAChD,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACzB,CAAC;IAEO,YAAY,CAAC,GAAW;QAC9B,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3B,CAAC;CACF"}
@@ -0,0 +1,7 @@
1
+ export interface Factor {
2
+ name: string;
3
+ values: string[];
4
+ }
5
+ export type Combination = Record<string, string>;
6
+ export declare function generatePairwiseCombinations(factors: Factor[]): Combination[];
7
+ //# sourceMappingURL=pairwise.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pairwise.d.ts","sourceRoot":"","sources":["../../src/scanner/pairwise.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEjD,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,WAAW,EAAE,CAmE7E"}
@@ -0,0 +1,64 @@
1
+ export function generatePairwiseCombinations(factors) {
2
+ if (factors.length === 0)
3
+ return [];
4
+ if (factors.length === 1) {
5
+ return factors[0].values.map(v => ({ [factors[0].name]: v }));
6
+ }
7
+ const uncoveredPairs = new Set();
8
+ for (let i = 0; i < factors.length; i++) {
9
+ for (let j = i + 1; j < factors.length; j++) {
10
+ for (const vi of factors[i].values) {
11
+ for (const vj of factors[j].values) {
12
+ uncoveredPairs.add(`${i}:${vi}|${j}:${vj}`);
13
+ }
14
+ }
15
+ }
16
+ }
17
+ const results = [];
18
+ while (uncoveredPairs.size > 0) {
19
+ const firstPair = uncoveredPairs.values().next().value;
20
+ const [partA, partB] = firstPair.split("|");
21
+ const [idxAStr, valA] = partA.split(":");
22
+ const [idxBStr, valB] = partB.split(":");
23
+ const idxA = parseInt(idxAStr);
24
+ const idxB = parseInt(idxBStr);
25
+ const candidate = {};
26
+ candidate[factors[idxA].name] = valA;
27
+ candidate[factors[idxB].name] = valB;
28
+ for (let f = 0; f < factors.length; f++) {
29
+ if (f === idxA || f === idxB)
30
+ continue;
31
+ let bestVal = factors[f].values[0];
32
+ let bestScore = 0;
33
+ for (const val of factors[f].values) {
34
+ candidate[factors[f].name] = val;
35
+ let score = 0;
36
+ for (let other = 0; other < factors.length; other++) {
37
+ if (other === f || !candidate[factors[other].name])
38
+ continue;
39
+ const [lo, hi] = f < other ? [f, other] : [other, f];
40
+ const [loVal, hiVal] = f < other
41
+ ? [val, candidate[factors[other].name]]
42
+ : [candidate[factors[other].name], val];
43
+ const key = `${lo}:${loVal}|${hi}:${hiVal}`;
44
+ if (uncoveredPairs.has(key))
45
+ score++;
46
+ }
47
+ if (score > bestScore) {
48
+ bestScore = score;
49
+ bestVal = val;
50
+ }
51
+ }
52
+ candidate[factors[f].name] = bestVal;
53
+ }
54
+ for (let i = 0; i < factors.length; i++) {
55
+ for (let j = i + 1; j < factors.length; j++) {
56
+ const key = `${i}:${candidate[factors[i].name]}|${j}:${candidate[factors[j].name]}`;
57
+ uncoveredPairs.delete(key);
58
+ }
59
+ }
60
+ results.push({ ...candidate });
61
+ }
62
+ return results;
63
+ }
64
+ //# sourceMappingURL=pairwise.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pairwise.js","sourceRoot":"","sources":["../../src/scanner/pairwise.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { BrowserAdapter } from "../adapter";
2
+ import type { UiPatternType, UiPattern, PatternInstance } from "@sudobility/testomniac_types";
3
+ export declare const PATTERN_TYPE_SELECTORS: Record<UiPatternType, string[]>;
4
+ export interface DetectedPattern extends UiPattern {
5
+ }
6
+ export interface DetectedPatternWithInstances {
7
+ type: UiPatternType;
8
+ selector: string;
9
+ count: number;
10
+ instances: PatternInstance[];
11
+ }
12
+ /**
13
+ * Detect UI patterns with full outerHtml for each instance.
14
+ * Used for decomposition (stripping patterns from content body).
15
+ */
16
+ export declare function detectPatternsWithInstances(adapter: BrowserAdapter): Promise<DetectedPatternWithInstances[]>;
17
+ /**
18
+ * Detect UI patterns (summary only, no outerHtml).
19
+ * Backward-compatible wrapper.
20
+ */
21
+ export declare function detectPatterns(adapter: BrowserAdapter): Promise<DetectedPattern[]>;
22
+ //# sourceMappingURL=pattern-detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pattern-detector.d.ts","sourceRoot":"","sources":["../../src/scanner/pattern-detector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,KAAK,EACV,aAAa,EACb,SAAS,EACT,eAAe,EAChB,MAAM,8BAA8B,CAAC;AAEtC,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,EAAE,CAkDlE,CAAC;AAEF,MAAM,WAAW,eAAgB,SAAQ,SAAS;CAEjD;AAED,MAAM,WAAW,4BAA4B;IAC3C,IAAI,EAAE,aAAa,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,eAAe,EAAE,CAAC;CAC9B;AAED;;;GAGG;AACH,wBAAsB,2BAA2B,CAC/C,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,4BAA4B,EAAE,CAAC,CAwDzC;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,eAAe,EAAE,CAAC,CAO5B"}
@@ -0,0 +1,108 @@
1
+ export const PATTERN_TYPE_SELECTORS = {
2
+ card: ["article", ".card", '[class*="card"]', ".product-item", ".post-item"],
3
+ table: ["table:has(thead)", '[role="grid"]', ".data-table", "table:has(th)"],
4
+ form: ['form:not([role="search"])'],
5
+ modal: ['[role="dialog"]', ".modal", "dialog", '[aria-modal="true"]'],
6
+ toast: ['[role="status"]', ".toast", ".notification", ".snackbar"],
7
+ alert: [".alert", '[role="alert"]', ".notice"],
8
+ tabs: ['[role="tablist"]', ".nav-tabs", ".tabs"],
9
+ accordion: ["details", ".accordion", "[role='region'][aria-labelledby]"],
10
+ carousel: [
11
+ ".carousel",
12
+ ".swiper",
13
+ ".slick-slider",
14
+ '[aria-roledescription="carousel"]',
15
+ ],
16
+ dropdown: ['[role="menu"]', ".dropdown-menu", '[aria-haspopup="true"]'],
17
+ pagination: [
18
+ 'nav[aria-label*="pagination" i]',
19
+ ".pagination",
20
+ '[aria-label*="page" i]',
21
+ ],
22
+ skeleton: [
23
+ ".skeleton",
24
+ '[class*="skeleton"]',
25
+ '[class*="shimmer"]',
26
+ '[aria-busy="true"]',
27
+ ],
28
+ emptyState: [
29
+ ".empty-state",
30
+ '[class*="empty-state"]',
31
+ '[class*="no-results"]',
32
+ '[class*="no-data"]',
33
+ ],
34
+ errorMessage: [
35
+ ".error-message",
36
+ ".form-error",
37
+ ".invalid-feedback",
38
+ '[class*="error-msg"]',
39
+ ],
40
+ progressBar: ['[role="progressbar"]', ".progress", "progress"],
41
+ tooltip: ['[role="tooltip"]', ".tooltip", "[data-tooltip]"],
42
+ badge: [".badge", '[class*="badge"]', ".tag-count"],
43
+ avatar: [".avatar", '[class*="avatar"]'],
44
+ tag: [".tag", ".chip", '[class*="tag-item"]'],
45
+ stepper: [
46
+ ".stepper",
47
+ ".wizard",
48
+ '[class*="step-indicator"]',
49
+ '[role="group"][aria-label*="step" i]',
50
+ ],
51
+ };
52
+ /**
53
+ * Detect UI patterns with full outerHtml for each instance.
54
+ * Used for decomposition (stripping patterns from content body).
55
+ */
56
+ export async function detectPatternsWithInstances(adapter) {
57
+ const results = [];
58
+ const patternData = await adapter.evaluate((...args) => {
59
+ const selectorMap = args[0];
60
+ const found = [];
61
+ for (const [type, selectors] of Object.entries(selectorMap)) {
62
+ for (const selector of selectors) {
63
+ try {
64
+ const elements = document.querySelectorAll(selector);
65
+ if (elements.length > 0) {
66
+ const htmls = [];
67
+ elements.forEach(el => {
68
+ htmls.push(el.outerHTML);
69
+ });
70
+ found.push({ type, selector, count: elements.length, htmls });
71
+ break; // first matching selector per type
72
+ }
73
+ }
74
+ catch {
75
+ // invalid selector, skip
76
+ }
77
+ }
78
+ }
79
+ return found;
80
+ }, PATTERN_TYPE_SELECTORS);
81
+ for (const item of patternData) {
82
+ results.push({
83
+ type: item.type,
84
+ selector: item.selector,
85
+ count: item.count,
86
+ instances: item.htmls.map(html => ({
87
+ type: item.type,
88
+ selector: item.selector,
89
+ outerHtml: html,
90
+ hash: "", // computed later in Node.js
91
+ })),
92
+ });
93
+ }
94
+ return results;
95
+ }
96
+ /**
97
+ * Detect UI patterns (summary only, no outerHtml).
98
+ * Backward-compatible wrapper.
99
+ */
100
+ export async function detectPatterns(adapter) {
101
+ const withInstances = await detectPatternsWithInstances(adapter);
102
+ return withInstances.map(p => ({
103
+ type: p.type,
104
+ selector: p.selector,
105
+ count: p.count,
106
+ }));
107
+ }
108
+ //# sourceMappingURL=pattern-detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pattern-detector.js","sourceRoot":"","sources":["../../src/scanner/pattern-detector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export declare class PhaseTimer {
2
+ private startTime;
3
+ private phases;
4
+ startPhase(name: string): void;
5
+ endPhase(name: string): number;
6
+ totalElapsed(): number;
7
+ getAllDurations(): Record<string, number>;
8
+ }
9
+ //# sourceMappingURL=phase-timer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phase-timer.d.ts","sourceRoot":"","sources":["../../src/scanner/phase-timer.ts"],"names":[],"mappings":"AAAA,qBAAa,UAAU;IACrB,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,MAAM,CAAsD;IAEpE,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAI9B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAO9B,YAAY,IAAI,MAAM;IAItB,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;CAS1C"}
@@ -0,0 +1,27 @@
1
+ export class PhaseTimer {
2
+ startTime = Date.now();
3
+ phases = new Map();
4
+ startPhase(name) {
5
+ this.phases.set(name, { start: Date.now() });
6
+ }
7
+ endPhase(name) {
8
+ const phase = this.phases.get(name);
9
+ if (!phase)
10
+ throw new Error(`Phase "${name}" not started`);
11
+ phase.end = Date.now();
12
+ return phase.end - phase.start;
13
+ }
14
+ totalElapsed() {
15
+ return Date.now() - this.startTime;
16
+ }
17
+ getAllDurations() {
18
+ const result = {};
19
+ for (const [name, phase] of this.phases) {
20
+ if (phase.end) {
21
+ result[name] = phase.end - phase.start;
22
+ }
23
+ }
24
+ return result;
25
+ }
26
+ }
27
+ //# sourceMappingURL=phase-timer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phase-timer.js","sourceRoot":"","sources":["../../src/scanner/phase-timer.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,UAAU;IACb,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,GAAG,IAAI,GAAG,EAA2C,CAAC;IAEpE,UAAU,CAAC,IAAY;QACrB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAC/C,CAAC;IAED,QAAQ,CAAC,IAAY;QACnB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,UAAU,IAAI,eAAe,CAAC,CAAC;QAC3D,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,OAAO,KAAK,CAAC,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC;IACjC,CAAC;IAED,YAAY;QACV,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;IACrC,CAAC;IAED,eAAe;QACb,MAAM,MAAM,GAA2B,EAAE,CAAC;QAC1C,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACxC,IAAI,KAAK,CAAC,GAAG,EAAE,CAAC;gBACd,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC;YACzC,CAAC;QACH,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;CACF"}
@@ -0,0 +1,13 @@
1
+ import type { ApiClient } from "../api/client";
2
+ import type { HtmlComponentType, ReusableHtmlElementResponse } from "@sudobility/testomniac_types";
3
+ export declare class ReusableElementCache {
4
+ private cache;
5
+ private appId;
6
+ private api;
7
+ constructor(appId: number, api: ApiClient);
8
+ preload(): Promise<void>;
9
+ findOrCreate(type: HtmlComponentType, html: string, hash: string): Promise<ReusableHtmlElementResponse>;
10
+ get(hash: string): ReusableHtmlElementResponse | undefined;
11
+ get size(): number;
12
+ }
13
+ //# sourceMappingURL=reusable-element-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reusable-element-cache.d.ts","sourceRoot":"","sources":["../../src/scanner/reusable-element-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,KAAK,EACV,iBAAiB,EACjB,2BAA2B,EAC5B,MAAM,8BAA8B,CAAC;AAEtC,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,KAAK,CAAkD;IAC/D,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,GAAG,CAAY;gBAEX,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS;IAKnC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IASxB,YAAY,CAChB,IAAI,EAAE,iBAAiB,EACvB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,2BAA2B,CAAC;IAcvC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,2BAA2B,GAAG,SAAS;IAI1D,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
@@ -0,0 +1,37 @@
1
+ export class ReusableElementCache {
2
+ cache = new Map();
3
+ appId;
4
+ api;
5
+ constructor(appId, api) {
6
+ this.appId = appId;
7
+ this.api = api;
8
+ }
9
+ async preload() {
10
+ const existing = await this.api.getReusableHtmlElements(this.appId);
11
+ for (const el of existing) {
12
+ if (el.htmlHash) {
13
+ this.cache.set(el.htmlHash, el);
14
+ }
15
+ }
16
+ }
17
+ async findOrCreate(type, html, hash) {
18
+ const cached = this.cache.get(hash);
19
+ if (cached)
20
+ return cached;
21
+ const result = await this.api.findOrCreateReusableHtmlElement({
22
+ appId: this.appId,
23
+ type,
24
+ html,
25
+ hash,
26
+ });
27
+ this.cache.set(hash, result);
28
+ return result;
29
+ }
30
+ get(hash) {
31
+ return this.cache.get(hash);
32
+ }
33
+ get size() {
34
+ return this.cache.size;
35
+ }
36
+ }
37
+ //# sourceMappingURL=reusable-element-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reusable-element-cache.js","sourceRoot":"","sources":["../../src/scanner/reusable-element-cache.ts"],"names":[],"mappings":"AAMA,MAAM,OAAO,oBAAoB;IACvB,KAAK,GAAG,IAAI,GAAG,EAAuC,CAAC;IACvD,KAAK,CAAS;IACd,GAAG,CAAY;IAEvB,YAAY,KAAa,EAAE,GAAc;QACvC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,uBAAuB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpE,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;YAC1B,IAAI,EAAE,CAAC,QAAQ,EAAE,CAAC;gBAChB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;YAClC,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,YAAY,CAChB,IAAuB,EACvB,IAAY,EACZ,IAAY;QAEZ,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC;QAE1B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,+BAA+B,CAAC;YAC5D,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,IAAI;YACJ,IAAI;YACJ,IAAI;SACL,CAAC,CAAC;QACH,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC7B,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,GAAG,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACzB,CAAC;CACF"}
@@ -0,0 +1,3 @@
1
+ import type { BrowserAdapter } from "../adapter";
2
+ export declare function scrollAndDiscoverElements(page: BrowserAdapter): Promise<number>;
3
+ //# sourceMappingURL=scroll-scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scroll-scanner.d.ts","sourceRoot":"","sources":["../../src/scanner/scroll-scanner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,wBAAsB,yBAAyB,CAC7C,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,MAAM,CAAC,CA+BjB"}
@@ -0,0 +1,19 @@
1
+ export async function scrollAndDiscoverElements(page) {
2
+ const viewportHeight = await page.evaluate(() => window.innerHeight);
3
+ const totalHeight = await page.evaluate(() => document.body.scrollHeight);
4
+ let scrolled = 0;
5
+ let newElementsFound = 0;
6
+ const knownCount = await page.evaluate((sel) => document.querySelectorAll(sel).length, 'a[href], button, input, select, textarea, [role="button"], [role="link"]');
7
+ while (scrolled < totalHeight) {
8
+ await page.evaluate((delta) => window.scrollBy(0, delta), viewportHeight);
9
+ scrolled += viewportHeight;
10
+ await new Promise(r => setTimeout(r, 500));
11
+ const currentCount = await page.evaluate((sel) => document.querySelectorAll(sel).length, 'a[href], button, input, select, textarea, [role="button"], [role="link"]');
12
+ if (currentCount > knownCount + newElementsFound) {
13
+ newElementsFound = currentCount - knownCount;
14
+ }
15
+ }
16
+ await page.evaluate(() => window.scrollTo(0, 0));
17
+ return newElementsFound;
18
+ }
19
+ //# sourceMappingURL=scroll-scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scroll-scanner.js","sourceRoot":"","sources":["../../src/scanner/scroll-scanner.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,IAAoB;IAEpB,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAS,GAAG,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IAC7E,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,QAAQ,CACrC,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,CACjC,CAAC;IACF,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,gBAAgB,GAAG,CAAC,CAAC;IACzB,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,QAAQ,CACpC,CAAC,GAAY,EAAE,EAAE,CAAC,QAAQ,CAAC,gBAAgB,CAAC,GAAa,CAAC,CAAC,MAAM,EACjE,0EAA0E,CAC3E,CAAC;IAEF,OAAO,QAAQ,GAAG,WAAW,EAAE,CAAC;QAC9B,MAAM,IAAI,CAAC,QAAQ,CACjB,CAAC,KAAc,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,KAAe,CAAC,EACvD,cAAc,CACf,CAAC;QACF,QAAQ,IAAI,cAAc,CAAC;QAC3B,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QAE3C,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,QAAQ,CACtC,CAAC,GAAY,EAAE,EAAE,CAAC,QAAQ,CAAC,gBAAgB,CAAC,GAAa,CAAC,CAAC,MAAM,EACjE,0EAA0E,CAC3E,CAAC;QACF,IAAI,YAAY,GAAG,UAAU,GAAG,gBAAgB,EAAE,CAAC;YACjD,gBAAgB,GAAG,YAAY,GAAG,UAAU,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,MAAM,IAAI,CAAC,QAAQ,CAAO,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACvD,OAAO,gBAAgB,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { BrowserAdapter } from "../adapter";
2
+ export declare class StateManager {
3
+ private currentPageStateId;
4
+ private currentUrl;
5
+ getCurrentPageStateId(): number | undefined;
6
+ getCurrentUrl(): string;
7
+ update(pageStateId: number, url: string): void;
8
+ matches(targetPageStateId: number | null | undefined): boolean;
9
+ navigateTo(page: BrowserAdapter, url: string): Promise<void>;
10
+ }
11
+ //# sourceMappingURL=state-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-manager.d.ts","sourceRoot":"","sources":["../../src/scanner/state-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,qBAAa,YAAY;IACvB,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,UAAU,CAAc;IAEhC,qBAAqB,IAAI,MAAM,GAAG,SAAS;IAI3C,aAAa,IAAI,MAAM;IAIvB,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAK9C,OAAO,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO;IAMxD,UAAU,CAAC,IAAI,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAInE"}
@@ -0,0 +1,24 @@
1
+ export class StateManager {
2
+ currentPageStateId;
3
+ currentUrl = "";
4
+ getCurrentPageStateId() {
5
+ return this.currentPageStateId;
6
+ }
7
+ getCurrentUrl() {
8
+ return this.currentUrl;
9
+ }
10
+ update(pageStateId, url) {
11
+ this.currentPageStateId = pageStateId;
12
+ this.currentUrl = url;
13
+ }
14
+ matches(targetPageStateId) {
15
+ if (targetPageStateId === undefined || targetPageStateId === null)
16
+ return true;
17
+ return this.currentPageStateId === targetPageStateId;
18
+ }
19
+ async navigateTo(page, url) {
20
+ await page.goto(url, { waitUntil: "networkidle0", timeout: 30_000 });
21
+ this.currentUrl = page.url();
22
+ }
23
+ }
24
+ //# sourceMappingURL=state-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-manager.js","sourceRoot":"","sources":["../../src/scanner/state-manager.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,YAAY;IACf,kBAAkB,CAAqB;IACvC,UAAU,GAAW,EAAE,CAAC;IAEhC,qBAAqB;QACnB,OAAO,IAAI,CAAC,kBAAkB,CAAC;IACjC,CAAC;IAED,aAAa;QACX,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED,MAAM,CAAC,WAAmB,EAAE,GAAW;QACrC,IAAI,CAAC,kBAAkB,GAAG,WAAW,CAAC;QACtC,IAAI,CAAC,UAAU,GAAG,GAAG,CAAC;IACxB,CAAC;IAED,OAAO,CAAC,iBAA4C;QAClD,IAAI,iBAAiB,KAAK,SAAS,IAAI,iBAAiB,KAAK,IAAI;YAC/D,OAAO,IAAI,CAAC;QACd,OAAO,IAAI,CAAC,kBAAkB,KAAK,iBAAiB,CAAC;IACvD,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,IAAoB,EAAE,GAAW;QAChD,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;QACrE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC/B,CAAC;CACF"}
package/package.json ADDED
@@ -0,0 +1,77 @@
1
+ {
2
+ "name": "@sudobility/testomniac_runner_service",
3
+ "version": "0.1.24",
4
+ "description": "Shared scanning logic for Testomniac scanner and Chrome extension",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "scripts": {
15
+ "build": "tsc -p tsconfig.build.json",
16
+ "dev": "tsc --watch",
17
+ "test": "vitest run",
18
+ "test:watch": "vitest",
19
+ "typecheck": "tsc --noEmit",
20
+ "lint": "bunx eslint src/",
21
+ "lint:fix": "bunx eslint src/ --fix",
22
+ "format": "bunx prettier --write \"src/**/*.ts\"",
23
+ "format:check": "bunx prettier --check \"src/**/*.ts\"",
24
+ "verify": "bun run typecheck && bun run lint && bun run test && bun run build",
25
+ "prepublishOnly": "bun run verify"
26
+ },
27
+ "files": [
28
+ "dist/**/*",
29
+ "CLAUDE.md"
30
+ ],
31
+ "keywords": [
32
+ "testomniac",
33
+ "scanning",
34
+ "browser",
35
+ "testing"
36
+ ],
37
+ "author": "Sudobility",
38
+ "license": "BUSL-1.1",
39
+ "peerDependencies": {
40
+ "@sudobility/testomniac_types": "^0.0.35",
41
+ "openai": ">=6.0.0",
42
+ "react": ">=18.0.0"
43
+ },
44
+ "peerDependenciesMeta": {
45
+ "openai": {
46
+ "optional": true
47
+ },
48
+ "react": {
49
+ "optional": true
50
+ }
51
+ },
52
+ "devDependencies": {
53
+ "@eslint/js": "^9.38.0",
54
+ "@sudobility/testomniac_types": "^0.0.35",
55
+ "@types/node": "^25.6.0",
56
+ "@types/react": "^19.2.0",
57
+ "@typescript-eslint/eslint-plugin": "^8.46.2",
58
+ "@typescript-eslint/parser": "^8.46.2",
59
+ "eslint": "^9.39.2",
60
+ "eslint-config-prettier": "^10.1.8",
61
+ "eslint-plugin-prettier": "^5.5.5",
62
+ "globals": "^16.4.0",
63
+ "openai": "^6.7.0",
64
+ "prettier": "^3.7.4",
65
+ "react": "^19.2.0",
66
+ "typescript": "~5.9.3",
67
+ "vitest": "^4.0.15",
68
+ "zustand": "^5.0.0"
69
+ },
70
+ "publishConfig": {
71
+ "access": "public"
72
+ },
73
+ "repository": {
74
+ "type": "git",
75
+ "url": "git@github.com:johnqh/testomniac_runner_service.git"
76
+ }
77
+ }