e11y 0.2.0 → 1.1.0
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +130 -10
- data/CHANGELOG.md +80 -1
- data/CLAUDE.md +168 -0
- data/CONTRIBUTING.md +640 -0
- data/README.md +165 -701
- data/RELEASE.md +41 -12
- data/Rakefile +249 -57
- data/config/README.md +1 -1
- data/config/loki-local-config.yaml +12 -0
- data/config/otel-collector-config.yaml +44 -0
- data/cucumber.yml +1 -0
- data/docker-compose.yml +18 -2
- data/docs/ADAPTERS.md +76 -0
- data/docs/ADAPTIVE_SAMPLING.md +59 -0
- data/docs/COMPARISON.md +104 -0
- data/docs/CONFIGURATION.md +52 -0
- data/docs/DISTRIBUTED_TRACING.md +44 -0
- data/docs/LIMITATIONS.md +13 -0
- data/docs/METRICS_DSL.md +84 -0
- data/docs/PERFORMANCE.md +60 -0
- data/docs/PII_FILTERING.md +40 -0
- data/docs/PRESETS.md +65 -0
- data/docs/QUICK-START.md +546 -587
- data/docs/RAILS_INTEGRATION.md +79 -0
- data/docs/SCHEMA_VALIDATION.md +63 -0
- data/docs/SLO-PROMQL-ALERTS.md +161 -0
- data/docs/TESTING.md +69 -0
- data/docs/{ADR-001-architecture.md → architecture/ADR-001-architecture.md} +36 -65
- data/docs/{ADR-002-metrics-yabeda.md → architecture/ADR-002-metrics-yabeda.md} +62 -236
- data/docs/architecture/ADR-003-slo-observability.md +1402 -0
- data/docs/{ADR-004-adapter-architecture.md → architecture/ADR-004-adapter-architecture.md} +163 -146
- data/docs/{ADR-005-tracing-context.md → architecture/ADR-005-tracing-context.md} +10 -9
- data/docs/{ADR-006-security-compliance.md → architecture/ADR-006-security-compliance.md} +184 -191
- data/docs/{ADR-007-opentelemetry-integration.md → architecture/ADR-007-opentelemetry-integration.md} +3 -21
- data/docs/{ADR-008-rails-integration.md → architecture/ADR-008-rails-integration.md} +182 -743
- data/docs/{ADR-009-cost-optimization.md → architecture/ADR-009-cost-optimization.md} +45 -54
- data/docs/architecture/ADR-010-developer-experience.md +522 -0
- data/docs/{ADR-011-testing-strategy.md → architecture/ADR-011-testing-strategy.md} +44 -86
- data/docs/{ADR-012-event-evolution.md → architecture/ADR-012-event-evolution.md} +11 -11
- data/docs/{ADR-013-reliability-error-handling.md → architecture/ADR-013-reliability-error-handling.md} +37 -12
- data/docs/{ADR-014-event-driven-slo.md → architecture/ADR-014-event-driven-slo.md} +12 -24
- data/docs/{ADR-015-middleware-order.md → architecture/ADR-015-middleware-order.md} +43 -59
- data/docs/{ADR-016-self-monitoring-slo.md → architecture/ADR-016-self-monitoring-slo.md} +58 -355
- data/docs/{ADR-017-multi-rails-compatibility.md → architecture/ADR-017-multi-rails-compatibility.md} +4 -11
- data/docs/architecture/ADR-018-memory-optimization.md +366 -0
- data/docs/{ADR-INDEX.md → architecture/ADR-INDEX.md} +11 -6
- data/docs/plans/2026-03-20-browser-overlay-svelte.md +281 -0
- data/docs/{00-ICP-AND-TIMELINE.md → prd/00-ICP-AND-TIMELINE.md} +6 -6
- data/docs/{01-SCALE-REQUIREMENTS.md → prd/01-SCALE-REQUIREMENTS.md} +6 -6
- data/docs/prd/01-overview-vision.md +19 -14
- data/docs/use_cases/README.md +22 -23
- data/docs/use_cases/UC-001-request-scoped-debug-buffering.md +50 -44
- data/docs/use_cases/UC-002-business-event-tracking.md +26 -95
- data/docs/use_cases/UC-003-event-metrics.md +66 -0
- data/docs/use_cases/UC-004-zero-config-slo-tracking.md +33 -684
- data/docs/use_cases/UC-005-sentry-integration.md +13 -15
- data/docs/use_cases/UC-006-trace-context-management.md +30 -28
- data/docs/use_cases/UC-007-pii-filtering.md +35 -87
- data/docs/use_cases/UC-008-opentelemetry-integration.md +51 -89
- data/docs/use_cases/UC-009-multi-service-tracing.md +30 -178
- data/docs/use_cases/UC-010-background-job-tracking.md +24 -91
- data/docs/use_cases/UC-011-rate-limiting.md +95 -168
- data/docs/use_cases/UC-012-audit-trail.md +21 -46
- data/docs/use_cases/UC-013-high-cardinality-protection.md +29 -167
- data/docs/use_cases/UC-014-adaptive-sampling.md +2 -2
- data/docs/use_cases/UC-015-cost-optimization.md +46 -99
- data/docs/use_cases/UC-016-rails-logger-migration.md +39 -213
- data/docs/use_cases/UC-017-local-development.md +203 -777
- data/docs/use_cases/UC-018-testing-events.md +3 -3
- data/docs/use_cases/UC-019-retention-based-routing.md +53 -106
- data/docs/use_cases/UC-020-event-versioning.md +8 -9
- data/docs/use_cases/UC-021-error-handling-retry-dlq.md +18 -22
- data/docs/use_cases/UC-022-event-registry.md +15 -21
- data/docs/use_cases/backlog.md +119 -87
- data/e11y.gemspec +2 -2
- data/gems/e11y-devtools/README.md +158 -0
- data/gems/e11y-devtools/config/routes.rb +15 -0
- data/gems/e11y-devtools/e11y-devtools.gemspec +25 -0
- data/gems/e11y-devtools/exe/e11y +34 -0
- data/gems/e11y-devtools/frontend/.gitignore +24 -0
- data/gems/e11y-devtools/frontend/README.md +51 -0
- data/gems/e11y-devtools/frontend/index.html +14 -0
- data/gems/e11y-devtools/frontend/package-lock.json +3707 -0
- data/gems/e11y-devtools/frontend/package.json +28 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/events/recent.json +4205 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/interactions.json +194 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/0a2e04027cfa22d014bc22e8b27cd913/events.json +86 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/0e1543af6a630fb3af6b52283154b3e0/events.json +169 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/1838b691faa49564f97db8592ff3978d/events.json +78 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/29f198f6588dacffb687777eb5f8f118/events.json +197 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/34bc3c9c0097de28a7a6f99b90a8e7bc/events.json +194 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/3ba6c20d068ab9cee00e51b180e66444/events.json +184 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/435bfd8f17b9009146a79812d7c3726d/events.json +144 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/4c7676e3fe668e99edb2b94d7d5678a9/events.json +222 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/6daf0d47974bedfc55d5de7004a3ea9f/events.json +194 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/8a81ada42834d15f287bb40010043605/events.json +194 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/8c0a98900edaae105469df8daedccf02/events.json +198 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/8e4f645180f8a7d1dce426b07380466b/events.json +222 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/93db346fa5d44a032605a13b627f4b80/events.json +128 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/98ff6146faf7bd9be8bd03a8275817ba/events.json +223 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/9997ddd0247bc7e25f2ca7a5c415c93d/events.json +197 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/99e35f8ef3baedd798cc4fd085980ad9/events.json +194 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/b4f3095c1909924cbc98889a86c83d6d/events.json +131 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/b54b7fc32b7575a7110de809d11ccda0/events.json +128 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/c0b48033fa06746bcc5886745e053cff/events.json +169 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/c44649ac76701b4558927cd2305ab535/events.json +169 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/d601ae3320057580a39dbdac2edfdf4a/events.json +248 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/e67e724bab422d2b52eeb49635e512e1/events.json +194 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/e6c72765a28f158a8485b35fa63f73da/events.json +194 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/f541b87405c9a54819b18ebe529f6419/events.json +194 -0
- data/gems/e11y-devtools/frontend/scripts/generate_mocks.rb +397 -0
- data/gems/e11y-devtools/frontend/src/App.svelte +827 -0
- data/gems/e11y-devtools/frontend/src/components/Fab.svelte +19 -0
- data/gems/e11y-devtools/frontend/src/components/FilterBar.svelte +38 -0
- data/gems/e11y-devtools/frontend/src/components/FullscreenPanel.svelte +82 -0
- data/gems/e11y-devtools/frontend/src/components/InteractionsTimeline.svelte +264 -0
- data/gems/e11y-devtools/frontend/src/components/RecentHistogram.svelte +354 -0
- data/gems/e11y-devtools/frontend/src/lib/api.ts +37 -0
- data/gems/e11y-devtools/frontend/src/lib/eventIdentity.ts +12 -0
- data/gems/e11y-devtools/frontend/src/lib/format.ts +37 -0
- data/gems/e11y-devtools/frontend/src/lib/listFilter.ts +43 -0
- data/gems/e11y-devtools/frontend/src/lib/recentVolume.ts +80 -0
- data/gems/e11y-devtools/frontend/src/lib/router.ts +12 -0
- data/gems/e11y-devtools/frontend/src/lib/transitions.ts +34 -0
- data/gems/e11y-devtools/frontend/src/lib/viewportOrigin.ts +25 -0
- data/gems/e11y-devtools/frontend/src/main.ts +8 -0
- data/gems/e11y-devtools/frontend/src/overlay-entry.ts +24 -0
- data/gems/e11y-devtools/frontend/src/overlay.css +1080 -0
- data/gems/e11y-devtools/frontend/svelte.config.js +2 -0
- data/gems/e11y-devtools/frontend/test_puppeteer.js +41 -0
- data/gems/e11y-devtools/frontend/test_scale.js +3 -0
- data/gems/e11y-devtools/frontend/tsconfig.app.json +21 -0
- data/gems/e11y-devtools/frontend/tsconfig.json +7 -0
- data/gems/e11y-devtools/frontend/tsconfig.node.json +26 -0
- data/gems/e11y-devtools/frontend/vite.config.ts +36 -0
- data/gems/e11y-devtools/lib/e11y/devtools/mcp/server.rb +96 -0
- data/gems/e11y-devtools/lib/e11y/devtools/mcp/tool_base.rb +25 -0
- data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/clear.rb +31 -0
- data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/errors.rb +35 -0
- data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/event_detail.rb +33 -0
- data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/events_by_trace.rb +33 -0
- data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/interactions.rb +40 -0
- data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/recent_events.rb +34 -0
- data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/search.rb +34 -0
- data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/stats.rb +30 -0
- data/gems/e11y-devtools/lib/e11y/devtools/overlay/assets/overlay.js +20 -0
- data/gems/e11y-devtools/lib/e11y/devtools/overlay/controller.rb +94 -0
- data/gems/e11y-devtools/lib/e11y/devtools/overlay/engine.rb +26 -0
- data/gems/e11y-devtools/lib/e11y/devtools/overlay/middleware.rb +80 -0
- data/gems/e11y-devtools/lib/e11y/devtools/overlay/rails_controller.rb +67 -0
- data/gems/e11y-devtools/lib/e11y/devtools/tui/app.rb +262 -0
- data/gems/e11y-devtools/lib/e11y/devtools/tui/grouping.rb +66 -0
- data/gems/e11y-devtools/lib/e11y/devtools/tui/widgets/event_detail.rb +62 -0
- data/gems/e11y-devtools/lib/e11y/devtools/tui/widgets/event_list.rb +70 -0
- data/gems/e11y-devtools/lib/e11y/devtools/tui/widgets/interaction_list.rb +47 -0
- data/gems/e11y-devtools/lib/e11y/devtools/version.rb +8 -0
- data/gems/e11y-devtools/lib/e11y/devtools.rb +13 -0
- data/gems/e11y-devtools/spec/e11y/devtools/mcp/tools_spec.rb +107 -0
- data/gems/e11y-devtools/spec/e11y/devtools/overlay/controller_spec.rb +91 -0
- data/gems/e11y-devtools/spec/e11y/devtools/overlay/middleware_spec.rb +46 -0
- data/gems/e11y-devtools/spec/e11y/devtools/tui/app_spec.rb +85 -0
- data/gems/e11y-devtools/spec/e11y/devtools/tui/grouping_spec.rb +64 -0
- data/gems/e11y-devtools/spec/spec_helper.rb +5 -0
- data/gems/e11y-devtools/spec/tui/widgets/event_list_spec.rb +44 -0
- data/gems/e11y-devtools/spec/tui/widgets/interaction_list_spec.rb +62 -0
- data/lib/e11y/adapters/audit_encrypted.rb +53 -11
- data/lib/e11y/adapters/base.rb +33 -34
- data/lib/e11y/adapters/dev_log/file_store.rb +143 -0
- data/lib/e11y/adapters/dev_log/query.rb +219 -0
- data/lib/e11y/adapters/dev_log.rb +118 -0
- data/lib/e11y/adapters/file.rb +3 -6
- data/lib/e11y/adapters/in_memory.rb +52 -5
- data/lib/e11y/adapters/in_memory_test.rb +29 -0
- data/lib/e11y/adapters/loki.rb +58 -23
- data/lib/e11y/adapters/null.rb +82 -0
- data/lib/e11y/adapters/opentelemetry_collector.rb +183 -0
- data/lib/e11y/adapters/otel_logs.rb +136 -23
- data/lib/e11y/adapters/sentry.rb +4 -7
- data/lib/e11y/adapters/stdout.rb +73 -7
- data/lib/e11y/adapters/yabeda.rb +153 -29
- data/lib/e11y/buffers/adaptive_buffer.rb +3 -17
- data/lib/e11y/buffers/{request_scoped_buffer.rb → ephemeral_buffer.rb} +72 -58
- data/lib/e11y/buffers/ring_buffer.rb +3 -16
- data/lib/e11y/configuration.rb +272 -0
- data/lib/e11y/console.rb +10 -17
- data/lib/e11y/current.rb +53 -1
- data/lib/e11y/debug/pipeline_inspector.rb +96 -0
- data/lib/e11y/documentation/generator.rb +48 -0
- data/lib/e11y/event/base.rb +176 -82
- data/lib/e11y/event/value_sampling_config.rb +1 -5
- data/lib/e11y/events/rails/database/query.rb +1 -4
- data/lib/e11y/events/rails/job/failed.rb +2 -0
- data/lib/e11y/instruments/active_job.rb +44 -12
- data/lib/e11y/instruments/rails_instrumentation.rb +49 -24
- data/lib/e11y/instruments/sidekiq.rb +135 -31
- data/lib/e11y/linters/base.rb +11 -0
- data/lib/e11y/linters/pii/pii_declaration_linter.rb +120 -0
- data/lib/e11y/linters/slo/config_consistency_linter.rb +76 -0
- data/lib/e11y/linters/slo/explicit_declaration_linter.rb +36 -0
- data/lib/e11y/linters/slo/slo_status_from_linter.rb +41 -0
- data/lib/e11y/logger/bridge.rb +26 -7
- data/lib/e11y/metrics/cardinality_protection.rb +10 -15
- data/lib/e11y/metrics/cardinality_tracker.rb +16 -6
- data/lib/e11y/metrics/registry.rb +3 -5
- data/lib/e11y/metrics/test_backend.rb +62 -0
- data/lib/e11y/metrics.rb +56 -10
- data/lib/e11y/middleware/adapter_resolver.rb +40 -0
- data/lib/e11y/middleware/audit_signing.rb +43 -6
- data/lib/e11y/middleware/baggage_protection.rb +75 -0
- data/lib/e11y/middleware/dev_log_source.rb +24 -0
- data/lib/e11y/middleware/event_slo.rb +23 -9
- data/lib/e11y/middleware/otel_span.rb +23 -0
- data/lib/e11y/middleware/pii_filter.rb +104 -75
- data/lib/e11y/middleware/rate_limiting.rb +54 -27
- data/lib/e11y/middleware/request.rb +70 -23
- data/lib/e11y/middleware/routing.rb +78 -21
- data/lib/e11y/middleware/sampling.rb +66 -17
- data/lib/e11y/middleware/self_monitoring_emit.rb +39 -0
- data/lib/e11y/middleware/trace_context.rb +45 -10
- data/lib/e11y/middleware/track_latency.rb +34 -0
- data/lib/e11y/middleware/validation.rb +7 -16
- data/lib/e11y/middleware/versioning.rb +26 -22
- data/lib/e11y/opentelemetry/semantic_conventions.rb +109 -0
- data/lib/e11y/opentelemetry/span_creator.rb +142 -0
- data/lib/e11y/pii/patterns.rb +12 -1
- data/lib/e11y/pipeline/builder.rb +4 -4
- data/lib/e11y/presets/audit_event.rb +13 -2
- data/lib/e11y/railtie.rb +52 -14
- data/lib/e11y/registry.rb +306 -0
- data/lib/e11y/reliability/circuit_breaker.rb +19 -21
- data/lib/e11y/reliability/dlq/base.rb +71 -0
- data/lib/e11y/reliability/dlq/file_adapter.rb +301 -0
- data/lib/e11y/reliability/dlq/file_storage.rb +63 -34
- data/lib/e11y/reliability/dlq/filter.rb +37 -54
- data/lib/e11y/reliability/retry_handler.rb +26 -29
- data/lib/e11y/reliability/retry_rate_limiter.rb +3 -11
- data/lib/e11y/sampling/error_spike_detector.rb +0 -2
- data/lib/e11y/sampling/load_monitor.rb +5 -9
- data/lib/e11y/sampling/stratified_tracker.rb +18 -0
- data/lib/e11y/self_monitoring/buffer_monitor.rb +2 -0
- data/lib/e11y/self_monitoring/performance_monitor.rb +19 -61
- data/lib/e11y/self_monitoring/reliability_monitor.rb +4 -74
- data/lib/e11y/slo/config_loader.rb +40 -0
- data/lib/e11y/slo/config_validator.rb +58 -0
- data/lib/e11y/slo/dashboard_generator.rb +122 -0
- data/lib/e11y/slo/event_driven.rb +8 -0
- data/lib/e11y/slo/tracker.rb +31 -4
- data/lib/e11y/testing/have_tracked_event_matcher.rb +190 -0
- data/lib/e11y/testing/rspec_matchers.rb +21 -0
- data/lib/e11y/testing/snapshot_matcher.rb +86 -0
- data/lib/e11y/trace_context/sampler.rb +35 -0
- data/lib/e11y/tracing/faraday_middleware.rb +31 -0
- data/lib/e11y/tracing/net_http_patch.rb +33 -0
- data/lib/e11y/tracing/propagator.rb +144 -0
- data/lib/e11y/tracing.rb +47 -0
- data/lib/e11y/version.rb +1 -1
- data/lib/e11y/versioning/version_extractor.rb +32 -0
- data/lib/e11y.rb +123 -266
- data/lib/generators/e11y/event/event_generator.rb +22 -0
- data/lib/generators/e11y/event/templates/event.rb.tt +16 -0
- data/lib/generators/e11y/grafana_dashboard/grafana_dashboard_generator.rb +30 -0
- data/lib/generators/e11y/grafana_dashboard/templates/e11y_dashboard.json +81 -0
- data/lib/generators/e11y/install/install_generator.rb +34 -0
- data/lib/generators/e11y/install/templates/e11y.rb +239 -0
- data/lib/generators/e11y/prometheus_alerts/prometheus_alerts_generator.rb +29 -0
- data/lib/generators/e11y/prometheus_alerts/templates/e11y_alerts.yml +28 -0
- data/lib/tasks/e11y_docs.rake +30 -0
- data/lib/tasks/e11y_events.rake +71 -0
- data/lib/tasks/e11y_lint.rake +91 -0
- data/lib/tasks/e11y_slo.rake +29 -0
- metadata +186 -39
- data/docs/ADR-003-slo-observability.md +0 -3337
- data/docs/ADR-010-developer-experience.md +0 -2166
- data/docs/API-REFERENCE-L28.md +0 -914
- data/docs/COMPREHENSIVE-CONFIGURATION.md +0 -2366
- data/docs/CONTRIBUTING.md +0 -312
- data/docs/IMPLEMENTATION_NOTES.md +0 -2804
- data/docs/IMPLEMENTATION_PLAN.md +0 -1971
- data/docs/IMPLEMENTATION_PLAN_ARCHITECTURE.md +0 -586
- data/docs/PLAN.md +0 -148
- data/docs/README.md +0 -296
- data/docs/design/00-memory-optimization.md +0 -593
- data/docs/guides/MIGRATION-L27-L28.md +0 -692
- data/docs/guides/PERFORMANCE-BENCHMARKS.md +0 -434
- data/docs/guides/README.md +0 -44
- data/docs/use_cases/UC-003-pattern-based-metrics.md +0 -1627
- data/lib/e11y/adapters/registry.rb +0 -141
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import puppeteer from 'puppeteer';
|
|
2
|
+
|
|
3
|
+
(async () => {
|
|
4
|
+
const browser = await puppeteer.launch({ headless: "new" });
|
|
5
|
+
const page = await browser.newPage();
|
|
6
|
+
|
|
7
|
+
page.on('console', msg => console.log('PAGE LOG:', msg.text()));
|
|
8
|
+
|
|
9
|
+
await page.goto('http://localhost:5173');
|
|
10
|
+
await page.waitForSelector('.e11y-fab', { timeout: 5000 });
|
|
11
|
+
await page.click('.e11y-fab');
|
|
12
|
+
await page.waitForSelector('.e11y-histo-chart-host', { timeout: 5000 });
|
|
13
|
+
|
|
14
|
+
await page.evaluate(() => {
|
|
15
|
+
const host = document.querySelector('.e11y-histo-brush-overlay');
|
|
16
|
+
if (host) {
|
|
17
|
+
const rect = host.getBoundingClientRect();
|
|
18
|
+
const down = new PointerEvent('pointerdown', { clientX: rect.left + 50, clientY: rect.top + 20, button: 0, bubbles: true });
|
|
19
|
+
host.dispatchEvent(down);
|
|
20
|
+
|
|
21
|
+
const move = new PointerEvent('pointermove', { clientX: rect.left + 150, clientY: rect.top + 20, button: 0, bubbles: true });
|
|
22
|
+
host.dispatchEvent(move);
|
|
23
|
+
|
|
24
|
+
const up = new PointerEvent('pointerup', { clientX: rect.left + 150, clientY: rect.top + 20, button: 0, bubbles: true });
|
|
25
|
+
host.dispatchEvent(up);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
await new Promise(r => setTimeout(r, 500));
|
|
30
|
+
|
|
31
|
+
await page.evaluate(() => {
|
|
32
|
+
const shade = document.querySelector('.e11y-histo-sel-shade');
|
|
33
|
+
if (shade) {
|
|
34
|
+
console.log('SHADE AFTER FAKE EVENT:', shade.style.left, shade.style.width);
|
|
35
|
+
} else {
|
|
36
|
+
console.log('SHADE AFTER FAKE EVENT: null');
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
await browser.close();
|
|
41
|
+
})();
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "@tsconfig/svelte/tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
5
|
+
"target": "ES2023",
|
|
6
|
+
"useDefineForClassFields": true,
|
|
7
|
+
"module": "ESNext",
|
|
8
|
+
"types": ["svelte", "vite/client"],
|
|
9
|
+
"noEmit": true,
|
|
10
|
+
/**
|
|
11
|
+
* Typecheck JS in `.svelte` and `.js` files by default.
|
|
12
|
+
* Disable checkJs if you'd like to use dynamic types in JS.
|
|
13
|
+
* Note that setting allowJs false does not prevent the use
|
|
14
|
+
* of JS in `.svelte` files.
|
|
15
|
+
*/
|
|
16
|
+
"allowJs": true,
|
|
17
|
+
"checkJs": true,
|
|
18
|
+
"moduleDetection": "force"
|
|
19
|
+
},
|
|
20
|
+
"include": ["src/**/*.ts", "src/**/*.js", "src/**/*.svelte"]
|
|
21
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
|
4
|
+
"target": "ES2023",
|
|
5
|
+
"lib": ["ES2023"],
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"types": ["node"],
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
|
|
10
|
+
/* Bundler mode */
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"allowImportingTsExtensions": true,
|
|
13
|
+
"verbatimModuleSyntax": true,
|
|
14
|
+
"moduleDetection": "force",
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
|
|
17
|
+
/* Linting */
|
|
18
|
+
"strict": true,
|
|
19
|
+
"noUnusedLocals": true,
|
|
20
|
+
"noUnusedParameters": true,
|
|
21
|
+
"erasableSyntaxOnly": true,
|
|
22
|
+
"noFallthroughCasesInSwitch": true,
|
|
23
|
+
"noUncheckedSideEffectImports": true
|
|
24
|
+
},
|
|
25
|
+
"include": ["vite.config.ts"]
|
|
26
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import path from "node:path"
|
|
2
|
+
import { fileURLToPath } from "node:url"
|
|
3
|
+
import { defineConfig } from "vite"
|
|
4
|
+
import { svelte } from "@sveltejs/vite-plugin-svelte"
|
|
5
|
+
import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js"
|
|
6
|
+
|
|
7
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
8
|
+
const assetOutDir = path.resolve(__dirname, "../lib/e11y/devtools/overlay/assets")
|
|
9
|
+
|
|
10
|
+
export default defineConfig(({ command }) => {
|
|
11
|
+
if (command === "serve") {
|
|
12
|
+
return {
|
|
13
|
+
plugins: [svelte()],
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
plugins: [svelte(), cssInjectedByJsPlugin()],
|
|
19
|
+
build: {
|
|
20
|
+
copyPublicDir: false,
|
|
21
|
+
lib: {
|
|
22
|
+
entry: path.resolve(__dirname, "src/overlay-entry.ts"),
|
|
23
|
+
name: "E11yDevtoolsOverlay",
|
|
24
|
+
formats: ["iife"],
|
|
25
|
+
fileName: () => "overlay.js",
|
|
26
|
+
},
|
|
27
|
+
outDir: assetOutDir,
|
|
28
|
+
emptyOutDir: false,
|
|
29
|
+
rollupOptions: {
|
|
30
|
+
output: {
|
|
31
|
+
inlineDynamicImports: true,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
})
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "pathname"
|
|
4
|
+
require "e11y/adapters/dev_log/query"
|
|
5
|
+
require_relative "tools/recent_events"
|
|
6
|
+
require_relative "tools/events_by_trace"
|
|
7
|
+
require_relative "tools/search"
|
|
8
|
+
require_relative "tools/stats"
|
|
9
|
+
require_relative "tools/interactions"
|
|
10
|
+
require_relative "tools/event_detail"
|
|
11
|
+
require_relative "tools/errors"
|
|
12
|
+
require_relative "tools/clear"
|
|
13
|
+
|
|
14
|
+
module E11y
|
|
15
|
+
module Devtools
|
|
16
|
+
module Mcp
|
|
17
|
+
# MCP Server wrapping the E11y DevLog adapter.
|
|
18
|
+
#
|
|
19
|
+
# Exposes 8 tools to AI tools like Cursor and Claude Code.
|
|
20
|
+
# Supports stdio (default) and StreamableHTTP transports.
|
|
21
|
+
#
|
|
22
|
+
# @example Start stdio server
|
|
23
|
+
# E11y::Devtools::Mcp::Server.new.run
|
|
24
|
+
#
|
|
25
|
+
# @example Start HTTP server on port 3099
|
|
26
|
+
# E11y::Devtools::Mcp::Server.new.run(transport: :http, port: 3099)
|
|
27
|
+
class Server
|
|
28
|
+
TOOLS = [
|
|
29
|
+
Tools::RecentEvents, Tools::EventsByTrace, Tools::Search,
|
|
30
|
+
Tools::Stats, Tools::Interactions, Tools::EventDetail,
|
|
31
|
+
Tools::Errors, Tools::Clear
|
|
32
|
+
].freeze
|
|
33
|
+
|
|
34
|
+
def initialize(log_path: nil)
|
|
35
|
+
@log_path = log_path || auto_detect_log_path
|
|
36
|
+
@store = E11y::Adapters::DevLog::Query.new(@log_path)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Start the MCP server.
|
|
40
|
+
#
|
|
41
|
+
# @param transport [:stdio, :http] Transport to use
|
|
42
|
+
# @param port [Integer, nil] HTTP port (default 3099)
|
|
43
|
+
def run(transport: :stdio, port: nil)
|
|
44
|
+
require "mcp"
|
|
45
|
+
server = build_mcp_server
|
|
46
|
+
case transport
|
|
47
|
+
when :stdio then run_stdio(server)
|
|
48
|
+
when :http then run_http(server, port || 3099)
|
|
49
|
+
else raise ArgumentError, "Unknown transport: #{transport}"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def build_mcp_server
|
|
56
|
+
MCP::Server.new(
|
|
57
|
+
name: "e11y",
|
|
58
|
+
version: E11y::Devtools::VERSION,
|
|
59
|
+
tools: TOOLS,
|
|
60
|
+
server_context: { store: @store }
|
|
61
|
+
)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def run_stdio(server)
|
|
65
|
+
t = MCP::Server::Transports::StdioTransport.new(server)
|
|
66
|
+
server.transport = t
|
|
67
|
+
t.open
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def run_http(server, port)
|
|
71
|
+
require "webrick"
|
|
72
|
+
t = MCP::Server::Transports::StreamableHTTPTransport.new(server)
|
|
73
|
+
server.transport = t
|
|
74
|
+
s = WEBrick::HTTPServer.new(Port: port, Logger: WEBrick::Log.new(nil))
|
|
75
|
+
s.mount("/mcp", t)
|
|
76
|
+
trap("INT") { s.shutdown }
|
|
77
|
+
s.start
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def auto_detect_log_path
|
|
81
|
+
dir = Pathname.new(Dir.pwd)
|
|
82
|
+
loop do
|
|
83
|
+
candidate = dir.join("log", "e11y_dev.jsonl")
|
|
84
|
+
return candidate.to_s if candidate.exist?
|
|
85
|
+
|
|
86
|
+
parent = dir.parent
|
|
87
|
+
break if parent == dir
|
|
88
|
+
|
|
89
|
+
dir = parent
|
|
90
|
+
end
|
|
91
|
+
"log/e11y_dev.jsonl"
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module E11y
|
|
4
|
+
module Devtools
|
|
5
|
+
module Mcp
|
|
6
|
+
# Conditional base: use MCP::Tool if available, otherwise plain class.
|
|
7
|
+
# This allows tests to run without the mcp gem installed.
|
|
8
|
+
ToolBase = if defined?(MCP::Tool)
|
|
9
|
+
MCP::Tool
|
|
10
|
+
else
|
|
11
|
+
Class.new do
|
|
12
|
+
def self.description(desc = nil)
|
|
13
|
+
@description = desc if desc
|
|
14
|
+
@description
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.input_schema(schema = nil)
|
|
18
|
+
@input_schema = schema if schema
|
|
19
|
+
@input_schema
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
require "mcp"
|
|
5
|
+
rescue LoadError
|
|
6
|
+
# mcp gem not available — ToolBase will use plain class
|
|
7
|
+
end
|
|
8
|
+
require_relative "../tool_base"
|
|
9
|
+
|
|
10
|
+
module E11y
|
|
11
|
+
module Devtools
|
|
12
|
+
module Mcp
|
|
13
|
+
module Tools
|
|
14
|
+
# Clears the E11y development log file.
|
|
15
|
+
class Clear < ToolBase
|
|
16
|
+
description "Clear the E11y development log file"
|
|
17
|
+
|
|
18
|
+
input_schema(
|
|
19
|
+
type: :object,
|
|
20
|
+
properties: {}
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
def self.call(server_context:, **_opts)
|
|
24
|
+
server_context[:store].clear!
|
|
25
|
+
"E11y log cleared successfully"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
require "mcp"
|
|
5
|
+
rescue LoadError
|
|
6
|
+
# mcp gem not available — ToolBase will use plain class
|
|
7
|
+
end
|
|
8
|
+
require_relative "../tool_base"
|
|
9
|
+
|
|
10
|
+
module E11y
|
|
11
|
+
module Devtools
|
|
12
|
+
module Mcp
|
|
13
|
+
module Tools
|
|
14
|
+
# Returns recent error and fatal events only.
|
|
15
|
+
class Errors < ToolBase
|
|
16
|
+
ERROR_SEVERITIES = %w[error fatal].freeze
|
|
17
|
+
|
|
18
|
+
description "Get recent error and fatal events only"
|
|
19
|
+
|
|
20
|
+
input_schema(
|
|
21
|
+
type: :object,
|
|
22
|
+
properties: {
|
|
23
|
+
limit: { type: :integer, description: "Max events", default: 20 }
|
|
24
|
+
}
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
def self.call(server_context:, limit: 20)
|
|
28
|
+
events = server_context[:store].stored_events(limit: limit * 5)
|
|
29
|
+
events.select { |e| ERROR_SEVERITIES.include?(e["severity"]) }.first(limit)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
require "mcp"
|
|
5
|
+
rescue LoadError
|
|
6
|
+
# mcp gem not available — ToolBase will use plain class
|
|
7
|
+
end
|
|
8
|
+
require_relative "../tool_base"
|
|
9
|
+
|
|
10
|
+
module E11y
|
|
11
|
+
module Devtools
|
|
12
|
+
module Mcp
|
|
13
|
+
module Tools
|
|
14
|
+
# Returns the full payload of a single event by ID.
|
|
15
|
+
class EventDetail < ToolBase
|
|
16
|
+
description "Get full payload of a single event by ID"
|
|
17
|
+
|
|
18
|
+
input_schema(
|
|
19
|
+
type: :object,
|
|
20
|
+
required: ["event_id"],
|
|
21
|
+
properties: {
|
|
22
|
+
event_id: { type: :string, description: "Event UUID" }
|
|
23
|
+
}
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
def self.call(event_id:, server_context:)
|
|
27
|
+
server_context[:store].find_event(event_id) || { error: "Event #{event_id} not found" }
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
require "mcp"
|
|
5
|
+
rescue LoadError
|
|
6
|
+
# mcp gem not available — ToolBase will use plain class
|
|
7
|
+
end
|
|
8
|
+
require_relative "../tool_base"
|
|
9
|
+
|
|
10
|
+
module E11y
|
|
11
|
+
module Devtools
|
|
12
|
+
module Mcp
|
|
13
|
+
module Tools
|
|
14
|
+
# Returns all events for a specific trace ID in chronological order.
|
|
15
|
+
class EventsByTrace < ToolBase
|
|
16
|
+
description "Get all events for a specific trace ID in chronological order"
|
|
17
|
+
|
|
18
|
+
input_schema(
|
|
19
|
+
type: :object,
|
|
20
|
+
required: ["trace_id"],
|
|
21
|
+
properties: {
|
|
22
|
+
trace_id: { type: :string, description: "Trace ID" }
|
|
23
|
+
}
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
def self.call(trace_id:, server_context:)
|
|
27
|
+
server_context[:store].events_by_trace(trace_id)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
require "mcp"
|
|
5
|
+
rescue LoadError
|
|
6
|
+
# mcp gem not available — ToolBase will use plain class
|
|
7
|
+
end
|
|
8
|
+
require_relative "../tool_base"
|
|
9
|
+
|
|
10
|
+
module E11y
|
|
11
|
+
module Devtools
|
|
12
|
+
module Mcp
|
|
13
|
+
module Tools
|
|
14
|
+
# Returns time-grouped interactions (parallel requests from one user action).
|
|
15
|
+
class Interactions < ToolBase
|
|
16
|
+
description "Get time-grouped interactions (parallel requests from one user action)"
|
|
17
|
+
|
|
18
|
+
input_schema(
|
|
19
|
+
type: :object,
|
|
20
|
+
properties: {
|
|
21
|
+
limit: { type: :integer, description: "Max interactions", default: 20 },
|
|
22
|
+
window_ms: { type: :integer, description: "Grouping window in ms", default: 500 }
|
|
23
|
+
}
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
def self.call(server_context:, limit: 20, window_ms: 500)
|
|
27
|
+
server_context[:store].interactions(limit: limit, window_ms: window_ms).map do |ix|
|
|
28
|
+
{
|
|
29
|
+
started_at: ix.started_at.iso8601(3),
|
|
30
|
+
trace_ids: ix.trace_ids,
|
|
31
|
+
has_error: ix.has_error?,
|
|
32
|
+
traces_count: ix.traces_count
|
|
33
|
+
}
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
require "mcp"
|
|
5
|
+
rescue LoadError
|
|
6
|
+
# mcp gem not available — ToolBase will use plain class
|
|
7
|
+
end
|
|
8
|
+
require_relative "../tool_base"
|
|
9
|
+
|
|
10
|
+
module E11y
|
|
11
|
+
module Devtools
|
|
12
|
+
module Mcp
|
|
13
|
+
module Tools
|
|
14
|
+
# Returns the most recent events from the dev log.
|
|
15
|
+
class RecentEvents < ToolBase
|
|
16
|
+
description "Get recent E11y events from the development log"
|
|
17
|
+
|
|
18
|
+
input_schema(
|
|
19
|
+
type: :object,
|
|
20
|
+
properties: {
|
|
21
|
+
limit: { type: :integer, description: "Max events to return (default 50)", default: 50 },
|
|
22
|
+
severity: { type: :string, description: "Filter by severity",
|
|
23
|
+
enum: %w[debug info warn error fatal] }
|
|
24
|
+
}
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
def self.call(server_context:, limit: 50, severity: nil)
|
|
28
|
+
server_context[:store].stored_events(limit: limit, severity: severity)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
require "mcp"
|
|
5
|
+
rescue LoadError
|
|
6
|
+
# mcp gem not available — ToolBase will use plain class
|
|
7
|
+
end
|
|
8
|
+
require_relative "../tool_base"
|
|
9
|
+
|
|
10
|
+
module E11y
|
|
11
|
+
module Devtools
|
|
12
|
+
module Mcp
|
|
13
|
+
module Tools
|
|
14
|
+
# Full-text search across event names and payload content.
|
|
15
|
+
class Search < ToolBase
|
|
16
|
+
description "Full-text search across event names and payload content"
|
|
17
|
+
|
|
18
|
+
input_schema(
|
|
19
|
+
type: :object,
|
|
20
|
+
required: ["query"],
|
|
21
|
+
properties: {
|
|
22
|
+
query: { type: :string, description: "Search term" },
|
|
23
|
+
limit: { type: :integer, description: "Max results", default: 50 }
|
|
24
|
+
}
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
def self.call(query:, server_context:, limit: 50)
|
|
28
|
+
server_context[:store].search(query, limit: limit)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
require "mcp"
|
|
5
|
+
rescue LoadError
|
|
6
|
+
# mcp gem not available — ToolBase will use plain class
|
|
7
|
+
end
|
|
8
|
+
require_relative "../tool_base"
|
|
9
|
+
|
|
10
|
+
module E11y
|
|
11
|
+
module Devtools
|
|
12
|
+
module Mcp
|
|
13
|
+
module Tools
|
|
14
|
+
# Returns aggregate statistics about the E11y development log.
|
|
15
|
+
class Stats < ToolBase
|
|
16
|
+
description "Get aggregate statistics about the E11y development log"
|
|
17
|
+
|
|
18
|
+
input_schema(
|
|
19
|
+
type: :object,
|
|
20
|
+
properties: {}
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
def self.call(server_context:, **_opts)
|
|
24
|
+
server_context[:store].stats
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|