@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
package/CLAUDE.md ADDED
@@ -0,0 +1,338 @@
1
+ # Testomniac Scanning Service
2
+
3
+ Shared TypeScript library containing all core scanning business logic, browser abstraction, extractors, planners, detectors, AI analysis, test generation, scan orchestration, and API client for the Testomniac testing platform.
4
+
5
+ **Package**: `@sudobility/testomniac_runner_service` v0.1.1 (published to npm, public)
6
+
7
+ ## Tech Stack
8
+
9
+ - **Language**: TypeScript (strict mode)
10
+ - **Runtime**: Bun
11
+ - **Package Manager**: Bun (do not use npm/yarn/pnpm for installing dependencies)
12
+ - **Build**: TypeScript compiler (tsc) to `dist/`
13
+ - **Test**: Vitest
14
+ - **Module**: ES Module (ESM only)
15
+
16
+ ## Project Structure
17
+
18
+ ```
19
+ src/
20
+ ├── index.ts # Public API exports (all modules below)
21
+ ├── adapter.ts # BrowserAdapter interface definition
22
+ ├── api/
23
+ │ └── client.ts # ApiClient — HTTP client for testomniac_api (55+ methods)
24
+ ├── browser/
25
+ │ ├── dom-snapshot.ts # buildDomSnapshot: two-pass DOM element discovery
26
+ │ ├── page-utils.ts # normalizeHtml, computeHashes (async sha256: Node + browser)
27
+ │ └── page-utils.test.ts
28
+ ├── config/
29
+ │ └── constants.ts # Timeouts, limits, URL patterns, error patterns
30
+ ├── extractors/ # Modular element extraction system (11 files)
31
+ │ ├── index.ts # extractActionableItems — registers extractors in priority order
32
+ │ ├── types.ts # ItemExtractor, DomSnapshotEntry, ExtractorCandidate, ActionKind
33
+ │ ├── helpers.ts # createCandidate, withResolvedSelector, uniqueBySelector
34
+ │ ├── selectors.ts # classifyActionKind: navigate|select|fill|toggle|click
35
+ │ ├── text-inputs.ts # <input>, <textarea>, role="textbox", contenteditable
36
+ │ ├── selects.ts # <select>, role="combobox"
37
+ │ ├── toggles.ts # checkbox, radio, role="switch"
38
+ │ ├── product-actions.ts # E-commerce actions (cart, checkout, options)
39
+ │ ├── buttons.ts # <button>, input[type="submit"]
40
+ │ ├── clickables.ts # Remaining clickable elements (links, divs, etc.)
41
+ │ └── form-extractor.ts # extractForms: discover form structures
42
+ ├── planners/
43
+ │ └── fill-value-planner.ts # RuleBasedFillValuePlanner: multi-signal form value heuristics
44
+ ├── detectors/ # Page-level quality checks + bug/modal detection
45
+ │ ├── index.ts # Re-exports all detectors
46
+ │ ├── bug-detector.ts # Comprehensive bug detection (broken links, visual, content, media)
47
+ │ ├── modal-handler.ts # Detect & dismiss Bootstrap, ARIA dialog, Fancybox, etc.
48
+ │ ├── link-checker.ts # Broken link detection (HEAD requests, 4xx/5xx)
49
+ │ ├── visual-checker.ts # Broken images, missing alt, duplicate IDs/headings, empty links
50
+ │ ├── content-checker.ts # Placeholder text, error pages, invalid prices, short pages
51
+ │ ├── functional-checker.ts # Console errors, network failures, error-after-click
52
+ │ └── *.test.ts # Colocated tests
53
+ ├── ai/ # AI analysis pipeline (GPT-4o)
54
+ │ ├── analyzer.ts # runAiAnalysis: orchestrates persona → use case → input generation
55
+ │ ├── persona-generator.ts # Generate 1-5 user personas from page content
56
+ │ ├── use-case-generator.ts # Generate 2-8 use cases per persona
57
+ │ ├── input-generator.ts # Generate realistic form inputs per use case
58
+ │ └── token-tracker.ts # AI token usage tracking (reported to API)
59
+ ├── generation/ # Test case generation (9 template files)
60
+ │ ├── generator.ts # generateTestCases: orchestrator
61
+ │ ├── suite-tagger.ts # Priority assignment (critical → low) by route keywords
62
+ │ ├── render.ts # Render test template (navigate + assert visibility)
63
+ │ ├── interaction.ts # Interaction test template (click/hover sequences)
64
+ │ ├── form.ts # Form positive test template
65
+ │ ├── form-negative.ts # Form negative/validation test template
66
+ │ ├── password.ts # Password requirement test template
67
+ │ ├── navigation.ts # Navigation flow test template
68
+ │ └── e2e.ts # E2E multi-step path enumeration + template
69
+ ├── orchestrator/ # Scan pipeline orchestration
70
+ │ ├── types.ts # ScanConfig, ScanEventHandler, TestExecutor, ScanResult, ScanPhase
71
+ │ ├── orchestrator.ts # runScan(adapter, config, api, eventHandler, testExecutor?)
72
+ │ ├── mouse-scanning.ts # Phase 1a: navigate → extract → hover/click → detect → discover
73
+ │ ├── ai-analysis.ts # Phase 1b: GPT-4o persona/use case/input generation
74
+ │ ├── input-scanning.ts # Phase 1c: pairwise form filling with AI-generated values
75
+ │ ├── test-generation.ts # Phase 3: JSON test case creation
76
+ │ └── test-execution.ts # Phase 4: test execution via TestExecutor interface
77
+ ├── scanner/ # Scanner utility modules
78
+ │ ├── action-classifier.ts # normalizeHref, shouldExpectNavigation, getActionPriority
79
+ │ ├── navigator.ts # Cross-page navigation tracking
80
+ │ ├── action-queue.ts # In-memory action queue
81
+ │ ├── state-manager.ts # Page state tracker
82
+ │ ├── loop-guard.ts # Action dedup + caps (200/page, 5000 total)
83
+ │ ├── phase-timer.ts # Per-phase duration tracking
84
+ │ ├── component-detector.ts # Reusable UI component detection across pages
85
+ │ ├── email-detector.ts # Email verification flow detection
86
+ │ ├── scroll-scanner.ts # Lazy-loaded element discovery via scrolling
87
+ │ ├── pairwise.ts # Pairwise combination generator
88
+ │ ├── issue-detector.ts # detectDeadClick, detectErrorOnPage, detectConsoleErrors
89
+ │ └── *.test.ts # Colocated tests
90
+ ├── plugins/ # Plugin interface + registry
91
+ │ ├── types.ts # Plugin, PluginContext, PluginResult, PluginIssue interfaces
92
+ │ └── registry.ts # registerPlugin, getPlugin, getEnabledPlugins
93
+ └── domain/
94
+ ├── types.ts # Re-exports from @sudobility/testomniac_types
95
+ └── url-ownership.ts # normalizeBaseUrl, getRegistrableDomain, emailMatchesUrlDomain
96
+ ```
97
+
98
+ ## Commands
99
+
100
+ ```bash
101
+ bun run build # Compile TypeScript to dist/ (tsc -p tsconfig.build.json)
102
+ bun run dev # Watch mode (tsc --watch)
103
+ bun run test # Run Vitest tests
104
+ bun run test:watch # Vitest watch mode
105
+ bun run typecheck # TypeScript check only (tsc --noEmit)
106
+ bun run lint # ESLint
107
+ bun run lint:fix # ESLint auto-fix
108
+ bun run format # Prettier write
109
+ bun run format:check # Prettier check
110
+ bun run verify # typecheck + lint + test + build (run before publish)
111
+ ```
112
+
113
+ ## Main Entry Point: `runScan()`
114
+
115
+ The orchestrator is the primary consumer API. Both `testomniac_runner` and `testomniac_extension` call it:
116
+
117
+ ```typescript
118
+ runScan(
119
+ adapter: BrowserAdapter, // ChromeAdapter or PuppeteerAdapter
120
+ config: ScanConfig, // runId, appId, baseUrl, phases, options
121
+ api: ApiClient, // HTTP client for testomniac_api
122
+ eventHandler: ScanEventHandler, // Progress callbacks
123
+ testExecutor?: TestExecutor // Optional test runner (server-side only)
124
+ ): Promise<ScanResult>
125
+ ```
126
+
127
+ ### ScanConfig
128
+
129
+ ```typescript
130
+ interface ScanConfig {
131
+ runId: number;
132
+ appId: number;
133
+ baseUrl: string;
134
+ phases: ScanPhase[]; // ["mouse_scanning", "ai_analysis", "input_scanning", ...]
135
+ sizeClass?: string;
136
+ openaiApiKey?: string;
137
+ openaiModel?: string;
138
+ testWorkerCount?: number;
139
+ }
140
+ ```
141
+
142
+ ### ScanEventHandler
143
+
144
+ Callback interface for progress reporting. Consumers implement this to bridge events to their UI (side panel messages for extension, Pino logging for scanner):
145
+
146
+ ```typescript
147
+ interface ScanEventHandler {
148
+ onPageFound(page): void;
149
+ onPageStateCreated(state): void;
150
+ onActionCompleted(action): void;
151
+ onIssueDetected(issue): void;
152
+ onPhaseChanged(phase): void;
153
+ onStatsUpdated(stats): void;
154
+ onScreenshotCaptured(data): void;
155
+ onScanComplete(summary): void;
156
+ onError(error): void;
157
+ }
158
+ ```
159
+
160
+ ### TestExecutor
161
+
162
+ Optional interface for test execution. Only the server-side scanner provides this (via Puppeteer worker pool):
163
+
164
+ ```typescript
165
+ interface TestExecutor {
166
+ executeTestCase(actions: TestAction[], screen: Screen): Promise<{ passed: boolean; error?: string; durationMs: number }>;
167
+ }
168
+ ```
169
+
170
+ ## Scan Pipeline Phases
171
+
172
+ ```
173
+ Phase: mouse_scanning
174
+ Navigate → buildDomSnapshot → extractActionableItems → hover/click each →
175
+ bug detection → modal dismissal → discover pages → enqueue
176
+ Output: pages, page_states, actionable_items, actions, issues
177
+
178
+ Phase: ai_analysis
179
+ Feed page content to GPT-4o → generate personas, use cases, input values
180
+ Output: personas, use_cases, input_values
181
+
182
+ Phase: input_scanning
183
+ Fill forms with pairwise combinations of AI-generated values
184
+ Output: form fill actions, new page_states
185
+
186
+ Phase: test_generation
187
+ Create JSON test cases: render, interaction, form, navigation, E2E
188
+ Assign priority tags based on route keywords
189
+ Output: test_cases with actions_json
190
+
191
+ Phase: test_execution
192
+ Delegate to TestExecutor interface for actual execution
193
+ Output: test_runs (pass/fail), issues for failures
194
+ ```
195
+
196
+ ## BrowserAdapter Interface
197
+
198
+ The core abstraction that allows the same scanning logic to work with both Puppeteer (server-side scanner) and Chrome DevTools Protocol (browser extension):
199
+
200
+ ```typescript
201
+ interface BrowserAdapter {
202
+ goto(url, options?): Promise<void>
203
+ waitForNavigation(options?): Promise<void>
204
+ url(): Promise<string>
205
+ content(): Promise<string>
206
+ evaluate<T>(fn, ...args): Promise<T>
207
+ waitForSelector(selector, options?): Promise<void>
208
+ click(selector, options?): Promise<void>
209
+ hover(selector, options?): Promise<void>
210
+ type(selector, text): Promise<void>
211
+ select(selector, value): Promise<void>
212
+ pressKey(key): Promise<void>
213
+ screenshot(options?): Promise<Uint8Array>
214
+ setViewport(width, height): Promise<void>
215
+ on(event, handler): void
216
+ close(): Promise<void>
217
+ }
218
+ ```
219
+
220
+ **Implementations**:
221
+ - `ChromeAdapter` in `testomniac_extension/src/adapters/ChromeAdapter.ts` (CDP via chrome.debugger)
222
+ - `PuppeteerAdapter` in `testomniac_runner/src/adapters/PuppeteerAdapter.ts` (Puppeteer page wrapper)
223
+
224
+ ## ApiClient
225
+
226
+ HTTP client for the `testomniac_api` scanner endpoints. Initialized via singleton factory:
227
+
228
+ ```typescript
229
+ const client = getApiClient(baseUrl, apiKey);
230
+ ```
231
+
232
+ **Method categories** (55+ methods):
233
+ - **Run management**: `getPendingRun()`, `updateRunPhase()`, `updateRunStats()`, `updatePhaseDuration()`, `completeRun()`
234
+ - **Page/state tracking**: `findOrCreatePage()`, `createPageState()`, `findMatchingPageState()`, `getPageState()`
235
+ - **Actionable items**: `insertActionableItems()`, `getItemsByPageState()`
236
+ - **Actions**: `createAction()`, `getNextOpenAction()`, `startAction()`, `completeAction()`, `getActionChain()`
237
+ - **Personas/use cases**: `createPersona()`, `createUseCase()`, `createInputValue()`
238
+ - **Forms/tests**: `insertForm()`, `insertTestCase()`, `createTestRun()`, `completeTestRun()`
239
+ - **Issues**: `createIssue()`, `getIssuesByRun()`
240
+ - **Other**: AI usage tracking, report emails, component saving, `getApp()`
241
+
242
+ All methods communicate via HTTP with `X-Scanner-Key` header authentication.
243
+
244
+ ## Extractors
245
+
246
+ The extraction system uses a two-pass approach in `dom-snapshot.ts`:
247
+
248
+ **Pass 1**: Query comprehensive CSS selectors for interactive elements (inputs, buttons, links, ARIA roles, event handlers, tabindex, contenteditable)
249
+
250
+ **Pass 2**: Find `cursor:pointer` elements missed by Pass 1 (framework-specific clickable divs, etc.)
251
+
252
+ 6 extractors registered in priority order: textInputs, selects, toggles, productActions, buttons, clickables. Each returns candidates that are deduplicated and classified into action kinds: `navigate`, `select`, `fill`, `toggle`, `click`.
253
+
254
+ ## Detectors
255
+
256
+ ### Bug Detector
257
+ Comprehensive bug detection combining broken link checking, visual analysis, content analysis, and media issues. Runs inline during mouse scanning.
258
+
259
+ ### Modal Handler
260
+ Detects and dismisses Bootstrap, Popup Maker, ARIA dialog, Fancybox modals. Uses close button, overlay click, or Escape key.
261
+
262
+ ### Link Checker / Visual Checker / Content Checker / Functional Checker
263
+ Individual page-level quality check modules. Pure functions that take HTML/text and return issue arrays.
264
+
265
+ ## Fill Value Planner
266
+
267
+ `RuleBasedFillValuePlanner` uses multi-signal heuristics for form field values:
268
+ 1. HTML input type (email, password, tel, date, etc.)
269
+ 2. HTML5 autocomplete attribute
270
+ 3. Keyword matching on combined signals (name, id, placeholder, label) — English, Spanish, French, German
271
+ 4. Default by tag
272
+
273
+ ## Key Constants
274
+
275
+ ```typescript
276
+ SCAN_TIMEOUT_MS = 300_000 // 5 min total scan timeout
277
+ ACTION_TIMEOUT_MS = 10_000 // 10 sec per action
278
+ TEST_TIMEOUT_MS = 30_000 // 30 sec per test
279
+ NETWORK_IDLE_TIMEOUT_MS = 5_000 // 5 sec network idle
280
+ POST_ACTION_SETTLE_MS = 2_000 // 2 sec post-action settle
281
+ HOVER_DELAY_MS = 500 // 500ms hover delay
282
+ MAX_PAGE_LIMIT = 100 // Max pages per run
283
+ MAX_E2E_PATHS = 20 // Max end-to-end test paths
284
+ MAX_E2E_DEPTH = 6 // Max steps in E2E path
285
+ SCREENSHOT_QUALITY = 72 // JPEG quality
286
+ DEFAULT_WORKERS = 3 // Concurrent test workers
287
+ ```
288
+
289
+ ## Page State Hashing
290
+
291
+ `computeHashes()` is **async** and uses universal SHA-256 (Node.js `crypto.createHash` when available, falls back to Web `crypto.subtle` for browser environments). Creates 4 hashes for deduplication:
292
+ - **htmlHash**: SHA-256 of raw HTML
293
+ - **normalizedHtmlHash**: SHA-256 of whitespace-normalized HTML
294
+ - **textHash**: SHA-256 of visible text only
295
+ - **actionableHash**: SHA-256 of sorted visible interactive items
296
+
297
+ ## Dependencies
298
+
299
+ **Peer (required)**:
300
+ - `@sudobility/testomniac_types` ^0.0.21
301
+
302
+ **Peer (optional)**:
303
+ - `openai` >=6.0.0
304
+ - `react` >=18.0.0
305
+
306
+ **Dev**: TypeScript ~5.9.3, Vitest 4, ESLint 9, Prettier 3
307
+
308
+ ## Related Projects (Testomniac Ecosystem)
309
+
310
+ This library is the **shared foundation** consumed by both scanning clients:
311
+
312
+ - **testomniac_runner** — Server-side Puppeteer worker. Thin wrapper that calls `runScan()` with `PuppeteerAdapter` and a `TestExecutor` backed by a worker pool. Provides auth, email, runner, and plugin implementations.
313
+ - **testomniac_extension** — Chrome extension. Thin wrapper that calls `runScan()` with `ChromeAdapter`. Only runs `mouse_scanning` phase. Bridges events to side panel UI.
314
+ - **testomniac_api** — REST API backend that `ApiClient` communicates with. Stores all scan data.
315
+ - **testomniac_types** (`@sudobility/testomniac_types`) — Shared type definitions re-exported by this library.
316
+
317
+ ## Coding Patterns
318
+
319
+ - **`runScan()` is the single entry point**: Both consumers call `runScan()` with their adapter, config, and event handler. All scanning phases, extraction, detection, and orchestration are managed internally.
320
+ - **Pure functions for detectors**: All detector modules export pure functions that take HTML/text and return issue arrays. No side effects, easy to test.
321
+ - **Interface-driven browser abstraction**: `BrowserAdapter` is a plain TypeScript interface, not a class. Implementations in consumer packages.
322
+ - **Singleton API client**: `getApiClient(baseUrl, apiKey)` returns a cached instance. Call once during initialization.
323
+ - **Async `computeHashes`**: Uses a universal `sha256()` that auto-detects Node vs browser runtime. Callers must `await` it.
324
+ - **Colocated tests**: Test files live next to source files (`*.test.ts` pattern). Run with `bun run test`.
325
+ - **Hash-based dedup**: Page states are compared via 4-level hashing, not string equality.
326
+ - **Constants, not config**: Timeouts, limits, and patterns are hardcoded constants. To change them, edit `config/constants.ts` and republish.
327
+ - **Plugin interface is defined here, implementations live in consumers**: `Plugin`, `PluginContext`, `PluginResult` types and the registry are in this library. Actual plugin code (SEO, security, content, UI consistency) lives in `testomniac_runner`.
328
+
329
+ ## Gotchas
330
+
331
+ - **Published to npm**: This is a library, not an application. Changes require `bun run verify` + `npm publish`. Consumer packages must update their dependency version.
332
+ - **No runtime dependencies**: All dependencies are peer or dev. Consumers must provide `@sudobility/testomniac_types` at minimum.
333
+ - **`computeHashes` is async**: Uses `await sha256()` internally with universal Node + browser support. The extension shims `node:crypto` via `SubtleCrypto` at the Vite level, but `page-utils.ts` also has its own runtime detection.
334
+ - **Constants are compile-time**: Changing a constant requires republishing. Consumer packages pick up changes only after updating their dependency.
335
+ - **LoopGuard caps are per-instance**: Each scanner run creates its own `LoopGuard` instance. The 200/5000 limits apply per run, not globally.
336
+ - **No logging**: This library does not import any logger. Consumers are responsible for logging around detector/scanner calls.
337
+ - **Plugin types reference Puppeteer and OpenAI**: `PluginContext` in `plugins/types.ts` references `Page` from `puppeteer-core` and `OpenAI`. These are optional peer dependencies. The extension does not use plugins.
338
+ - **`buildDomSnapshot` runs inside `page.evaluate()`**: The DOM snapshot code executes in the browser context. It cannot reference Node.js modules or closures from the calling scope.
package/README.md ADDED
@@ -0,0 +1,15 @@
1
+ # testomniac_runner_service
2
+
3
+ To install dependencies:
4
+
5
+ ```bash
6
+ bun install
7
+ ```
8
+
9
+ To run:
10
+
11
+ ```bash
12
+ bun run index.ts
13
+ ```
14
+
15
+ This project was created using `bun init` in bun v1.3.10. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Abstract browser interface that both Puppeteer (server) and Chrome APIs (extension) implement.
3
+ */
4
+ export interface BrowserAdapter {
5
+ /** Navigate to a URL */
6
+ goto(url: string, options?: {
7
+ waitUntil?: string;
8
+ timeout?: number;
9
+ }): Promise<void>;
10
+ /** Click an element by CSS selector */
11
+ click(selector: string, options?: {
12
+ timeout?: number;
13
+ }): Promise<void>;
14
+ /** Hover over an element by CSS selector */
15
+ hover(selector: string, options?: {
16
+ timeout?: number;
17
+ }): Promise<void>;
18
+ /** Type text into an element */
19
+ type(selector: string, text: string): Promise<void>;
20
+ /** Wait for an element to appear */
21
+ waitForSelector(selector: string, options?: {
22
+ visible?: boolean;
23
+ timeout?: number;
24
+ }): Promise<boolean>;
25
+ /** Wait for navigation to complete */
26
+ waitForNavigation(options?: {
27
+ waitUntil?: string;
28
+ timeout?: number;
29
+ }): Promise<void>;
30
+ /** Execute JavaScript in the page context */
31
+ evaluate<T>(fn: string | ((...args: unknown[]) => T), ...args: unknown[]): Promise<T>;
32
+ /** Get the full page HTML */
33
+ content(): Promise<string>;
34
+ /** Get the current URL */
35
+ url(): string;
36
+ /** Take a screenshot */
37
+ screenshot(options?: {
38
+ type?: string;
39
+ quality?: number;
40
+ }): Promise<Uint8Array>;
41
+ /** Set viewport dimensions */
42
+ setViewport(width: number, height: number): Promise<void>;
43
+ /** Press a keyboard key */
44
+ pressKey(key: string): Promise<void>;
45
+ /** Select an option in a <select> element */
46
+ select(selector: string, value: string): Promise<void>;
47
+ /** Close the page/tab */
48
+ close(): Promise<void>;
49
+ /** Subscribe to page events */
50
+ on(event: "console" | "response", handler: (...args: unknown[]) => void): void;
51
+ /** Get the current URL (async — needed by adapters that require async I/O for URL lookup) */
52
+ getUrl(): Promise<string>;
53
+ /** Submit a text entry by pressing Enter on the focused field */
54
+ submitTextEntry(selector: string): Promise<void>;
55
+ }
56
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,wBAAwB;IACxB,IAAI,CACF,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GACjD,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB,uCAAuC;IACvC,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvE,4CAA4C;IAC5C,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvE,gCAAgC;IAChC,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpD,oCAAoC;IACpC,eAAe,CACb,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAChD,OAAO,CAAC,OAAO,CAAC,CAAC;IAEpB,sCAAsC;IACtC,iBAAiB,CAAC,OAAO,CAAC,EAAE;QAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAElB,6CAA6C;IAC7C,QAAQ,CAAC,CAAC,EACR,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,EACxC,GAAG,IAAI,EAAE,OAAO,EAAE,GACjB,OAAO,CAAC,CAAC,CAAC,CAAC;IAEd,6BAA6B;IAC7B,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAE3B,0BAA0B;IAC1B,GAAG,IAAI,MAAM,CAAC;IAEd,wBAAwB;IACxB,UAAU,CAAC,OAAO,CAAC,EAAE;QACnB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAExB,8BAA8B;IAC9B,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE1D,2BAA2B;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAErC,6CAA6C;IAC7C,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvD,yBAAyB;IACzB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvB,+BAA+B;IAC/B,EAAE,CACA,KAAK,EAAE,SAAS,GAAG,UAAU,EAC7B,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,GACpC,IAAI,CAAC;IAER,6FAA6F;IAC7F,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAE1B,iEAAiE;IACjE,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClD"}
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":""}
@@ -0,0 +1,16 @@
1
+ import type OpenAI from "openai";
2
+ import type { ApiClient } from "../api/client";
3
+ import type { FormField } from "../domain/types";
4
+ export interface AnalyzerOptions {
5
+ appId: number;
6
+ runId: number;
7
+ forms: Array<{
8
+ pageUrl: string;
9
+ fields: FormField[];
10
+ }>;
11
+ openaiClient: OpenAI;
12
+ api: ApiClient;
13
+ onComplete?: (personas: string[], useCases: string[]) => void;
14
+ }
15
+ export declare function runAiAnalysis(options: AnalyzerOptions): Promise<void>;
16
+ //# sourceMappingURL=analyzer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../../src/ai/analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AACjC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAKjD,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,SAAS,EAAE,CAAA;KAAE,CAAC,CAAC;IACvD,YAAY,EAAE,MAAM,CAAC;IACrB,GAAG,EAAE,SAAS,CAAC;IACf,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CAC/D;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAuF3E"}
@@ -0,0 +1,55 @@
1
+ import { generatePersonas } from "./persona-generator";
2
+ import { generateUseCases } from "./use-case-generator";
3
+ import { generateInputValues } from "./input-generator";
4
+ export async function runAiAnalysis(options) {
5
+ const { appId, forms, openaiClient: client, api } = options;
6
+ const pages = await api.getPagesByApp(appId);
7
+ const pageContents = [];
8
+ for (const page of pages) {
9
+ const states = await api.getPageStates(page.id);
10
+ for (const state of states) {
11
+ if (state.contentText && state.contentText.trim().length > 50) {
12
+ pageContents.push(`[${page.relativePath}]\n${state.contentText.slice(0, 1000)}`);
13
+ }
14
+ }
15
+ }
16
+ if (pageContents.length === 0)
17
+ return;
18
+ const summaryResponse = await client.chat.completions.create({
19
+ model: "gpt-4o",
20
+ messages: [
21
+ {
22
+ role: "user",
23
+ content: `Summarize this website in 2-3 sentences based on these page contents:\n\n${pageContents
24
+ .slice(0, 5)
25
+ .map(c => c.slice(0, 500))
26
+ .join("\n\n")}`,
27
+ },
28
+ ],
29
+ temperature: 0.3,
30
+ });
31
+ const siteSummary = summaryResponse.choices[0].message.content || "Unknown website";
32
+ const personaResults = await generatePersonas(client, siteSummary, pageContents);
33
+ const personaNames = [];
34
+ const useCaseNames = [];
35
+ for (const pr of personaResults) {
36
+ const persona = await api.createPersona(appId, pr.name, pr.description);
37
+ personaNames.push(pr.name);
38
+ const ucResults = await generateUseCases(client, pr.name, pr.description, siteSummary, pageContents);
39
+ for (const ucr of ucResults) {
40
+ const useCase = await api.createUseCase(persona.id, ucr.name, ucr.description);
41
+ useCaseNames.push(ucr.name);
42
+ for (const form of forms) {
43
+ const textFields = form.fields.filter(f => ["text", "email", "tel", "url", "search", "textarea"].includes(f.type));
44
+ if (textFields.length === 0)
45
+ continue;
46
+ const ivResults = await generateInputValues(client, pr.name, ucr.name, textFields);
47
+ for (const iv of ivResults) {
48
+ await api.createInputValue(useCase.id, iv.fieldSelector, iv.fieldName, iv.value);
49
+ }
50
+ }
51
+ }
52
+ }
53
+ options.onComplete?.(personaNames, useCaseNames);
54
+ }
55
+ //# sourceMappingURL=analyzer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzer.js","sourceRoot":"","sources":["../../src/ai/analyzer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type OpenAI from "openai";
2
+ import type { FormField } from "../domain/types";
3
+ export interface InputValueResult {
4
+ fieldSelector: string;
5
+ fieldName: string;
6
+ value: string;
7
+ }
8
+ export declare function generateInputValues(client: OpenAI, personaName: string, useCaseName: string, fields: FormField[]): Promise<InputValueResult[]>;
9
+ //# sourceMappingURL=input-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input-generator.d.ts","sourceRoot":"","sources":["../../src/ai/input-generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AACjC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,SAAS,EAAE,GAClB,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAwB7B"}
@@ -0,0 +1,20 @@
1
+ export async function generateInputValues(client, personaName, useCaseName, fields) {
2
+ const fieldDescriptions = fields
3
+ .map(f => `selector: "${f.selector}", name: "${f.name}", type: "${f.type}", label: "${f.label}", required: ${f.required}${f.options ? `, options: [${f.options.join(", ")}]` : ""}`)
4
+ .join("\n");
5
+ const prompt = `For the persona "${personaName}" performing the use case "${useCaseName}", generate realistic form input values for these fields:
6
+
7
+ ${fieldDescriptions}
8
+
9
+ Respond with a JSON object with a "values" key containing an array of objects with "fieldSelector", "fieldName", and "value" fields. Use realistic data appropriate for this persona and use case.`;
10
+ const response = await client.chat.completions.create({
11
+ model: "gpt-4o",
12
+ messages: [{ role: "user", content: prompt }],
13
+ response_format: { type: "json_object" },
14
+ temperature: 0.3,
15
+ });
16
+ const content = response.choices[0].message.content || '{"values":[]}';
17
+ const parsed = JSON.parse(content);
18
+ return parsed.values || parsed.inputValues || [];
19
+ }
20
+ //# sourceMappingURL=input-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input-generator.js","sourceRoot":"","sources":["../../src/ai/input-generator.ts"],"names":[],"mappings":"AASA,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAAc,EACd,WAAmB,EACnB,WAAmB,EACnB,MAAmB;IAEnB,MAAM,iBAAiB,GAAG,MAAM;SAC7B,GAAG,CACF,CAAC,CAAC,EAAE,CACF,cAAc,CAAC,CAAC,QAAQ,aAAa,CAAC,CAAC,IAAI,aAAa,CAAC,CAAC,IAAI,cAAc,CAAC,CAAC,KAAK,gBAAgB,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAC5K;SACA,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,MAAM,MAAM,GAAG,oBAAoB,WAAW,8BAA8B,WAAW;;EAEvF,iBAAiB;;mMAEgL,CAAC;IAElM,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;QACpD,KAAK,EAAE,QAAQ;QACf,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAC7C,eAAe,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE;QACxC,WAAW,EAAE,GAAG;KACjB,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,IAAI,eAAe,CAAC;IACvE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACnC,OAAO,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC;AACnD,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type OpenAI from "openai";
2
+ export interface PersonaResult {
3
+ name: string;
4
+ description: string;
5
+ }
6
+ export declare function generatePersonas(client: OpenAI, siteSummary: string, pageContents: string[]): Promise<PersonaResult[]>;
7
+ //# sourceMappingURL=persona-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persona-generator.d.ts","sourceRoot":"","sources":["../../src/ai/persona-generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAEjC,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EAAE,GACrB,OAAO,CAAC,aAAa,EAAE,CAAC,CAuB1B"}
@@ -0,0 +1,23 @@
1
+ export async function generatePersonas(client, siteSummary, pageContents) {
2
+ const prompt = `You are analyzing a website. Based on the following site content, identify the distinct types of users (personas) who would use this site. For each persona, provide a name and a brief description.
3
+
4
+ Site summary: ${siteSummary}
5
+
6
+ Page contents (sampled):
7
+ ${pageContents
8
+ .slice(0, 10)
9
+ .map((c, i) => `Page ${i + 1}: ${c.slice(0, 500)}`)
10
+ .join("\n\n")}
11
+
12
+ Respond with a JSON object with a "personas" key containing an array of objects with "name" and "description" fields. Minimum 1 persona, maximum 5.`;
13
+ const response = await client.chat.completions.create({
14
+ model: "gpt-4o",
15
+ messages: [{ role: "user", content: prompt }],
16
+ response_format: { type: "json_object" },
17
+ temperature: 0.3,
18
+ });
19
+ const content = response.choices[0].message.content || '{"personas":[]}';
20
+ const parsed = JSON.parse(content);
21
+ return parsed.personas || [];
22
+ }
23
+ //# sourceMappingURL=persona-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persona-generator.js","sourceRoot":"","sources":["../../src/ai/persona-generator.ts"],"names":[],"mappings":"AAOA,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAAc,EACd,WAAmB,EACnB,YAAsB;IAEtB,MAAM,MAAM,GAAG;;gBAED,WAAW;;;EAGzB,YAAY;SACX,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;SACZ,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;SAClD,IAAI,CAAC,MAAM,CAAC;;oJAEqI,CAAC;IAEnJ,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;QACpD,KAAK,EAAE,QAAQ;QACf,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAC7C,eAAe,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE;QACxC,WAAW,EAAE,GAAG;KACjB,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,IAAI,iBAAiB,CAAC;IACzE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACnC,OAAO,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC;AAC/B,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type OpenAI from "openai";
2
+ export interface UseCaseResult {
3
+ name: string;
4
+ description: string;
5
+ }
6
+ export declare function generateUseCases(client: OpenAI, personaName: string, personaDescription: string, siteSummary: string, pageContents: string[]): Promise<UseCaseResult[]>;
7
+ //# sourceMappingURL=use-case-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-case-generator.d.ts","sourceRoot":"","sources":["../../src/ai/use-case-generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAEjC,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,kBAAkB,EAAE,MAAM,EAC1B,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EAAE,GACrB,OAAO,CAAC,aAAa,EAAE,CAAC,CAuB1B"}
@@ -0,0 +1,23 @@
1
+ export async function generateUseCases(client, personaName, personaDescription, siteSummary, pageContents) {
2
+ const prompt = `For the persona "${personaName}" (${personaDescription}) on this website, identify specific use cases — goals this user would try to accomplish.
3
+
4
+ Site summary: ${siteSummary}
5
+
6
+ Pages available:
7
+ ${pageContents
8
+ .slice(0, 10)
9
+ .map((c, i) => `Page ${i + 1}: ${c.slice(0, 300)}`)
10
+ .join("\n\n")}
11
+
12
+ Respond with a JSON object with a "useCases" key containing an array of objects with "name" and "description" fields. 2-8 use cases.`;
13
+ const response = await client.chat.completions.create({
14
+ model: "gpt-4o",
15
+ messages: [{ role: "user", content: prompt }],
16
+ response_format: { type: "json_object" },
17
+ temperature: 0.3,
18
+ });
19
+ const content = response.choices[0].message.content || '{"useCases":[]}';
20
+ const parsed = JSON.parse(content);
21
+ return parsed.useCases || parsed.use_cases || [];
22
+ }
23
+ //# sourceMappingURL=use-case-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-case-generator.js","sourceRoot":"","sources":["../../src/ai/use-case-generator.ts"],"names":[],"mappings":"AAOA,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAAc,EACd,WAAmB,EACnB,kBAA0B,EAC1B,WAAmB,EACnB,YAAsB;IAEtB,MAAM,MAAM,GAAG,oBAAoB,WAAW,MAAM,kBAAkB;;gBAExD,WAAW;;;EAGzB,YAAY;SACX,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;SACZ,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;SAClD,IAAI,CAAC,MAAM,CAAC;;qIAEsH,CAAC;IAEpI,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;QACpD,KAAK,EAAE,QAAQ;QACf,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAC7C,eAAe,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE;QACxC,WAAW,EAAE,GAAG;KACjB,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,IAAI,iBAAiB,CAAC;IACzE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACnC,OAAO,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC;AACnD,CAAC"}