@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.
- package/CLAUDE.md +338 -0
- package/README.md +15 -0
- package/dist/adapter.d.ts +56 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +1 -0
- package/dist/adapter.js.map +1 -0
- package/dist/ai/analyzer.d.ts +16 -0
- package/dist/ai/analyzer.d.ts.map +1 -0
- package/dist/ai/analyzer.js +55 -0
- package/dist/ai/analyzer.js.map +1 -0
- package/dist/ai/input-generator.d.ts +9 -0
- package/dist/ai/input-generator.d.ts.map +1 -0
- package/dist/ai/input-generator.js +20 -0
- package/dist/ai/input-generator.js.map +1 -0
- package/dist/ai/persona-generator.d.ts +7 -0
- package/dist/ai/persona-generator.d.ts.map +1 -0
- package/dist/ai/persona-generator.js +23 -0
- package/dist/ai/persona-generator.js.map +1 -0
- package/dist/ai/use-case-generator.d.ts +7 -0
- package/dist/ai/use-case-generator.d.ts.map +1 -0
- package/dist/ai/use-case-generator.js +23 -0
- package/dist/ai/use-case-generator.js.map +1 -0
- package/dist/api/client.d.ts +69 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +327 -0
- package/dist/api/client.js.map +1 -0
- package/dist/browser/dom-snapshot.d.ts +4 -0
- package/dist/browser/dom-snapshot.d.ts.map +1 -0
- package/dist/browser/dom-snapshot.js +382 -0
- package/dist/browser/dom-snapshot.js.map +1 -0
- package/dist/browser/page-utils.d.ts +9 -0
- package/dist/browser/page-utils.d.ts.map +1 -0
- package/dist/browser/page-utils.js +63 -0
- package/dist/browser/page-utils.js.map +1 -0
- package/dist/config/constants.d.ts +28 -0
- package/dist/config/constants.d.ts.map +1 -0
- package/dist/config/constants.js +99 -0
- package/dist/config/constants.js.map +1 -0
- package/dist/detectors/action-description.d.ts +4 -0
- package/dist/detectors/action-description.d.ts.map +1 -0
- package/dist/detectors/action-description.js +25 -0
- package/dist/detectors/action-description.js.map +1 -0
- package/dist/detectors/bug-detector.d.ts +23 -0
- package/dist/detectors/bug-detector.d.ts.map +1 -0
- package/dist/detectors/bug-detector.js +214 -0
- package/dist/detectors/bug-detector.js.map +1 -0
- package/dist/detectors/content-checker.d.ts +10 -0
- package/dist/detectors/content-checker.d.ts.map +1 -0
- package/dist/detectors/content-checker.js +76 -0
- package/dist/detectors/content-checker.js.map +1 -0
- package/dist/detectors/detection-rule.d.ts +28 -0
- package/dist/detectors/detection-rule.d.ts.map +1 -0
- package/dist/detectors/detection-rule.js +1 -0
- package/dist/detectors/detection-rule.js.map +1 -0
- package/dist/detectors/functional-checker.d.ts +22 -0
- package/dist/detectors/functional-checker.d.ts.map +1 -0
- package/dist/detectors/functional-checker.js +88 -0
- package/dist/detectors/functional-checker.js.map +1 -0
- package/dist/detectors/index.d.ts +10 -0
- package/dist/detectors/index.d.ts.map +1 -0
- package/dist/detectors/index.js +9 -0
- package/dist/detectors/index.js.map +1 -0
- package/dist/detectors/link-checker.d.ts +24 -0
- package/dist/detectors/link-checker.d.ts.map +1 -0
- package/dist/detectors/link-checker.js +85 -0
- package/dist/detectors/link-checker.js.map +1 -0
- package/dist/detectors/modal-handler.d.ts +7 -0
- package/dist/detectors/modal-handler.d.ts.map +1 -0
- package/dist/detectors/modal-handler.js +60 -0
- package/dist/detectors/modal-handler.js.map +1 -0
- package/dist/detectors/rules/blank-page-rule.d.ts +3 -0
- package/dist/detectors/rules/blank-page-rule.d.ts.map +1 -0
- package/dist/detectors/rules/blank-page-rule.js +21 -0
- package/dist/detectors/rules/blank-page-rule.js.map +1 -0
- package/dist/detectors/rules/broken-image-rule.d.ts +3 -0
- package/dist/detectors/rules/broken-image-rule.d.ts.map +1 -0
- package/dist/detectors/rules/broken-image-rule.js +34 -0
- package/dist/detectors/rules/broken-image-rule.js.map +1 -0
- package/dist/detectors/rules/broken-link-rule.d.ts +3 -0
- package/dist/detectors/rules/broken-link-rule.d.ts.map +1 -0
- package/dist/detectors/rules/broken-link-rule.js +55 -0
- package/dist/detectors/rules/broken-link-rule.js.map +1 -0
- package/dist/detectors/rules/broken-media-rule.d.ts +3 -0
- package/dist/detectors/rules/broken-media-rule.d.ts.map +1 -0
- package/dist/detectors/rules/broken-media-rule.js +46 -0
- package/dist/detectors/rules/broken-media-rule.js.map +1 -0
- package/dist/detectors/rules/console-error-rule.d.ts +3 -0
- package/dist/detectors/rules/console-error-rule.d.ts.map +1 -0
- package/dist/detectors/rules/console-error-rule.js +9 -0
- package/dist/detectors/rules/console-error-rule.js.map +1 -0
- package/dist/detectors/rules/dead-click-rule.d.ts +3 -0
- package/dist/detectors/rules/dead-click-rule.d.ts.map +1 -0
- package/dist/detectors/rules/dead-click-rule.js +9 -0
- package/dist/detectors/rules/dead-click-rule.js.map +1 -0
- package/dist/detectors/rules/duplicate-heading-rule.d.ts +3 -0
- package/dist/detectors/rules/duplicate-heading-rule.d.ts.map +1 -0
- package/dist/detectors/rules/duplicate-heading-rule.js +35 -0
- package/dist/detectors/rules/duplicate-heading-rule.js.map +1 -0
- package/dist/detectors/rules/duplicate-id-rule.d.ts +3 -0
- package/dist/detectors/rules/duplicate-id-rule.d.ts.map +1 -0
- package/dist/detectors/rules/duplicate-id-rule.js +33 -0
- package/dist/detectors/rules/duplicate-id-rule.js.map +1 -0
- package/dist/detectors/rules/empty-link-rule.d.ts +3 -0
- package/dist/detectors/rules/empty-link-rule.d.ts.map +1 -0
- package/dist/detectors/rules/empty-link-rule.js +35 -0
- package/dist/detectors/rules/empty-link-rule.js.map +1 -0
- package/dist/detectors/rules/error-page-rule.d.ts +3 -0
- package/dist/detectors/rules/error-page-rule.d.ts.map +1 -0
- package/dist/detectors/rules/error-page-rule.js +40 -0
- package/dist/detectors/rules/error-page-rule.js.map +1 -0
- package/dist/detectors/rules/index.d.ts +16 -0
- package/dist/detectors/rules/index.d.ts.map +1 -0
- package/dist/detectors/rules/index.js +32 -0
- package/dist/detectors/rules/index.js.map +1 -0
- package/dist/detectors/rules/network-error-rule.d.ts +3 -0
- package/dist/detectors/rules/network-error-rule.d.ts.map +1 -0
- package/dist/detectors/rules/network-error-rule.js +9 -0
- package/dist/detectors/rules/network-error-rule.js.map +1 -0
- package/dist/detectors/rules/placeholder-text-rule.d.ts +3 -0
- package/dist/detectors/rules/placeholder-text-rule.d.ts.map +1 -0
- package/dist/detectors/rules/placeholder-text-rule.js +34 -0
- package/dist/detectors/rules/placeholder-text-rule.js.map +1 -0
- package/dist/detectors/visual-checker.d.ts +11 -0
- package/dist/detectors/visual-checker.d.ts.map +1 -0
- package/dist/detectors/visual-checker.js +78 -0
- package/dist/detectors/visual-checker.js.map +1 -0
- package/dist/domain/types.d.ts +3 -0
- package/dist/domain/types.d.ts.map +1 -0
- package/dist/domain/types.js +3 -0
- package/dist/domain/types.js.map +1 -0
- package/dist/domain/url-ownership.d.ts +7 -0
- package/dist/domain/url-ownership.d.ts.map +1 -0
- package/dist/domain/url-ownership.js +49 -0
- package/dist/domain/url-ownership.js.map +1 -0
- package/dist/extractors/buttons.d.ts +3 -0
- package/dist/extractors/buttons.d.ts.map +1 -0
- package/dist/extractors/buttons.js +30 -0
- package/dist/extractors/buttons.js.map +1 -0
- package/dist/extractors/clickables.d.ts +3 -0
- package/dist/extractors/clickables.d.ts.map +1 -0
- package/dist/extractors/clickables.js +35 -0
- package/dist/extractors/clickables.js.map +1 -0
- package/dist/extractors/form-extractor.d.ts +4 -0
- package/dist/extractors/form-extractor.d.ts.map +1 -0
- package/dist/extractors/form-extractor.js +47 -0
- package/dist/extractors/form-extractor.js.map +1 -0
- package/dist/extractors/helpers.d.ts +7 -0
- package/dist/extractors/helpers.d.ts.map +1 -0
- package/dist/extractors/helpers.js +43 -0
- package/dist/extractors/helpers.js.map +1 -0
- package/dist/extractors/index.d.ts +5 -0
- package/dist/extractors/index.d.ts.map +1 -0
- package/dist/extractors/index.js +25 -0
- package/dist/extractors/index.js.map +1 -0
- package/dist/extractors/product-actions.d.ts +3 -0
- package/dist/extractors/product-actions.d.ts.map +1 -0
- package/dist/extractors/product-actions.js +23 -0
- package/dist/extractors/product-actions.js.map +1 -0
- package/dist/extractors/selectors.d.ts +3 -0
- package/dist/extractors/selectors.d.ts.map +1 -0
- package/dist/extractors/selectors.js +42 -0
- package/dist/extractors/selectors.js.map +1 -0
- package/dist/extractors/selects.d.ts +3 -0
- package/dist/extractors/selects.d.ts.map +1 -0
- package/dist/extractors/selects.js +13 -0
- package/dist/extractors/selects.js.map +1 -0
- package/dist/extractors/text-inputs.d.ts +3 -0
- package/dist/extractors/text-inputs.d.ts.map +1 -0
- package/dist/extractors/text-inputs.js +35 -0
- package/dist/extractors/text-inputs.js.map +1 -0
- package/dist/extractors/toggles.d.ts +3 -0
- package/dist/extractors/toggles.d.ts.map +1 -0
- package/dist/extractors/toggles.js +14 -0
- package/dist/extractors/toggles.js.map +1 -0
- package/dist/extractors/types.d.ts +36 -0
- package/dist/extractors/types.d.ts.map +1 -0
- package/dist/extractors/types.js +1 -0
- package/dist/extractors/types.js.map +1 -0
- package/dist/generation/e2e.d.ts +15 -0
- package/dist/generation/e2e.d.ts.map +1 -0
- package/dist/generation/e2e.js +59 -0
- package/dist/generation/e2e.js.map +1 -0
- package/dist/generation/form-negative.d.ts +14 -0
- package/dist/generation/form-negative.d.ts.map +1 -0
- package/dist/generation/form-negative.js +36 -0
- package/dist/generation/form-negative.js.map +1 -0
- package/dist/generation/form.d.ts +23 -0
- package/dist/generation/form.d.ts.map +1 -0
- package/dist/generation/form.js +48 -0
- package/dist/generation/form.js.map +1 -0
- package/dist/generation/generator.d.ts +13 -0
- package/dist/generation/generator.d.ts.map +1 -0
- package/dist/generation/generator.js +38 -0
- package/dist/generation/generator.js.map +1 -0
- package/dist/generation/interaction.d.ts +14 -0
- package/dist/generation/interaction.d.ts.map +1 -0
- package/dist/generation/interaction.js +26 -0
- package/dist/generation/interaction.js.map +1 -0
- package/dist/generation/navigation.d.ts +14 -0
- package/dist/generation/navigation.d.ts.map +1 -0
- package/dist/generation/navigation.js +21 -0
- package/dist/generation/navigation.js.map +1 -0
- package/dist/generation/password.d.ts +20 -0
- package/dist/generation/password.d.ts.map +1 -0
- package/dist/generation/password.js +45 -0
- package/dist/generation/password.js.map +1 -0
- package/dist/generation/playwright-export.d.ts +3 -0
- package/dist/generation/playwright-export.d.ts.map +1 -0
- package/dist/generation/playwright-export.js +22 -0
- package/dist/generation/playwright-export.js.map +1 -0
- package/dist/generation/render.d.ts +29 -0
- package/dist/generation/render.d.ts.map +1 -0
- package/dist/generation/render.js +94 -0
- package/dist/generation/render.js.map +1 -0
- package/dist/generation/suite-tagger.d.ts +4 -0
- package/dist/generation/suite-tagger.d.ts.map +1 -0
- package/dist/generation/suite-tagger.js +20 -0
- package/dist/generation/suite-tagger.js.map +1 -0
- package/dist/identity/element-matcher.d.ts +23 -0
- package/dist/identity/element-matcher.d.ts.map +1 -0
- package/dist/identity/element-matcher.js +60 -0
- package/dist/identity/element-matcher.js.map +1 -0
- package/dist/identity/identity-cache.d.ts +12 -0
- package/dist/identity/identity-cache.d.ts.map +1 -0
- package/dist/identity/identity-cache.js +25 -0
- package/dist/identity/identity-cache.js.map +1 -0
- package/dist/identity/playwright-locator.d.ts +4 -0
- package/dist/identity/playwright-locator.d.ts.map +1 -0
- package/dist/identity/playwright-locator.js +67 -0
- package/dist/identity/playwright-locator.js.map +1 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +56 -0
- package/dist/index.js.map +1 -0
- package/dist/orchestrator/decomposition.d.ts +6 -0
- package/dist/orchestrator/decomposition.d.ts.map +1 -0
- package/dist/orchestrator/decomposition.js +55 -0
- package/dist/orchestrator/decomposition.js.map +1 -0
- package/dist/orchestrator/orchestrator.d.ts +5 -0
- package/dist/orchestrator/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator/orchestrator.js +122 -0
- package/dist/orchestrator/orchestrator.js.map +1 -0
- package/dist/orchestrator/test-execution.d.ts +5 -0
- package/dist/orchestrator/test-execution.d.ts.map +1 -0
- package/dist/orchestrator/test-execution.js +205 -0
- package/dist/orchestrator/test-execution.js.map +1 -0
- package/dist/orchestrator/types.d.ts +69 -0
- package/dist/orchestrator/types.d.ts.map +1 -0
- package/dist/orchestrator/types.js +1 -0
- package/dist/orchestrator/types.js.map +1 -0
- package/dist/planners/fill-value-planner.d.ts +9 -0
- package/dist/planners/fill-value-planner.d.ts.map +1 -0
- package/dist/planners/fill-value-planner.js +166 -0
- package/dist/planners/fill-value-planner.js.map +1 -0
- package/dist/plugins/registry.d.ts +6 -0
- package/dist/plugins/registry.d.ts.map +1 -0
- package/dist/plugins/registry.js +16 -0
- package/dist/plugins/registry.js.map +1 -0
- package/dist/plugins/types.d.ts +42 -0
- package/dist/plugins/types.d.ts.map +1 -0
- package/dist/plugins/types.js +1 -0
- package/dist/plugins/types.js.map +1 -0
- package/dist/scanner/action-classifier.d.ts +7 -0
- package/dist/scanner/action-classifier.d.ts.map +1 -0
- package/dist/scanner/action-classifier.js +64 -0
- package/dist/scanner/action-classifier.js.map +1 -0
- package/dist/scanner/action-queue.d.ts +18 -0
- package/dist/scanner/action-queue.d.ts.map +1 -0
- package/dist/scanner/action-queue.js +20 -0
- package/dist/scanner/action-queue.js.map +1 -0
- package/dist/scanner/component-detector.d.ts +43 -0
- package/dist/scanner/component-detector.d.ts.map +1 -0
- package/dist/scanner/component-detector.js +231 -0
- package/dist/scanner/component-detector.js.map +1 -0
- package/dist/scanner/email-detector.d.ts +3 -0
- package/dist/scanner/email-detector.d.ts.map +1 -0
- package/dist/scanner/email-detector.js +12 -0
- package/dist/scanner/email-detector.js.map +1 -0
- package/dist/scanner/html-decomposer.d.ts +19 -0
- package/dist/scanner/html-decomposer.d.ts.map +1 -0
- package/dist/scanner/html-decomposer.js +41 -0
- package/dist/scanner/html-decomposer.js.map +1 -0
- package/dist/scanner/issue-detector.d.ts +8 -0
- package/dist/scanner/issue-detector.d.ts.map +1 -0
- package/dist/scanner/issue-detector.js +71 -0
- package/dist/scanner/issue-detector.js.map +1 -0
- package/dist/scanner/loop-guard.d.ts +18 -0
- package/dist/scanner/loop-guard.d.ts.map +1 -0
- package/dist/scanner/loop-guard.js +33 -0
- package/dist/scanner/loop-guard.js.map +1 -0
- package/dist/scanner/navigator.d.ts +25 -0
- package/dist/scanner/navigator.d.ts.map +1 -0
- package/dist/scanner/navigator.js +56 -0
- package/dist/scanner/navigator.js.map +1 -0
- package/dist/scanner/page-cache.d.ts +22 -0
- package/dist/scanner/page-cache.d.ts.map +1 -0
- package/dist/scanner/page-cache.js +42 -0
- package/dist/scanner/page-cache.js.map +1 -0
- package/dist/scanner/pairwise.d.ts +7 -0
- package/dist/scanner/pairwise.d.ts.map +1 -0
- package/dist/scanner/pairwise.js +64 -0
- package/dist/scanner/pairwise.js.map +1 -0
- package/dist/scanner/pattern-detector.d.ts +22 -0
- package/dist/scanner/pattern-detector.d.ts.map +1 -0
- package/dist/scanner/pattern-detector.js +108 -0
- package/dist/scanner/pattern-detector.js.map +1 -0
- package/dist/scanner/phase-timer.d.ts +9 -0
- package/dist/scanner/phase-timer.d.ts.map +1 -0
- package/dist/scanner/phase-timer.js +27 -0
- package/dist/scanner/phase-timer.js.map +1 -0
- package/dist/scanner/reusable-element-cache.d.ts +13 -0
- package/dist/scanner/reusable-element-cache.d.ts.map +1 -0
- package/dist/scanner/reusable-element-cache.js +37 -0
- package/dist/scanner/reusable-element-cache.js.map +1 -0
- package/dist/scanner/scroll-scanner.d.ts +3 -0
- package/dist/scanner/scroll-scanner.d.ts.map +1 -0
- package/dist/scanner/scroll-scanner.js +19 -0
- package/dist/scanner/scroll-scanner.js.map +1 -0
- package/dist/scanner/state-manager.d.ts +11 -0
- package/dist/scanner/state-manager.d.ts.map +1 -0
- package/dist/scanner/state-manager.js +24 -0
- package/dist/scanner/state-manager.js.map +1 -0
- 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"}
|
package/dist/adapter.js
ADDED
|
@@ -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"}
|