apm-optima 1.0.5 → 2.0.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.
- package/README.md +54 -23
- package/dashboard-out/404/index.html +1 -1
- package/dashboard-out/404.html +1 -1
- package/dashboard-out/__next.__PAGE__.txt +4 -4
- package/dashboard-out/__next._full.txt +17 -17
- package/dashboard-out/__next._head.txt +4 -4
- package/dashboard-out/__next._index.txt +10 -10
- package/dashboard-out/__next._tree.txt +2 -2
- package/dashboard-out/_next/static/chunks/{2xjcsu4pgp14_.js → 062oipeclh9bq.js} +1 -1
- package/dashboard-out/_next/static/chunks/0ncbimuip0-o7.css +3 -0
- package/dashboard-out/_next/static/chunks/0xtgenoch3q77.js +1 -0
- package/dashboard-out/_next/static/chunks/1xln1-s87x5yp.js +27 -0
- package/dashboard-out/_not-found/__next._full.txt +15 -15
- package/dashboard-out/_not-found/__next._head.txt +4 -4
- package/dashboard-out/_not-found/__next._index.txt +10 -10
- package/dashboard-out/_not-found/__next._not-found.__PAGE__.txt +2 -2
- package/dashboard-out/_not-found/__next._not-found.txt +3 -3
- package/dashboard-out/_not-found/__next._tree.txt +2 -2
- package/dashboard-out/_not-found/index.html +1 -1
- package/dashboard-out/_not-found/index.txt +15 -15
- package/dashboard-out/index.html +1 -1
- package/dashboard-out/index.txt +17 -17
- package/dist/adapters/session-export.handler.d.ts +4 -0
- package/dist/adapters/session-export.handler.js +29 -0
- package/dist/adapters/socket.options.d.ts +3 -0
- package/dist/adapters/socket.options.js +16 -0
- package/dist/config/config.manager.d.ts +80 -3
- package/dist/config/config.manager.js +269 -89
- package/dist/config/config.types.d.ts +89 -13
- package/dist/core/delivery/buckets-request.d.ts +2 -0
- package/dist/core/delivery/buckets-request.js +18 -0
- package/dist/core/delivery/index.d.ts +1 -0
- package/dist/core/delivery/index.js +1 -0
- package/dist/core/delivery/metrics.publisher.d.ts +23 -10
- package/dist/core/delivery/metrics.publisher.js +22 -22
- package/dist/core/delivery/websocket.events.d.ts +14 -3
- package/dist/core/delivery/websocket.events.js +13 -2
- package/dist/core/domain/correlation.types.d.ts +37 -0
- package/dist/core/domain/correlation.types.js +2 -0
- package/dist/core/domain/dashboard.types.d.ts +16 -69
- package/dist/core/domain/dashboard.types.js +29 -0
- package/dist/core/domain/endpoints.types.d.ts +3 -0
- package/dist/core/domain/event.types.d.ts +14 -0
- package/dist/core/domain/event.types.js +2 -0
- package/dist/core/domain/incident.types.d.ts +50 -0
- package/dist/core/domain/incident.types.js +2 -0
- package/dist/core/domain/index.d.ts +5 -0
- package/dist/core/domain/index.js +5 -0
- package/dist/core/domain/live-bucket.types.d.ts +86 -0
- package/dist/core/domain/live-bucket.types.js +2 -0
- package/dist/core/domain/metrics.types.d.ts +41 -11
- package/dist/core/domain/session.types.d.ts +93 -0
- package/dist/core/domain/session.types.js +2 -0
- package/dist/core/domain/telemetry.types.d.ts +1 -0
- package/dist/core/organizers/event.manager.d.ts +19 -0
- package/dist/core/organizers/event.manager.js +73 -0
- package/dist/core/organizers/index.d.ts +2 -0
- package/dist/core/organizers/index.js +18 -0
- package/dist/core/organizers/interval.manager.d.ts +18 -0
- package/dist/core/organizers/interval.manager.js +54 -0
- package/dist/core/storage/index.d.ts +1 -0
- package/dist/core/storage/index.js +1 -0
- package/dist/core/storage/local.repository.d.ts +12 -12
- package/dist/core/storage/local.repository.js +38 -38
- package/dist/core/storage/persistence/persistence-writer.d.ts +48 -0
- package/dist/core/storage/persistence/persistence-writer.js +191 -0
- package/dist/core/storage/persistence/segment-index.d.ts +27 -0
- package/dist/core/storage/persistence/segment-index.js +206 -0
- package/dist/core/storage/persistence/session-registry.d.ts +28 -0
- package/dist/core/storage/persistence/session-registry.js +127 -0
- package/dist/core/storage/persistence/session-summary.d.ts +3 -0
- package/dist/core/storage/persistence/session-summary.js +112 -0
- package/dist/core/storage/persistence.layer.d.ts +69 -0
- package/dist/core/storage/persistence.layer.js +404 -0
- package/dist/core/storage/runtime/anomaly-detector.d.ts +11 -0
- package/dist/core/storage/runtime/anomaly-detector.js +42 -0
- package/dist/core/storage/stores/analytics.store.d.ts +5 -5
- package/dist/core/storage/stores/analytics.store.js +39 -12
- package/dist/core/storage/stores/bucket-metric.d.ts +85 -0
- package/dist/core/storage/stores/bucket-metric.js +168 -0
- package/dist/core/storage/stores/bucket-view.d.ts +44 -0
- package/dist/core/storage/stores/bucket-view.js +63 -0
- package/dist/core/storage/stores/bucket.store.d.ts +39 -36
- package/dist/core/storage/stores/bucket.store.js +112 -60
- package/dist/core/storage/stores/dashboard.store.d.ts +11 -4
- package/dist/core/storage/stores/dashboard.store.js +179 -47
- package/dist/core/storage/stores/endpoint.store.d.ts +1 -0
- package/dist/core/storage/stores/endpoint.store.js +21 -5
- package/dist/core/storage/stores/index.d.ts +0 -7
- package/dist/core/storage/stores/index.js +0 -7
- package/dist/core/storage/stores/metrics.store.d.ts +1 -1
- package/dist/core/storage/stores/metrics.store.js +1 -0
- package/dist/core/storage/stores/runtime.store.d.ts +77 -0
- package/dist/core/storage/stores/runtime.store.js +222 -0
- package/dist/core/storage/utility/common.d.ts +1 -0
- package/dist/core/storage/utility/common.js +11 -0
- package/dist/core/storage/utility/file-buffer.d.ts +31 -0
- package/dist/core/storage/utility/file-buffer.js +238 -0
- package/dist/core/storage/utility/histogram.d.ts +1 -2
- package/dist/core/storage/utility/histogram.js +1 -2
- package/dist/core/storage/utility/index.d.ts +2 -0
- package/dist/core/storage/utility/index.js +2 -0
- package/dist/core/storage/utility/persistence-files.d.ts +3 -0
- package/dist/core/storage/utility/persistence-files.js +34 -0
- package/dist/core/storage/utility/persistence-writer.d.ts +39 -0
- package/dist/core/storage/utility/persistence-writer.js +183 -0
- package/dist/core/storage/utility/session-export.d.ts +3 -0
- package/dist/core/storage/utility/session-export.js +31 -0
- package/dist/core/storage/utility/session-registry.d.ts +21 -0
- package/dist/core/storage/utility/session-registry.js +91 -0
- package/dist/core/storage/utility/session-window.d.ts +8 -0
- package/dist/core/storage/utility/session-window.js +43 -0
- package/dist/core/telemetry/analytics.service.d.ts +18 -0
- package/dist/core/telemetry/analytics.service.js +100 -0
- package/dist/core/telemetry/collector.service.d.ts +17 -15
- package/dist/core/telemetry/collector.service.js +23 -80
- package/dist/core/telemetry/correlation.service.d.ts +57 -9
- package/dist/core/telemetry/correlation.service.js +246 -54
- package/dist/core/telemetry/dashboard-live.service.d.ts +10 -0
- package/dist/core/telemetry/dashboard-live.service.js +110 -0
- package/dist/core/telemetry/dashboard.service.d.ts +20 -0
- package/dist/core/telemetry/dashboard.service.js +140 -0
- package/dist/core/telemetry/incident.service.d.ts +71 -0
- package/dist/core/telemetry/incident.service.js +239 -0
- package/dist/core/telemetry/logger.d.ts +4 -0
- package/dist/core/telemetry/logger.js +5 -2
- package/dist/core/telemetry/runtime.service.d.ts +14 -0
- package/dist/core/telemetry/runtime.service.js +56 -0
- package/dist/core/telemetry/telemetry-query.service.d.ts +82 -0
- package/dist/core/telemetry/telemetry-query.service.js +231 -0
- package/dist/core/telemetry/telemetry.service.d.ts +10 -4
- package/dist/core/telemetry/telemetry.service.js +60 -10
- package/dist/core/telemetry/utility/correlation-analysis.d.ts +9 -0
- package/dist/core/telemetry/utility/correlation-analysis.js +143 -0
- package/dist/core/telemetry/utility/correlation-finding.d.ts +70 -0
- package/dist/core/telemetry/utility/correlation-finding.js +41 -0
- package/dist/core/telemetry/utility/correlation-input.d.ts +59 -0
- package/dist/core/telemetry/utility/correlation-input.js +139 -0
- package/dist/core/telemetry/utility/correlation-replay.d.ts +24 -0
- package/dist/core/telemetry/utility/correlation-replay.js +46 -0
- package/dist/core/utility/correlation.analysis.d.ts +9 -0
- package/dist/core/utility/correlation.analysis.js +144 -0
- package/dist/core/utility/statistics.d.ts +65 -0
- package/dist/core/utility/statistics.js +141 -0
- package/dist/express/index.d.ts +1 -1
- package/dist/express/index.js +7 -5
- package/dist/express/metrics-websocket.adapter.d.ts +8 -2
- package/dist/express/metrics-websocket.adapter.js +52 -19
- package/dist/express/metrics.adapter.d.ts +1 -0
- package/dist/express/metrics.adapter.js +8 -1
- package/dist/express/metrics.bootstrap.d.ts +6 -1
- package/dist/express/metrics.bootstrap.js +48 -22
- package/dist/express/metrics.dashboard.d.ts +2 -1
- package/dist/express/metrics.dashboard.js +4 -1
- package/dist/express/metrics.middleware.d.ts +2 -1
- package/dist/express/metrics.middleware.js +23 -20
- package/dist/nest/metrics-websocket.adapter.d.ts +19 -3
- package/dist/nest/metrics-websocket.adapter.js +101 -21
- package/dist/nest/metrics.adapter.d.ts +4 -1
- package/dist/nest/metrics.adapter.js +24 -1
- package/dist/nest/metrics.bootstrap.service.d.ts +7 -3
- package/dist/nest/metrics.bootstrap.service.js +56 -21
- package/dist/nest/metrics.interceptor.d.ts +3 -3
- package/dist/nest/metrics.interceptor.js +11 -8
- package/dist/nest/metrics.module.d.ts +3 -0
- package/dist/nest/metrics.module.js +27 -9
- package/dist/runtime/index.d.ts +32 -0
- package/dist/runtime/index.js +104 -0
- package/dist/simulation/simulation.d.ts +5 -16
- package/dist/simulation/simulation.js +5 -8
- package/package.json +2 -1
- package/dashboard-out/_next/static/chunks/23soo6qkun-yv.js +0 -1
- package/dashboard-out/_next/static/chunks/3xoopocxi2vi8.css +0 -3
- package/dashboard-out/_next/static/chunks/3z-nzyp9z8vih.js +0 -3
- /package/dashboard-out/_next/static/{kFIRZt4fuL4Zsh38-jGui → 17egwqgk36_DSRF55LJZG}/_buildManifest.js +0 -0
- /package/dashboard-out/_next/static/{kFIRZt4fuL4Zsh38-jGui → 17egwqgk36_DSRF55LJZG}/_clientMiddlewareManifest.js +0 -0
- /package/dashboard-out/_next/static/{kFIRZt4fuL4Zsh38-jGui → 17egwqgk36_DSRF55LJZG}/_ssgManifest.js +0 -0
|
@@ -5,6 +5,12 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
5
5
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
6
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
7
|
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
8
14
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
9
15
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
16
|
};
|
|
@@ -19,36 +25,49 @@ const metrics_interceptor_1 = require("./metrics.interceptor");
|
|
|
19
25
|
const metrics_adapter_1 = require("./metrics.adapter");
|
|
20
26
|
const metrics_websocket_adapter_1 = require("./metrics-websocket.adapter");
|
|
21
27
|
const metrics_bootstrap_service_1 = require("./metrics.bootstrap.service");
|
|
22
|
-
const telemetry_service_1 = __importDefault(require("../core/telemetry/telemetry.service"));
|
|
23
28
|
const config_1 = require("../config");
|
|
24
29
|
const fs_1 = __importDefault(require("fs"));
|
|
25
30
|
const logger_1 = __importDefault(require("../core/telemetry/logger"));
|
|
31
|
+
const runtime_1 = require("../runtime");
|
|
32
|
+
const session_export_handler_1 = require("../adapters/session-export.handler");
|
|
26
33
|
let MetricsModule = MetricsModule_1 = class MetricsModule {
|
|
34
|
+
dependencies;
|
|
35
|
+
constructor(dependencies) {
|
|
36
|
+
this.dependencies = dependencies;
|
|
37
|
+
}
|
|
27
38
|
static forRoot(options) {
|
|
28
39
|
config_1.ConfigManager.getInstance().initialize(options);
|
|
40
|
+
const dependencies = (0, runtime_1.createOptimaRuntime)(config_1.ConfigManager.getInstance().get());
|
|
29
41
|
return {
|
|
30
42
|
module: MetricsModule_1,
|
|
31
43
|
providers: [
|
|
32
44
|
{
|
|
33
45
|
provide: 'METRICS_CONFIG',
|
|
34
46
|
useValue: config_1.ConfigManager.getInstance().get(),
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
provide: 'OPTIMA_RUNTIME_DEPENDENCIES',
|
|
50
|
+
useValue: dependencies,
|
|
35
51
|
}
|
|
36
52
|
],
|
|
37
53
|
exports: ['METRICS_CONFIG'],
|
|
38
54
|
};
|
|
39
55
|
}
|
|
40
56
|
configure(consumer) {
|
|
41
|
-
const config =
|
|
42
|
-
if (config.
|
|
57
|
+
const config = this.dependencies.config;
|
|
58
|
+
if (config.dashboard === false) {
|
|
43
59
|
return;
|
|
44
60
|
}
|
|
45
|
-
const dashboardRoute = config.
|
|
61
|
+
const dashboardRoute = config.dashboard.path;
|
|
46
62
|
const dashboardDir = path_1.default.join(__dirname, '../../dashboard-out');
|
|
47
63
|
const indexHtmlPath = path_1.default.join(dashboardDir, 'index.html');
|
|
48
64
|
if (!fs_1.default.existsSync(indexHtmlPath)) {
|
|
49
65
|
logger_1.default.error(`Dashboard not found at ${indexHtmlPath}. Please build the dashboard first.`);
|
|
50
66
|
return;
|
|
51
67
|
}
|
|
68
|
+
consumer
|
|
69
|
+
.apply((0, session_export_handler_1.createSessionExportHandler)(this.dependencies.persistence))
|
|
70
|
+
.forRoutes({ path: (0, session_export_handler_1.sessionExportRoute)(dashboardRoute), method: common_1.RequestMethod.GET });
|
|
52
71
|
consumer
|
|
53
72
|
.apply(express_1.default.static(dashboardDir), (req, res, next) => {
|
|
54
73
|
// SPA Fallback
|
|
@@ -65,7 +84,6 @@ exports.MetricsModule = MetricsModule = MetricsModule_1 = __decorate([
|
|
|
65
84
|
(0, common_1.Global)(),
|
|
66
85
|
(0, common_1.Module)({
|
|
67
86
|
providers: [
|
|
68
|
-
telemetry_service_1.default,
|
|
69
87
|
metrics_adapter_1.NestAdapter,
|
|
70
88
|
metrics_websocket_adapter_1.NestWebSocketAdapter,
|
|
71
89
|
metrics_bootstrap_service_1.MetricsBootstrapService,
|
|
@@ -74,8 +92,8 @@ exports.MetricsModule = MetricsModule = MetricsModule_1 = __decorate([
|
|
|
74
92
|
useClass: metrics_interceptor_1.MetricsInterceptor
|
|
75
93
|
},
|
|
76
94
|
],
|
|
77
|
-
exports: [
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
95
|
+
exports: ['METRICS_CONFIG', 'OPTIMA_RUNTIME_DEPENDENCIES'],
|
|
96
|
+
}),
|
|
97
|
+
__param(0, (0, common_1.Inject)('OPTIMA_RUNTIME_DEPENDENCIES')),
|
|
98
|
+
__metadata("design:paramtypes", [Object])
|
|
81
99
|
], MetricsModule);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type ReadonlyConfig } from '../config';
|
|
2
|
+
import { LocalRepository, PersistenceRepository } from '../core/storage';
|
|
3
|
+
import { CollectorService } from '../core/telemetry/collector.service';
|
|
4
|
+
import { CorrelationService } from '../core/telemetry/correlation.service';
|
|
5
|
+
import { TelemetryService } from '../core/telemetry/telemetry.service';
|
|
6
|
+
import { DashboardService } from "../core/telemetry/dashboard.service";
|
|
7
|
+
import { AnalyticsService } from '../core/telemetry/analytics.service';
|
|
8
|
+
import { RuntimeService } from '../core/telemetry/runtime.service';
|
|
9
|
+
import { TelemetryQueryService } from '../core/telemetry/telemetry-query.service';
|
|
10
|
+
import { IncidentService } from '../core/telemetry/incident.service';
|
|
11
|
+
export interface OptimaRuntimeDependencies {
|
|
12
|
+
config: ReadonlyConfig;
|
|
13
|
+
storage: LocalRepository;
|
|
14
|
+
persistence: PersistenceRepository;
|
|
15
|
+
queries: TelemetryQueryService;
|
|
16
|
+
dashboard: DashboardService;
|
|
17
|
+
analytics: AnalyticsService;
|
|
18
|
+
runtime: RuntimeService;
|
|
19
|
+
collector: CollectorService;
|
|
20
|
+
correlation: CorrelationService;
|
|
21
|
+
incidents: IncidentService;
|
|
22
|
+
telemetry: TelemetryService;
|
|
23
|
+
}
|
|
24
|
+
export declare function createOptimaRuntime(config: ReadonlyConfig): OptimaRuntimeDependencies;
|
|
25
|
+
export interface ShutdownSteps {
|
|
26
|
+
stopIntake?: () => void | Promise<void>;
|
|
27
|
+
stopProducers: () => void;
|
|
28
|
+
emitShutdownEvent?: () => Promise<void>;
|
|
29
|
+
closeTransport?: () => void | Promise<void>;
|
|
30
|
+
drainTimeoutMs?: number;
|
|
31
|
+
}
|
|
32
|
+
export declare function shutdownOptimaRuntime(deps: OptimaRuntimeDependencies, steps: ShutdownSteps): Promise<void>;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createOptimaRuntime = createOptimaRuntime;
|
|
7
|
+
exports.shutdownOptimaRuntime = shutdownOptimaRuntime;
|
|
8
|
+
const config_1 = require("../config");
|
|
9
|
+
const storage_1 = require("../core/storage");
|
|
10
|
+
const collector_service_1 = require("../core/telemetry/collector.service");
|
|
11
|
+
const correlation_service_1 = require("../core/telemetry/correlation.service");
|
|
12
|
+
const telemetry_service_1 = require("../core/telemetry/telemetry.service");
|
|
13
|
+
const dashboard_service_1 = require("../core/telemetry/dashboard.service");
|
|
14
|
+
const analytics_service_1 = require("../core/telemetry/analytics.service");
|
|
15
|
+
const runtime_service_1 = require("../core/telemetry/runtime.service");
|
|
16
|
+
const telemetry_query_service_1 = require("../core/telemetry/telemetry-query.service");
|
|
17
|
+
const logger_1 = __importDefault(require("../core/telemetry/logger"));
|
|
18
|
+
const incident_service_1 = require("../core/telemetry/incident.service");
|
|
19
|
+
const organizers_1 = require("../core/organizers");
|
|
20
|
+
function createOptimaRuntime(config) {
|
|
21
|
+
logger_1.default.configure({ consoleLog: config.logging.consoleLog });
|
|
22
|
+
const dashboardSettings = config.dashboard === false ? config_1.DEFAULT_CONFIG.dashboard : config.dashboard;
|
|
23
|
+
const persistence = new storage_1.PersistenceRepository(config);
|
|
24
|
+
const storage = new storage_1.LocalRepository(persistence, config);
|
|
25
|
+
const queries = new telemetry_query_service_1.TelemetryQueryService({
|
|
26
|
+
committed: persistence.bucketSource,
|
|
27
|
+
live: {
|
|
28
|
+
snapshot: () => storage.bucket.getHistory().map(bucket => ({
|
|
29
|
+
bucket,
|
|
30
|
+
committed: storage.bucket.isCommitted(bucket.sequence)
|
|
31
|
+
})),
|
|
32
|
+
},
|
|
33
|
+
currentSessionId: () => persistence.sessionId
|
|
34
|
+
});
|
|
35
|
+
persistence.attachQueryService(queries);
|
|
36
|
+
const dashboard = new dashboard_service_1.DashboardService(queries, () => persistence.sessionId, {
|
|
37
|
+
liveWindow: dashboardSettings.liveWindow,
|
|
38
|
+
backfillLimit: dashboardSettings.backfillLimit,
|
|
39
|
+
maxBackfillAgeMs: dashboardSettings.maxBackfillAgeMs,
|
|
40
|
+
});
|
|
41
|
+
const analytics = new analytics_service_1.AnalyticsService(queries, config);
|
|
42
|
+
const runtime = new runtime_service_1.RuntimeService(storage, config);
|
|
43
|
+
const collector = new collector_service_1.CollectorService(storage, dashboard, analytics, runtime, config);
|
|
44
|
+
const correlation = new correlation_service_1.CorrelationService({
|
|
45
|
+
queries,
|
|
46
|
+
finding: {
|
|
47
|
+
save: f => persistence.saveCorrelationFinding(f),
|
|
48
|
+
find: id => persistence.findCorrelationFinding(id),
|
|
49
|
+
get durable() { return persistence.isEnabled; },
|
|
50
|
+
},
|
|
51
|
+
identity: { sessionId: () => persistence.sessionId, instanceId: storage.bucket.instanceId },
|
|
52
|
+
}, {
|
|
53
|
+
...config.correlation,
|
|
54
|
+
analysisIntervalMs: config.collection.bucketIntervalMs,
|
|
55
|
+
});
|
|
56
|
+
const incidents = new incident_service_1.IncidentService({
|
|
57
|
+
queries,
|
|
58
|
+
sessionId: () => persistence.sessionId,
|
|
59
|
+
durable: () => persistence.isEnabled,
|
|
60
|
+
related: metric => correlation.relatedFindings(metric),
|
|
61
|
+
emit: event => organizers_1.ApplicationEventManager.instance?.emit(event),
|
|
62
|
+
}, {
|
|
63
|
+
...config.incidents,
|
|
64
|
+
p95LatencyMs: config.thresholds.slowLatencyMs,
|
|
65
|
+
eventLoopLagMs: config.thresholds.eventLoopLagMs,
|
|
66
|
+
});
|
|
67
|
+
const telemetry = new telemetry_service_1.TelemetryService(storage, config);
|
|
68
|
+
return {
|
|
69
|
+
config,
|
|
70
|
+
storage,
|
|
71
|
+
persistence,
|
|
72
|
+
queries,
|
|
73
|
+
collector,
|
|
74
|
+
dashboard,
|
|
75
|
+
analytics,
|
|
76
|
+
runtime,
|
|
77
|
+
correlation,
|
|
78
|
+
incidents,
|
|
79
|
+
telemetry
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
async function shutdownOptimaRuntime(deps, steps) {
|
|
83
|
+
const run = async (label, fn) => {
|
|
84
|
+
try {
|
|
85
|
+
await fn();
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
logger_1.default.error(`Shutdown step "${label}" failed:`, err);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
await run('stop intake', () => steps.stopIntake?.());
|
|
92
|
+
const persistenceSettings = deps.config.persistence === false ? config_1.DEFAULT_CONFIG.persistence : deps.config.persistence;
|
|
93
|
+
const drainTimeoutMs = steps.drainTimeoutMs ?? persistenceSettings.shutdownDrainTimeoutMs;
|
|
94
|
+
const drained = await deps.telemetry.whenDrained(drainTimeoutMs);
|
|
95
|
+
if (!drained) {
|
|
96
|
+
logger_1.default.error(`Shutdown: ${deps.telemetry.inFlightCount} request(s) still in flight after ${drainTimeoutMs}ms; closing anyway.`);
|
|
97
|
+
}
|
|
98
|
+
await run('stop producers', () => steps.stopProducers());
|
|
99
|
+
await run('close last interval', () => deps.storage.closeLastInterval());
|
|
100
|
+
await run('emit shutdown event', () => steps.emitShutdownEvent?.());
|
|
101
|
+
await run('persistence shutdown', () => deps.persistence.shutdown());
|
|
102
|
+
await run('dispose runtime collectors', () => deps.storage.dispose());
|
|
103
|
+
await run('close transport', () => steps.closeTransport?.());
|
|
104
|
+
}
|
|
@@ -1,23 +1,12 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { LocalRepository } from "../core/storage/local.repository";
|
|
2
2
|
export declare class TrafficSimulator {
|
|
3
3
|
private readonly repository;
|
|
4
4
|
private timer?;
|
|
5
5
|
private running;
|
|
6
|
-
constructor(repository
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
readonly health: import("../core/storage").HealthStore;
|
|
11
|
-
readonly bucket: import("../core/storage").BucketStore;
|
|
12
|
-
readonly dashboard: import("../core/storage").DashboardStore;
|
|
13
|
-
readonly analytics: import("../core/storage").AnalyticsStore;
|
|
14
|
-
readonly system: import("../core/storage").SystemStore;
|
|
15
|
-
record(request: TelemetryRequest): void;
|
|
16
|
-
tick(): void;
|
|
17
|
-
});
|
|
18
|
-
start(options?: {
|
|
19
|
-
intervalMs?: number;
|
|
20
|
-
requestsPerTick?: number;
|
|
6
|
+
constructor(repository: LocalRepository);
|
|
7
|
+
start(options: {
|
|
8
|
+
intervalMs: number;
|
|
9
|
+
requestsPerTick: number;
|
|
21
10
|
}): void;
|
|
22
11
|
stop(): void;
|
|
23
12
|
isRunning(): boolean;
|
|
@@ -4,7 +4,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.TrafficSimulator = void 0;
|
|
7
|
-
const local_repository_1 = require("../core/storage/local.repository");
|
|
8
7
|
const config_1 = require("./config");
|
|
9
8
|
const scenarios_1 = require("./scenarios");
|
|
10
9
|
const journey_1 = require("./journey");
|
|
@@ -116,7 +115,7 @@ class TrafficSimulator {
|
|
|
116
115
|
repository;
|
|
117
116
|
timer;
|
|
118
117
|
running = false;
|
|
119
|
-
constructor(repository
|
|
118
|
+
constructor(repository) {
|
|
120
119
|
this.repository = repository;
|
|
121
120
|
}
|
|
122
121
|
start(options) {
|
|
@@ -124,20 +123,18 @@ class TrafficSimulator {
|
|
|
124
123
|
return;
|
|
125
124
|
}
|
|
126
125
|
this.running = true;
|
|
127
|
-
const interval = options
|
|
128
|
-
const baseRequests = options
|
|
126
|
+
const interval = options.intervalMs;
|
|
127
|
+
const baseRequests = options.requestsPerTick;
|
|
129
128
|
logger_1.default.debug("Traffic simulation started!");
|
|
130
129
|
this.timer = setInterval(() => {
|
|
131
130
|
if (IS_STABLE) {
|
|
132
|
-
|
|
133
|
-
this.repository.record(request);
|
|
131
|
+
this.repository.record({ ...createStableRequest(), origin: 'synthetic' });
|
|
134
132
|
}
|
|
135
133
|
else {
|
|
136
134
|
const multiplier = scenarios_1.scenario.trafficMultiplier;
|
|
137
135
|
const amount = Math.floor(baseRequests * multiplier);
|
|
138
136
|
for (let i = 0; i < amount; i++) {
|
|
139
|
-
|
|
140
|
-
this.repository.record(request);
|
|
137
|
+
this.repository.record({ ...createRequest(), origin: 'synthetic' });
|
|
141
138
|
}
|
|
142
139
|
}
|
|
143
140
|
}, interval);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apm-optima",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"publishConfig": {
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
],
|
|
41
41
|
"scripts": {
|
|
42
42
|
"build": "tsc",
|
|
43
|
+
"prepublishOnly": "npm run build",
|
|
43
44
|
"test": "jest"
|
|
44
45
|
},
|
|
45
46
|
"peerDependencies": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,42775,e=>{"use strict";var t=e.i(43476),s=e.i(84789);function a({data:e}){let r,{width:l,height:o,isHydrated:i}=(0,s.useSize)("line-showcase"),n=0===(r=Math.max(...e.points.map(e=>e.y)))?1:r,c=n+(n-0)*.1,d=0-(n-0)*.1,x=e.points.map(e=>({x:e.x,y:(e.y-d)/(c-d)*.5})),u=x.map((e,t)=>{let s=t/(x.length-1)*l,a=o-e.y*o;return`${s},${a}`}).join(" ");return(0,t.jsx)("div",{className:"w-full h-16 rounded-md",id:"line-showcase",children:i&&(0,t.jsx)("svg",{className:"w-full h-full",children:(0,t.jsx)("polyline",{fill:"none",stroke:e.color,strokeWidth:"2",points:u})})})}var r=e.i(71645),l=e.i(74080);function o({children:e,content:s}){let[a,i]=(0,r.useState)(!1),[n,c]=(0,r.useState)({top:0,bottom:0,left:0,arrowX:"50%"}),[d,x]=(0,r.useState)(!1),u=(0,r.useRef)(null),m=(0,r.useRef)(null);(0,r.useEffect)(()=>(x(!0),()=>x(!1)),[]);let p=()=>{if(!u.current)return;let e=u.current.getBoundingClientRect(),t=e.left+e.width/2,s=m.current?m.current.offsetWidth:200,a=m.current?m.current.offsetHeight:44,r=t-s/2;r<12?r=12:r+s>window.innerWidth-12&&(r=window.innerWidth-s-12);let l=t-r;c({top:e.top+window.scrollY-a-8,bottom:e.bottom+window.scrollY+8,left:r+window.scrollX,arrowX:`${l}px`})};(0,r.useEffect)(()=>{if(a){p();let e=setTimeout(p,0);return window.addEventListener("scroll",p),window.addEventListener("resize",p),()=>{clearTimeout(e),window.removeEventListener("scroll",p),window.removeEventListener("resize",p)}}},[a]);let h=()=>i(!0),f=()=>i(!1);return(0,t.jsxs)(t.Fragment,{children:[r.default.cloneElement(e,{ref:t=>{u.current=t;let{ref:s}=e;"function"==typeof s?s(t):s&&(s.current=t)},onMouseEnter:h,onMouseLeave:f,onFocus:h,onBlur:f}),a&&d&&(0,l.createPortal)((0,t.jsxs)("div",{ref:m,style:{position:"absolute",top:`${n.bottom}px`,left:`${n.left}px`},className:"z-[9999] pointer-events-none bg-black text-white text-xs px-3 py-2 rounded shadow-xl max-w-[220px] break-words text-center leading-relaxed",children:[(0,t.jsx)("div",{style:{left:n.arrowX},className:"absolute bottom-full -translate-x-1/2 w-2 h-2 rotate-45 bg-black -mb-1"}),s]}),document.body)]})}e.i(49645);var i=e.i(92432),i=i;function n({title:e,timestamp:s,isFolded:a,onToggle:r,tooltip:l}){return(0,t.jsxs)("div",{className:"flex items-center justify-between mb-1 cursor-pointer p-2 border-b-1 border-accent-soft",onClick:r,children:[(0,t.jsx)("h2",{className:"text-sm font-semibold text-accent",children:e}),(0,t.jsx)("p",{className:"text-xs text-accent-soft",children:s}),l&&(0,t.jsx)(o,{content:l,children:(0,t.jsx)(i.default,{className:"w-4 h-4 text-accent cursor-pointer"})})]})}function c({header:e,children:s,padding:a}){let[l,o]=r.default.useState(!1);return(0,t.jsxs)("div",{className:"bg-[var(--variant-1)] border border-[var(--variant-3)] rounded-md animate-fade-in",children:[e&&(0,t.jsx)(n,{title:e.title,timestamp:e.description||"",isFolded:l,onToggle:()=>{o(!l)},tooltip:e.tooltip}),(0,t.jsx)("div",{className:`justify-between ${a?"p-3":""}`,children:!l&&s})]})}var i=i;function d({children:e,header:s}){return(0,t.jsx)(c,{header:s,padding:!0,children:e})}function x({title:e,value:s,unit:a="",color:r="accent",tooltip:l}){let n="number"==typeof s?s.toFixed(2):s;return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)("div",{className:"flex items-center justify-between mb-1",children:[(0,t.jsx)("h2",{className:"text-sm font-semibold text-accent",children:e}),l&&(0,t.jsx)(o,{content:l,children:(0,t.jsx)(i.default,{className:"w-4 h-4 text-accent cursor-pointer"})})]}),(0,t.jsxs)("p",{className:"text-2xl font-bold",style:r?{color:r}:void 0,children:[n,a&&(0,t.jsxs)("span",{className:"text-sm text-gray-400",children:[" ",a]})]})]})}function u({variant:e="default",values:s}){return"default"===e?(0,t.jsx)("div",{className:"mt-2 mb-2 space-y-1",children:s&&Object.entries(s).map(([e,s])=>(0,t.jsxs)("div",{className:"flex justify-between text-sm text-accent-soft",children:[(0,t.jsxs)("p",{className:"text-sm text-accent-soft",children:[e,":"]}),(0,t.jsx)("span",{children:s})]},e))}):(0,t.jsx)(t.Fragment,{children:s&&Object.entries(s).map(([e,s])=>(0,t.jsxs)("div",{className:"flex flex-row justify-between h-full",children:[(0,t.jsx)("span",{className:"text-[10px] text-foreground uppercase tracking-wider font-semibold block mb-1",children:e}),(0,t.jsx)("span",{className:"text-[10px] text-foreground-soft uppercase tracking-wider font-semibold block mb-1 ml-2",children:s})]},e))})}let m=e=>null==e?"0":e>=1e3?(e/1e3).toFixed(1).replace(/\.0$/,"")+"k":e>=1e6?(e/1e6).toFixed(1).replace(/\.0$/,"")+"m":e.toString();function p({data:e}){return(0,t.jsxs)("div",{className:"grid grid-cols-1 md:grid-cols-5 gap-4",children:[(0,t.jsxs)(d,{children:[(0,t.jsx)(x,{title:"CURRENT RPS",value:m(e.current.rps),unit:"req/s",color:"var(--chart-6)",tooltip:"The number of HTTP requests processed by the server during the last measurement interval. Higher values indicate increased traffic."}),(0,t.jsx)(a,{data:{points:e.history.rps.map((e,t)=>({x:t,y:e})),color:"var(--chart-6)",type:"solid"}})]}),(0,t.jsxs)(d,{children:[(0,t.jsx)(x,{title:"AVG LATENCY",value:e.current.latency,unit:"ms",color:"var(--chart-2)",tooltip:"Average response time of all processed requests. Shows how long users wait for server responses."}),(0,t.jsx)(a,{data:{points:e.history.latency.map((e,t)=>({x:t,y:e})),color:"var(--chart-2)",type:"solid"}})]}),(0,t.jsxs)(d,{children:[(0,t.jsx)(x,{title:"ERROR RATE",value:e.current.errorRate,unit:"%",color:"var(--chart-5)",tooltip:"Percentage of requests that failed with an HTTP error response. Includes client errors (4xx) and server errors (5xx)."}),(0,t.jsx)(a,{data:{points:e.history.errorRate.map((e,t)=>({x:t,y:e})),color:"var(--chart-5)",type:"solid"}})]}),(0,t.jsxs)(d,{children:[(0,t.jsx)(x,{title:"EVENT LOOP LAG",value:e.current.eventLoopLag,unit:"ms",color:"var(--chart-4)",tooltip:"Delay between scheduled Node.js tasks and their actual execution time. High values indicate that the server is blocked by CPU-heavy operations or synchronous code."}),(0,t.jsx)(a,{data:{points:e.history.eventLoopLag.map((e,t)=>({x:t,y:e})),color:"var(--chart-4)",type:"solid"}})]}),(0,t.jsxs)(d,{children:[(0,t.jsx)(x,{title:"HEAP USAGE",value:e.current.heapUsage,color:"var(--chart-1)",unit:"MB",tooltip:"Current JavaScript heap memory usage compared to the allocated heap limit."}),(0,t.jsx)(a,{data:{points:e.history.heapUsage.map((e,t)=>({x:t,y:e})),color:"var(--chart-1)",type:"solid"}})]})]})}e.i(17508);var h=e.i(12656),f=e.i(71034),g=e.i(55803);e.i(3203),e.i(20530),e.i(44802);var v=g;function j({data:e}){return(0,t.jsxs)("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4 mt-4",children:[(0,t.jsx)(c,{header:{title:"Throughput",description:"Requests per second",tooltip:"The number of HTTP requests processed by the server during the last measurement interval. Higher values indicate increased traffic."},children:(0,t.jsx)(v.default,{data:[{points:e.history.rps.map((e,t)=>({x:t,y:e})),color:"var(--chart-6)",type:"solid",label:"RPS"},{points:e.charts.throughput.errorClient.map((e,t)=>({x:t,y:e})),color:"var(--chart-4)",type:"dashed",label:"Client Errors (4xx)"},{points:e.charts.throughput.errorServer.map((e,t)=>({x:t,y:e})),color:"var(--chart-5)",type:"dashed",label:"Server Errors (5xx)"}],withDots:!1})}),(0,t.jsx)(c,{header:{title:"Latency",description:"Response time percentiles",tooltip:"Percentile response times. Shows how fast most users experience the system. Example: 50% of users waited less than 30ms, 95% waited less than 100ms, and 99% waited less than 200ms."},children:(0,t.jsx)(v.default,{data:[{points:e.charts.percentiles.p50.map((e,t)=>({x:t,y:e})),color:"var(--chart-1)",type:"dashed",label:"P50 ms",fillArea:!1},{points:e.charts.percentiles.p95.map((e,t)=>({x:t,y:e})),color:"var(--chart-2)",type:"dashed",label:"P95 ms",fillArea:!1},{points:e.charts.percentiles.p99.map((e,t)=>({x:t,y:e})),color:"var(--chart-4)",type:"dashed",label:"P99 ms",fillArea:!1}],withDots:!1})}),(0,t.jsx)(c,{header:{title:"Memory Usage",description:"Heap memory usage and size",tooltip:"Current JavaScript heap memory usage compared to the allocated heap limit."},children:(0,t.jsx)(v.default,{data:[{points:e.charts.runtimePerformance.heapUsage.map((e,t)=>({x:t,y:e})),color:"var(--chart-1)",type:"solid",label:"heapUsed",fillArea:!1},{points:e.charts.runtimePerformance.heapSize.map((e,t)=>({x:t,y:e})),color:"var(--chart-2)",type:"dashed",label:"heapTotal"}],withDots:!1})}),(0,t.jsx)(c,{header:{title:"Event Loop Lag",description:"Event loop lag over time",tooltip:"Delay between scheduled Node.js tasks and their actual execution time. High values indicate that the server is blocked by CPU-heavy operations or synchronous code."},children:(0,t.jsx)(v.default,{data:[{points:e.charts.runtimePerformance.lag.map((e,t)=>({x:t,y:e})),color:"var(--chart-4)",type:"solid",label:"lag",fillArea:!1}],withDots:!1})})]})}function b({title:e,timestamp:s,type:a}){return(0,t.jsxs)("div",{className:`flex flex-col items-left justify-between p-2 border-l-3 ${function(e){switch(e){case"info":return"border-[var(--chart-6)]";case"advisory":return"border-[var(--chart-3)]";case"warning":return"border-[var(--chart-4)]";case"critical":return"border-[var(--chart-5)]";default:return"border-[var(--chart-1)]"}}(a)} m-2`,children:[(0,t.jsx)("p",{className:"text-sm text-foreground",children:e}),(0,t.jsxs)("p",{className:"text-xs text-accent-soft",children:[s," * ",a]})]})}function y({columns:e,data:s}){return(0,t.jsx)("div",{className:"overflow-x-auto",children:(0,t.jsxs)("table",{className:"min-w-full divide-y divide-border",children:[(0,t.jsx)("thead",{children:(0,t.jsx)("tr",{children:e.map((e,s)=>(0,t.jsx)("th",{scope:"col",className:"px-6 py-3 text-left text-xs font-medium text-accent-soft uppercase tracking-wider",children:e},s))})}),(0,t.jsx)("tbody",{className:"divide-y divide-border",children:s.map((s,a)=>(0,t.jsx)("tr",{children:e.map((e,a)=>(0,t.jsx)("td",{className:"px-6 py-4 whitespace-nowrap text-sm text-accent-soft",children:s[e]},a))},a))})]})})}function w({data:e}){return(0,t.jsx)(c,{header:{title:"Live Alerts",description:"Stream"},children:e.map((e,s)=>(0,t.jsx)(b,{title:e.title,timestamp:e.timestamp,type:e.severity},s))})}function N({data:e}){let s=e.map(e=>({METHOD:e.method,ROUTE:e.route,RPS:e.rps,P95:`${e.p95?.toFixed(2)} ms`,"ERR%":`${e.errorRate?.toFixed(2)} %`,STATUS:e.status}));return(0,t.jsx)(c,{header:{title:"Top routes by Impact",description:"Volume"},children:(0,t.jsx)(y,{columns:["METHOD","ROUTE","RPS","P95","ERR%","STATUS"],data:s})})}function T({data:e}){return(0,t.jsxs)("div",{className:"grid grid-cols-1 md:grid-cols-5 gap-4 mt-4",children:[(0,t.jsx)("div",{className:"col-span-3 md:col-span-3",children:(0,t.jsx)(N,{data:e.impactEndpoints})}),(0,t.jsx)("div",{className:"col-span-2 md:col-span-2",children:(0,t.jsx)(w,{data:e.alerts})})]})}var S=e.i(65327);function C(){let{data:e}=(0,S.useMetrics)(),{dashboard:s}=e;return(0,t.jsxs)("div",{className:"p-6",children:[(0,t.jsx)(p,{data:s}),(0,t.jsx)(j,{data:s}),(0,t.jsx)(T,{data:s})]})}function E({data:e}){let[s,a]=r.default.useState(0),l=e.latencyDistribution.slice(5*s,(s+1)*5);return(0,t.jsx)("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4 mt-6",children:(0,t.jsxs)(c,{header:{title:"Latency Distribution",description:"P50 * P95 * P99"},children:[(0,t.jsx)(f.default,{categories:l.map(e=>e.endpoint),data:[{label:"P50",values:l.map(e=>e.p50),color:"var(--chart-3)"},{label:"P95",values:l.map(e=>e.p95),color:"var(--chart-4)"},{label:"P99",values:l.map(e=>e.p99),color:"var(--chart-5)"}]}),(0,t.jsxs)("div",{className:"flex justify-center gap-2 mb-2",children:[(0,t.jsx)("button",{className:"px-2 bg-background text-foreground rounded disabled:opacity-50",onClick:()=>a(e=>Math.max(e-1,0)),disabled:0===s,children:"<"}),(0,t.jsx)("button",{className:"px-2 bg-background text-foreground rounded disabled:opacity-50",onClick:()=>a(e=>e+1),disabled:(s+1)*5>=e.latencyDistribution.length,children:">"})]})]})})}function P({data:e}){return(0,t.jsxs)("div",{className:"grid grid-cols-2 md:grid-cols-4 gap-4",children:[(0,t.jsx)(d,{children:(0,t.jsx)(x,{title:"Total Endpoints",value:e.summary.totalEndpoints,tooltip:"The total number of unique API endpoints that have been accessed by clients."})}),(0,t.jsx)(d,{children:(0,t.jsx)(x,{title:"Healthy Endpoints",value:e.summary.healthyEndpoints,color:"var(--chart-3)",tooltip:"The number of API endpoints that are currently healthy and responding successfully to client requests."})}),(0,t.jsx)(d,{children:(0,t.jsx)(x,{title:`Slow Endpoints (>${e.summary.slowEndpointsThreshold}ms)`,value:e.summary.slowEndpoints,color:"var(--chart-4)",tooltip:`The number of API endpoints that have an average response time exceeding ${e.summary.slowEndpointsThreshold} milliseconds.`})}),(0,t.jsx)(d,{children:(0,t.jsx)(x,{title:"High Error Rate",value:e.summary.errorEndpoints,color:"var(--chart-5)",tooltip:"The number of API endpoints that are currently experiencing a high error rate, indicating potential issues with the server or application."})})]})}function L({settings:e,onChange:s}){return(0,t.jsx)(c,{padding:!0,children:(0,t.jsxs)("div",{className:"grid grid-cols-1 sm:grid-cols-2 lg:flex lg:flex-row gap-4 items-end w-full",children:[(0,t.jsx)("div",{className:"w-full sm:col-span-2 lg:flex-1",children:(0,t.jsx)("input",{type:"text",placeholder:"Search...",className:"w-full bg-background text-foreground px-3 py-2 rounded-md border border-border focus:outline-none focus:border-accent text-sm"})}),(0,t.jsxs)("div",{className:"flex flex-col w-full lg:w-40",children:[(0,t.jsx)("span",{className:"text-[10px] font-bold text-accent-soft tracking-wider mb-1 uppercase",children:"METHOD"}),(0,t.jsx)("select",{className:"w-full bg-background text-foreground px-3 py-2 rounded-md border border-border focus:outline-none focus:border-accent text-sm cursor-pointer",onChange:e=>s&&s({method:e.target.value}),children:["ALL","GET","POST","PUT","DELETE"].map(e=>(0,t.jsx)("option",{value:e,children:e},e))})]}),(0,t.jsxs)("div",{className:"flex flex-col w-full lg:w-40",children:[(0,t.jsx)("span",{className:"text-[10px] font-bold text-accent-soft tracking-wider mb-1 uppercase",children:"STATUS"}),(0,t.jsx)("select",{className:"w-full bg-background text-foreground px-3 py-2 rounded-md border border-border focus:outline-none focus:border-accent text-sm cursor-pointer",onChange:e=>s&&s({status:e.target.value}),children:["ALL","2xx","4xx","5xx"].map(e=>(0,t.jsx)("option",{value:e,children:e},e))})]})]})})}function M({data:e}){return(0,t.jsx)(c,{padding:!0,children:(0,t.jsx)("div",{className:"overflow-x-auto",children:(0,t.jsxs)("table",{className:"min-w-full divide-y divide-border",children:[(0,t.jsx)("thead",{children:(0,t.jsx)("tr",{children:["#","Method","Route","Req Count","Avg Latency","P95","P99","Error Percentage"].map(e=>(0,t.jsx)("th",{className:"px-6 py-3 text-left text-xs font-medium text-accent-soft uppercase tracking-wider",children:e},e))})}),(0,t.jsx)("tbody",{className:"divide-y divide-border",children:e.data.map((e,s)=>(0,t.jsxs)("tr",{children:[(0,t.jsx)("td",{className:"px-6 py-4 whitespace-nowrap text-sm text-accent-soft",children:s+1}),(0,t.jsx)("td",{className:"px-6 py-4 whitespace-nowrap text-sm text-accent-soft",children:e.method}),(0,t.jsx)("td",{className:"px-6 py-4 whitespace-nowrap text-sm text-accent-soft",children:e.route}),(0,t.jsx)("td",{className:"px-6 py-4 whitespace-nowrap text-sm text-accent-soft",children:m(e.requestCount)}),(0,t.jsxs)("td",{className:"px-6 py-4 whitespace-nowrap text-sm text-accent-soft",children:[e.averageLatency?.toFixed(2)," ms"]}),(0,t.jsxs)("td",{className:"px-6 py-4 whitespace-nowrap text-sm text-accent-soft",children:[e.p95?.toFixed(2)," ms"]}),(0,t.jsxs)("td",{className:"px-6 py-4 whitespace-nowrap text-sm text-accent-soft",children:[e.p99?.toFixed(2)," ms"]}),(0,t.jsxs)("td",{className:"px-6 py-4 whitespace-nowrap text-sm text-accent-soft",children:[e.errorRate?.toFixed(2),"%"]})]},s))})]})})})}function k({data:e}){let s=e.pagination.page,a=[s-2,s-1,s,s+1,s+2].filter(t=>t>0&&t<=e.pagination.totalCount/e.pagination.perPageCount);return(0,t.jsxs)("div",{className:"flex justify-between items-center mt-4",children:[(0,t.jsxs)("span",{className:"text-sm text-accent-soft",children:["Showing ",e.data.length," of ",e.pagination.totalCount," entries"]}),(0,t.jsxs)("div",{className:"flex gap-2",children:[(0,t.jsx)("button",{className:"bg-card text-foreground px-3 py-1 rounded-md",children:"Previous"}),a.map(e=>(0,t.jsx)("button",{className:`bg-card text-foreground px-3 py-1 rounded-md ${e===s?"bg-[#3B82F6]":""}`,children:e},e)),(0,t.jsx)("button",{className:"bg-card text-foreground px-3 py-1 rounded-md",children:"Next"})]})]})}function R({data:e,filters:s,onFilterChange:a}){return(0,t.jsxs)("div",{className:"flex flex-col gap-4 pt-4 md:mt-0 md:col-span-2",children:[(0,t.jsx)(L,{settings:s,onChange:a}),(0,t.jsx)(M,{data:e.endpointsTable}),(0,t.jsx)(k,{data:e.endpointsTable})]})}function A(){let{data:e}=(0,S.useMetrics)(),{analytics:s}=e;return(0,t.jsxs)("div",{className:"p-6",children:[(0,t.jsx)(P,{data:s}),(0,t.jsx)(E,{data:s}),(0,t.jsx)(R,{data:s,filters:{method:"ALL",status:"ALL"},onFilterChange:e=>{console.log("Filter settings changed:",e)}})]})}function $({data:e,maxValue:s,color:a="var(--chart-6)"}){if(!e||0===e.length||e.every(e=>0===e.value))return null;let r=s??e.reduce((e,t)=>e+t.value,0);return(0,t.jsx)("div",{className:"w-full bg-[var(--variant-3)] rounded-full h-[10px] flex overflow-hidden",children:e.map((e,s)=>{let l=e.value/r*100;return l<=0?null:(0,t.jsx)("div",{className:"h-full transition-all duration-500 ease-in-out",style:{width:`${l}%`,backgroundColor:e.color||a},title:`${e.label}: ${e.value}`},s)})})}function U({data:e}){let s=[{label:"RSS Memory",value:`${e.memory.rssMemory.toFixed(2)} MB`,sublabel:`/ ${e.memory.rssMemoryTotal.toFixed(2)} MB total`,color:"var(--chart-1)"},{label:"External Memory",value:`${e.memory.externalMemory.toFixed(2)} MB`,sublabel:"native C++ objects",color:"var(--chart-2)"},{label:"GC Pause Average",value:`${e.garbageCollection.gcPauseAverage.toFixed(2)} ms`,sublabel:"milliseconds",color:"var(--chart-3)"},{label:"Active Handles",value:`${e.handles.activeHandles}`,sublabel:`timers: ${e.handles.activeHandlesTimers} * sockets: ${e.handles.activeHandlesSockets}`,color:"var(--chart-4)"}];return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-4",children:[(0,t.jsxs)(d,{children:[(0,t.jsx)(x,{title:"CPU Usage",value:e.cpu.usageRate,unit:"%",color:"[var(--chart-6)]"}),(0,t.jsx)($,{data:[{label:"User",value:e.cpu.userUsage,color:"var(--chart-5)"},{label:"System",value:e.cpu.systemUsage,color:"var(--chart-1)"}],maxValue:100}),(0,t.jsx)(u,{values:{"Number of Cores":e.cpu.numberOfCores.toString(),"User Usage":`${e.cpu.userUsage.toFixed(2)}%`,"System Usage":`${e.cpu.systemUsage.toFixed(2)}%`,"Idle Usage":`${e.cpu.idleUsage.toFixed(2)}%`}})]}),(0,t.jsxs)(d,{children:[(0,t.jsx)(x,{title:"Heap Usage",value:e.memory.heapUsage,unit:"MB",color:"[var(--chart-2)]"}),(0,t.jsx)($,{data:[{label:"Heap Usage",value:e.memory.heapUsage,color:"var(--chart-2)"}],maxValue:e.memory.heapSize}),(0,t.jsx)(u,{values:{"Heap Size":`${e.memory.heapSize.toFixed(2)} MB`,"External Memory":`${e.memory.externalMemory.toFixed(2)} MB`}}),(0,t.jsx)($,{data:[{label:"RSS Memory",value:e.memory.rssMemory,color:"var(--chart-5)"}],maxValue:e.memory.rssMemoryTotal}),(0,t.jsx)(u,{values:{"RSS Memory":`${e.memory.rssMemory.toFixed(2)} MB`,"RSS Memory Total":`${e.memory.rssMemoryTotal.toFixed(2)} MB`}})]}),(0,t.jsxs)(d,{children:[(0,t.jsx)(x,{title:"Event Loop Lag",value:e.eventLoop.lag,unit:"ms",color:"[var(--chart-4)]"}),(0,t.jsx)($,{data:[{label:"Event Loop Lag",value:e.eventLoop.lag,color:"var(--chart-4)"}],maxValue:e.eventLoop.threshold}),(0,t.jsx)(u,{values:{Threshold:`${e.eventLoop.threshold} ms`}})]})]}),(0,t.jsx)("div",{className:"grid grid-cols-2 md:grid-cols-4 gap-4 mt-4",children:s.map((e,s)=>(0,t.jsxs)(c,{padding:!0,children:[(0,t.jsx)("h2",{className:"text-lg font-semibold text-foreground mb-2",children:e.label}),(0,t.jsx)("p",{className:`text-2xl font-bold text-[${e.color}] mb-1`,children:e.value}),(0,t.jsx)("div",{className:"flex justify-between text-sm text-accent-soft",children:(0,t.jsx)("span",{children:e.sublabel})})]},s))})]})}var v=g;function F({data:e}){return(0,t.jsxs)("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4 mt-4",children:[(0,t.jsx)(c,{header:{title:"Event Loop Lag",description:"Event loop lag over time",tooltip:"The event loop lag indicates how long it takes for the Node.js event loop to process events. A higher lag may indicate that the server is under heavy load or that there are blocking operations."},children:(0,t.jsx)(v.default,{data:[{label:"Event Loop Lag",points:e.history.eventLoopLag.map((e,t)=>({x:t,y:e})),color:"var(--chart-3)",type:"solid",fillArea:!1}],withDots:!1})}),(0,t.jsx)(c,{header:{title:"Heap Memory Usage",description:"Heap memory usage over time",tooltip:"Heap memory is the memory used by the JavaScript engine to store objects, strings, and other data. This graph shows the amount of heap memory used over time."},children:(0,t.jsx)(v.default,{data:[{label:"Used",points:e.history.memoryBreakdown.usedHeap.map((e,t)=>({x:t,y:e})),color:"var(--chart-3)",type:"solid",fillArea:!1},{label:"Total",points:e.history.memoryBreakdown.totalHeap.map((e,t)=>({x:t,y:e})),color:"var(--chart-2)",type:"dashed",fillArea:!1},{label:"RSS",points:e.history.memoryBreakdown.rssMemory.map((e,t)=>({x:t,y:e})),color:"var(--chart-4)",type:"solid",fillArea:!1}],withDots:!1})})]})}function H({type:e="horizontal"}){return(0,t.jsx)("div",{className:`w-full ${"horizontal"===e?"h-px my-2":"h-full w-px mx-2"} bg-gray-300 dark:bg-gray-700`})}function B({title:e,value:s}){return(0,t.jsxs)("div",{className:"flex flex-row justify-between h-full",children:[(0,t.jsx)("span",{className:"text-[10px] text-foreground uppercase tracking-wider font-semibold block mb-1",children:e}),(0,t.jsx)("span",{className:"text-[10px] text-foreground-soft uppercase tracking-wider font-semibold block mb-1 ml-2",children:s})]})}function D({data:e}){let s=[["PID",e.runtime.pid],["Platform",e.runtime.platform],["NODE.JS",e.runtime.nodeVersion],["V8 ENGINE",e.runtime.v8Version],["LIBUV",e.runtime.libuvVersion],["OPENSSL",e.runtime.openSSLVersion]];return(0,t.jsxs)("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-4 mt-4",children:[(0,t.jsx)(d,{header:{title:"CPU Per Core Utilization"},children:(0,t.jsx)(h.default,{data:e.cpu.perCoreUsage,binCount:e.cpu.perCoreUsage.length,label:"CPU Usage (%)"})}),(0,t.jsxs)(d,{header:{title:"Garbage Collection"},children:[(0,t.jsx)(u,{variant:"accent",values:{"Minor GC (SCAVENGE)":`Run Count: ${e.garbageCollection.minorGC.runCount} Total Time: ${e.garbageCollection.minorGC.averageTime.toFixed(2)} ms`,"Major GC (MARK_SWEEP)":`Run Count: ${e.garbageCollection.majorGC.runCount} Total Time: ${e.garbageCollection.majorGC.averageTime.toFixed(2)} ms`,"Incremental GC (INCREMENTAL_MARKING)":`Run Count: ${e.garbageCollection.incrementalGC.runCount} Total Time: ${e.garbageCollection.incrementalGC.averageTime.toFixed(2)} ms`}}),(0,t.jsx)("div",{className:"space-y-2",children:(0,t.jsxs)("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4 mt-4",children:[(0,t.jsx)(c,{padding:!0,children:(0,t.jsxs)("div",{className:"flex flex-col justify-between h-full",children:[(0,t.jsx)("p",{className:"text-[10px] text-accent-soft uppercase tracking-wider font-semibold block",children:"HEAP SPACES"}),e.garbageCollection.heapSpaces.map((e,s)=>(0,t.jsx)(B,{title:e.label,value:`${e.used.toFixed(2)} MB`},s))]})}),(0,t.jsx)(c,{padding:!0,children:(0,t.jsxs)("div",{className:"flex flex-col justify-between h-full",children:[(0,t.jsx)("p",{className:"text-[10px] text-accent-soft uppercase tracking-wider font-semibold block",children:"GC TOTALS"}),(0,t.jsx)(u,{variant:"accent",values:{"Total Pause":`${e.garbageCollection.gcTotals.totalPauseTime.toFixed(2)} ms`,"Freed Memory":`${e.garbageCollection.gcTotals.freedMemory.toFixed(2)} MB`,Promotions:`${e.garbageCollection.gcTotals.promotions}`,"Tenured Size":`${e.garbageCollection.gcTotals.tenuredSize.toFixed(2)} MB`}})]})})]})})]}),(0,t.jsxs)(d,{header:{title:"Process & Runtime Info"},children:[(0,t.jsx)("div",{className:"grid grid-cols-3 md:grid-cols-2 gap-4",children:s.map(([e,s],a)=>(0,t.jsxs)("div",{children:[(0,t.jsx)("p",{className:"text-[10px] text-accent-soft uppercase tracking-wider font-semibold block",children:e}),(0,t.jsx)("p",{className:"text-sm text-foreground",children:s})]},a))}),(0,t.jsx)(H,{}),(0,t.jsx)("p",{className:"text-[10px] text-accent-soft uppercase tracking-wider font-semibold block",children:"ACTIVE LIBUV HANDLES"}),(0,t.jsxs)("div",{className:"flex flex-row justify-between text-sm text-accent-soft",children:[(0,t.jsx)("p",{children:"TCP sockets:"}),(0,t.jsx)("span",{children:e.handles.activeLibuvHandles})]}),(0,t.jsxs)("div",{className:"flex flex-row justify-between text-sm text-accent-soft",children:[(0,t.jsx)("p",{children:"Timers:"}),(0,t.jsx)("span",{children:e.handles.timers})]}),(0,t.jsxs)("div",{className:"flex flex-row justify-between text-sm text-accent-soft",children:[(0,t.jsx)("p",{children:"File descriptors:"}),(0,t.jsx)("span",{children:e.handles.fileDescriptors})]}),(0,t.jsx)(H,{}),(0,t.jsx)("p",{className:"text-[10px] text-accent-soft uppercase tracking-wider font-semibold block",children:"THREAD POOL (UV_THREADPOOL_SIZE)"}),(0,t.jsxs)("div",{className:"flex flex-row justify-between text-sm text-accent-soft",children:[(0,t.jsx)("p",{children:"Size:"}),(0,t.jsx)("span",{children:e.runtime.threadPoolSize})]}),(0,t.jsxs)("div",{className:"flex flex-row justify-between text-sm text-accent-soft",children:[(0,t.jsx)("p",{children:"Active Threads:"}),(0,t.jsx)("span",{children:e.runtime.activeThreads})]}),(0,t.jsx)(H,{}),(0,t.jsx)("p",{className:"text-[10px] text-accent-soft uppercase tracking-wider font-semibold block",children:"STARTUP TIME"}),(0,t.jsxs)("div",{className:"flex flex-row justify-between text-sm text-accent-soft",children:[(0,t.jsx)("p",{children:"Bootstrap:"}),(0,t.jsxs)("span",{children:[e.runtime.startup.bootstrapTime.toFixed(2)," ms"]})]}),(0,t.jsxs)("div",{className:"flex flex-row justify-between text-sm text-accent-soft",children:[(0,t.jsx)("p",{children:"Requires:"}),(0,t.jsxs)("span",{children:[e.runtime.startup.requiredModules," modules"]})]})]})]})}function O(){let{data:e}=(0,S.useMetrics)(),{health:s}=e;return(0,t.jsxs)("div",{className:"p-6",children:[(0,t.jsx)(U,{data:s}),(0,t.jsx)(F,{data:s}),(0,t.jsx)(D,{data:s})]})}var z=e.i(52446),G=e.i(11350);function I(){let{configuration:e}=(0,G.useConnection)(),s=`[${(e?.excludePaths||[]).join("], [")}]`,a=e=>"boolean"==typeof e?e?"true":"false":e?.toString()||"";return(0,t.jsxs)("div",{className:"flex flex-col gap-4 m-4",children:[(0,t.jsx)(d,{header:{title:"Main"},children:(0,t.jsx)(u,{variant:"default",values:{dashboardPath:a(e?.dashboardPath),excludedPaths:a(s),tickIntervalMs:a(e?.tickIntervalMs),consoleLog:a(e?.consoleLog),ringBufferSize:a(e?.ringBufferSize),alertBufferSize:a(e?.alertBufferSize)}})}),(0,t.jsx)(d,{header:{title:"Simulation"},children:e?.simulation!==!1?(0,t.jsx)(u,{variant:"default",values:{intervalMs:a(e?.simulation.intervalMs),requestsPerTick:a(e?.simulation.requestsPerTick)}}):(0,t.jsx)("p",{children:"No simulation configured"})}),(0,t.jsx)(d,{header:{title:"Publisher"},children:(0,t.jsx)(u,{variant:"default",values:{intervalMs:a(e?.publisher.intervalMs),slowLatencyThresholdMs:a(e?.publisher.slowLatencyThresholdMs),eventLoopLagThresholdMs:a(e?.publisher.eventLoopLagThresholdMs),eventLoopResolutionMs:a(e?.publisher.eventLoopResolutionMs)}})})]})}e.s(["default",0,function(){let{activeTab:e}=(0,z.useGate)();return(0,t.jsxs)(t.Fragment,{children:["/"===e&&(0,t.jsx)(C,{}),"/analytics"===e&&(0,t.jsx)(A,{}),"/health"===e&&(0,t.jsx)(O,{}),"/configuration"===e&&(0,t.jsx)(I,{})]})}],42775)}]);
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
@font-face{font-family:Geist;font-style:normal;font-weight:100 900;font-display:swap;src:url(../media/fef07dbb0973bf53-s.3p2_lha1f2xer.woff2)format("woff2");unicode-range:U+460-52F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Geist;font-style:normal;font-weight:100 900;font-display:swap;src:url(../media/8a480f0b521d4e75-s.1qq4vpdcun5oj.woff2)format("woff2");unicode-range:U+301,U+400-45F,U+490-491,U+4B0-4B1,U+2116}@font-face{font-family:Geist;font-style:normal;font-weight:100 900;font-display:swap;src:url(../media/53b9e256198e5412-s.390ncx5urfkfu.woff2)format("woff2");unicode-range:U+102-103,U+110-111,U+128-129,U+168-169,U+1A0-1A1,U+1AF-1B0,U+300-301,U+303-304,U+308-309,U+323,U+329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Geist;font-style:normal;font-weight:100 900;font-display:swap;src:url(../media/7178b3e590c64307-s.21jp631_3pja2.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Geist;font-style:normal;font-weight:100 900;font-display:swap;src:url(../media/caa3a2e1cccd8315-s.p.0wgildi0cnwt9.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Geist Fallback;src:local(Arial);ascent-override:95.94%;descent-override:28.16%;line-gap-override:0.0%;size-adjust:104.76%}.geist_a71539c9-module__T19VSG__className{font-family:Geist,Geist Fallback;font-style:normal}.geist_a71539c9-module__T19VSG__variable{--font-geist-sans:"Geist", "Geist Fallback"}
|
|
2
|
-
@font-face{font-family:Geist Mono;font-style:normal;font-weight:100 900;font-display:swap;src:url(../media/5ce348bf30bf5439-s.31988l_ccedte.woff2)format("woff2");unicode-range:U+460-52F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Geist Mono;font-style:normal;font-weight:100 900;font-display:swap;src:url(../media/4fa387ec64143e14-s.2tuy5pz7dlieh.woff2)format("woff2");unicode-range:U+301,U+400-45F,U+490-491,U+4B0-4B1,U+2116}@font-face{font-family:Geist Mono;font-style:normal;font-weight:100 900;font-display:swap;src:url(../media/6306c77e7c8268e4-s.2dbetqa9o8jxf.woff2)format("woff2");unicode-range:U+2000-2001,U+2004-2008,U+200A,U+23B8-23BD,U+2500-259F}@font-face{font-family:Geist Mono;font-style:normal;font-weight:100 900;font-display:swap;src:url(../media/7d817b4c03b0c5f1-s.1uyisp29ctx0d.woff2)format("woff2");unicode-range:U+102-103,U+110-111,U+128-129,U+168-169,U+1A0-1A1,U+1AF-1B0,U+300-301,U+303-304,U+308-309,U+323,U+329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Geist Mono;font-style:normal;font-weight:100 900;font-display:swap;src:url(../media/bbc41e54d2fcbd21-s.1rgnod-3esatf.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Geist Mono;font-style:normal;font-weight:100 900;font-display:swap;src:url(../media/797e433ab948586e-s.p.0r6juujl39pe6.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Geist Mono Fallback;src:local(Arial);ascent-override:74.67%;descent-override:21.92%;line-gap-override:0.0%;size-adjust:134.59%}.geist_mono_8d43a2aa-module__8Li5zG__className{font-family:Geist Mono,Geist Mono Fallback;font-style:normal}.geist_mono_8d43a2aa-module__8Li5zG__variable{--font-geist-mono:"Geist Mono", "Geist Mono Fallback"}
|
|
3
|
-
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-space-x-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-duration:initial;--tw-ease:initial}}}@layer theme{:root,:host{--color-gray-100:#f3f4f6;--color-gray-300:#d1d5dc;--color-gray-400:#99a1af;--color-gray-700:#364153;--color-gray-800:#1e2939;--color-gray-900:#101828;--color-gray-950:#030712;--color-black:#000;--color-white:#fff;--spacing:.25rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-wider:.05em;--leading-relaxed:1.625;--radius-sm:.25rem;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--ease-out:cubic-bezier(0, 0, .2, 1);--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-geist-sans);--default-mono-font-family:var(--font-geist-mono);--color-background:var(--variant-2);--color-foreground:var(--variant-6);--color-accent-soft:var(--variant-5)}@supports (color:lab(0% 0 0)){:root,:host{--color-gray-100:lab(96.1596% -.0823438 -1.13575);--color-gray-300:lab(85.1236% -.612259 -3.7138);--color-gray-400:lab(65.9269% -.832707 -8.17473);--color-gray-700:lab(27.1134% -.956401 -12.3224);--color-gray-800:lab(16.1051% -1.18239 -11.7533);--color-gray-900:lab(8.11897% .811279 -12.254);--color-gray-950:lab(1.90334% .278696 -5.48866)}}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-auto{pointer-events:auto}.pointer-events-none{pointer-events:none}.absolute{position:absolute}.relative{position:relative}.inset-0{inset:0}.top-0{top:0}.bottom-0{bottom:0}.bottom-3{bottom:calc(var(--spacing) * 3)}.bottom-full{bottom:100%}.left-0{left:0}.left-2\.5{left:calc(var(--spacing) * 2.5)}.z-\[9999\]{z-index:9999}.col-span-2{grid-column:span 2/span 2}.col-span-3{grid-column:span 3/span 3}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.m-2{margin:calc(var(--spacing) * 2)}.m-4{margin:calc(var(--spacing) * 4)}.mx-1{margin-inline:var(--spacing)}.mx-2{margin-inline:calc(var(--spacing) * 2)}.mx-auto{margin-inline:auto}.my-2{margin-block:calc(var(--spacing) * 2)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-6{margin-top:calc(var(--spacing) * 6)}.-mb-1{margin-bottom:calc(var(--spacing) * -1)}.mb-1{margin-bottom:var(--spacing)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.ml-2{margin-left:calc(var(--spacing) * 2)}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.h-2{height:calc(var(--spacing) * 2)}.h-2\.5{height:calc(var(--spacing) * 2.5)}.h-3{height:calc(var(--spacing) * 3)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-6{height:calc(var(--spacing) * 6)}.h-7{height:calc(var(--spacing) * 7)}.h-10{height:calc(var(--spacing) * 10)}.h-16{height:calc(var(--spacing) * 16)}.h-20{height:calc(var(--spacing) * 20)}.h-48{height:calc(var(--spacing) * 48)}.h-80{height:calc(var(--spacing) * 80)}.h-\[10px\]{height:10px}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.max-h-\[12px\]{max-height:12px}.max-h-\[300px\]{max-height:300px}.min-h-screen{min-height:100vh}.w-0{width:0}.w-2{width:calc(var(--spacing) * 2)}.w-2\.5{width:calc(var(--spacing) * 2.5)}.w-3{width:calc(var(--spacing) * 3)}.w-4{width:calc(var(--spacing) * 4)}.w-5{width:calc(var(--spacing) * 5)}.w-6{width:calc(var(--spacing) * 6)}.w-7{width:calc(var(--spacing) * 7)}.w-11{width:calc(var(--spacing) * 11)}.w-20{width:calc(var(--spacing) * 20)}.w-32{width:calc(var(--spacing) * 32)}.w-\[1px\]{width:1px}.w-full{width:100%}.w-px{width:1px}.w-screen{width:100vw}.max-w-\[220px\]{max-width:220px}.min-w-full{min-width:100%}.flex-1{flex:1}.shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.-translate-x-1\/2{--tw-translate-x:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.rotate-45{rotate:45deg}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-pointer{cursor:pointer}.resize{resize:both}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-end{align-items:flex-end}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-4{gap:calc(var(--spacing) * 4)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 2) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-x-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-border>:not(:last-child)){border-color:var(--variant-3)}:where(.divide-gray-800>:not(:last-child)){border-color:var(--color-gray-800)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-visible{overflow:visible}.overflow-x-auto{overflow-x:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-sm{border-radius:var(--radius-sm)}.rounded-xl{border-radius:var(--radius-xl)}.rounded-r-sm{border-top-right-radius:var(--radius-sm);border-bottom-right-radius:var(--radius-sm)}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b,.border-b-1{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-l-3{border-left-style:var(--tw-border-style);border-left-width:3px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-\[var\(--chart-1\)\]{border-color:var(--chart-1)}.border-\[var\(--chart-3\)\]{border-color:var(--chart-3)}.border-\[var\(--chart-4\)\]{border-color:var(--chart-4)}.border-\[var\(--chart-5\)\]{border-color:var(--chart-5)}.border-\[var\(--chart-6\)\]{border-color:var(--chart-6)}.border-\[var\(--variant-3\)\]{border-color:var(--variant-3)}.border-accent{border-color:var(--variant-4)}.border-accent-soft{border-color:var(--variant-5)}.border-border{border-color:var(--variant-3)}.border-foreground{border-color:var(--variant-6)}.border-gray-100{border-color:var(--color-gray-100)}.border-gray-800{border-color:var(--color-gray-800)}.border-t-accent{border-top-color:var(--variant-4)}.bg-\[\#3B82F6\]{background-color:#3b82f6}.bg-\[\#10B981\]{background-color:#10b981}.bg-\[\#EF4444\]{background-color:#ef4444}.bg-\[var\(--variant-1\)\]{background-color:var(--variant-1)}.bg-\[var\(--variant-3\)\]{background-color:var(--variant-3)}.bg-accent{background-color:var(--variant-4)}.bg-background{background-color:var(--variant-2)}.bg-black{background-color:var(--color-black)}.bg-border{background-color:var(--variant-3)}.bg-card{background-color:var(--variant-1)}.bg-gray-300{background-color:var(--color-gray-300)}.bg-gray-900{background-color:var(--color-gray-900)}.bg-gray-950{background-color:var(--color-gray-950)}.stroke-2{stroke-width:2px}.p-1{padding:var(--spacing)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-6{padding:calc(var(--spacing) * 6)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-1{padding-block:var(--spacing)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pr-3{padding-right:calc(var(--spacing) * 3)}.pb-8{padding-bottom:calc(var(--spacing) * 8)}.text-center{text-align:center}.text-end{text-align:end}.text-left{text-align:left}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.break-words{overflow-wrap:break-word}.whitespace-nowrap{white-space:nowrap}.text-\[\#9CA3AF\]{color:#9ca3af}.text-\[var\(--variant-6\)\]{color:var(--variant-6)}.text-accent{color:var(--variant-4)}.text-accent-soft{color:var(--variant-5)}.text-foreground{color:var(--variant-6)}.text-gray-400{color:var(--color-gray-400)}.text-gray-800{color:var(--color-gray-800)}.text-white{color:var(--color-white)}.uppercase{text-transform:uppercase}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.opacity-60{opacity:.6}.shadow-inner{--tw-shadow:inset 0 2px 4px 0 var(--tw-shadow-color,#0000000d);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-150{--tw-duration:.15s;transition-duration:.15s}.duration-300{--tw-duration:.3s;transition-duration:.3s}.duration-500{--tw-duration:.5s;transition-duration:.5s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.select-none{-webkit-user-select:none;user-select:none}@media (hover:hover){.hover\:bg-accent\/10:hover{background-color:var(--variant-4)}@supports (color:color-mix(in lab, red, red)){.hover\:bg-accent\/10:hover{background-color:color-mix(in oklab, var(--variant-4) 10%, transparent)}}.hover\:stroke-white\/50:hover{stroke:#ffffff80}@supports (color:color-mix(in lab, red, red)){.hover\:stroke-white\/50:hover{stroke:color-mix(in oklab, var(--color-white) 50%, transparent)}}.hover\:stroke-2:hover{stroke-width:2px}.hover\:opacity-80:hover{opacity:.8}}.focus\:border-accent:focus{border-color:var(--variant-4)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.disabled\:opacity-50:disabled{opacity:.5}@media (min-width:40rem){.sm\:col-span-2{grid-column:span 2/span 2}.sm\:mx-2{margin-inline:calc(var(--spacing) * 2)}.sm\:h-5{height:calc(var(--spacing) * 5)}.sm\:h-6{height:calc(var(--spacing) * 6)}.sm\:w-5{width:calc(var(--spacing) * 5)}.sm\:w-6{width:calc(var(--spacing) * 6)}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:gap-3{gap:calc(var(--spacing) * 3)}.sm\:px-4{padding-inline:calc(var(--spacing) * 4)}.sm\:text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.sm\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}}@media (min-width:48rem){.md\:col-span-2{grid-column:span 2/span 2}.md\:col-span-3{grid-column:span 3/span 3}.md\:mt-0{margin-top:0}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}}@media (min-width:64rem){.lg\:block{display:block}.lg\:flex{display:flex}.lg\:w-40{width:calc(var(--spacing) * 40)}.lg\:flex-1{flex:1}.lg\:flex-row{flex-direction:row}}@media (prefers-color-scheme:dark){.dark\:bg-gray-700{background-color:var(--color-gray-700)}.dark\:text-white{color:var(--color-white)}}}:root{--chart-1:#059669;--chart-2:#4f46e5;--chart-3:#10b981;--chart-4:#d97706;--chart-5:#dc2626;--chart-6:#0d9488;--variant-6:#18221f;--variant-5:#52635d;--variant-4:#059669;--variant-3:#d1ded9;--variant-2:#f2f5f4;--variant-1:#fff}.dark{--chart-1:#34d399;--chart-2:#818cf8;--chart-3:#6ee7b7;--chart-4:#fbbf24;--chart-5:#f87171;--chart-6:#2dd4bf;--variant-6:#e2e8f0;--variant-5:#94a3b8;--variant-4:#34d399;--variant-3:#1e293b;--variant-2:#0b1311;--variant-1:#121e1b}body{background:radial-gradient(circle at top, var(--variant-3), transparent 70%), linear-gradient(180deg, var(--color-background) 0%, var(--color-background) 100%);color:var(--color-foreground);font-family:var(--font-geist-sans), Arial, Helvetica, sans-serif;min-height:100vh}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-space-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}
|