@taodigital/crawler 0.0.0-stage → 1.2.1
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/Dockerfile +82 -0
- package/README.md +10 -2
- package/dist/accessibility-auditor.d.ts +29 -0
- package/dist/accessibility-auditor.js +57 -0
- package/dist/accessibility-auditor.js.map +7 -0
- package/dist/accessibility-tree-crawler.d.ts +44 -0
- package/dist/accessibility-tree-crawler.js +156 -0
- package/dist/accessibility-tree-crawler.js.map +7 -0
- package/dist/advanced-ui-probes.d.ts +47 -0
- package/dist/advanced-ui-probes.js +310 -0
- package/dist/advanced-ui-probes.js.map +7 -0
- package/dist/ai-login.d.ts +32 -0
- package/dist/ai-login.js +250 -0
- package/dist/ai-login.js.map +7 -0
- package/dist/auth-token-rotator.d.ts +47 -0
- package/dist/auth-token-rotator.js +58 -0
- package/dist/auth-token-rotator.js.map +7 -0
- package/dist/auth.d.ts +20 -0
- package/dist/auth.js +348 -0
- package/dist/auth.js.map +7 -0
- package/dist/browser-extension.d.ts +37 -0
- package/dist/browser-extension.js +113 -0
- package/dist/browser-extension.js.map +7 -0
- package/dist/captcha-solver.d.ts +16 -0
- package/dist/captcha-solver.js +105 -0
- package/dist/captcha-solver.js.map +7 -0
- package/dist/color-contrast.d.ts +30 -0
- package/dist/color-contrast.js +78 -0
- package/dist/color-contrast.js.map +7 -0
- package/dist/content-chunker.d.ts +18 -0
- package/dist/content-chunker.js +152 -0
- package/dist/content-chunker.js.map +7 -0
- package/dist/content-dedup.d.ts +23 -0
- package/dist/content-dedup.js +60 -0
- package/dist/content-dedup.js.map +7 -0
- package/dist/cookie-consent.d.ts +25 -0
- package/dist/cookie-consent.js +78 -0
- package/dist/cookie-consent.js.map +7 -0
- package/dist/cookie-jar.d.ts +20 -0
- package/dist/cookie-jar.js +61 -0
- package/dist/cookie-jar.js.map +7 -0
- package/dist/crawl-session-manager.d.ts +44 -0
- package/dist/crawl-session-manager.js +145 -0
- package/dist/crawl-session-manager.js.map +7 -0
- package/dist/crawler.d.ts +233 -0
- package/dist/crawler.js +6239 -0
- package/dist/crawler.js.map +7 -0
- package/dist/crud-interaction.d.ts +31 -0
- package/dist/crud-interaction.js +264 -0
- package/dist/crud-interaction.js.map +7 -0
- package/dist/custom-js-injector.d.ts +42 -0
- package/dist/custom-js-injector.js +50 -0
- package/dist/custom-js-injector.js.map +7 -0
- package/dist/dark-mode-capture.d.ts +23 -0
- package/dist/dark-mode-capture.js +75 -0
- package/dist/dark-mode-capture.js.map +7 -0
- package/dist/dead-link-detector.d.ts +37 -0
- package/dist/dead-link-detector.js +50 -0
- package/dist/dead-link-detector.js.map +7 -0
- package/dist/dropdown-enumerator.d.ts +20 -0
- package/dist/dropdown-enumerator.js +230 -0
- package/dist/dropdown-enumerator.js.map +7 -0
- package/dist/element-hash.d.ts +9 -0
- package/dist/element-hash.js +9 -0
- package/dist/element-hash.js.map +7 -0
- package/dist/email-otp.d.ts +42 -0
- package/dist/email-otp.js +177 -0
- package/dist/email-otp.js.map +7 -0
- package/dist/engine-router.d.ts +47 -0
- package/dist/engine-router.js +94 -0
- package/dist/engine-router.js.map +7 -0
- package/dist/export/bulk-export.d.ts +26 -0
- package/dist/export/bulk-export.js +76 -0
- package/dist/export/bulk-export.js.map +7 -0
- package/dist/export/vector-export.d.ts +76 -0
- package/dist/export/vector-export.js +85 -0
- package/dist/export/vector-export.js.map +7 -0
- package/dist/extractors/advanced.d.ts +64 -0
- package/dist/extractors/advanced.js +265 -0
- package/dist/extractors/advanced.js.map +7 -0
- package/dist/extractors/artifact-envelope.d.ts +63 -0
- package/dist/extractors/artifact-envelope.js +63 -0
- package/dist/extractors/artifact-envelope.js.map +7 -0
- package/dist/extractors/branding-extractor.d.ts +46 -0
- package/dist/extractors/branding-extractor.js +104 -0
- package/dist/extractors/branding-extractor.js.map +7 -0
- package/dist/extractors/summary-extractor.d.ts +10 -0
- package/dist/extractors/summary-extractor.js +36 -0
- package/dist/extractors/summary-extractor.js.map +7 -0
- package/dist/failure-analysis-agent.d.ts +16 -0
- package/dist/failure-analysis-agent.js +42 -0
- package/dist/failure-analysis-agent.js.map +7 -0
- package/dist/feature-flag-detector.d.ts +30 -0
- package/dist/feature-flag-detector.js +55 -0
- package/dist/feature-flag-detector.js.map +7 -0
- package/dist/filter-pagination-crawler.d.ts +52 -0
- package/dist/filter-pagination-crawler.js +299 -0
- package/dist/filter-pagination-crawler.js.map +7 -0
- package/dist/form-detector.d.ts +34 -0
- package/dist/form-detector.js +58 -0
- package/dist/form-detector.js.map +7 -0
- package/dist/frontier.d.ts +40 -0
- package/dist/frontier.js +103 -0
- package/dist/frontier.js.map +7 -0
- package/dist/gap-pass.d.ts +36 -0
- package/dist/gap-pass.js +115 -0
- package/dist/gap-pass.js.map +7 -0
- package/dist/graphql-detector.d.ts +39 -0
- package/dist/graphql-detector.js +86 -0
- package/dist/graphql-detector.js.map +7 -0
- package/dist/http-engine.d.ts +80 -0
- package/dist/http-engine.js +318 -0
- package/dist/http-engine.js.map +7 -0
- package/dist/incremental-diff.d.ts +116 -0
- package/dist/incremental-diff.js +137 -0
- package/dist/incremental-diff.js.map +7 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.js +83 -0
- package/dist/index.js.map +7 -0
- package/dist/input-fuzzer.d.ts +30 -0
- package/dist/input-fuzzer.js +105 -0
- package/dist/input-fuzzer.js.map +7 -0
- package/dist/interactive-probe.d.ts +144 -0
- package/dist/interactive-probe.js +865 -0
- package/dist/interactive-probe.js.map +7 -0
- package/dist/job-queue.d.ts +297 -0
- package/dist/job-queue.js +3812 -0
- package/dist/job-queue.js.map +7 -0
- package/dist/js-coverage.d.ts +29 -0
- package/dist/js-coverage.js +56 -0
- package/dist/js-coverage.js.map +7 -0
- package/dist/js-router-detector.d.ts +17 -0
- package/dist/js-router-detector.js +90 -0
- package/dist/js-router-detector.js.map +7 -0
- package/dist/language-detector.d.ts +22 -0
- package/dist/language-detector.js +57 -0
- package/dist/language-detector.js.map +7 -0
- package/dist/link-classifier.d.ts +23 -0
- package/dist/link-classifier.js +40 -0
- package/dist/link-classifier.js.map +7 -0
- package/dist/live-view.d.ts +36 -0
- package/dist/live-view.js +105 -0
- package/dist/live-view.js.map +7 -0
- package/dist/locator-extractor.d.ts +46 -0
- package/dist/locator-extractor.js +220 -0
- package/dist/locator-extractor.js.map +7 -0
- package/dist/logger.d.ts +2 -0
- package/dist/logger.js +10 -0
- package/dist/logger.js.map +7 -0
- package/dist/markdown-extract.d.ts +8 -0
- package/dist/markdown-extract.js +54 -0
- package/dist/markdown-extract.js.map +7 -0
- package/dist/multi-context-handler.d.ts +55 -0
- package/dist/multi-context-handler.js +182 -0
- package/dist/multi-context-handler.js.map +7 -0
- package/dist/native-crawler.d.ts +69 -0
- package/dist/native-crawler.js +839 -0
- package/dist/native-crawler.js.map +7 -0
- package/dist/nested-window-crawler.d.ts +41 -0
- package/dist/nested-window-crawler.js +172 -0
- package/dist/nested-window-crawler.js.map +7 -0
- package/dist/network-action-correlator.d.ts +47 -0
- package/dist/network-action-correlator.js +209 -0
- package/dist/network-action-correlator.js.map +7 -0
- package/dist/network-observer.d.ts +49 -0
- package/dist/network-observer.js +149 -0
- package/dist/network-observer.js.map +7 -0
- package/dist/network-throttle.d.ts +25 -0
- package/dist/network-throttle.js +34 -0
- package/dist/network-throttle.js.map +7 -0
- package/dist/openapi-generator.d.ts +49 -0
- package/dist/openapi-generator.js +89 -0
- package/dist/openapi-generator.js.map +7 -0
- package/dist/pdf-extractor.d.ts +28 -0
- package/dist/pdf-extractor.js +81 -0
- package/dist/pdf-extractor.js.map +7 -0
- package/dist/performance-metrics-collector.d.ts +50 -0
- package/dist/performance-metrics-collector.js +155 -0
- package/dist/performance-metrics-collector.js.map +7 -0
- package/dist/performance-metrics.d.ts +52 -0
- package/dist/performance-metrics.js +83 -0
- package/dist/performance-metrics.js.map +7 -0
- package/dist/phash.d.ts +50 -0
- package/dist/phash.js +136 -0
- package/dist/phash.js.map +7 -0
- package/dist/pii-scrubber.d.ts +21 -0
- package/dist/pii-scrubber.js +75 -0
- package/dist/pii-scrubber.js.map +7 -0
- package/dist/playwright-recorder.d.ts +39 -0
- package/dist/playwright-recorder.js +123 -0
- package/dist/playwright-recorder.js.map +7 -0
- package/dist/policies/crawl-safety.d.ts +9 -0
- package/dist/policies/crawl-safety.js +41 -0
- package/dist/policies/crawl-safety.js.map +7 -0
- package/dist/profiles/domain-profile.d.ts +19 -0
- package/dist/profiles/domain-profile.js +160 -0
- package/dist/profiles/domain-profile.js.map +7 -0
- package/dist/proxy.d.ts +50 -0
- package/dist/proxy.js +165 -0
- package/dist/proxy.js.map +7 -0
- package/dist/rate-limiter.d.ts +31 -0
- package/dist/rate-limiter.js +64 -0
- package/dist/rate-limiter.js.map +7 -0
- package/dist/redirect-tracker.d.ts +37 -0
- package/dist/redirect-tracker.js +65 -0
- package/dist/redirect-tracker.js.map +7 -0
- package/dist/redis-frontier.d.ts +48 -0
- package/dist/redis-frontier.js +126 -0
- package/dist/redis-frontier.js.map +7 -0
- package/dist/redis-queue.d.ts +48 -0
- package/dist/redis-queue.js +165 -0
- package/dist/redis-queue.js.map +7 -0
- package/dist/response-cache.d.ts +38 -0
- package/dist/response-cache.js +140 -0
- package/dist/response-cache.js.map +7 -0
- package/dist/responsive-detector.d.ts +29 -0
- package/dist/responsive-detector.js +46 -0
- package/dist/responsive-detector.js.map +7 -0
- package/dist/retry-policy.d.ts +20 -0
- package/dist/retry-policy.js +54 -0
- package/dist/retry-policy.js.map +7 -0
- package/dist/robots-txt.d.ts +9 -0
- package/dist/robots-txt.js +77 -0
- package/dist/robots-txt.js.map +7 -0
- package/dist/screen-reader.d.ts +15 -0
- package/dist/screen-reader.js +48 -0
- package/dist/screen-reader.js.map +7 -0
- package/dist/screenshot-masker.d.ts +28 -0
- package/dist/screenshot-masker.js +62 -0
- package/dist/screenshot-masker.js.map +7 -0
- package/dist/seo-checker.d.ts +33 -0
- package/dist/seo-checker.js +91 -0
- package/dist/seo-checker.js.map +7 -0
- package/dist/service-worker-interceptor.d.ts +36 -0
- package/dist/service-worker-interceptor.js +136 -0
- package/dist/service-worker-interceptor.js.map +7 -0
- package/dist/session-capture.d.ts +39 -0
- package/dist/session-capture.js +274 -0
- package/dist/session-capture.js.map +7 -0
- package/dist/session-export.d.ts +49 -0
- package/dist/session-export.js +117 -0
- package/dist/session-export.js.map +7 -0
- package/dist/shadow-dom-crawler.d.ts +35 -0
- package/dist/shadow-dom-crawler.js +132 -0
- package/dist/shadow-dom-crawler.js.map +7 -0
- package/dist/sitemap-writer.d.ts +21 -0
- package/dist/sitemap-writer.js +47 -0
- package/dist/sitemap-writer.js.map +7 -0
- package/dist/sitemap.d.ts +51 -0
- package/dist/sitemap.js +160 -0
- package/dist/sitemap.js.map +7 -0
- package/dist/skyvern.d.ts +56 -0
- package/dist/skyvern.js +129 -0
- package/dist/skyvern.js.map +7 -0
- package/dist/sqs-queue.d.ts +13 -0
- package/dist/sqs-queue.js +115 -0
- package/dist/sqs-queue.js.map +7 -0
- package/dist/sso-token.d.ts +29 -0
- package/dist/sso-token.js +76 -0
- package/dist/sso-token.js.map +7 -0
- package/dist/state-graph.d.ts +59 -0
- package/dist/state-graph.js +193 -0
- package/dist/state-graph.js.map +7 -0
- package/dist/stealth/human-behavior.d.ts +20 -0
- package/dist/stealth/human-behavior.js +202 -0
- package/dist/stealth/human-behavior.js.map +7 -0
- package/dist/telemetry.d.ts +34 -0
- package/dist/telemetry.js +77 -0
- package/dist/telemetry.js.map +7 -0
- package/dist/test-executor.d.ts +81 -0
- package/dist/test-executor.js +443 -0
- package/dist/test-executor.js.map +7 -0
- package/dist/tls-fingerprint.d.ts +50 -0
- package/dist/tls-fingerprint.js +81 -0
- package/dist/tls-fingerprint.js.map +7 -0
- package/dist/url-pattern-completer.d.ts +28 -0
- package/dist/url-pattern-completer.js +122 -0
- package/dist/url-pattern-completer.js.map +7 -0
- package/dist/visual-diff-verdict.d.ts +40 -0
- package/dist/visual-diff-verdict.js +27 -0
- package/dist/visual-diff-verdict.js.map +7 -0
- package/dist/webhook-notifier.d.ts +30 -0
- package/dist/webhook-notifier.js +76 -0
- package/dist/webhook-notifier.js.map +7 -0
- package/dist/websocket-detector.d.ts +24 -0
- package/dist/websocket-detector.js +60 -0
- package/dist/websocket-detector.js.map +7 -0
- package/dist/worker.d.ts +1 -0
- package/dist/worker.js +33 -0
- package/dist/worker.js.map +7 -0
- package/package.json +49 -4
- package/src/accessibility-auditor.ts +102 -0
- package/src/accessibility-tree-crawler.ts +246 -0
- package/src/advanced-ui-probes.ts +470 -0
- package/src/ai-login.ts +298 -0
- package/src/auth-token-rotator.ts +89 -0
- package/src/auth.ts +418 -0
- package/src/browser-extension.ts +186 -0
- package/src/captcha-solver.ts +143 -0
- package/src/color-contrast.ts +116 -0
- package/src/content-chunker.ts +170 -0
- package/src/content-dedup.ts +99 -0
- package/src/cookie-consent.ts +106 -0
- package/src/cookie-jar.ts +91 -0
- package/src/crawl-session-manager.ts +231 -0
- package/src/crawler.ts +7123 -0
- package/src/crawler.ts.rej +12 -0
- package/src/crud-interaction.ts +386 -0
- package/src/custom-js-injector.ts +93 -0
- package/src/dark-mode-capture.ts +110 -0
- package/src/dead-link-detector.ts +76 -0
- package/src/dropdown-enumerator.ts +312 -0
- package/src/element-hash.ts +16 -0
- package/src/email-otp.ts +252 -0
- package/src/engine-router.ts +141 -0
- package/src/export/bulk-export.ts +85 -0
- package/src/export/vector-export.ts +160 -0
- package/src/extractors/advanced.ts +309 -0
- package/src/extractors/artifact-envelope.ts +111 -0
- package/src/extractors/branding-extractor.ts +159 -0
- package/src/extractors/summary-extractor.ts +49 -0
- package/src/failure-analysis-agent.ts +51 -0
- package/src/feature-flag-detector.ts +97 -0
- package/src/filter-pagination-crawler.ts +500 -0
- package/src/form-detector.ts +103 -0
- package/src/frontier.test.ts +64 -0
- package/src/frontier.ts +144 -0
- package/src/gap-pass.ts +165 -0
- package/src/graphql-detector.ts +129 -0
- package/src/http-engine.ts +453 -0
- package/src/incremental-diff.ts +259 -0
- package/src/index.ts +52 -0
- package/src/input-fuzzer.ts +129 -0
- package/src/interactive-probe.ts +1136 -0
- package/src/job-queue.test.ts +187 -0
- package/src/job-queue.ts +4504 -0
- package/src/js-coverage.ts +87 -0
- package/src/js-router-detector.ts +112 -0
- package/src/language-detector.ts +82 -0
- package/src/link-classifier.ts +65 -0
- package/src/live-view.ts +146 -0
- package/src/locator-extractor.ts +309 -0
- package/src/logger.ts +9 -0
- package/src/markdown-extract.test.ts +73 -0
- package/src/markdown-extract.ts +76 -0
- package/src/multi-context-handler.ts +296 -0
- package/src/native-crawler.ts +1108 -0
- package/src/nested-window-crawler.ts +310 -0
- package/src/network-action-correlator.ts +320 -0
- package/src/network-observer.ts +204 -0
- package/src/network-throttle.ts +69 -0
- package/src/openapi-generator.ts +135 -0
- package/src/pdf-extractor.ts +123 -0
- package/src/performance-metrics-collector.ts +243 -0
- package/src/performance-metrics.ts +134 -0
- package/src/phash.ts +184 -0
- package/src/pii-scrubber.ts +99 -0
- package/src/playwright-recorder.ts +170 -0
- package/src/policies/crawl-safety.ts +52 -0
- package/src/profiles/domain-profile.ts +190 -0
- package/src/proxy.ts +238 -0
- package/src/rate-limiter.ts +87 -0
- package/src/redirect-tracker.ts +97 -0
- package/src/redis-frontier.ts +143 -0
- package/src/redis-queue.ts +208 -0
- package/src/response-cache.ts +150 -0
- package/src/responsive-detector.ts +92 -0
- package/src/retry-policy.ts +87 -0
- package/src/robots-txt.ts +96 -0
- package/src/screen-reader.ts +98 -0
- package/src/screenshot-masker.ts +98 -0
- package/src/seo-checker.ts +134 -0
- package/src/service-worker-interceptor.ts +185 -0
- package/src/session-capture.ts +335 -0
- package/src/session-export.test.ts +75 -0
- package/src/session-export.ts +172 -0
- package/src/shadow-dom-crawler.ts +198 -0
- package/src/sitemap-writer.ts +65 -0
- package/src/sitemap.test.ts +127 -0
- package/src/sitemap.ts +233 -0
- package/src/skyvern.ts +199 -0
- package/src/sqs-queue.ts +129 -0
- package/src/sso-token.ts +120 -0
- package/src/state-graph.ts +280 -0
- package/src/stealth/human-behavior.ts +227 -0
- package/src/telemetry.ts +122 -0
- package/src/test-executor.test.ts +72 -0
- package/src/test-executor.ts +578 -0
- package/src/tls-fingerprint.ts +111 -0
- package/src/url-pattern-completer.ts +161 -0
- package/src/vendor.d.ts +20 -0
- package/src/visual-diff-verdict.test.ts +80 -0
- package/src/visual-diff-verdict.ts +59 -0
- package/src/webhook-notifier.ts +109 -0
- package/src/websocket-detector.ts +83 -0
- package/src/worker.ts +50 -0
- package/tsconfig.json +13 -0
- package/types/accessibility-auditor.d.ts +29 -0
- package/types/accessibility-tree-crawler.d.ts +44 -0
- package/types/advanced-ui-probes.d.ts +47 -0
- package/types/ai-login.d.ts +32 -0
- package/types/auth-token-rotator.d.ts +47 -0
- package/types/auth.d.ts +20 -0
- package/types/browser-extension.d.ts +37 -0
- package/types/bull-worker.d.ts +6 -0
- package/types/captcha-solver.d.ts +16 -0
- package/types/color-contrast.d.ts +30 -0
- package/types/content-chunker.d.ts +18 -0
- package/types/content-dedup.d.ts +23 -0
- package/types/cookie-consent.d.ts +25 -0
- package/types/cookie-jar.d.ts +20 -0
- package/types/crawl-session-manager.d.ts +44 -0
- package/types/crawler.d.ts +233 -0
- package/types/crud-interaction.d.ts +31 -0
- package/types/custom-js-injector.d.ts +42 -0
- package/types/dark-mode-capture.d.ts +23 -0
- package/types/dead-link-detector.d.ts +37 -0
- package/types/dropdown-enumerator.d.ts +20 -0
- package/types/element-hash.d.ts +9 -0
- package/types/email-otp.d.ts +42 -0
- package/types/engine-router.d.ts +47 -0
- package/types/export/bulk-export.d.ts +26 -0
- package/types/export/vector-export.d.ts +76 -0
- package/types/extractors/advanced.d.ts +64 -0
- package/types/extractors/artifact-envelope.d.ts +63 -0
- package/types/extractors/branding-extractor.d.ts +46 -0
- package/types/extractors/summary-extractor.d.ts +10 -0
- package/types/failure-analysis-agent.d.ts +16 -0
- package/types/feature-flag-detector.d.ts +30 -0
- package/types/filter-pagination-crawler.d.ts +52 -0
- package/types/form-detector.d.ts +34 -0
- package/types/frontier.d.ts +40 -0
- package/types/gap-pass.d.ts +36 -0
- package/types/graphql-detector.d.ts +39 -0
- package/types/http-engine.d.ts +80 -0
- package/types/incremental-diff.d.ts +116 -0
- package/types/index.d.ts +52 -0
- package/types/input-fuzzer.d.ts +30 -0
- package/types/interactive-probe.d.ts +144 -0
- package/types/job-queue.d.ts +308 -0
- package/types/js-coverage.d.ts +29 -0
- package/types/js-router-detector.d.ts +17 -0
- package/types/language-detector.d.ts +22 -0
- package/types/link-classifier.d.ts +23 -0
- package/types/live-view.d.ts +36 -0
- package/types/locator-extractor.d.ts +46 -0
- package/types/logger.d.ts +2 -0
- package/types/markdown-extract.d.ts +8 -0
- package/types/multi-context-handler.d.ts +55 -0
- package/types/native-crawler.d.ts +69 -0
- package/types/nested-window-crawler.d.ts +41 -0
- package/types/network-action-correlator.d.ts +47 -0
- package/types/network-observer.d.ts +49 -0
- package/types/network-throttle.d.ts +25 -0
- package/types/openapi-generator.d.ts +49 -0
- package/types/pdf-extractor.d.ts +28 -0
- package/types/performance-metrics-collector.d.ts +50 -0
- package/types/performance-metrics.d.ts +52 -0
- package/types/phash.d.ts +50 -0
- package/types/pii-scrubber.d.ts +21 -0
- package/types/playwright-recorder.d.ts +39 -0
- package/types/policies/crawl-safety.d.ts +9 -0
- package/types/profiles/domain-profile.d.ts +19 -0
- package/types/proxy.d.ts +50 -0
- package/types/rate-limiter.d.ts +31 -0
- package/types/redirect-tracker.d.ts +37 -0
- package/types/redis-frontier.d.ts +48 -0
- package/types/redis-queue.d.ts +48 -0
- package/types/response-cache.d.ts +38 -0
- package/types/responsive-detector.d.ts +29 -0
- package/types/retry-policy.d.ts +20 -0
- package/types/robots-txt.d.ts +9 -0
- package/types/screen-reader.d.ts +15 -0
- package/types/screenshot-masker.d.ts +28 -0
- package/types/seo-checker.d.ts +33 -0
- package/types/service-worker-interceptor.d.ts +36 -0
- package/types/session-capture.d.ts +39 -0
- package/types/session-export.d.ts +49 -0
- package/types/shadow-dom-crawler.d.ts +35 -0
- package/types/sitemap-writer.d.ts +21 -0
- package/types/sitemap.d.ts +51 -0
- package/types/skyvern.d.ts +56 -0
- package/types/sqs-queue.d.ts +13 -0
- package/types/sso-token.d.ts +29 -0
- package/types/state-graph.d.ts +59 -0
- package/types/stealth/human-behavior.d.ts +20 -0
- package/types/telemetry.d.ts +34 -0
- package/types/test-executor.d.ts +84 -0
- package/types/tls-fingerprint.d.ts +50 -0
- package/types/url-pattern-completer.d.ts +28 -0
- package/types/visual-diff-verdict.d.ts +40 -0
- package/types/webhook-notifier.d.ts +30 -0
- package/types/websocket-detector.d.ts +24 -0
- package/types/worker.d.ts +1 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
function extractPathParams(urlPattern) {
|
|
2
|
+
const params = [];
|
|
3
|
+
const matches = urlPattern.matchAll(/\{(\w+)\}/g);
|
|
4
|
+
for (const m of matches) {
|
|
5
|
+
params.push({ name: m[1], in: "path", required: true, schema: { type: "string" } });
|
|
6
|
+
}
|
|
7
|
+
return params;
|
|
8
|
+
}
|
|
9
|
+
function methodSummary(method, path) {
|
|
10
|
+
const resource = path.split("/").filter((s) => !s.startsWith("{") && s.length > 0).pop() ?? "resource";
|
|
11
|
+
const summaries = {
|
|
12
|
+
GET: `Get ${resource}`,
|
|
13
|
+
POST: `Create ${resource}`,
|
|
14
|
+
PUT: `Update ${resource}`,
|
|
15
|
+
PATCH: `Partially update ${resource}`,
|
|
16
|
+
DELETE: `Delete ${resource}`
|
|
17
|
+
};
|
|
18
|
+
return summaries[method.toUpperCase()] ?? `${method} ${resource}`;
|
|
19
|
+
}
|
|
20
|
+
function inferTag(path) {
|
|
21
|
+
const parts = path.split("/").filter((s) => s.length > 0 && !s.startsWith("{"));
|
|
22
|
+
return parts[0] ?? "default";
|
|
23
|
+
}
|
|
24
|
+
function generateOpenApiSpec(observations, opts = {}) {
|
|
25
|
+
const { title = "ZeTa Discovered API", baseUrl = "/", version = "1.0.0" } = opts;
|
|
26
|
+
const spec = {
|
|
27
|
+
openapi: "3.0.3",
|
|
28
|
+
info: { title, version, description: "Auto-generated by ZeTa crawler from observed network traffic." },
|
|
29
|
+
servers: [{ url: baseUrl }],
|
|
30
|
+
paths: {}
|
|
31
|
+
};
|
|
32
|
+
for (const obs of observations) {
|
|
33
|
+
const path = obs.urlPattern;
|
|
34
|
+
const method = obs.method.toLowerCase();
|
|
35
|
+
if (!spec.paths[path]) spec.paths[path] = {};
|
|
36
|
+
const params = extractPathParams(path);
|
|
37
|
+
const statusCode = obs.statusCode ?? 200;
|
|
38
|
+
const responses = {
|
|
39
|
+
[String(statusCode)]: {
|
|
40
|
+
description: statusCode < 400 ? "Successful response" : "Error response",
|
|
41
|
+
content: obs.responseShape ? { "application/json": { schema: shapeToJsonSchema(obs.responseShape) } } : void 0
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
const op = {
|
|
45
|
+
summary: methodSummary(obs.method, path),
|
|
46
|
+
parameters: params.length > 0 ? params : void 0,
|
|
47
|
+
responses,
|
|
48
|
+
tags: [inferTag(path)]
|
|
49
|
+
};
|
|
50
|
+
spec.paths[path][method] = op;
|
|
51
|
+
}
|
|
52
|
+
return spec;
|
|
53
|
+
}
|
|
54
|
+
function shapeToJsonSchema(shape) {
|
|
55
|
+
if (Array.isArray(shape)) return { type: "array", items: shape.length > 0 ? shapeToJsonSchema(shape[0]) : {} };
|
|
56
|
+
if (shape && typeof shape === "object") {
|
|
57
|
+
const props = {};
|
|
58
|
+
for (const [k, v] of Object.entries(shape)) props[k] = shapeToJsonSchema(v);
|
|
59
|
+
return { type: "object", properties: props };
|
|
60
|
+
}
|
|
61
|
+
return { type: String(shape) };
|
|
62
|
+
}
|
|
63
|
+
function specToYaml(spec) {
|
|
64
|
+
function toYaml(val, indent = 0) {
|
|
65
|
+
const pad = " ".repeat(indent);
|
|
66
|
+
if (val === null || val === void 0) return "null";
|
|
67
|
+
if (typeof val === "string") return val.includes("\n") || val.includes(":") ? `"${val.replace(/"/g, '\\"')}"` : val;
|
|
68
|
+
if (typeof val === "number" || typeof val === "boolean") return String(val);
|
|
69
|
+
if (Array.isArray(val)) return val.map((v) => `
|
|
70
|
+
${pad}- ${toYaml(v, indent + 1).trimStart()}`).join("") || "[]";
|
|
71
|
+
if (typeof val === "object") {
|
|
72
|
+
const entries = Object.entries(val);
|
|
73
|
+
if (entries.length === 0) return "{}";
|
|
74
|
+
return entries.map(([k, v]) => {
|
|
75
|
+
const vStr = toYaml(v, indent + 1);
|
|
76
|
+
return typeof v === "object" && v !== null && !Array.isArray(v) ? `
|
|
77
|
+
${pad}${k}:${vStr}` : `
|
|
78
|
+
${pad}${k}: ${vStr.trimStart()}`;
|
|
79
|
+
}).join("");
|
|
80
|
+
}
|
|
81
|
+
return String(val);
|
|
82
|
+
}
|
|
83
|
+
return `openapi: "${spec.openapi}"${toYaml(spec, 0)}`;
|
|
84
|
+
}
|
|
85
|
+
export {
|
|
86
|
+
generateOpenApiSpec,
|
|
87
|
+
specToYaml
|
|
88
|
+
};
|
|
89
|
+
//# sourceMappingURL=openapi-generator.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/openapi-generator.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * OpenAPI 3.0 spec generator from observed network traffic.\n *\n * Takes the ApiEndpointObservationInput[] collected by the network observer\n * during crawl and generates a minimal but valid OpenAPI 3.0 spec.\n *\n * The spec captures: endpoints, methods, observed response shapes, status codes.\n * It does NOT capture request bodies (we'd need to intercept requests too) \u2014 that's\n * marked as a TODO. But it's enough for generating smoke-test suites.\n */\n\nimport type { ApiEndpointObservationInput } from './network-observer.js';\n\nexport interface OpenApiSpec {\n openapi: '3.0.3';\n info: { title: string; version: string; description?: string };\n servers: Array<{ url: string }>;\n paths: Record<string, Record<string, OpenApiOperation>>;\n}\n\ninterface OpenApiOperation {\n summary: string;\n parameters?: OpenApiParam[];\n responses: Record<string, { description: string; content?: Record<string, { schema: unknown }> }>;\n tags?: string[];\n}\n\ninterface OpenApiParam {\n name: string;\n in: 'path' | 'query';\n required: boolean;\n schema: { type: string };\n}\n\nfunction extractPathParams(urlPattern: string): OpenApiParam[] {\n const params: OpenApiParam[] = [];\n const matches = urlPattern.matchAll(/\\{(\\w+)\\}/g);\n for (const m of matches) {\n params.push({ name: m[1], in: 'path', required: true, schema: { type: 'string' } });\n }\n return params;\n}\n\nfunction methodSummary(method: string, path: string): string {\n const resource = path.split('/').filter(s => !s.startsWith('{') && s.length > 0).pop() ?? 'resource';\n const summaries: Record<string, string> = {\n GET: `Get ${resource}`,\n POST: `Create ${resource}`,\n PUT: `Update ${resource}`,\n PATCH: `Partially update ${resource}`,\n DELETE: `Delete ${resource}`,\n };\n return summaries[method.toUpperCase()] ?? `${method} ${resource}`;\n}\n\nfunction inferTag(path: string): string {\n const parts = path.split('/').filter(s => s.length > 0 && !s.startsWith('{'));\n return parts[0] ?? 'default';\n}\n\nexport function generateOpenApiSpec(\n observations: ApiEndpointObservationInput[],\n opts: { title?: string; baseUrl?: string; version?: string } = {},\n): OpenApiSpec {\n const { title = 'ZeTa Discovered API', baseUrl = '/', version = '1.0.0' } = opts;\n\n const spec: OpenApiSpec = {\n openapi: '3.0.3',\n info: { title, version, description: 'Auto-generated by ZeTa crawler from observed network traffic.' },\n servers: [{ url: baseUrl }],\n paths: {},\n };\n\n for (const obs of observations) {\n const path = obs.urlPattern;\n const method = obs.method.toLowerCase();\n\n if (!spec.paths[path]) spec.paths[path] = {};\n\n const params = extractPathParams(path);\n const statusCode = obs.statusCode ?? 200;\n const responses: OpenApiOperation['responses'] = {\n [String(statusCode)]: {\n description: statusCode < 400 ? 'Successful response' : 'Error response',\n content: obs.responseShape\n ? { 'application/json': { schema: shapeToJsonSchema(obs.responseShape) } }\n : undefined,\n },\n };\n\n const op: OpenApiOperation = {\n summary: methodSummary(obs.method, path),\n parameters: params.length > 0 ? params : undefined,\n responses,\n tags: [inferTag(path)],\n };\n\n spec.paths[path][method] = op;\n }\n\n return spec;\n}\n\nfunction shapeToJsonSchema(shape: unknown): unknown {\n if (Array.isArray(shape)) return { type: 'array', items: shape.length > 0 ? shapeToJsonSchema(shape[0]) : {} };\n if (shape && typeof shape === 'object') {\n const props: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(shape as object)) props[k] = shapeToJsonSchema(v);\n return { type: 'object', properties: props };\n }\n return { type: String(shape) };\n}\n\nexport function specToYaml(spec: OpenApiSpec): string {\n // Minimal YAML serializer \u2014 avoids yaml dep\n function toYaml(val: unknown, indent = 0): string {\n const pad = ' '.repeat(indent);\n if (val === null || val === undefined) return 'null';\n if (typeof val === 'string') return val.includes('\\n') || val.includes(':') ? `\"${val.replace(/\"/g, '\\\\\"')}\"` : val;\n if (typeof val === 'number' || typeof val === 'boolean') return String(val);\n if (Array.isArray(val)) return val.map(v => `\\n${pad}- ${toYaml(v, indent + 1).trimStart()}`).join('') || '[]';\n if (typeof val === 'object') {\n const entries = Object.entries(val as object);\n if (entries.length === 0) return '{}';\n return entries.map(([k, v]) => {\n const vStr = toYaml(v, indent + 1);\n return typeof v === 'object' && v !== null && !Array.isArray(v)\n ? `\\n${pad}${k}:${vStr}`\n : `\\n${pad}${k}: ${vStr.trimStart()}`;\n }).join('');\n }\n return String(val);\n }\n return `openapi: \"${spec.openapi}\"${toYaml(spec, 0)}`;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PDF extraction — discovers and extracts text from PDF URLs found during crawl.
|
|
3
|
+
*
|
|
4
|
+
* When the HTTP engine or browser engine discovers a link to a .pdf file,
|
|
5
|
+
* this module fetches the PDF, extracts its text content, and returns it
|
|
6
|
+
* as a screen-compatible markdown string for test generation.
|
|
7
|
+
*
|
|
8
|
+
* Uses pdf-parse (already installed in API package, accessible via dynamic import).
|
|
9
|
+
*/
|
|
10
|
+
export interface PdfScreen {
|
|
11
|
+
url: string;
|
|
12
|
+
title: string;
|
|
13
|
+
markdown: string;
|
|
14
|
+
pageCount: number;
|
|
15
|
+
info: Record<string, string>;
|
|
16
|
+
sizeBytes: number;
|
|
17
|
+
}
|
|
18
|
+
export declare function isPdfUrl(url: string, contentType?: string): boolean;
|
|
19
|
+
export declare function extractPdfFromUrl(url: string, opts?: {
|
|
20
|
+
userAgent?: string;
|
|
21
|
+
timeoutMs?: number;
|
|
22
|
+
maxSizeBytes?: number;
|
|
23
|
+
}): Promise<PdfScreen | null>;
|
|
24
|
+
/**
|
|
25
|
+
* Filter a list of discovered URLs to find PDF links.
|
|
26
|
+
* Used by http-engine and browser crawler to decide which links to pass to extractPdfFromUrl.
|
|
27
|
+
*/
|
|
28
|
+
export declare function filterPdfUrls(urls: string[]): string[];
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
const PDF_EXT = /\.pdf(\?.*)?$/i;
|
|
2
|
+
const PDF_MIME = "application/pdf";
|
|
3
|
+
function isPdfUrl(url, contentType) {
|
|
4
|
+
return PDF_EXT.test(url) || (contentType?.includes(PDF_MIME) ?? false);
|
|
5
|
+
}
|
|
6
|
+
async function extractPdfFromUrl(url, opts = {}) {
|
|
7
|
+
const { userAgent = "ZeTa-Crawler/1.0", timeoutMs = 3e4, maxSizeBytes = 50 * 1024 * 1024 } = opts;
|
|
8
|
+
let buffer;
|
|
9
|
+
let sizeBytes;
|
|
10
|
+
try {
|
|
11
|
+
const response = await fetch(url, {
|
|
12
|
+
headers: { "User-Agent": userAgent, "Accept": "application/pdf,*/*" },
|
|
13
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
14
|
+
});
|
|
15
|
+
if (!response.ok) return null;
|
|
16
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
17
|
+
if (!contentType.includes("pdf") && !PDF_EXT.test(url)) return null;
|
|
18
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
19
|
+
sizeBytes = arrayBuffer.byteLength;
|
|
20
|
+
if (sizeBytes > maxSizeBytes) {
|
|
21
|
+
console.warn(`[pdf-extractor] PDF too large (${sizeBytes} bytes), skipping: ${url}`);
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
buffer = Buffer.from(arrayBuffer);
|
|
25
|
+
} catch (e) {
|
|
26
|
+
console.error(`[pdf-extractor] Failed to fetch PDF ${url}: ${e.message}`);
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
const pdfParse = await import("pdf-parse/lib/pdf-parse.js").catch(
|
|
31
|
+
() => import("pdf-parse")
|
|
32
|
+
);
|
|
33
|
+
const parse = pdfParse.default ?? pdfParse;
|
|
34
|
+
const data = await parse(buffer, { max: 0 });
|
|
35
|
+
const title = data.info?.Title || extractTitleFromUrl(url);
|
|
36
|
+
const markdown = pdfTextToMarkdown(data.text ?? "", title);
|
|
37
|
+
return {
|
|
38
|
+
url,
|
|
39
|
+
title,
|
|
40
|
+
markdown,
|
|
41
|
+
pageCount: data.numpages ?? 0,
|
|
42
|
+
info: {
|
|
43
|
+
author: data.info?.Author ?? "",
|
|
44
|
+
subject: data.info?.Subject ?? "",
|
|
45
|
+
keywords: data.info?.Keywords ?? "",
|
|
46
|
+
creator: data.info?.Creator ?? ""
|
|
47
|
+
},
|
|
48
|
+
sizeBytes
|
|
49
|
+
};
|
|
50
|
+
} catch (e) {
|
|
51
|
+
console.error(`[pdf-extractor] Failed to parse PDF ${url}: ${e.message}`);
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function extractTitleFromUrl(url) {
|
|
56
|
+
try {
|
|
57
|
+
const { pathname } = new URL(url);
|
|
58
|
+
const filename = pathname.split("/").pop() ?? "document";
|
|
59
|
+
return filename.replace(/\.pdf$/i, "").replace(/[-_]/g, " ").trim();
|
|
60
|
+
} catch {
|
|
61
|
+
return "PDF Document";
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function pdfTextToMarkdown(text, title) {
|
|
65
|
+
if (!text.trim()) return `# ${title}
|
|
66
|
+
|
|
67
|
+
*No extractable text content.*`;
|
|
68
|
+
const cleaned = text.replace(/\f/g, "\n\n---\n\n").replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\n{3,}/g, "\n\n").replace(/[ \t]{2,}/g, " ").trim();
|
|
69
|
+
return `# ${title}
|
|
70
|
+
|
|
71
|
+
${cleaned}`;
|
|
72
|
+
}
|
|
73
|
+
function filterPdfUrls(urls) {
|
|
74
|
+
return urls.filter((u) => PDF_EXT.test(u));
|
|
75
|
+
}
|
|
76
|
+
export {
|
|
77
|
+
extractPdfFromUrl,
|
|
78
|
+
filterPdfUrls,
|
|
79
|
+
isPdfUrl
|
|
80
|
+
};
|
|
81
|
+
//# sourceMappingURL=pdf-extractor.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/pdf-extractor.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * PDF extraction \u2014 discovers and extracts text from PDF URLs found during crawl.\n *\n * When the HTTP engine or browser engine discovers a link to a .pdf file,\n * this module fetches the PDF, extracts its text content, and returns it\n * as a screen-compatible markdown string for test generation.\n *\n * Uses pdf-parse (already installed in API package, accessible via dynamic import).\n */\n\nconst PDF_EXT = /\\.pdf(\\?.*)?$/i;\nconst PDF_MIME = 'application/pdf';\n\nexport interface PdfScreen {\n url: string;\n title: string;\n markdown: string;\n pageCount: number;\n info: Record<string, string>;\n sizeBytes: number;\n}\n\nexport function isPdfUrl(url: string, contentType?: string): boolean {\n return PDF_EXT.test(url) || (contentType?.includes(PDF_MIME) ?? false);\n}\n\nexport async function extractPdfFromUrl(url: string, opts: {\n userAgent?: string;\n timeoutMs?: number;\n maxSizeBytes?: number;\n} = {}): Promise<PdfScreen | null> {\n const { userAgent = 'ZeTa-Crawler/1.0', timeoutMs = 30_000, maxSizeBytes = 50 * 1024 * 1024 } = opts;\n\n let buffer: Buffer;\n let sizeBytes: number;\n\n try {\n const response = await fetch(url, {\n headers: { 'User-Agent': userAgent, 'Accept': 'application/pdf,*/*' },\n signal: AbortSignal.timeout(timeoutMs),\n });\n if (!response.ok) return null;\n\n const contentType = response.headers.get('content-type') ?? '';\n if (!contentType.includes('pdf') && !PDF_EXT.test(url)) return null;\n\n const arrayBuffer = await response.arrayBuffer();\n sizeBytes = arrayBuffer.byteLength;\n if (sizeBytes > maxSizeBytes) {\n console.warn(`[pdf-extractor] PDF too large (${sizeBytes} bytes), skipping: ${url}`);\n return null;\n }\n buffer = Buffer.from(arrayBuffer);\n } catch (e: any) {\n console.error(`[pdf-extractor] Failed to fetch PDF ${url}: ${e.message}`);\n return null;\n }\n\n try {\n // pdf-parse is installed in the API package \u2014 use dynamic import\n // so this file compiles without adding pdf-parse to crawler's package.json\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore \u2013 no types for pdf-parse/lib/pdf-parse.js\n const pdfParse = await import('pdf-parse/lib/pdf-parse.js').catch(\n () => import('pdf-parse' as any)\n ) as any;\n const parse = pdfParse.default ?? pdfParse;\n const data = await parse(buffer, { max: 0 }); // max: 0 = all pages\n\n const title = (data.info?.Title as string) || extractTitleFromUrl(url);\n const markdown = pdfTextToMarkdown(data.text ?? '', title);\n\n return {\n url,\n title,\n markdown,\n pageCount: data.numpages ?? 0,\n info: {\n author: data.info?.Author ?? '',\n subject: data.info?.Subject ?? '',\n keywords: data.info?.Keywords ?? '',\n creator: data.info?.Creator ?? '',\n },\n sizeBytes,\n };\n } catch (e: any) {\n console.error(`[pdf-extractor] Failed to parse PDF ${url}: ${e.message}`);\n return null;\n }\n}\n\nfunction extractTitleFromUrl(url: string): string {\n try {\n const { pathname } = new URL(url);\n const filename = pathname.split('/').pop() ?? 'document';\n return filename.replace(/\\.pdf$/i, '').replace(/[-_]/g, ' ').trim();\n } catch {\n return 'PDF Document';\n }\n}\n\nfunction pdfTextToMarkdown(text: string, title: string): string {\n if (!text.trim()) return `# ${title}\\n\\n*No extractable text content.*`;\n\n // Clean up common PDF extraction artifacts\n const cleaned = text\n .replace(/\\f/g, '\\n\\n---\\n\\n') // form feed = page break\n .replace(/\\r\\n/g, '\\n')\n .replace(/\\r/g, '\\n')\n .replace(/\\n{3,}/g, '\\n\\n')\n .replace(/[ \\t]{2,}/g, ' ')\n .trim();\n\n return `# ${title}\\n\\n${cleaned}`;\n}\n\n/**\n * Filter a list of discovered URLs to find PDF links.\n * Used by http-engine and browser crawler to decide which links to pass to extractPdfFromUrl.\n */\nexport function filterPdfUrls(urls: string[]): string[] {\n return urls.filter(u => PDF_EXT.test(u));\n}\n"],
|
|
5
|
+
"mappings": "AAUA,MAAM,UAAU;AAChB,MAAM,WAAW;AAWV,SAAS,SAAS,KAAa,aAA+B;AACnE,SAAO,QAAQ,KAAK,GAAG,MAAM,aAAa,SAAS,QAAQ,KAAK;AAClE;AAEA,eAAsB,kBAAkB,KAAa,OAIjD,CAAC,GAA8B;AACjC,QAAM,EAAE,YAAY,oBAAoB,YAAY,KAAQ,eAAe,KAAK,OAAO,KAAK,IAAI;AAEhG,MAAI;AACJ,MAAI;AAEJ,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,KAAK;AAAA,MAChC,SAAS,EAAE,cAAc,WAAW,UAAU,sBAAsB;AAAA,MACpE,QAAQ,YAAY,QAAQ,SAAS;AAAA,IACvC,CAAC;AACD,QAAI,CAAC,SAAS,GAAI,QAAO;AAEzB,UAAM,cAAc,SAAS,QAAQ,IAAI,cAAc,KAAK;AAC5D,QAAI,CAAC,YAAY,SAAS,KAAK,KAAK,CAAC,QAAQ,KAAK,GAAG,EAAG,QAAO;AAE/D,UAAM,cAAc,MAAM,SAAS,YAAY;AAC/C,gBAAY,YAAY;AACxB,QAAI,YAAY,cAAc;AAC5B,cAAQ,KAAK,kCAAkC,SAAS,sBAAsB,GAAG,EAAE;AACnF,aAAO;AAAA,IACT;AACA,aAAS,OAAO,KAAK,WAAW;AAAA,EAClC,SAAS,GAAQ;AACf,YAAQ,MAAM,uCAAuC,GAAG,KAAK,EAAE,OAAO,EAAE;AACxE,WAAO;AAAA,EACT;AAEA,MAAI;AAKF,UAAM,WAAW,MAAM,OAAO,4BAA4B,EAAE;AAAA,MAC1D,MAAM,OAAO,WAAkB;AAAA,IACjC;AACA,UAAM,QAAQ,SAAS,WAAW;AAClC,UAAM,OAAO,MAAM,MAAM,QAAQ,EAAE,KAAK,EAAE,CAAC;AAE3C,UAAM,QAAS,KAAK,MAAM,SAAoB,oBAAoB,GAAG;AACrE,UAAM,WAAW,kBAAkB,KAAK,QAAQ,IAAI,KAAK;AAEzD,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,KAAK,YAAY;AAAA,MAC5B,MAAM;AAAA,QACJ,QAAQ,KAAK,MAAM,UAAU;AAAA,QAC7B,SAAS,KAAK,MAAM,WAAW;AAAA,QAC/B,UAAU,KAAK,MAAM,YAAY;AAAA,QACjC,SAAS,KAAK,MAAM,WAAW;AAAA,MACjC;AAAA,MACA;AAAA,IACF;AAAA,EACF,SAAS,GAAQ;AACf,YAAQ,MAAM,uCAAuC,GAAG,KAAK,EAAE,OAAO,EAAE;AACxE,WAAO;AAAA,EACT;AACF;AAEA,SAAS,oBAAoB,KAAqB;AAChD,MAAI;AACF,UAAM,EAAE,SAAS,IAAI,IAAI,IAAI,GAAG;AAChC,UAAM,WAAW,SAAS,MAAM,GAAG,EAAE,IAAI,KAAK;AAC9C,WAAO,SAAS,QAAQ,WAAW,EAAE,EAAE,QAAQ,SAAS,GAAG,EAAE,KAAK;AAAA,EACpE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,kBAAkB,MAAc,OAAuB;AAC9D,MAAI,CAAC,KAAK,KAAK,EAAG,QAAO,KAAK,KAAK;AAAA;AAAA;AAGnC,QAAM,UAAU,KACb,QAAQ,OAAO,aAAa,EAC5B,QAAQ,SAAS,IAAI,EACrB,QAAQ,OAAO,IAAI,EACnB,QAAQ,WAAW,MAAM,EACzB,QAAQ,cAAc,GAAG,EACzB,KAAK;AAER,SAAO,KAAK,KAAK;AAAA;AAAA,EAAO,OAAO;AACjC;AAMO,SAAS,cAAc,MAA0B;AACtD,SAAO,KAAK,OAAO,OAAK,QAAQ,KAAK,CAAC,CAAC;AACzC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Performance metrics collector — captures Core Web Vitals and navigation timing
|
|
3
|
+
* for pages visited during crawl runs.
|
|
4
|
+
* Collects LCP, CLS, INP, TTFB, long tasks, and slow resources via PerformanceObserver
|
|
5
|
+
* and the Navigation/Resource Timing APIs.
|
|
6
|
+
*/
|
|
7
|
+
import type { Page } from 'playwright';
|
|
8
|
+
export type CwvScore = 'good' | 'needs-improvement' | 'poor';
|
|
9
|
+
export interface LcpMetric {
|
|
10
|
+
value: number;
|
|
11
|
+
score: CwvScore;
|
|
12
|
+
elementTag: string | null;
|
|
13
|
+
url: string | null;
|
|
14
|
+
}
|
|
15
|
+
export interface ClsMetric {
|
|
16
|
+
value: number;
|
|
17
|
+
score: CwvScore;
|
|
18
|
+
shiftCount: number;
|
|
19
|
+
}
|
|
20
|
+
export interface InpMetric {
|
|
21
|
+
p75: number;
|
|
22
|
+
score: CwvScore;
|
|
23
|
+
sampleCount: number;
|
|
24
|
+
}
|
|
25
|
+
export interface NavTimingMetric {
|
|
26
|
+
ttfb: number;
|
|
27
|
+
ttfbScore: CwvScore;
|
|
28
|
+
domContentLoaded: number;
|
|
29
|
+
domComplete: number;
|
|
30
|
+
loadComplete: number;
|
|
31
|
+
transferSizeBytes: number;
|
|
32
|
+
protocol: string;
|
|
33
|
+
}
|
|
34
|
+
export interface SlowResource {
|
|
35
|
+
path: string;
|
|
36
|
+
durationMs: number;
|
|
37
|
+
transferSizeBytes: number;
|
|
38
|
+
initiatorType: string;
|
|
39
|
+
}
|
|
40
|
+
export interface PerformanceMetricsResult {
|
|
41
|
+
lcp: LcpMetric | null;
|
|
42
|
+
cls: ClsMetric | null;
|
|
43
|
+
inp: InpMetric | null;
|
|
44
|
+
navTiming: NavTimingMetric | null;
|
|
45
|
+
slowResources: SlowResource[];
|
|
46
|
+
longTaskCount: number;
|
|
47
|
+
captureSuccess: boolean;
|
|
48
|
+
}
|
|
49
|
+
export declare function injectPerformanceObservers(page: Page): Promise<void>;
|
|
50
|
+
export declare function collectPagePerformance(page: Page): Promise<PerformanceMetricsResult>;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
async function injectPerformanceObservers(page) {
|
|
2
|
+
await page.addInitScript(() => {
|
|
3
|
+
window.__zetaPerf = { lcp: null, cls: 0, clsCount: 0, inp: [], longTaskCount: 0 };
|
|
4
|
+
try {
|
|
5
|
+
new PerformanceObserver((list) => {
|
|
6
|
+
const entries = list.getEntries();
|
|
7
|
+
const last = entries[entries.length - 1];
|
|
8
|
+
if (last) {
|
|
9
|
+
window.__zetaPerf.lcp = {
|
|
10
|
+
value: last.startTime,
|
|
11
|
+
elementTag: last.element?.tagName || null,
|
|
12
|
+
url: last.url || null
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
}).observe({ entryTypes: ["largest-contentful-paint"] });
|
|
16
|
+
} catch {
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
new PerformanceObserver((list) => {
|
|
20
|
+
for (const entry of list.getEntries()) {
|
|
21
|
+
if (!entry.hadRecentInput) {
|
|
22
|
+
window.__zetaPerf.cls += entry.value;
|
|
23
|
+
window.__zetaPerf.clsCount++;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}).observe({ entryTypes: ["layout-shift"] });
|
|
27
|
+
} catch {
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
new PerformanceObserver((list) => {
|
|
31
|
+
for (const entry of list.getEntries()) {
|
|
32
|
+
window.__zetaPerf.inp.push(entry.duration);
|
|
33
|
+
}
|
|
34
|
+
}).observe({ entryTypes: ["event"], durationThreshold: 40 });
|
|
35
|
+
} catch {
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
new PerformanceObserver((list) => {
|
|
39
|
+
window.__zetaPerf.longTaskCount += list.getEntries().length;
|
|
40
|
+
}).observe({ entryTypes: ["longtask"] });
|
|
41
|
+
} catch {
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function scoreLcp(value) {
|
|
46
|
+
if (value <= 2500) return "good";
|
|
47
|
+
if (value <= 4e3) return "needs-improvement";
|
|
48
|
+
return "poor";
|
|
49
|
+
}
|
|
50
|
+
function scoreCls(value) {
|
|
51
|
+
if (value <= 0.1) return "good";
|
|
52
|
+
if (value <= 0.25) return "needs-improvement";
|
|
53
|
+
return "poor";
|
|
54
|
+
}
|
|
55
|
+
function scoreInp(p75) {
|
|
56
|
+
if (p75 <= 200) return "good";
|
|
57
|
+
if (p75 <= 500) return "needs-improvement";
|
|
58
|
+
return "poor";
|
|
59
|
+
}
|
|
60
|
+
function scoreTtfb(value) {
|
|
61
|
+
if (value <= 800) return "good";
|
|
62
|
+
if (value <= 1800) return "needs-improvement";
|
|
63
|
+
return "poor";
|
|
64
|
+
}
|
|
65
|
+
async function collectMetrics(page) {
|
|
66
|
+
const rawMetrics = await page.evaluate(() => {
|
|
67
|
+
const p = window.__zetaPerf || {};
|
|
68
|
+
const nav = performance.getEntriesByType("navigation")[0];
|
|
69
|
+
const resources = performance.getEntriesByType("resource").filter((r) => r.duration > 500).sort((a, b) => b.duration - a.duration).slice(0, 10).map((r) => ({
|
|
70
|
+
path: (() => {
|
|
71
|
+
try {
|
|
72
|
+
return new URL(r.name).pathname;
|
|
73
|
+
} catch {
|
|
74
|
+
return r.name.slice(0, 80);
|
|
75
|
+
}
|
|
76
|
+
})(),
|
|
77
|
+
durationMs: Math.round(r.duration),
|
|
78
|
+
transferSizeBytes: r.transferSize || 0,
|
|
79
|
+
initiatorType: r.initiatorType || ""
|
|
80
|
+
}));
|
|
81
|
+
const inpValues = p.inp || [];
|
|
82
|
+
const p75 = inpValues.length > 0 ? inpValues.sort((a, b) => a - b)[Math.floor(inpValues.length * 0.75)] : 0;
|
|
83
|
+
return {
|
|
84
|
+
lcp: p.lcp || null,
|
|
85
|
+
cls: p.cls ?? null,
|
|
86
|
+
clsCount: p.clsCount ?? 0,
|
|
87
|
+
inpP75: p75,
|
|
88
|
+
inpCount: inpValues.length,
|
|
89
|
+
longTaskCount: p.longTaskCount || 0,
|
|
90
|
+
nav: nav ? {
|
|
91
|
+
ttfb: Math.round(nav.responseStart - nav.requestStart),
|
|
92
|
+
domContentLoaded: Math.round(nav.domContentLoadedEventEnd - nav.startTime),
|
|
93
|
+
domComplete: Math.round(nav.domComplete - nav.startTime),
|
|
94
|
+
loadComplete: Math.round(nav.loadEventEnd - nav.startTime),
|
|
95
|
+
transferSizeBytes: nav.transferSize || 0,
|
|
96
|
+
protocol: nav.nextHopProtocol || ""
|
|
97
|
+
} : null,
|
|
98
|
+
resources
|
|
99
|
+
};
|
|
100
|
+
});
|
|
101
|
+
const lcp = rawMetrics.lcp ? {
|
|
102
|
+
value: rawMetrics.lcp.value,
|
|
103
|
+
score: scoreLcp(rawMetrics.lcp.value),
|
|
104
|
+
elementTag: rawMetrics.lcp.elementTag,
|
|
105
|
+
url: rawMetrics.lcp.url
|
|
106
|
+
} : null;
|
|
107
|
+
const cls = rawMetrics.cls !== null ? {
|
|
108
|
+
value: rawMetrics.cls,
|
|
109
|
+
score: scoreCls(rawMetrics.cls),
|
|
110
|
+
shiftCount: rawMetrics.clsCount
|
|
111
|
+
} : null;
|
|
112
|
+
const inp = rawMetrics.inpCount > 0 ? {
|
|
113
|
+
p75: rawMetrics.inpP75,
|
|
114
|
+
score: scoreInp(rawMetrics.inpP75),
|
|
115
|
+
sampleCount: rawMetrics.inpCount
|
|
116
|
+
} : null;
|
|
117
|
+
const navTiming = rawMetrics.nav ? {
|
|
118
|
+
ttfb: rawMetrics.nav.ttfb,
|
|
119
|
+
ttfbScore: scoreTtfb(rawMetrics.nav.ttfb),
|
|
120
|
+
domContentLoaded: rawMetrics.nav.domContentLoaded,
|
|
121
|
+
domComplete: rawMetrics.nav.domComplete,
|
|
122
|
+
loadComplete: rawMetrics.nav.loadComplete,
|
|
123
|
+
transferSizeBytes: rawMetrics.nav.transferSizeBytes,
|
|
124
|
+
protocol: rawMetrics.nav.protocol
|
|
125
|
+
} : null;
|
|
126
|
+
return {
|
|
127
|
+
lcp,
|
|
128
|
+
cls,
|
|
129
|
+
inp,
|
|
130
|
+
navTiming,
|
|
131
|
+
slowResources: rawMetrics.resources,
|
|
132
|
+
longTaskCount: rawMetrics.longTaskCount,
|
|
133
|
+
captureSuccess: true
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
const emptyResult = {
|
|
137
|
+
lcp: null,
|
|
138
|
+
cls: null,
|
|
139
|
+
inp: null,
|
|
140
|
+
navTiming: null,
|
|
141
|
+
slowResources: [],
|
|
142
|
+
longTaskCount: 0
|
|
143
|
+
};
|
|
144
|
+
async function collectPagePerformance(page) {
|
|
145
|
+
try {
|
|
146
|
+
return await collectMetrics(page);
|
|
147
|
+
} catch {
|
|
148
|
+
return { ...emptyResult, captureSuccess: false };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
export {
|
|
152
|
+
collectPagePerformance,
|
|
153
|
+
injectPerformanceObservers
|
|
154
|
+
};
|
|
155
|
+
//# sourceMappingURL=performance-metrics-collector.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/performance-metrics-collector.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Performance metrics collector \u2014 captures Core Web Vitals and navigation timing\n * for pages visited during crawl runs.\n * Collects LCP, CLS, INP, TTFB, long tasks, and slow resources via PerformanceObserver\n * and the Navigation/Resource Timing APIs.\n */\n\nimport type { Page } from 'playwright';\n\nexport type CwvScore = 'good' | 'needs-improvement' | 'poor';\n\nexport interface LcpMetric {\n value: number;\n score: CwvScore;\n elementTag: string | null;\n url: string | null;\n}\n\nexport interface ClsMetric {\n value: number;\n score: CwvScore;\n shiftCount: number;\n}\n\nexport interface InpMetric {\n p75: number;\n score: CwvScore;\n sampleCount: number;\n}\n\nexport interface NavTimingMetric {\n ttfb: number;\n ttfbScore: CwvScore;\n domContentLoaded: number;\n domComplete: number;\n loadComplete: number;\n transferSizeBytes: number;\n protocol: string;\n}\n\nexport interface SlowResource {\n path: string;\n durationMs: number;\n transferSizeBytes: number;\n initiatorType: string;\n}\n\nexport interface PerformanceMetricsResult {\n lcp: LcpMetric | null;\n cls: ClsMetric | null;\n inp: InpMetric | null;\n navTiming: NavTimingMetric | null;\n slowResources: SlowResource[];\n longTaskCount: number;\n captureSuccess: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Observer injection \u2014 call before page navigation via addInitScript\n// ---------------------------------------------------------------------------\n\nexport async function injectPerformanceObservers(page: Page): Promise<void> {\n await page.addInitScript(() => {\n (window as any).__zetaPerf = { lcp: null, cls: 0, clsCount: 0, inp: [], longTaskCount: 0 };\n\n try {\n new PerformanceObserver(list => {\n const entries = list.getEntries();\n const last = entries[entries.length - 1] as any;\n if (last) {\n (window as any).__zetaPerf.lcp = {\n value: last.startTime,\n elementTag: last.element?.tagName || null,\n url: last.url || null,\n };\n }\n }).observe({ entryTypes: ['largest-contentful-paint'] });\n } catch {}\n\n try {\n new PerformanceObserver(list => {\n for (const entry of list.getEntries() as any[]) {\n if (!entry.hadRecentInput) {\n (window as any).__zetaPerf.cls += entry.value;\n (window as any).__zetaPerf.clsCount++;\n }\n }\n }).observe({ entryTypes: ['layout-shift'] });\n } catch {}\n\n try {\n new PerformanceObserver(list => {\n for (const entry of list.getEntries() as any[]) {\n (window as any).__zetaPerf.inp.push(entry.duration);\n }\n }).observe({ entryTypes: ['event'], durationThreshold: 40 } as any);\n } catch {}\n\n try {\n new PerformanceObserver(list => {\n (window as any).__zetaPerf.longTaskCount += list.getEntries().length;\n }).observe({ entryTypes: ['longtask'] });\n } catch {}\n });\n}\n\n// ---------------------------------------------------------------------------\n// Scoring helpers\n// ---------------------------------------------------------------------------\n\nfunction scoreLcp(value: number): CwvScore {\n if (value <= 2500) return 'good';\n if (value <= 4000) return 'needs-improvement';\n return 'poor';\n}\n\nfunction scoreCls(value: number): CwvScore {\n if (value <= 0.1) return 'good';\n if (value <= 0.25) return 'needs-improvement';\n return 'poor';\n}\n\nfunction scoreInp(p75: number): CwvScore {\n if (p75 <= 200) return 'good';\n if (p75 <= 500) return 'needs-improvement';\n return 'poor';\n}\n\nfunction scoreTtfb(value: number): CwvScore {\n if (value <= 800) return 'good';\n if (value <= 1800) return 'needs-improvement';\n return 'poor';\n}\n\n// ---------------------------------------------------------------------------\n// Internal collector\n// ---------------------------------------------------------------------------\n\nasync function collectMetrics(page: Page): Promise<PerformanceMetricsResult> {\n const rawMetrics = await page.evaluate(() => {\n const p = (window as any).__zetaPerf || {};\n const nav = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming | undefined;\n const resources = performance.getEntriesByType('resource')\n .filter(r => r.duration > 500)\n .sort((a, b) => b.duration - a.duration)\n .slice(0, 10)\n .map(r => ({\n path: (() => { try { return new URL(r.name).pathname; } catch { return r.name.slice(0, 80); } })(),\n durationMs: Math.round(r.duration),\n transferSizeBytes: (r as any).transferSize || 0,\n initiatorType: (r as any).initiatorType || '',\n }));\n const inpValues = p.inp || [];\n const p75 = inpValues.length > 0\n ? inpValues.sort((a: number, b: number) => a - b)[Math.floor(inpValues.length * 0.75)]\n : 0;\n return {\n lcp: p.lcp || null,\n cls: p.cls ?? null,\n clsCount: p.clsCount ?? 0,\n inpP75: p75,\n inpCount: inpValues.length,\n longTaskCount: p.longTaskCount || 0,\n nav: nav ? {\n ttfb: Math.round(nav.responseStart - nav.requestStart),\n domContentLoaded: Math.round(nav.domContentLoadedEventEnd - nav.startTime),\n domComplete: Math.round(nav.domComplete - nav.startTime),\n loadComplete: Math.round(nav.loadEventEnd - nav.startTime),\n transferSizeBytes: (nav as any).transferSize || 0,\n protocol: (nav as any).nextHopProtocol || '',\n } : null,\n resources,\n };\n });\n\n const lcp: LcpMetric | null = rawMetrics.lcp\n ? {\n value: rawMetrics.lcp.value,\n score: scoreLcp(rawMetrics.lcp.value),\n elementTag: rawMetrics.lcp.elementTag,\n url: rawMetrics.lcp.url,\n }\n : null;\n\n const cls: ClsMetric | null = rawMetrics.cls !== null\n ? {\n value: rawMetrics.cls,\n score: scoreCls(rawMetrics.cls),\n shiftCount: rawMetrics.clsCount,\n }\n : null;\n\n const inp: InpMetric | null = rawMetrics.inpCount > 0\n ? {\n p75: rawMetrics.inpP75,\n score: scoreInp(rawMetrics.inpP75),\n sampleCount: rawMetrics.inpCount,\n }\n : null;\n\n const navTiming: NavTimingMetric | null = rawMetrics.nav\n ? {\n ttfb: rawMetrics.nav.ttfb,\n ttfbScore: scoreTtfb(rawMetrics.nav.ttfb),\n domContentLoaded: rawMetrics.nav.domContentLoaded,\n domComplete: rawMetrics.nav.domComplete,\n loadComplete: rawMetrics.nav.loadComplete,\n transferSizeBytes: rawMetrics.nav.transferSizeBytes,\n protocol: rawMetrics.nav.protocol,\n }\n : null;\n\n return {\n lcp,\n cls,\n inp,\n navTiming,\n slowResources: rawMetrics.resources,\n longTaskCount: rawMetrics.longTaskCount,\n captureSuccess: true,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nconst emptyResult: Omit<PerformanceMetricsResult, 'captureSuccess'> = {\n lcp: null,\n cls: null,\n inp: null,\n navTiming: null,\n slowResources: [],\n longTaskCount: 0,\n};\n\nexport async function collectPagePerformance(page: Page): Promise<PerformanceMetricsResult> {\n try {\n return await collectMetrics(page);\n } catch {\n return { ...emptyResult, captureSuccess: false };\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Performance metrics collection via CDP + Playwright page.evaluate().
|
|
3
|
+
*
|
|
4
|
+
* Collects Core Web Vitals and resource timing after page navigation.
|
|
5
|
+
* Stored alongside screen data for test generation context and regression detection.
|
|
6
|
+
*
|
|
7
|
+
* Metrics:
|
|
8
|
+
* - LCP (Largest Contentful Paint) — via PerformanceObserver in page
|
|
9
|
+
* - CLS (Cumulative Layout Shift) — via PerformanceObserver in page
|
|
10
|
+
* - TTFB (Time to First Byte) — from navigation timing
|
|
11
|
+
* - DOMContentLoaded — from navigation timing
|
|
12
|
+
* - Resource count and size by type
|
|
13
|
+
*/
|
|
14
|
+
export interface CoreWebVitals {
|
|
15
|
+
lcp?: number;
|
|
16
|
+
cls?: number;
|
|
17
|
+
ttfb?: number;
|
|
18
|
+
fcp?: number;
|
|
19
|
+
domContentLoaded?: number;
|
|
20
|
+
loadEventEnd?: number;
|
|
21
|
+
}
|
|
22
|
+
export interface ResourceSummary {
|
|
23
|
+
scripts: {
|
|
24
|
+
count: number;
|
|
25
|
+
totalBytes: number;
|
|
26
|
+
};
|
|
27
|
+
styles: {
|
|
28
|
+
count: number;
|
|
29
|
+
totalBytes: number;
|
|
30
|
+
};
|
|
31
|
+
images: {
|
|
32
|
+
count: number;
|
|
33
|
+
totalBytes: number;
|
|
34
|
+
};
|
|
35
|
+
fonts: {
|
|
36
|
+
count: number;
|
|
37
|
+
totalBytes: number;
|
|
38
|
+
};
|
|
39
|
+
other: {
|
|
40
|
+
count: number;
|
|
41
|
+
totalBytes: number;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export interface PagePerfMetrics {
|
|
45
|
+
url: string;
|
|
46
|
+
vitals: CoreWebVitals;
|
|
47
|
+
resources: ResourceSummary;
|
|
48
|
+
totalTransferBytes: number;
|
|
49
|
+
requestCount: number;
|
|
50
|
+
measuredAt: string;
|
|
51
|
+
}
|
|
52
|
+
export declare function collectPerfMetrics(page: any, url: string): Promise<PagePerfMetrics>;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
async function collectPerfMetrics(page, url) {
|
|
2
|
+
const measuredAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3
|
+
let vitals = {};
|
|
4
|
+
try {
|
|
5
|
+
vitals = await page.evaluate(() => {
|
|
6
|
+
return new Promise((resolve) => {
|
|
7
|
+
const result = {};
|
|
8
|
+
const nav = performance.getEntriesByType("navigation")[0];
|
|
9
|
+
if (nav) {
|
|
10
|
+
result.ttfb = Math.round(nav.responseStart - nav.requestStart);
|
|
11
|
+
result.domContentLoaded = Math.round(nav.domContentLoadedEventEnd);
|
|
12
|
+
result.loadEventEnd = Math.round(nav.loadEventEnd);
|
|
13
|
+
}
|
|
14
|
+
let lcpDone = false;
|
|
15
|
+
let clsDone = false;
|
|
16
|
+
const tryResolve = () => {
|
|
17
|
+
if (lcpDone && clsDone) resolve(result);
|
|
18
|
+
};
|
|
19
|
+
try {
|
|
20
|
+
new PerformanceObserver((list) => {
|
|
21
|
+
const entries = list.getEntries();
|
|
22
|
+
if (entries.length > 0) result.lcp = Math.round(entries[entries.length - 1].startTime);
|
|
23
|
+
lcpDone = true;
|
|
24
|
+
tryResolve();
|
|
25
|
+
}).observe({ type: "largest-contentful-paint", buffered: true });
|
|
26
|
+
} catch {
|
|
27
|
+
lcpDone = true;
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
let cls = 0;
|
|
31
|
+
new PerformanceObserver((list) => {
|
|
32
|
+
for (const e of list.getEntries()) {
|
|
33
|
+
if (!e.hadRecentInput) cls += e.value;
|
|
34
|
+
}
|
|
35
|
+
result.cls = Math.round(cls * 1e3) / 1e3;
|
|
36
|
+
clsDone = true;
|
|
37
|
+
tryResolve();
|
|
38
|
+
}).observe({ type: "layout-shift", buffered: true });
|
|
39
|
+
} catch {
|
|
40
|
+
clsDone = true;
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
const fcp = performance.getEntriesByName("first-contentful-paint")[0];
|
|
44
|
+
if (fcp) result.fcp = Math.round(fcp.startTime);
|
|
45
|
+
} catch {
|
|
46
|
+
}
|
|
47
|
+
setTimeout(() => resolve(result), 3e3);
|
|
48
|
+
});
|
|
49
|
+
}).catch(() => ({}));
|
|
50
|
+
} catch {
|
|
51
|
+
}
|
|
52
|
+
let resources = {
|
|
53
|
+
scripts: { count: 0, totalBytes: 0 },
|
|
54
|
+
styles: { count: 0, totalBytes: 0 },
|
|
55
|
+
images: { count: 0, totalBytes: 0 },
|
|
56
|
+
fonts: { count: 0, totalBytes: 0 },
|
|
57
|
+
other: { count: 0, totalBytes: 0 }
|
|
58
|
+
};
|
|
59
|
+
let totalTransferBytes = 0;
|
|
60
|
+
let requestCount = 0;
|
|
61
|
+
try {
|
|
62
|
+
const entries = await page.evaluate(
|
|
63
|
+
() => performance.getEntriesByType("resource").map((e) => ({
|
|
64
|
+
name: e.name,
|
|
65
|
+
initiatorType: e.initiatorType,
|
|
66
|
+
transferSize: e.transferSize ?? 0
|
|
67
|
+
}))
|
|
68
|
+
).catch(() => []);
|
|
69
|
+
for (const e of entries) {
|
|
70
|
+
requestCount++;
|
|
71
|
+
totalTransferBytes += e.transferSize;
|
|
72
|
+
const cat = e.initiatorType === "script" ? "scripts" : e.initiatorType === "css" || e.name?.endsWith(".css") ? "styles" : e.initiatorType === "img" ? "images" : e.name?.match(/\.(woff|woff2|ttf|otf|eot)/) ? "fonts" : "other";
|
|
73
|
+
resources[cat].count++;
|
|
74
|
+
resources[cat].totalBytes += e.transferSize;
|
|
75
|
+
}
|
|
76
|
+
} catch {
|
|
77
|
+
}
|
|
78
|
+
return { url, vitals, resources, totalTransferBytes, requestCount, measuredAt };
|
|
79
|
+
}
|
|
80
|
+
export {
|
|
81
|
+
collectPerfMetrics
|
|
82
|
+
};
|
|
83
|
+
//# sourceMappingURL=performance-metrics.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/performance-metrics.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Performance metrics collection via CDP + Playwright page.evaluate().\n *\n * Collects Core Web Vitals and resource timing after page navigation.\n * Stored alongside screen data for test generation context and regression detection.\n *\n * Metrics:\n * - LCP (Largest Contentful Paint) \u2014 via PerformanceObserver in page\n * - CLS (Cumulative Layout Shift) \u2014 via PerformanceObserver in page\n * - TTFB (Time to First Byte) \u2014 from navigation timing\n * - DOMContentLoaded \u2014 from navigation timing\n * - Resource count and size by type\n */\n\nimport type { Page } from 'playwright';\n\nexport interface CoreWebVitals {\n lcp?: number; // ms\n cls?: number; // unitless score\n ttfb?: number; // ms\n fcp?: number; // First Contentful Paint ms\n domContentLoaded?: number; // ms\n loadEventEnd?: number; // ms\n}\n\nexport interface ResourceSummary {\n scripts: { count: number; totalBytes: number };\n styles: { count: number; totalBytes: number };\n images: { count: number; totalBytes: number };\n fonts: { count: number; totalBytes: number };\n other: { count: number; totalBytes: number };\n}\n\nexport interface PagePerfMetrics {\n url: string;\n vitals: CoreWebVitals;\n resources: ResourceSummary;\n totalTransferBytes: number;\n requestCount: number;\n measuredAt: string;\n}\n\nexport async function collectPerfMetrics(page: any, url: string): Promise<PagePerfMetrics> {\n const measuredAt = new Date().toISOString();\n\n // Inject PerformanceObserver for LCP + CLS, wait up to 3s\n let vitals: CoreWebVitals = {};\n try {\n vitals = await page.evaluate(() => {\n return new Promise<any>((resolve) => {\n const result: any = {};\n\n // Navigation timing\n const nav = performance.getEntriesByType('navigation')[0] as any;\n if (nav) {\n result.ttfb = Math.round(nav.responseStart - nav.requestStart);\n result.domContentLoaded = Math.round(nav.domContentLoadedEventEnd);\n result.loadEventEnd = Math.round(nav.loadEventEnd);\n }\n\n let lcpDone = false;\n let clsDone = false;\n const tryResolve = () => { if (lcpDone && clsDone) resolve(result); };\n\n // LCP\n try {\n new PerformanceObserver((list) => {\n const entries = list.getEntries();\n if (entries.length > 0) result.lcp = Math.round((entries[entries.length - 1] as any).startTime);\n lcpDone = true;\n tryResolve();\n }).observe({ type: 'largest-contentful-paint', buffered: true });\n } catch { lcpDone = true; }\n\n // CLS\n try {\n let cls = 0;\n new PerformanceObserver((list) => {\n for (const e of list.getEntries() as any[]) {\n if (!e.hadRecentInput) cls += e.value;\n }\n result.cls = Math.round(cls * 1000) / 1000;\n clsDone = true;\n tryResolve();\n }).observe({ type: 'layout-shift', buffered: true });\n } catch { clsDone = true; }\n\n // FCP\n try {\n const fcp = performance.getEntriesByName('first-contentful-paint')[0] as any;\n if (fcp) result.fcp = Math.round(fcp.startTime);\n } catch {}\n\n // Fallback resolve after 3s\n setTimeout(() => resolve(result), 3_000);\n });\n }).catch(() => ({}));\n } catch { /* page closed or navigate already */ }\n\n // Resource timing\n let resources: ResourceSummary = {\n scripts: { count: 0, totalBytes: 0 },\n styles: { count: 0, totalBytes: 0 },\n images: { count: 0, totalBytes: 0 },\n fonts: { count: 0, totalBytes: 0 },\n other: { count: 0, totalBytes: 0 },\n };\n let totalTransferBytes = 0;\n let requestCount = 0;\n\n try {\n const entries = await page.evaluate(() =>\n performance.getEntriesByType('resource').map((e: any) => ({\n name: e.name,\n initiatorType: e.initiatorType,\n transferSize: e.transferSize ?? 0,\n }))\n ).catch(() => [] as any[]);\n\n for (const e of entries) {\n requestCount++;\n totalTransferBytes += e.transferSize;\n const cat = e.initiatorType === 'script' ? 'scripts'\n : e.initiatorType === 'css' || e.name?.endsWith('.css') ? 'styles'\n : e.initiatorType === 'img' ? 'images'\n : e.name?.match(/\\.(woff|woff2|ttf|otf|eot)/) ? 'fonts'\n : 'other';\n resources[cat as keyof ResourceSummary].count++;\n resources[cat as keyof ResourceSummary].totalBytes += e.transferSize;\n }\n } catch {}\n\n return { url, vitals, resources, totalTransferBytes, requestCount, measuredAt };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|